Compare commits

...

6 Commits

Author SHA1 Message Date
5debac3aa8 Claude UI team working now 2026-03-16 17:06:11 -04:00
41975b806c docs: checkpoint session — phase 4-6 research + sidebar UX findings
Research complete for remaining token consolidation (icon sizing,
typography scale, responsive breakpoints) and sidebar UX issues
(divider inconsistency across themes, price letter-spacing).
Design phase not started — continuation prompt ready.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 17:05:16 -04:00
121dd63169 docs: update SESSION-HANDOFF.md — token consolidation + cleanup complete
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 16:53:42 -04:00
a42ce662b8 docs: remove 11 outdated stage prompts and superseded docs
Deleted 8 historical STAGE*-SESSION-PROMPT.md files (all stages complete),
plus ai-session-brief.md, code-verification.md, and phase-roadmap.md
(superseded by CLAUDE.md and 254-test suite). Updated QUICK-REF.md
directory listing to reflect current docs structure.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 16:52:38 -04:00
786a8505ac css: tokenize borders, spacing, and radii — spacing audit pass 4
Phase 1: border widths (1/2/3px → --border-thin/medium/thick) — 76 replacements
Phase 2: adopt existing spacing tokens (--space-xs through --space-xl) — 18 replacements
Phase 3: border-radius scale (--radius-sm/md/lg/pill) — 16 replacements
Total: 110 replacements + 7 new tokens across 7 files

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 16:51:43 -04:00
e462953a2a css: tokenize admin fee margins + remove redundant inline style — spacing audit pass 3
Replaced 2 hardcoded 6px margin-bottom values with var(--space-xs) in admin-fee-header
and admin-waive-savings. Removed redundant inline style="margin-bottom:16px" from sec-01
subtitle (handled by section-content * + * rule). Full visual audit across Dark/Light/Glass
at 1920px, print media, and mobile (375px/600px/780px landscape) — no regressions found.
Updated SESSION-HANDOFF, CLAUDE.md, DECISION-LOG, KNOWN-ISSUES, and MASTER-SESSION-PROMPT.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 15:28:49 -04:00
29 changed files with 1172 additions and 1634 deletions

View File

@@ -0,0 +1,19 @@
{
"permissions": {
"allow": [
"Bash(ls -lh *.css)",
"Bash(node tests/*)",
"Bash(python3 .claude/skills/*)",
"Bash(git status*)",
"Bash(git add*)",
"Bash(git commit*)",
"Bash(git log*)",
"Bash(git diff*)",
"Bash(git branch*)",
"Bash(git checkout*)",
"Read(//c/Users/JohnOReilly/OneDrive - Silicon Valley Services/Documents/Projects VS CODE/SVS MSP Calculator/**)",
"mcp__plugin_playwright_playwright__browser_evaluate",
"mcp__plugin_playwright_playwright__browser_run_code"
]
}
}

View File

@@ -0,0 +1 @@
[ 245ms] [ERROR] Failed to load resource: the server responded with a status of 404 (File not found) @ http://localhost:8765/favicon.ico:0

248
CLAUDE.md Normal file
View File

@@ -0,0 +1,248 @@
# CLAUDE.md — SVS MSP Calculator
> Master instruction set for Claude Code. This is the SINGLE file to read on session start.
> It references deeper docs — do not duplicate them here.
## Project Identity
**App:** SVS MSP Calculator — a live quote/pricing calculator for SVS Managed Services.
**Used by:** Sales team, live on screen with prospects during discovery calls.
**Tech:** Vanilla HTML5 / CSS3 / JS (ES5-compatible). No frameworks, no npm, no build tools. Open `SVS-MSP-Calculator.html` in a browser — it runs.
**Status:** Alpha-ready, pushing to Beta.
**Tests:** 254 passing (`node tests/test-quote-engine.js`)
**Themes:** 3 — Dark (flagship), Light, Glass
For full architecture, file maps, DOM IDs, and pricing constants see `docs/QUICK-REF.md`.
For business logic and pricing rules see `docs/quote-rules.md`.
For the complete architecture brief see `docs/MASTER-SESSION-PROMPT.md`.
---
## Session Start Protocol
On every new conversation, execute in order:
1. **Read context** (parallel):
- `docs/SESSION-HANDOFF.md` — what happened last, what is next
- `docs/QUICK-REF.md` — file map, DOM IDs, pricing, danger zones
2. **Run baseline tests:** `node tests/test-quote-engine.js` — confirm 254/254 pass
3. **Ask the user** what they want to work on. Do not assume. Do not start changing things.
4. **Read task-specific docs only when needed:**
- `docs/quote-rules.md` — pricing or business logic work
- `docs/regression-checklist.md` — validation work
- `docs/MASTER-SESSION-PROMPT.md` — unfamiliar areas, full architecture
- `docs/DECISION-LOG.md` — check if a relevant decision was already made
- `docs/KNOWN-ISSUES.md` — check if the issue is already tracked
---
## Session End Protocol
Before ending any session:
1. **Run full test suite:** `node tests/test-quote-engine.js` — all 254 must pass
2. **Update `docs/SESSION-HANDOFF.md`** with:
- What was done (brief, specific)
- Files modified (table format)
- Test status (pass count)
- What is next (prioritized)
3. **Update `docs/CHECKPOINT.md`** if structural work was completed
4. **Update `docs/DECISION-LOG.md`** if any decisions were made
5. **Use `superpowers:finishing-a-development-branch`** for commit and cleanup
---
## Orchestration Engine — Superpowers
Use the **superpowers** plugin as the execution backbone for all non-trivial work.
### Workflow Selection
| Situation | Superpowers Skill to Use |
|-----------|--------------------------|
| Planning any feature or multi-step work | `superpowers:writing-plans` |
| Executing a plan with review gates | `superpowers:subagent-driven-development` |
| 2+ independent tasks (e.g., fix CSS in all 3 themes) | `superpowers:dispatching-parallel-agents` |
| Investigating a bug or failure | `superpowers:systematic-debugging` |
| Writing or modifying quote engine logic | `superpowers:test-driven-development` |
| Final checks before marking work done | `superpowers:verification-before-completion` |
| Committing, branch cleanup, merge prep | `superpowers:finishing-a-development-branch` |
| Visual brainstorming for design or UX | `superpowers:brainstorming` |
### Subagent-Driven Development Flow
For any plan with 2+ tasks, use `superpowers:subagent-driven-development`:
```
Per task:
1. Dispatch implementer subagent (with role-specific model — see below)
2. Spec compliance review (did it match requirements?)
3. Code quality review (is it well-built?)
4. Mark task complete
After all tasks:
Final code review → superpowers:finishing-a-development-branch
```
---
## Agent Roles & Model Routing
When dispatching subagents (via superpowers or directly), route to the right role and model.
### Frontend Coder — Opus (default)
**When:** JS logic, CSS changes, HTML structure, bug fixes, refactoring.
**How:** Surgical precision. Read the file first, make the minimal change.
**Post-change:** Run the full validation pipeline.
### UI/UX Designer — Opus + ui-ux-pro-max Skill
**When:** Design decisions — palettes, typography, spacing, layout, visual hierarchy, component styling.
**How:** Invoke the ui-ux-pro-max skill BEFORE implementation. Feed design system output to the implementer subagent.
**Sub-skills:** banner-design, brand, design-system, design, slides, ui-styling
**Constraint:** Translate all output to vanilla CSS — this project has no framework.
### Copywriter — Sonnet Model
**When:** ALL user-facing text — button labels, section descriptions, nudge messages, tooltip copy, sidebar labels, sales language, feature descriptions, comparison text, pitch bar copy.
**How:** Dispatch Agent with `model: "sonnet"`. Provide context about:
- The sales use case (live on calls with prospects)
- The specific UI element being written for
- The current text (if revising)
**Tone:** Confident, concise, client-facing. Not marketing fluff. This is a tool used live.
**Rule:** Copy is UX. Every label guides behavior. Every nudge drives a decision.
### Calculation Validator — Opus
**When:** After ANY change to `quote-engine.js`, `quote-pricing.js`, `package-prices-data.js`, or sidebar render values.
**How:**
1. Run `node tests/test-quote-engine.js` — all 254 must pass
2. Manually verify 2+ quote configs against the verification matrix in `docs/MASTER-SESSION-PROMPT.md` (Priority 4)
3. Cross-check sidebar display values against engine output
4. Verify admin fee floor/threshold logic at edge cases (0 users, 1 endpoint)
### QA / Regression Tester — Opus
**When:** After any visual, structural, or behavioral change.
**How:** Run the validation pipeline below. Use Playwright MCP for visual verification.
**Reference:** `docs/regression-checklist.md` for full manual QA procedures.
---
## Validation Pipeline
After every change, validate in order. Stop and fix at the first failure.
```
1. TESTS node tests/test-quote-engine.js (254/254 must pass)
2. SYNTAX No console errors on fresh browser load
3. THEMES Playwright: verify Dark, Light, Glass all render correctly
4. MOBILE Playwright: verify at 375px — floating MRR pill, bottom sheet, sync
5. PRINT If CSS touched: verify print output is unaffected
6. PERSISTENCE If state/form touched: save → reload → verify all values restore
7. EXPORT If export touched: JSON export valid, version field present
```
---
## Hard Constraints
These are inviolable. Every change must preserve them.
| # | Constraint |
|---|-----------|
| 1 | **DOM IDs are a contract.** `mobile-sync.js` maps 100+ ID pairs (desktop ↔ `_m` suffix). Renaming breaks sync silently. |
| 2 | **Quote math is sacred.** Any `quote-engine.js` or `quote-pricing.js` change requires test validation. |
| 3 | **localStorage round-trip must work.** Key: `svs-msp-quote-v1`. Verify after form/state changes. |
| 4 | **All 3 themes must work.** Dark (flagship), Light, Glass. Token/component changes cascade to all. |
| 5 | **Mobile parity maintained.** Sidebar clone in mobile panel must stay in sync. Usable at 375px. |
| 6 | **Print/PDF tested after CSS changes.** Print CSS is sensitive to component class changes. |
| 7 | **No framework or build-tool migration.** Vanilla JS by design. |
| 8 | **No broad rewrites.** Surgical, approved changes only. |
| 9 | **Read before editing.** Always inspect current code before making changes. |
| 10 | **Ask before assuming.** When requirements are ambiguous, ask the user. |
---
## Regression Hotspots
| Area | Risk | Why |
|------|------|-----|
| `quote-engine.js` math | Critical | Wrong quotes in real sales calls |
| localStorage round-trip | High | Silent failures lose configured quotes |
| Mobile sync ID map | High | 100+ pairs desync silently if IDs change |
| Print/PDF CSS | Medium | Separate cascade, sensitive to class changes |
| Theme switching | Medium | All 3 themes affected by token changes |
| `update()` call chain | Medium | Side effects cascade: calc → render → sidebar → nudges → summaries → save |
---
## Installed Plugins & Skills
### Plugins (use when applicable)
| Plugin | When to Use |
|--------|-------------|
| `superpowers` | Orchestration: planning, agents, reviews, TDD, debugging, branch mgmt |
| `frontend-design` | Frontend design patterns and implementation |
| `code-review` | Structured code review |
| `code-simplifier` | Simplification and cleanup passes |
| `playwright` | Browser automation, visual verification, screenshot comparison |
| `claude-md-management` | Maintaining this CLAUDE.md file |
| `skill-creator` | Creating new custom skills for this project |
### Skills (ui-ux-pro-max)
| Skill | When to Use |
|-------|-------------|
| `ui-ux-pro-max` | Main design intelligence — styles, colors, typography, UX rules |
| `design-system` | Token architecture, component specs |
| `brand` | Voice, visual identity, messaging consistency |
| `ui-styling` | Component styling (translate to vanilla CSS) |
| `design` | Logo, icons, visual assets |
| `banner-design` | Marketing banners and heroes |
| `slides` | HTML presentations |
**Search command** (requires Python 3):
```bash
python3 .claude/skills/ui-ux-pro-max/src/ui-ux-pro-max/scripts/search.py "<query>" --domain <domain>
```
Domains: `style`, `color`, `typography`, `product`, `landing`, `chart`, `ux`
---
## Commit Protocol
- One concern per commit. Do not bundle unrelated changes.
- Message format: `<area>: <what> — <why>`
- Examples: `css: fix glass theme sidebar blur — backdrop-filter not applying at 900px`
- Examples: `engine: correct admin fee at zero users — floor logic was bypassed`
- Do not commit temp files or `.bak-focusmode` files.
- Run validation pipeline before committing.
- Use `superpowers:finishing-a-development-branch` for final commit + cleanup.
---
## Design Principles
1. **Sales clarity over visual novelty.** Prospects must read numbers at a glance.
2. **Trust through polish.** Misaligned inputs erode prospect confidence.
3. **Progressive disclosure.** Lead with MRR total. Detail unfolds on demand.
4. **Feedback immediacy.** Every input change updates sidebar within 1 frame.
5. **Dark theme is the flagship.** Light and Glass must meet the same bar.
6. **The sidebar is the hero.** Design it like a financial summary, not a DOM dump.
7. **Copy is UX.** Labels, nudges, and button text are design decisions.
8. **Mobile is first-class.** A sales rep on a tablet must run a full quote.
---
## File Reference
See `docs/QUICK-REF.md` for the complete file map, DOM IDs, and pricing constants.
| Category | Key Files |
|----------|-----------|
| Orchestration | `SVS-MSP-Calculator.js`, `SVS-MSP-Calculator.html` |
| Quote Engine | `quote-engine.js`, `quote-pricing.js`, `package-prices-data.js` |
| Rendering | `quote-render.js`, `mobile-sync.js`, `theme-manager.js` |
| Persistence | `quote-persistence.js`, `quote-export.js`, `quote-import.js` |
| CSS Tokens | `SVS-MSP-Calculator-tokens.css` (source of truth for design tokens) |
| CSS Themes | `*-light.css`, `*-glass.css` |
| CSS Layout | `*-layout.css`, `*-components.css`, `*-responsive.css`, `*-print.css` |
| Tests | `tests/test-quote-engine.js` (254 tests, zero deps) |
| Docs | `docs/QUICK-REF.md`, `docs/SESSION-HANDOFF.md`, `docs/MASTER-SESSION-PROMPT.md`, `docs/quote-rules.md`, `docs/DECISION-LOG.md`, `docs/KNOWN-ISSUES.md` |

View File

@@ -19,7 +19,7 @@
top: 0; top: 0;
z-index: 100; z-index: 100;
background: var(--top-bar-bg); background: var(--top-bar-bg);
border-bottom: 2px solid var(--top-bar-border); border-bottom: var(--border-medium) solid var(--top-bar-border);
box-shadow: var(--top-bar-shadow); box-shadow: var(--top-bar-shadow);
padding: 14px 0; padding: 14px 0;
display: flex; display: flex;
@@ -32,7 +32,7 @@
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 16px; gap: var(--space-stack-roomy);
} }
.top-bar-logo { margin-left: clamp(26px, 5.2vw, 78px); flex-shrink: 0; } .top-bar-logo { margin-left: clamp(26px, 5.2vw, 78px); flex-shrink: 0; }
.top-bar-right { .top-bar-right {
@@ -52,8 +52,8 @@
─────────────────────────────────────────────────────────────── */ ─────────────────────────────────────────────────────────────── */
.theme-toggle-btn { .theme-toggle-btn {
background: var(--theme-chip-bg); background: var(--theme-chip-bg);
border: 1px solid var(--theme-chip-border); border: var(--border-thin) solid var(--theme-chip-border);
border-radius: 8px; border-radius: var(--radius-md);
width: 36px; width: 36px;
height: 36px; height: 36px;
cursor: pointer; cursor: pointer;

View File

@@ -14,8 +14,8 @@
container-type: inline-size; container-type: inline-size;
margin-left: var(--section-offset); margin-left: var(--section-offset);
border-radius: var(--radius-card); border-radius: var(--radius-card);
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
border-left: 3px solid transparent; border-left: var(--border-thick) solid transparent;
background: var(--surface-section); background: var(--surface-section);
padding: var(--section-padding-top) var(--section-padding-x) var(--section-padding-bottom); padding: var(--section-padding-top) var(--section-padding-x) var(--section-padding-bottom);
} }
@@ -108,7 +108,7 @@
.group-divider { .group-divider {
order: 3; order: 3;
border: none; border: none;
border-top: 1px solid var(--border); border-top: var(--border-thin) solid var(--border);
margin: var(--space-xs) var(--section-offset) var(--space-xs) var(--section-offset); margin: var(--space-xs) var(--section-offset) var(--space-xs) var(--section-offset);
opacity: 0.5; opacity: 0.5;
} }
@@ -121,20 +121,20 @@
top: -12px; top: -12px;
bottom: -12px; bottom: -12px;
width: 6px; width: 6px;
border-left: 3px solid var(--group-strip); border-left: var(--border-thick) solid var(--group-strip);
pointer-events: none; pointer-events: none;
z-index: 0; z-index: 0;
} }
/* Top cap — wraps above first section */ /* Top cap — wraps above first section */
#sec-02::after { #sec-02::after {
top: -12px; top: -12px;
border-top: 3px solid var(--group-strip); border-top: var(--border-thick) solid var(--group-strip);
border-top-left-radius: 4px; border-top-left-radius: 4px;
} }
/* Bottom cap — wraps below last section */ /* Bottom cap — wraps below last section */
#sec-01::after { #sec-01::after {
bottom: -12px; bottom: -12px;
border-bottom: 3px solid var(--group-strip); border-bottom: var(--border-thick) solid var(--group-strip);
border-bottom-left-radius: 4px; border-bottom-left-radius: 4px;
} }
@@ -223,7 +223,7 @@
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.1em; letter-spacing: 0.1em;
padding: var(--control-pad-y-tight) var(--control-pad-x-tight); padding: var(--control-pad-y-tight) var(--control-pad-x-tight);
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
border-radius: var(--radius-control); border-radius: var(--radius-control);
color: var(--muted); color: var(--muted);
display: inline-block; display: inline-block;
@@ -343,9 +343,9 @@
letter-spacing: 0.08em; letter-spacing: 0.08em;
color: var(--accent); color: var(--accent);
background: var(--surface-summary-badge); background: var(--surface-summary-badge);
border: 2px solid var(--border-summary-badge); border: var(--border-medium) solid var(--border-summary-badge);
border-radius: 8px; border-radius: var(--radius-md);
padding: 8px 16px; padding: var(--space-sm) var(--space-stack-roomy);
white-space: normal; white-space: normal;
line-height: var(--text-compact-line); line-height: var(--text-compact-line);
max-width: min(100%, 26ch); max-width: min(100%, 26ch);
@@ -413,7 +413,7 @@
padding: 0 var(--control-pad-x-tight); padding: 0 var(--control-pad-x-tight);
white-space: nowrap; white-space: nowrap;
line-height: 1; line-height: 1;
border-width: 1px; border-width: var(--border-thin);
box-sizing: border-box; box-sizing: border-box;
max-width: none; max-width: none;
} }
@@ -464,7 +464,7 @@
.pill-toggle { .pill-toggle {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
border-radius: var(--radius-control); border-radius: var(--radius-control);
overflow: hidden; overflow: hidden;
margin-bottom: 0; margin-bottom: 0;
@@ -473,7 +473,7 @@
.pill-toggle label { .pill-toggle label {
padding: var(--space-lg) var(--space-xl); padding: var(--space-lg) var(--space-xl);
cursor: pointer; cursor: pointer;
border-right: 1px solid var(--border); border-right: var(--border-thin) solid var(--border);
transition: background var(--transition-fast); transition: background var(--transition-fast);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -527,7 +527,7 @@
.tier-seg-wrap { .tier-seg-wrap {
display: grid; display: grid;
grid-template-columns: 1fr 1fr 1fr; grid-template-columns: 1fr 1fr 1fr;
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
border-radius: var(--radius-control); border-radius: var(--radius-control);
overflow: hidden; overflow: hidden;
margin-bottom: 0; margin-bottom: 0;
@@ -540,7 +540,7 @@
.tier-seg { .tier-seg {
padding: var(--space-stack-roomy) var(--space-stack-tight); padding: var(--space-stack-roomy) var(--space-stack-tight);
cursor: pointer; cursor: pointer;
border-right: 1px solid var(--border); border-right: var(--border-thin) solid var(--border);
text-align: center; text-align: center;
transition: background var(--transition-fast); transition: background var(--transition-fast);
} }
@@ -576,8 +576,8 @@
gap: var(--space-sm); gap: var(--space-sm);
cursor: pointer; cursor: pointer;
padding: var(--space-stack) var(--space-md); padding: var(--space-stack) var(--space-md);
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
border-radius: 8px; border-radius: var(--radius-md);
margin-top: var(--space-sm); margin-top: var(--space-sm);
background: var(--surface-feature); background: var(--surface-feature);
user-select: none; user-select: none;
@@ -622,7 +622,7 @@
text-transform: uppercase; text-transform: uppercase;
letter-spacing: 0.07em; letter-spacing: 0.07em;
color: var(--muted); color: var(--muted);
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
border-radius: var(--radius-control); border-radius: var(--radius-control);
padding: var(--control-pad-y-tight) var(--control-pad-x-tight); padding: var(--control-pad-y-tight) var(--control-pad-x-tight);
white-space: nowrap; white-space: nowrap;
@@ -682,7 +682,7 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: var(--space-stack-roomy) 0; padding: var(--space-stack-roomy) 0;
border-top: 1px solid var(--border); border-top: var(--border-thin) solid var(--border);
} }
.input-label { font-family: 'Lato', sans-serif; font-weight: 700; font-size: 16px; } .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); } .input-sublabel { font-size: 13px; color: var(--muted); margin-top: 6px; line-height: var(--text-compact-line); }
@@ -693,7 +693,7 @@
} }
.step-btn { .step-btn {
background: var(--surface-step); background: var(--surface-step);
border: 1px solid var(--surface-step-border); border: var(--border-thin) solid var(--surface-step-border);
color: var(--text-step); color: var(--text-step);
font-size: 1.125rem; font-size: 1.125rem;
font-weight: 400; font-weight: 400;
@@ -714,7 +714,7 @@
.step-btn:active { background: var(--surface-step-active); color: var(--btn-primary-fg); border-color: var(--accent); } .step-btn:active { background: var(--surface-step-active); color: var(--btn-primary-fg); border-color: var(--accent); }
.num-input { .num-input {
background: var(--surface-input); background: var(--surface-input);
border: 1px solid var(--surface-step-border); border: var(--border-thin) solid var(--surface-step-border);
border-radius: 0; border-radius: 0;
color: var(--ink); color: var(--ink);
font-family: 'DM Mono', monospace; font-family: 'DM Mono', monospace;
@@ -743,9 +743,9 @@
align-items: flex-start; align-items: flex-start;
gap: var(--space-md); gap: var(--space-md);
padding: var(--space-md) var(--space-stack); padding: var(--space-md) var(--space-stack);
border-radius: 8px; border-radius: var(--radius-md);
cursor: pointer; cursor: pointer;
border: 1px solid transparent; border: var(--border-thin) solid transparent;
transition: background 0.12s, border-color 0.12s; transition: background 0.12s, border-color 0.12s;
} }
.addon-row:hover { .addon-row:hover {
@@ -758,8 +758,8 @@
appearance: none; appearance: none;
width: 18px; width: 18px;
height: 18px; height: 18px;
border: 2px solid var(--border); border: var(--border-medium) solid var(--border);
border-radius: 4px; border-radius: var(--radius-sm);
background: transparent; background: transparent;
flex-shrink: 0; flex-shrink: 0;
margin-top: 3px; margin-top: 3px;
@@ -779,7 +779,7 @@
width: 5px; width: 5px;
height: 10px; height: 10px;
border: solid var(--text-on-accent); border: solid var(--text-on-accent);
border-width: 0 2px 2px 0; border-width: 0 var(--border-medium) var(--border-medium) 0;
transform: rotate(45deg); transform: rotate(45deg);
} }
.addon-row:hover input[type=checkbox] { .addon-row:hover input[type=checkbox] {
@@ -845,7 +845,7 @@
─────────────────────────────────────────────────────────────── */ ─────────────────────────────────────────────────────────────── */
.callout-green { .callout-green {
background: var(--surface-success); background: var(--surface-success);
border: 1px solid var(--surface-success-border); border: var(--border-thin) solid var(--surface-success-border);
border-radius: var(--radius-control); border-radius: var(--radius-control);
padding: var(--space-stack-roomy) var(--space-xl); padding: var(--space-stack-roomy) var(--space-xl);
font-family: 'DM Mono', monospace; font-family: 'DM Mono', monospace;
@@ -859,7 +859,7 @@
} }
.callout-red { .callout-red {
background: var(--surface-danger); background: var(--surface-danger);
border: 1px solid var(--surface-danger-border); border: var(--border-thin) solid var(--surface-danger-border);
border-radius: var(--radius-control); border-radius: var(--radius-control);
padding: var(--space-stack-roomy) var(--space-xl); padding: var(--space-stack-roomy) var(--space-xl);
font-family: 'DM Mono', monospace; font-family: 'DM Mono', monospace;
@@ -898,7 +898,7 @@
.progress-track { .progress-track {
height: 7px; height: 7px;
background: var(--border); background: var(--border);
border-radius: 4px; border-radius: var(--radius-sm);
overflow: hidden; overflow: hidden;
} }
.progress-fill { .progress-fill {
@@ -916,7 +916,7 @@
.fee-table { width: 100%; border-collapse: collapse; margin-top: var(--space-stack-roomy); font-size: 14px; } .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 { 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 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; } .fee-table tr.fee-total td { border-top: var(--border-thin) solid var(--border); padding-top: var(--space-stack); color: var(--ink); font-weight: 600; }
/* ── FEATURE CARDS (Section I — What's Covered) ──────────────── /* ── FEATURE CARDS (Section I — What's Covered) ────────────────
Static content — 8 cards in single-column grid. Static content — 8 cards in single-column grid.
@@ -931,15 +931,15 @@
} }
.feature-card { .feature-card {
background: var(--surface-feature); background: var(--surface-feature);
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
border-radius: 10px; border-radius: var(--radius-lg);
padding: var(--space-stack) var(--space-stack-roomy); 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-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); } .feature-card-desc { font-size: 0.8125rem; color: var(--muted); line-height: var(--text-copy-line); }
.m365-app-strip { .m365-app-strip {
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
border-radius: var(--radius-card); border-radius: var(--radius-card);
padding: var(--space-lg) var(--space-lg) var(--space-stack); padding: var(--space-lg) var(--space-lg) var(--space-stack);
background: var(--surface-feature); background: var(--surface-feature);
@@ -954,10 +954,10 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
gap: 8px; gap: var(--space-sm);
text-align: center; text-align: center;
padding: var(--space-stack) var(--space-sm); padding: var(--space-stack) var(--space-sm);
border-radius: 10px; border-radius: var(--radius-lg);
transition: background var(--transition-fast), transform var(--transition-fast); transition: background var(--transition-fast), transform var(--transition-fast);
} }
.m365-app-item:hover { .m365-app-item:hover {
@@ -989,7 +989,7 @@
.m365-app-note-byol { display: none; } .m365-app-note-byol { display: none; }
.m365-app-strip.byol-disabled .m365-app-item { .m365-app-strip.byol-disabled .m365-app-item {
background: transparent; background: transparent;
border: 1px dashed var(--border); border: var(--border-thin) dashed var(--border);
} }
.m365-app-strip.byol-disabled .m365-app-icon { .m365-app-strip.byol-disabled .m365-app-icon {
filter: grayscale(1) saturate(0.2); filter: grayscale(1) saturate(0.2);
@@ -1022,7 +1022,7 @@
--sidebar-mono-size: 0.75rem; --sidebar-mono-size: 0.75rem;
--sidebar-mrr-size: 2.8125rem; --sidebar-mrr-size: 2.8125rem;
background: var(--surface-sidebar); background: var(--surface-sidebar);
border: 1px solid var(--border-sidebar); border: var(--border-thin) solid var(--border-sidebar);
border-radius: var(--radius-card); border-radius: var(--radius-card);
overflow: hidden; overflow: hidden;
box-shadow: var(--shadow-sidebar); box-shadow: var(--shadow-sidebar);
@@ -1063,8 +1063,8 @@
justify-content: center; justify-content: center;
height: 34px; height: 34px;
padding: 0 var(--space-stack); padding: 0 var(--space-stack);
border: 1px solid var(--border-overlay-btn); border: var(--border-thin) solid var(--border-overlay-btn);
border-radius: 10px; border-radius: var(--radius-lg);
background: var(--surface-overlay-btn); background: var(--surface-overlay-btn);
color: var(--text-sidebar-kicker); color: var(--text-sidebar-kicker);
font-family: 'DM Mono', monospace; font-family: 'DM Mono', monospace;
@@ -1084,8 +1084,8 @@
.sidebar-focus-toggle { .sidebar-focus-toggle {
width: 34px; width: 34px;
height: 34px; height: 34px;
border: 1px solid var(--border-overlay-btn); border: var(--border-thin) solid var(--border-overlay-btn);
border-radius: 10px; border-radius: var(--radius-lg);
background: var(--surface-overlay-btn); background: var(--surface-overlay-btn);
color: var(--text-sidebar-kicker); color: var(--text-sidebar-kicker);
display: inline-flex; display: inline-flex;
@@ -1189,8 +1189,8 @@
color: var(--muted); color: var(--muted);
line-height: var(--sidebar-copy-line); line-height: var(--sidebar-copy-line);
padding: var(--space-stack-tight) var(--space-xs); padding: var(--space-stack-tight) var(--space-xs);
border-bottom: 1px var(--sidebar-line-rule-style) var(--sidebar-rule-color); border-bottom: var(--border-thin) var(--sidebar-line-rule-style) var(--sidebar-rule-color);
border-left: 2px solid transparent; border-left: var(--border-medium) solid transparent;
border-radius: 2px; border-radius: 2px;
transition: background var(--transition-fast, 150ms) ease, transition: background var(--transition-fast, 150ms) ease,
border-color var(--transition-fast, 150ms) ease, border-color var(--transition-fast, 150ms) ease,
@@ -1283,7 +1283,7 @@
.vs-table td:last-child { text-align: right; font-family: 'DM Mono', monospace; white-space: nowrap; } .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:not(.vs-save-row) { opacity: 0.85; }
.vs-table tr.vs-save-row { opacity: 1; } .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:first-child td { padding-top: 2px; padding-bottom: var(--space-stack-roomy); border-bottom: var(--border-thin) solid var(--border); }
.vs-table tr:nth-child(2) td, .vs-table tr:nth-child(2) td,
.vs-table tr:nth-child(4) td { padding-top: var(--space-stack-roomy); } .vs-table tr:nth-child(4) td { padding-top: var(--space-stack-roomy); }
.vs-save-row td { .vs-save-row td {
@@ -1293,8 +1293,8 @@
letter-spacing: 0.07em; letter-spacing: 0.07em;
line-height: 1.35; line-height: 1.35;
} }
.vs-save-row td:first-child { border-radius: 8px 0 0 8px; } .vs-save-row td:first-child { border-radius: var(--radius-md) 0 0 var(--radius-md); }
.vs-save-row td:last-child { border-radius: 0 8px 8px 0; } .vs-save-row td:last-child { border-radius: 0 var(--radius-md) var(--radius-md) 0; }
.vs-label { .vs-label {
font-family: 'DM Mono', monospace; font-family: 'DM Mono', monospace;
font-size: 0.6875rem; font-size: 0.6875rem;
@@ -1357,7 +1357,7 @@
} }
.nudge-nav-btn { .nudge-nav-btn {
background: var(--surface-ghost); background: var(--surface-ghost);
border: 1px solid var(--border-nudge-nav); border: var(--border-thin) solid var(--border-nudge-nav);
cursor: pointer; cursor: pointer;
padding: 0; padding: 0;
width: 34px; width: 34px;
@@ -1400,7 +1400,7 @@
text-transform: uppercase; text-transform: uppercase;
color: var(--muted); color: var(--muted);
background: none; background: none;
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
border-radius: var(--radius-control); border-radius: var(--radius-control);
padding: var(--control-pad-y) var(--control-pad-x); padding: var(--control-pad-y) var(--control-pad-x);
cursor: pointer; cursor: pointer;
@@ -1421,7 +1421,7 @@
align-items: stretch; align-items: stretch;
gap: 0; gap: 0;
background: var(--surface-settings); background: var(--surface-settings);
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
border-radius: var(--radius-card); border-radius: var(--radius-card);
overflow: hidden; overflow: hidden;
} }
@@ -1450,7 +1450,7 @@
} }
.qs-term-wrap { .qs-term-wrap {
margin-bottom: 0; margin-bottom: 0;
border: 1px solid var(--border-term-wrap); border: var(--border-thin) solid var(--border-term-wrap);
border-radius: var(--space-stack); border-radius: var(--space-stack);
background: var(--surface-term-wrap); background: var(--surface-term-wrap);
box-shadow: var(--shadow-term-wrap); box-shadow: var(--shadow-term-wrap);
@@ -1507,7 +1507,7 @@
text-transform: uppercase; text-transform: uppercase;
color: var(--text-best-value); color: var(--text-best-value);
background: var(--surface-best-value); background: var(--surface-best-value);
border: 1px solid var(--border-best-value); border: var(--border-thin) solid var(--border-best-value);
border-radius: var(--radius-control); border-radius: var(--radius-control);
padding: 2px var(--space-sm); padding: 2px var(--space-sm);
vertical-align: middle; vertical-align: middle;
@@ -1545,7 +1545,7 @@
color: var(--green); color: var(--green);
margin-top: 0; margin-top: 0;
padding: var(--space-stack) var(--space-lg); padding: var(--space-stack) var(--space-lg);
border: 1px solid color-mix(in srgb, var(--green) 22%, var(--border)); border: var(--border-thin) solid color-mix(in srgb, var(--green) 22%, var(--border));
border-left: 4px solid var(--green); border-left: 4px solid var(--green);
border-radius: var(--radius-control); border-radius: var(--radius-control);
background: color-mix(in srgb, var(--surface-positive-badge) 60%, transparent); background: color-mix(in srgb, var(--surface-positive-badge) 60%, transparent);
@@ -1611,7 +1611,7 @@
width: 34px; width: 34px;
height: 20px; height: 20px;
background: var(--surface-switch-off); background: var(--surface-switch-off);
border-radius: 10px; border-radius: var(--radius-lg);
position: relative; position: relative;
transition: background 0.2s; transition: background 0.2s;
flex-shrink: 0; flex-shrink: 0;
@@ -1657,8 +1657,8 @@
margin-left: 0; margin-left: 0;
align-self: flex-start; align-self: flex-start;
padding: var(--control-pad-y-tight) var(--control-pad-x-tight); padding: var(--control-pad-y-tight) var(--control-pad-x-tight);
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
border-radius: 999px; border-radius: var(--radius-pill);
background: color-mix(in srgb, var(--surface-input) 85%, transparent); background: color-mix(in srgb, var(--surface-input) 85%, transparent);
} }
.qs-fee-label { .qs-fee-label {
@@ -1672,8 +1672,8 @@
display: flex; display: flex;
align-items: center; align-items: center;
background: var(--surface-input); background: var(--surface-input);
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
border-radius: 10px; border-radius: var(--radius-lg);
overflow: hidden; overflow: hidden;
width: 100%; width: 100%;
} }
@@ -1683,7 +1683,7 @@
font-size: 14px; font-size: 14px;
color: var(--muted); color: var(--muted);
background: var(--surface-input); background: var(--surface-input);
border-right: 1px solid var(--border); border-right: var(--border-thin) solid var(--border);
} }
.qs-fee-input { .qs-fee-input {
background: var(--surface-input); background: var(--surface-input);
@@ -1737,7 +1737,7 @@
grid-template-columns: minmax(0, 1fr) auto; grid-template-columns: minmax(0, 1fr) auto;
column-gap: var(--space-md); column-gap: var(--space-md);
padding: 5px 0 0; padding: 5px 0 0;
border-top: 1px dotted color-mix(in srgb, var(--sidebar-rule-color) 88%, transparent); border-top: var(--border-thin) dotted color-mix(in srgb, var(--sidebar-rule-color) 88%, transparent);
} }
.sl-sub-row + .sl-sub-row { .sl-sub-row + .sl-sub-row {
margin-top: 2px; margin-top: 2px;
@@ -1764,7 +1764,7 @@
margin-bottom: var(--space-stack); margin-bottom: var(--space-stack);
padding: var(--space-xl) var(--space-xl) var(--space-lg); padding: var(--space-xl) var(--space-xl) var(--space-lg);
background: var(--surface-compare); background: var(--surface-compare);
border: 1px solid var(--border-compare); border: var(--border-thin) solid var(--border-compare);
border-radius: var(--radius-card); 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-svs-label { font-size: 0.9375rem; color: var(--text-vs-heading); font-weight: 600; line-height: 1.3; }
@@ -1776,7 +1776,7 @@
color: var(--muted); color: var(--muted);
margin-top: var(--space-stack); margin-top: var(--space-stack);
padding-top: var(--space-md); padding-top: var(--space-md);
border-top: 1px solid var(--border); border-top: var(--border-thin) solid var(--border);
line-height: 1.6; line-height: 1.6;
font-style: italic; font-style: italic;
} }
@@ -1790,12 +1790,12 @@
.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); } .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 WAIVED display */
.admin-fee-header { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-stack-tight); margin-bottom: 6px; } .admin-fee-header { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-stack-tight); margin-bottom: var(--space-xs); }
.admin-fee-waive-toggle { margin-left: auto; } .admin-fee-waive-toggle { margin-left: auto; }
.admin-fee-strike { text-decoration: line-through; color: var(--muted); text-decoration-color: var(--muted); } .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; } .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: var(--border-thin) 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); } .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 { 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: var(--border-thin) 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: var(--space-xs); }
.admin-waive-savings.hidden { display: none; } .admin-waive-savings.hidden { display: none; }
#adminWaivedAmt { font-weight: 700; } #adminWaivedAmt { font-weight: 700; }
@@ -1821,13 +1821,13 @@
margin-top: 0; margin-top: 0;
padding-top: var(--space-stack-tight); padding-top: var(--space-stack-tight);
padding-bottom: var(--space-stack-tight); padding-bottom: var(--space-stack-tight);
border-top: 1px var(--sidebar-line-rule-style) var(--sidebar-rule-color); border-top: var(--border-thin) var(--sidebar-line-rule-style) var(--sidebar-rule-color);
border-bottom: none; border-bottom: none;
} }
.sidebar-line.sidebar-line-total { .sidebar-line.sidebar-line-total {
font-weight: 600; font-weight: 600;
margin-top: var(--space-sm); margin-top: var(--space-sm);
border-top: 2px solid color-mix(in srgb, var(--accent) 30%, transparent); border-top: var(--border-medium) solid color-mix(in srgb, var(--accent) 30%, transparent);
border-bottom: none; border-bottom: none;
padding-top: var(--space-stack-roomy); padding-top: var(--space-stack-roomy);
} }
@@ -1941,7 +1941,7 @@
transition: border-color var(--transition-medium, 0.2s) ease, box-shadow var(--transition-medium, 0.2s) ease; transition: border-color var(--transition-medium, 0.2s) ease, box-shadow var(--transition-medium, 0.2s) ease;
} }
.section.sec-active { .section.sec-active {
border-left: 3px solid color-mix(in srgb, var(--accent) 50%, transparent); border-left: var(--border-thick) solid color-mix(in srgb, var(--accent) 50%, transparent);
} }
.section:hover { .section:hover {
border-color: var(--section-hover-border); border-color: var(--section-hover-border);
@@ -1999,7 +1999,7 @@
.quote-notes-input { .quote-notes-input {
width: 100%; width: 100%;
background: var(--surface-card); background: var(--surface-card);
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
border-radius: var(--radius-control); border-radius: var(--radius-control);
color: var(--ink); color: var(--ink);
font-family: 'Poppins', sans-serif; font-family: 'Poppins', sans-serif;
@@ -2057,7 +2057,7 @@
.btn-export:active { transform: scale(0.97); filter: brightness(0.95); } .btn-export:active { transform: scale(0.97); filter: brightness(0.95); }
.btn-export-secondary { .btn-export-secondary {
background: transparent; background: transparent;
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
color: var(--muted); color: var(--muted);
font-size: 0.71875rem; font-size: 0.71875rem;
padding: var(--space-stack-tight) var(--space-stack); padding: var(--space-stack-tight) var(--space-stack);
@@ -2076,7 +2076,7 @@
gap: var(--space-md); gap: var(--space-md);
margin-top: var(--space-stack-roomy); margin-top: var(--space-stack-roomy);
padding-top: var(--space-stack-roomy); padding-top: var(--space-stack-roomy);
border-top: 1px solid var(--border); border-top: var(--border-thin) solid var(--border);
} }
.savings-input-row label { .savings-input-row label {
font-size: 0.875rem; font-size: 0.875rem;
@@ -2085,7 +2085,7 @@
} }
.savings-input-row input { .savings-input-row input {
background: var(--card); background: var(--card);
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
border-radius: var(--radius-control); border-radius: var(--radius-control);
color: var(--ink); color: var(--ink);
font-family: 'DM Mono', monospace; font-family: 'DM Mono', monospace;
@@ -2098,7 +2098,7 @@
.savings-result { .savings-result {
margin-top: var(--space-md); margin-top: var(--space-md);
background: var(--surface-success); background: var(--surface-success);
border: 1px solid var(--surface-success-border); border: var(--border-thin) solid var(--surface-success-border);
border-radius: var(--radius-control); border-radius: var(--radius-control);
padding: var(--control-pad-y) var(--control-pad-x); padding: var(--control-pad-y) var(--control-pad-x);
font-family: 'DM Mono', monospace; font-family: 'DM Mono', monospace;
@@ -2129,7 +2129,7 @@
margin-left: 0; margin-left: 0;
background: var(--card); background: var(--card);
border: none; border: none;
border-top: 1px solid var(--border); border-top: var(--border-thin) solid var(--border);
border-radius: 0; border-radius: 0;
overflow: hidden; overflow: hidden;
} }
@@ -2139,7 +2139,7 @@
} }
.pitch-item { .pitch-item {
padding: 26px 22px; padding: 26px 22px;
border-right: 1px solid var(--border); border-right: var(--border-thin) solid var(--border);
} }
.pitch-item:last-child { border-right: none; } .pitch-item:last-child { border-right: none; }
.pitch-head { .pitch-head {
@@ -2167,7 +2167,7 @@
.pitch-desc { font-size: var(--text-copy-size); color: var(--muted); line-height: var(--text-copy-line); } .pitch-desc { font-size: var(--text-copy-size); color: var(--muted); line-height: var(--text-copy-line); }
.pitch-footer { .pitch-footer {
background: var(--surface-success); background: var(--surface-success);
border-top: 1px solid var(--surface-success-border); border-top: var(--border-thin) solid var(--surface-success-border);
padding: var(--space-sm) var(--space-3xl); padding: var(--space-sm) var(--space-3xl);
font-family: 'DM Mono', monospace; font-family: 'DM Mono', monospace;
font-size: 11px; font-size: 11px;

View File

@@ -224,7 +224,7 @@ body::before {
} }
.top-bar { .top-bar {
border-bottom-width: 1px !important; border-bottom-width: var(--border-thin) !important;
box-shadow: var(--top-bar-shadow) !important; box-shadow: var(--top-bar-shadow) !important;
backdrop-filter: blur(18px) saturate(135%); backdrop-filter: blur(18px) saturate(135%);
-webkit-backdrop-filter: blur(18px) saturate(135%); -webkit-backdrop-filter: blur(18px) saturate(135%);

View File

@@ -21,12 +21,12 @@
} }
.main-col { display: flex; flex-direction: column; gap: clamp(12px, 1.2vw, 20px); container-type: inline-size; } .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; } .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; } .sidebar-utility { margin-bottom: var(--sidebar-stack-gap); display: flex; flex-direction: column; gap: var(--space-sm); }
.btn-reset-quote, .btn-reset-quote,
.btn-import-quote { .btn-import-quote {
width: 100%; width: 100%;
background: var(--surface-sidebar-utility); background: var(--surface-sidebar-utility);
border: 1px solid var(--surface-sidebar-utility-border); border: var(--border-thin) solid var(--surface-sidebar-utility-border);
border-radius: var(--radius-control); border-radius: var(--radius-control);
min-height: var(--control-min-height); min-height: var(--control-min-height);
padding: var(--control-pad-y) var(--control-pad-x); padding: var(--control-pad-y) var(--control-pad-x);
@@ -75,9 +75,9 @@
width: min(460px, calc(100% - 32px)); width: min(460px, calc(100% - 32px));
margin: 12vh auto 0; margin: 12vh auto 0;
background: var(--surface-modal); background: var(--surface-modal);
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
border-radius: 14px; border-radius: 14px;
padding: 22px 22px 20px; padding: 22px 22px var(--space-xl);
box-shadow: var(--shadow-modal); box-shadow: var(--shadow-modal);
} }
.confirm-modal-eyebrow { .confirm-modal-eyebrow {
@@ -86,25 +86,25 @@
letter-spacing: 0.12em; letter-spacing: 0.12em;
text-transform: uppercase; text-transform: uppercase;
color: var(--amber); color: var(--amber);
margin-bottom: 10px; margin-bottom: var(--space-stack-tight);
} }
.confirm-modal-title { .confirm-modal-title {
font-family: 'Poppins', sans-serif; font-family: 'Poppins', sans-serif;
font-size: 1.5rem; font-size: 1.5rem;
line-height: 1.3; line-height: 1.3;
color: var(--ink); color: var(--ink);
margin-bottom: 10px; margin-bottom: var(--space-stack-tight);
} }
.confirm-modal-copy { .confirm-modal-copy {
font-size: 0.875rem; font-size: 0.875rem;
line-height: 1.7; line-height: 1.7;
color: var(--muted); color: var(--muted);
margin-bottom: 18px; margin-bottom: var(--space-lg);
} }
.confirm-modal-actions { .confirm-modal-actions {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
gap: 10px; gap: var(--space-stack-tight);
} }
.confirm-btn { .confirm-btn {
border-radius: var(--radius-control); border-radius: var(--radius-control);
@@ -121,7 +121,7 @@
.confirm-btn-secondary { .confirm-btn-secondary {
background: transparent; background: transparent;
color: var(--muted); color: var(--muted);
border: 1px solid var(--border); border: var(--border-thin) solid var(--border);
} }
.confirm-btn-secondary:hover { .confirm-btn-secondary:hover {
background: var(--surface-ghost-hover); background: var(--surface-ghost-hover);
@@ -131,7 +131,7 @@
.confirm-btn-danger { .confirm-btn-danger {
background: var(--amber); background: var(--amber);
color: var(--btn-primary-fg); color: var(--btn-primary-fg);
border: 1px solid transparent; border: var(--border-thin) solid transparent;
} }
.confirm-btn-danger:hover { filter: brightness(1.05); } .confirm-btn-danger:hover { filter: brightness(1.05); }
@@ -151,12 +151,12 @@
letter-spacing: 0.12em; letter-spacing: 0.12em;
text-transform: uppercase; text-transform: uppercase;
color: var(--muted); color: var(--muted);
margin-bottom: 12px; margin-bottom: var(--space-md);
} }
.client-input { .client-input {
background: transparent; background: transparent;
border: none; border: none;
border-bottom: 1px solid var(--border); border-bottom: var(--border-thin) solid var(--border);
color: var(--accent); color: var(--accent);
font-family: 'Poppins', sans-serif; font-family: 'Poppins', sans-serif;
font-weight: 600; font-weight: 600;
@@ -164,14 +164,14 @@
width: 100%; width: 100%;
max-width: 560px; max-width: 560px;
outline: none; outline: none;
padding: 4px 0; padding: var(--space-xs) 0;
} }
.client-input::placeholder { color: var(--muted); opacity: 0.6; font-weight: 400; } .client-input::placeholder { color: var(--muted); opacity: 0.6; font-weight: 400; }
.client-rep-row { .client-rep-row {
margin-top: 10px; margin-top: var(--space-stack-tight);
} }
.client-rep-row .client-label { .client-rep-row .client-label {
margin-bottom: 4px; margin-bottom: var(--space-xs);
font-size: 10px; font-size: 10px;
} }
.client-input--rep { .client-input--rep {

View File

@@ -71,8 +71,8 @@
/* ── Top bar: keep accent, reduce height ── */ /* ── Top bar: keep accent, reduce height ── */
.top-bar { .top-bar {
position: static !important; position: static !important;
padding: 10px 20px !important; padding: var(--space-stack-tight) var(--space-xl) !important;
border-bottom: 2px solid var(--print-accent) !important; border-bottom: var(--border-medium) solid var(--print-accent) !important;
background: var(--print-paper) !important; background: var(--print-paper) !important;
} }
.top-bar-inner { padding: 0 !important; } .top-bar-inner { padding: 0 !important; }
@@ -81,13 +81,13 @@
/* ── Section cards: clean borders, no dark bg ── */ /* ── Section cards: clean borders, no dark bg ── */
.section { .section {
background: var(--print-paper) !important; background: var(--print-paper) !important;
border: 1px solid var(--print-border) !important; border: var(--border-thin) solid var(--print-border) !important;
box-shadow: none !important; box-shadow: none !important;
margin-left: 0 !important; margin-left: 0 !important;
page-break-inside: avoid; page-break-inside: avoid;
break-inside: avoid; break-inside: avoid;
padding: 16px 20px !important; padding: var(--space-stack-roomy) var(--space-xl) !important;
margin-bottom: 12px !important; margin-bottom: var(--space-md) !important;
} }
.section-num { color: var(--print-section-num) !important; } .section-num { color: var(--print-section-num) !important; }
.section-title { font-size: 16px !important; } .section-title { font-size: 16px !important; }
@@ -95,9 +95,9 @@
/* ── Sidebar: show inline after sections, styled for print ── */ /* ── Sidebar: show inline after sections, styled for print ── */
.sidebar { .sidebar {
background: var(--print-paper) !important; background: var(--print-paper) !important;
border: 2px solid var(--print-accent) !important; border: var(--border-medium) solid var(--print-accent) !important;
border-radius: 6px !important; border-radius: 6px !important;
margin: 16px 0 !important; margin: var(--space-stack-roomy) 0 !important;
page-break-inside: avoid; page-break-inside: avoid;
break-inside: avoid; break-inside: avoid;
} }
@@ -149,9 +149,9 @@
display: block; display: block;
font-size: 11px; font-size: 11px;
color: var(--print-footer-note); color: var(--print-footer-note);
border-top: 1px solid var(--print-border-strong); border-top: var(--border-thin) solid var(--print-border-strong);
padding-top: 10px; padding-top: var(--space-stack-tight);
margin-top: 20px; margin-top: var(--space-xl);
font-family: 'DM Mono', monospace; font-family: 'DM Mono', monospace;
} }
} }

View File

@@ -21,8 +21,8 @@
.pitch-inner { margin-left: 0; } .pitch-inner { margin-left: 0; }
.pitch-grid { grid-template-columns: repeat(2, 1fr); } .pitch-grid { grid-template-columns: repeat(2, 1fr); }
.pitch-item:nth-child(2) { border-right: none; } .pitch-item:nth-child(2) { border-right: none; }
.pitch-item:nth-child(3) { border-top: 1px solid var(--border); } .pitch-item:nth-child(3) { border-top: var(--border-thin) solid var(--border); }
.pitch-item:nth-child(4) { border-top: 1px solid var(--border); border-right: none; } .pitch-item:nth-child(4) { border-top: var(--border-thin) solid var(--border); border-right: none; }
} }
/* ── PHONE (≤ 600px) ──────────────────────────────────────────── /* ── PHONE (≤ 600px) ────────────────────────────────────────────
@@ -35,7 +35,7 @@
} }
.top-bar-logo { margin-left: 0; } .top-bar-logo { margin-left: 0; }
.section { border-radius: 10px; } .section { border-radius: var(--radius-lg); }
.client-bar { padding: var(--space-xl) 0 var(--space-xl) 0; } .client-bar { padding: var(--space-xl) 0 var(--space-xl) 0; }
.sections-toolbar { margin-left: 0; margin-bottom: var(--space-md); } .sections-toolbar { margin-left: 0; margin-bottom: var(--space-md); }
@@ -48,7 +48,7 @@
.group-label { margin-left: 0; } .group-label { margin-left: 0; }
/* Mobile grouping — accent left border on Managed IT sections */ /* Mobile grouping — accent left border on Managed IT sections */
#sec-02, #sec-03, #sec-01 { #sec-02, #sec-03, #sec-01 {
border-left: 3px solid var(--group-strip); border-left: var(--border-thick) solid var(--group-strip);
} }
.group-divider { margin-left: 0; margin-right: 0; } .group-divider { margin-left: 0; margin-right: 0; }
.mobile-quote-pill { top: 12vh; } .mobile-quote-pill { top: 12vh; }
@@ -57,7 +57,7 @@
.pill-toggle { .pill-toggle {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
.pill-toggle label { border-right: none; border-bottom: 1px solid var(--border); } .pill-toggle label { border-right: none; border-bottom: var(--border-thin) solid var(--border); }
.pill-toggle label:last-child { border-bottom: none; } .pill-toggle label:last-child { border-bottom: none; }
/* Contract terms — vertical stack on phones */ /* Contract terms — vertical stack on phones */
@@ -70,7 +70,7 @@
.qs-term-wrap .tier-seg { .qs-term-wrap .tier-seg {
padding: var(--space-stack) var(--space-stack) 13px; padding: var(--space-stack) var(--space-stack) 13px;
border-right: none; border-right: none;
border-bottom: 1px solid var(--border); border-bottom: var(--border-thin) solid var(--border);
text-align: left; text-align: left;
} }
.qs-term-wrap .tier-seg:last-of-type { .qs-term-wrap .tier-seg:last-of-type {
@@ -164,8 +164,8 @@
.pitch-grid { grid-template-columns: 1fr 1fr; } .pitch-grid { grid-template-columns: 1fr 1fr; }
.pitch-item { padding: var(--space-xl) var(--space-stack-roomy); } .pitch-item { padding: var(--space-xl) var(--space-stack-roomy); }
.pitch-item:nth-child(2) { border-right: none; } .pitch-item:nth-child(2) { border-right: none; }
.pitch-item:nth-child(3) { border-top: 1px solid var(--border); } .pitch-item:nth-child(3) { border-top: var(--border-thin) solid var(--border); }
.pitch-item:nth-child(4) { border-top: 1px solid var(--border); border-right: none; } .pitch-item:nth-child(4) { border-top: var(--border-thin) solid var(--border); border-right: none; }
.pitch-title { font-size: 0.875rem; } .pitch-title { font-size: 0.875rem; }
.pitch-desc { font-size: 0.8125rem; } .pitch-desc { font-size: 0.8125rem; }
.pitch-footer { padding: var(--space-stack) var(--space-stack-roomy); font-size: 0.75rem; } .pitch-footer { padding: var(--space-stack) var(--space-stack-roomy); font-size: 0.75rem; }
@@ -332,7 +332,7 @@
transform: translateY(100%); transform: translateY(100%);
transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1); transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1);
will-change: transform; will-change: transform;
border-top: 1px solid var(--border-mobile-sheet); border-top: var(--border-thin) solid var(--border-mobile-sheet);
padding-bottom: env(safe-area-inset-bottom, 0px); padding-bottom: env(safe-area-inset-bottom, 0px);
} }
.mobile-quote-panel.open .mobile-panel-sheet { .mobile-quote-panel.open .mobile-panel-sheet {
@@ -355,13 +355,13 @@
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: var(--space-stack-roomy) var(--space-xl) var(--space-md); padding: var(--space-stack-roomy) var(--space-xl) var(--space-md);
border-bottom: 1px solid var(--border-mobile-row); border-bottom: var(--border-thin) solid var(--border-mobile-row);
background: var(--surface-mobile-close-row); background: var(--surface-mobile-close-row);
} }
.mobile-panel-actions { .mobile-panel-actions {
display: block; display: block;
padding: 0 var(--space-xl) var(--space-md); padding: 0 var(--space-xl) var(--space-md);
border-bottom: 1px solid var(--border-mobile-row); border-bottom: var(--border-thin) solid var(--border-mobile-row);
background: var(--surface-mobile-actions); background: var(--surface-mobile-actions);
} }
.mobile-panel-actions .btn-export { .mobile-panel-actions .btn-export {

View File

@@ -64,8 +64,15 @@
--space-2xl: 24px; --space-2xl: 24px;
--space-3xl: 28px; --space-3xl: 28px;
--space-4xl: 32px; --space-4xl: 32px;
--border-thin: 1px;
--border-medium: 2px;
--border-thick: 3px;
--radius-sm: 4px;
--radius-control: 6px; --radius-control: 6px;
--radius-md: 8px;
--radius-lg: 10px;
--radius-card: 12px; --radius-card: 12px;
--radius-pill: 999px;
--control-min-height: 46px; --control-min-height: 46px;
--control-pad-y: 10px; --control-pad-y: 10px;
--control-pad-x: 16px; --control-pad-x: 16px;

View File

@@ -209,7 +209,7 @@
<div class="section-body" id="sec-01-body" style="display:none;"> <div class="section-body" id="sec-01-body" style="display:none;">
<div class="section-content"> <div class="section-content">
<div class="section-subtitle" style="margin-bottom:16px;">Ongoing oversight for your environment, vendors, and IT documentation so everything stays coordinated, accountable, and easier to support.</div> <div class="section-subtitle">Ongoing oversight for your environment, vendors, and IT documentation so everything stays coordinated, accountable, and easier to support.</div>
<div class="admin-fee-header"> <div class="admin-fee-header">
<span class="admin-fee-title">Site Admin Fee</span> <span class="admin-fee-title">Site Admin Fee</span>

35
docs/DECISION-LOG.md Normal file
View File

@@ -0,0 +1,35 @@
# Decision Log — SVS MSP Calculator
Settled decisions that should not be re-debated in future sessions.
---
## 2026-03-15 — Retro Theme Removed from Cycle
**Decision:** 70s Retro theme removed from the 3-theme toggle cycle (Dark → Light → Glass).
**Why:** The warm paper/neon palette had low contrast and muddy colors. Full design pass deferred.
**Impact:** `theme-manager.js` cycles 3 themes. `SVS-MSP-Calculator-70retro.css` file retained in repo for potential future revival. All theme references should say "3 themes" not "4 themes."
## 2026-03-14 — INKY Add-on Price $5 → $8
**Decision:** INKY Pro email protection add-on repriced from $5/user/mo to $8/user/mo.
**Why:** Vendor price increase.
**Impact:** `quote-pricing.js` ADDON_INKY default updated. 4 test expectations updated in `test-quote-engine.js`.
## 2026-03-15 — Pricing CSV → JSON Migration
**Decision:** Runtime pricing loaded from `package-prices-data.js` (script tag) instead of CSV.
**Why:** JSON is structured, human-readable, and works on `file://` protocol without fetch/CORS issues.
**Impact:** `package-prices.csv` retained as read-only reference. `package-prices-data.js` is the single source of truth. Edit `value` fields there to change pricing.
## 2026-03-15 — JSON Export Schema v1.1
**Decision:** Export schema bumped to v1.1 adding `repName` and `quoteNotes` fields.
**Why:** Print invoice now includes rep name and quote notes.
**Impact:** Import handles both v1.0 and v1.1 payloads (backward-compatible).
## 2026-03-15 — Spacing Token Consolidation Deferred
**Decision:** 150+ magic-number spacing values will NOT be migrated to tokens in the current phase.
**Why:** Too many touchpoints (10px: 36, 12px: 35, 14px: 36, 16px: 24, 20px: 19). Migration scope too broad for surgical approach.
**Impact:** Deferred to a dedicated spacing-focused stage. Existing `--space-stack-*` tokens used only 4× out of 150+.
## 2026-03-15 — Font Awesome Icons as Inline Data URIs
**Decision:** All 36 FA Sharp Solid SVG references converted to inline `data:image/svg+xml` URIs.
**Why:** CSS `mask-image: url()` is blocked by browser security on `file://` protocol.
**Impact:** Icons work on local file open. No external SVG file dependencies for icon rendering.

24
docs/KNOWN-ISSUES.md Normal file
View File

@@ -0,0 +1,24 @@
# Known Issues & Deferred Work — SVS MSP Calculator
Items tracked here are intentional deferrals, not bugs. Check this list before starting new work.
---
## Deferred Work
| # | Item | Severity | Source | Notes |
|---|------|----------|--------|-------|
| D1 | Spacing token consolidation | Low | Stage 8 audit | 150+ magic-number px values across components.css. Needs dedicated stage. |
| D2 | `fmt()` duplicated in quote-render.js and quote-export.js | Low | Stage 3 audit | Both inside IIFEs — intentional isolation. One-liner formatter. |
| D3 | Retro theme full design pass | Low | Stage 5 | Warm paper/neon palette functional but not polished. CSS file retained. |
| D4 | Retro theme QA at all viewports | Low | Stage 5 | Only mobile verified during overhaul. Desktop/wide not fully tested. |
| D5 | Sections IVVI content activation | Medium | Master prompt | Server, ZT Networking, VoIP — structurally complete, content gated. Engine calculates when inputs present. |
## Known Quirks (Intentional)
| # | Quirk | Why It's OK |
|---|-------|-------------|
| Q1 | Glass theme uses 97 `!important` declarations | Standard glassmorphism override pattern — theme must beat compound parent selectors |
| Q2 | `console.warn()` in pricing/persistence/import | Intentional error reporting for debugging — not dead code |
| Q3 | 70retro.css still in repo but not in theme cycle | Retained for potential future revival per decision log |
| Q4 | Sidebar focus toggle uses white rgba on colored header | Correct in all themes — verified during Phase 2 audit |

View File

@@ -26,7 +26,7 @@ You do not introduce change for its own sake. You improve with purpose, validate
### What "Beta" Means Here ### What "Beta" Means Here
- All active sections (IIII) are visually polished and production-quality - All active sections (IIII) are visually polished and production-quality
- All four themes (Dark, Light, Glass, 70s Retro) are consistent, tested, and professional - All three themes (Dark, Light, Glass) are consistent, tested, and professional
- Mobile experience is smooth, reliable, and parity-complete with desktop - Mobile experience is smooth, reliable, and parity-complete with desktop
- Print/PDF invoice is clean, branded, and pixel-accurate - Print/PDF invoice is clean, branded, and pixel-accurate
- Quote math, persistence, and export are verified and regression-safe - Quote math, persistence, and export are verified and regression-safe
@@ -47,7 +47,7 @@ svsmspcalc/
├── quote-render.js # DOM rendering + nudge engine (662 lines) ├── quote-render.js # DOM rendering + nudge engine (662 lines)
├── quote-persistence.js # localStorage save/restore (212 lines) ├── quote-persistence.js # localStorage save/restore (212 lines)
├── quote-export.js # Print/PDF + JSON export (295 lines) ├── quote-export.js # Print/PDF + JSON export (295 lines)
├── theme-manager.js # Dark/Light/Glass/70s Retro switching (110 lines) ├── theme-manager.js # Dark/Light/Glass switching (110 lines)
├── mobile-sync.js # Mobile panel + 100+ element sync (236 lines) ├── mobile-sync.js # Mobile panel + 100+ element sync (236 lines)
├── SVS-MSP-Calculator.css # Manifest: @imports all CSS files ├── SVS-MSP-Calculator.css # Manifest: @imports all CSS files
├── SVS-MSP-Calculator-tokens.css # Design tokens + CSS vars ├── SVS-MSP-Calculator-tokens.css # Design tokens + CSS vars
@@ -58,7 +58,6 @@ svsmspcalc/
├── SVS-MSP-Calculator-print.css # Print-specific rules ├── SVS-MSP-Calculator-print.css # Print-specific rules
├── SVS-MSP-Calculator-light.css # Light theme overrides ├── SVS-MSP-Calculator-light.css # Light theme overrides
├── SVS-MSP-Calculator-glass.css # Glass theme (glassmorphism) ├── SVS-MSP-Calculator-glass.css # Glass theme (glassmorphism)
├── SVS-MSP-Calculator-70retro.css # 70s Retro theme overrides
├── package-prices.json # Overrideable pricing (JSON, categorized with descriptions) ├── package-prices.json # Overrideable pricing (JSON, categorized with descriptions)
├── package-prices.csv # Legacy pricing reference (no longer loaded at runtime) ├── package-prices.csv # Legacy pricing reference (no longer loaded at runtime)
├── tests/ ├── tests/
@@ -78,7 +77,7 @@ svsmspcalc/
- **No build tools.** Open the HTML in a browser — it runs. - **No build tools.** Open the HTML in a browser — it runs.
- **No npm.** No webpack, Vite, Rollup, Parcel, or transpilation. - **No npm.** No webpack, Vite, Rollup, Parcel, or transpilation.
- **No TypeScript.** Plain `.js` files. - **No TypeScript.** Plain `.js` files.
- **CSS architecture:** Tokenized custom properties. Modular files. Four theme override layers. - **CSS architecture:** Tokenized custom properties. Modular files. Three theme override layers (Dark, Light, Glass).
- **State:** localStorage only. Key: `svs-msp-quote-v1`. - **State:** localStorage only. Key: `svs-msp-quote-v1`.
- **Fonts:** Google Fonts (Cinzel, Poppins, Lato, DM Mono). - **Fonts:** Google Fonts (Cinzel, Poppins, Lato, DM Mono).
- **Icons:** Font Awesome 7 Sharp (local SVGs), M365 icon set (local). - **Icons:** Font Awesome 7 Sharp (local SVGs), M365 icon set (local).
@@ -95,7 +94,7 @@ debouncedSave() → auto-save to localStorage (debounced 400ms)
``` ```
node svsmspcalc/tests/test-quote-engine.js node svsmspcalc/tests/test-quote-engine.js
``` ```
88 tests, zero dependencies. Tests the pure `calculateQuote(state, pricing)` function against known-good expected values using default pricing. Covers: rates, add-ons, admin fee logic, discounts, HST, VoIP, ZT networking, edge cases, and MRR integrity. 254 tests, zero dependencies. Tests the pure `calculateQuote(state, pricing)` function against known-good expected values using default pricing. Covers: rates, add-ons, admin fee logic, discounts, HST, VoIP, ZT networking, edge cases, MRR integrity, export schema, persistence shape, import mapping, and quote output invariants.
Run after any change to `quote-engine.js`, `quote-pricing.js`, or pricing JSON values. Run after any change to `quote-engine.js`, `quote-pricing.js`, or pricing JSON values.
@@ -146,7 +145,7 @@ These are inviolable. Every change must preserve them:
1. **HTML shell is stable.** DOM IDs are a contract. Mobile sync maps 100+ ID pairs (desktop ↔ `_m` suffix). Renaming an ID breaks sync silently and catastrophically. 1. **HTML shell is stable.** DOM IDs are a contract. Mobile sync maps 100+ ID pairs (desktop ↔ `_m` suffix). Renaming an ID breaks sync silently and catastrophically.
2. **Quote math is correct.** Any change to `quote-engine.js` requires before/after validation of all outputs. Do not "clean up" math without proving equivalence. 2. **Quote math is correct.** Any change to `quote-engine.js` requires before/after validation of all outputs. Do not "clean up" math without proving equivalence.
3. **localStorage persistence is intact.** `saveState()` and `restoreState()` must round-trip cleanly. Verify after any form/state changes. 3. **localStorage persistence is intact.** `saveState()` and `restoreState()` must round-trip cleanly. Verify after any form/state changes.
4. **All four themes must work.** Dark (default), Light (soft khaki), Glass (glassmorphism), 70s Retro. Changes to tokens or components cascade to all four. 4. **All three themes must work.** Dark (default), Light (soft khaki), Glass (glassmorphism). Changes to tokens or components cascade to all three.
5. **Mobile parity is maintained.** The sidebar clone in the mobile panel must stay in sync. Mobile UX must be usable on a 375px viewport. 5. **Mobile parity is maintained.** The sidebar clone in the mobile panel must stay in sync. Mobile UX must be usable on a 375px viewport.
6. **Print/PDF export must be tested after CSS changes.** Print CSS lives in a separate file but is sensitive to component class changes. 6. **Print/PDF export must be tested after CSS changes.** Print CSS lives in a separate file but is sensitive to component class changes.
7. **No framework or build-tool migration.** This is vanilla JS by design. 7. **No framework or build-tool migration.** This is vanilla JS by design.
@@ -170,7 +169,7 @@ Work in this order unless directed otherwise. Each priority includes UX, code, a
- [ ] Typography hierarchy — Is Cinzel/Poppins/Lato used consistently? Are font sizes, weights, and line-heights harmonious across sections? - [ ] Typography hierarchy — Is Cinzel/Poppins/Lato used consistently? Are font sizes, weights, and line-heights harmonious across sections?
- [ ] Spacing system — Is padding/margin using tokens consistently? Are section cards visually balanced? - [ ] Spacing system — Is padding/margin using tokens consistently? Are section cards visually balanced?
- [ ] Color usage — Are `--accent`, `--green`, `--amber`, `--muted` used purposefully, not decoratively? - [ ] Color usage — Are `--accent`, `--green`, `--amber`, `--muted` used purposefully, not decoratively?
- [ ] Interactive states — Do all buttons, inputs, toggles, and checkboxes have clear hover/focus/active states in all four themes? - [ ] Interactive states — Do all buttons, inputs, toggles, and checkboxes have clear hover/focus/active states in all three themes?
- [ ] Card hierarchy — Is there a clear visual distinction between level-1 containers, level-2 cards, and level-3 controls? - [ ] Card hierarchy — Is there a clear visual distinction between level-1 containers, level-2 cards, and level-3 controls?
- [ ] Icon consistency — Are Font Awesome icons used at consistent sizes, weights, and optical alignment? - [ ] Icon consistency — Are Font Awesome icons used at consistent sizes, weights, and optical alignment?
- [ ] Section header design — Are the numbered section headers (I, II, III) visually strong and scannable? - [ ] Section header design — Are the numbered section headers (I, II, III) visually strong and scannable?
@@ -348,7 +347,7 @@ Work in this order unless directed otherwise. Each priority includes UX, code, a
### After Making Changes ### After Making Changes
1. Verify syntax (especially JS — no console errors on load) 1. Verify syntax (especially JS — no console errors on load)
2. Check all four themes render correctly 2. Check all three themes render correctly
3. Check mobile panel renders correctly at ≤780px 3. Check mobile panel renders correctly at ≤780px
4. Verify sidebar totals are mathematically correct for a test quote 4. Verify sidebar totals are mathematically correct for a test quote
5. If CSS touched: verify print output is unaffected 5. If CSS touched: verify print output is unaffected
@@ -398,7 +397,7 @@ For manual QA: `docs/regression-checklist.md`
The build is ready to call "beta" when: The build is ready to call "beta" when:
- [ ] All Sections IIII are visually polished and functionally complete - [ ] All Sections IIII are visually polished and functionally complete
- [ ] All four themes pass a full visual review at all major breakpoints - [ ] All three themes pass a full visual review at all major breakpoints
- [ ] Print/PDF invoice is clean and professionally branded - [ ] Print/PDF invoice is clean and professionally branded
- [ ] Mobile panel is fully synced and usable at 375px - [ ] Mobile panel is fully synced and usable at 375px
- [ ] Quote math passes all combinations in the verification matrix - [ ] Quote math passes all combinations in the verification matrix

View File

@@ -151,7 +151,9 @@ svsmspcalc/
├── SESSION-HANDOFF.md # Current status + next steps ├── SESSION-HANDOFF.md # Current status + next steps
├── CHECKPOINT.md # Historical checkpoint ├── CHECKPOINT.md # Historical checkpoint
├── MASTER-SESSION-PROMPT.md # Master rebuild prompt ├── MASTER-SESSION-PROMPT.md # Master rebuild prompt
├── STAGE3-11 prompts # Historical stage prompts ├── DECISION-LOG.md # Decision history
├── KNOWN-ISSUES.md # Tracked bugs
├── quote-rules.md # Business logic rules ├── quote-rules.md # Business logic rules
── regression-checklist.md # QA checklist ── regression-checklist.md # QA checklist
└── README.md # Project overview
``` ```

View File

@@ -1,56 +1,149 @@
# Session Handoff — SVS MSP CALC # Session Handoff — SVS MSP CALC
**Last updated:** 2026-03-16 **Last updated:** 2026-03-16
**Session:** Section I & II Visual Polish (Pass 2) **Session:** Spacing Token Consolidation + Docs Cleanup + Phase 4-6 Research
**Status:** COMPLETE — all 3 issues from Pass 1 resolved **Status:** COMMITTEDtoken consolidation + docs cleanup merged. Phase 4-6 research complete, design not started.
**Tests:** 254/254 passing
## What Was Done This Session ## What Was Done This Session
### Section Polish Pass 2 — Premium Feel ### Spacing Token Consolidation (Phases 1-3) — COMMITTED
Addressed all 3 issues flagged after Pass 1, verified across Dark/Light/Glass at 1920px + 600px. Commit `786a850`: `css: tokenize borders, spacing, and radii — spacing audit pass 4`
- Phase 1: border widths (76 replacements), Phase 2: spacing tokens (18), Phase 3: border-radius (16)
- 110 replacements + 7 new tokens across 7 CSS files
- Playwright verified: 9 screenshots (3 themes x 3 viewports) — zero regressions
1. **Vertical spacing rhythm** — bumped `.section-content > * + *` base gap from `--space-stack` (14px) to `--space-stack-roomy` (16px) for more breathing room between elements ### Docs Cleanup — COMMITTED
2. **M365 app strip cleanup** — removed all per-item borders and backgrounds; items now float cleanly within the container. Removed icon drop-shadows. Removed accent-tinted container background/border (now uses neutral `--surface-feature` + `--border`). Removed border-top separator from note area. Commit `a42ce66`: removed 11 outdated files (1,480 lines)
3. **Collapsible header refinement** — switched from accent-tinted background (`--surface-accent-soft`) to neutral `--surface-feature`. Border changed from accent-tinted `color-mix()` to plain `--border`. Hover uses very subtle accent (6% mix) instead of heavy 10%. Result feels native across all 3 themes. - 8 STAGE*-SESSION-PROMPT.md files, ai-session-brief.md, code-verification.md, phase-roadmap.md
- Updated QUICK-REF.md directory listing
### Visual QA ### Phase 4-6 Research — NOT COMMITTED (findings below)
- Playwright screenshots taken at 1920px desktop and 600px mobile Deep exploration of remaining token consolidation + sidebar UX issues. Ready to brainstorm into design.
- Dark, Light, Glass themes all verified — collapsibles and M365 strip look native in each
- Section II (Endpoint Package) confirmed consistent with Section I styling ## Research Findings: Remaining Token Consolidation (Phases 4-6)
### Phase 4: Icon/Button Sizing (~12 locations)
Hardcoded icon/button sizes scattered across components.css:
- 14px (export icon), 15px (sidebar icon), 16px (nudge nav), 18px (addon checkbox)
- 20px (HST toggle), 22px (M365 icon), 28px (mobile pill), 34px (chevron/toggle buttons)
- **Missing tokens:** `--icon-size-xs` through `--icon-size-xl`, `--control-icon-size`
### Phase 5: Typography Scale (~36 locations)
**Letter-spacing (26+ occurrences, no tokens):**
- 0.02em (price values), 0.04em (meta labels), 0.06em (waive labels)
- 0.07em (badges/floor notes), 0.08em (buttons/badges), 0.1em (section titles)
- 0.12em (labels/headings), 0.14em (nav), 0.18em (uppercase kickers), -0.02em (MRR hero)
- **Missing tokens:** `--text-spacing-tight/compact/normal/loose` etc.
**Font-size (hardcoded, not using tokens):**
- 8px (best value badge), 10px (mobile pill label), 12px (various labels)
- 13px (sublabels), 14px (addon name, fee table), 15px (section meta)
- 16px (input label), 18px (sidebar hero responsive), 20px (modal icon)
**Sidebar typography tokens defined in components.css (should be in tokens.css):**
- `--sidebar-copy-size`, `--sidebar-copy-line`, `--sidebar-note-size`, `--sidebar-mono-size`, `--sidebar-mrr-size` (components.css lines 1019-1023)
### Phase 6: Responsive Breakpoints (optional)
Hardcoded responsive font-sizes in @media rules:
- 1.375rem, 1.125rem, 1.1rem (mobile text), 2.25rem/1.875rem (sidebar MRR at breakpoints)
- Could benefit from `--text-size-responsive-*` tokens
## Research Findings: Sidebar UX Issues
### Divider/Line Inconsistency Across Themes
Sidebar divider tokens exist but values differ per theme:
- **Dark:** `color-mix(in srgb, var(--border) 88%, transparent)` — dashed
- **Light:** `color-mix(in srgb, var(--border) 70%, transparent)` — lighter
- **Glass:** `rgba(143, 183, 221, 0.12)` — very light
- **Risk:** Extra/mismatched horizontal lines visible on some themes. Needs Playwright visual audit.
### Price Value Letter-Spacing
- Current: `letter-spacing: 0.02em` on `.sidebar-line .val` (components.css ~line 1211)
- User feedback: prices ($x.xx) look "bunched" — needs more breathing room
- `font-variant-numeric: tabular-nums` IS applied globally (good)
- Consider bumping to 0.04em-0.06em and tokenizing as `--text-spacing-money`
### Sidebar Line Variants
| Element | Style | Notes |
|---------|-------|-------|
| `.sidebar-line` | dashed bottom border | Uses `--sidebar-line-rule-style` |
| `.sidebar-line-opportunity` | dotted | No token for line-style |
| `.sidebar-line-discount` | dashed, opacity 0.8 | Hardcoded opacity |
| `.sidebar-line-hst` | double top border | Uses token |
| `.sidebar-line-total` | solid top border | Uses `--sidebar-total-rule` (good) |
## Files Modified (this session) ## Files Modified (this session)
| File | Changes | | File | Change |
|------|---------| |------|--------|
| `SVS-MSP-Calculator-components.css` | Section content gap 14→16px; M365 strip: removed per-item borders/bg/shadow, neutral container; collapsible headers: neutral bg/border, softer hover; strip note: removed border-top | | 7 CSS files | Token consolidation (committed) |
| 11 docs files | Deleted (committed) |
| `docs/QUICK-REF.md` | Updated directory listing (committed) |
| `docs/SESSION-HANDOFF.md` | Updated (committed) |
## Test Status ## Test Status
``` ```
254/254 tests passing 254/254 tests passing
node svsmspcalc/tests/test-quote-engine.js node tests/test-quote-engine.js
``` ```
## What's Next ## What's Next
Potential next steps (user to prioritize): **Priority 1 (next session):** Sidebar UX fine-tune + Phase 4-6 token consolidation
1. **Deeper spacing audit** — check vertical rhythm within expanded collapsible bodies (feature list items, addon rows) if user wants finer tuning - Brainstorm into design (was about to start — visual companion offered, user paused)
2. **Section III (Site Management)** polish pass — same treatment if needed - Use `superpowers:brainstorming` → design spec → `superpowers:writing-plans`
3. **Print/PDF verification** — confirm CSS changes don't affect print layout - Dispatch UI/UX designer (Opus + ui-ux-pro-max) for sidebar line/spacing decisions
4. **Mobile panel** — verify mobile panel rendering matches desktop sidebar - Use Playwright MCP for visual comparison across themes
**Priority 2:** Sections IV-VI activation
**Priority 3:** Mobile panel UX enhancements
**Priority 4:** Beta readiness audit
## Continuation Prompt ## Continuation Prompt
``` ```
Read svsmspcalc/docs/SESSION-HANDOFF.md then svsmspcalc/docs/QUICK-REF.md. Read docs/SESSION-HANDOFF.md then docs/QUICK-REF.md.
3 themes: Dark, Light, Glass. Pre-alpha in svsmspcalc/pre-alpha/ (read-only). 3 themes: Dark, Light, Glass.
254 tests must pass: node svsmspcalc/tests/test-quote-engine.js 254 tests must pass: node tests/test-quote-engine.js
## Resume Point: Sidebar UX Fine-Tune + Token Consolidation (Phases 4-6)
Research is DONE (see SESSION-HANDOFF.md "Research Findings" sections).
Design has NOT started yet. Pick up at brainstorming.
### Two workstreams to combine into one design:
**1. Sidebar UX fine-tune (user-reported issues):**
- Extra horizontal lines in sidebar that don't match across Dark/Light/Glass themes
- Price values ($x.xx) look "bunched" — need more letter-spacing
- Divider opacity/color differs per theme (Dark 88%, Light 70%, Glass 12% rgba)
- Use Playwright MCP to screenshot sidebar across all 3 themes for visual comparison
- Dispatch UI/UX designer (Opus + ui-ux-pro-max skill) for design decisions
**2. Token consolidation Phases 4-6:**
- Phase 4: icon/button sizing (~12 hardcoded values → --icon-size-* tokens)
- Phase 5: typography scale (~36 locations — letter-spacing + font-size + move sidebar tokens to tokens.css)
- Phase 6: responsive breakpoints (optional — hardcoded rem values in @media)
### Action: Start with superpowers:brainstorming
- Project context: ALREADY EXPLORED (skip step 1 of brainstorming checklist)
- Offer visual companion (Playwright) — user was offered, hadn't accepted yet
- Then clarifying questions → approaches → design → spec → plan
## Key References
- Token file: SVS-MSP-Calculator-tokens.css
- Sidebar styles: SVS-MSP-Calculator-components.css (lines 1019-1023 sidebar tokens, 1183-1250 sidebar lines)
- Theme overrides: SVS-MSP-Calculator-light.css (lines 122-123), SVS-MSP-Calculator-glass.css (lines 194-195)
## Plugins Available — USE THEM ## Plugins Available — USE THEM
- **frontend-design** — invoke for design decisions, spacing, color, layout - **superpowers** — planning, subagent-driven development, parallel agents, TDD, debugging
- **playwright** — open calculator in real browser, expand sections, toggle - **ui-ux-pro-max** — design intelligence (palettes, typography, styles)
themes, screenshot before/after every CSS change - **playwright** — visual verification in real browser
- **code-simplifier** — clean up CSS after edits - **frontend-design**, **code-review**, **code-simplifier**
- **superpowers** — brainstorming, planning workflows
## Clear up DOCS folder
- clean up docs, remove old phases no longer under considertation, old instructions that no longer apply
Budget: stay under 60% context. Checkpoint before ending. Budget: stay under 60% context. Checkpoint before ending.
``` ```

View File

@@ -1,89 +0,0 @@
# SVS MSP CALC — STAGE 10: UI & THEME POLISH
# Generated: 2026-03-15
---
## WHERE WE ARE
**Stages 19: COMPLETE.** Beta build is production-quality for Sections IIII.
**Tests:** 254/254 passing.
**Spacing tokens:** Consolidated (150+ magic numbers → CSS custom properties).
**Retro theme visual QA:** Passed at all viewports (375/1100/1400/2500/2800px).
**Pricing:** Single source in `package-prices-data.js`, addon labels sync dynamically.
---
## STAGE 10 FOCUS: UI / THEME / COLOR CONSISTENCY
This stage is about visual polish, color consistency, and interface usability across all 4 themes.
### Known Issues to Investigate
- **Too many similar-but-slightly-off colors** within individual themes — need audit and consolidation
- **Color consistency per theme** — some elements use raw hex where they should use tokens
- **Usability polish** — any controls, labels, or interactions that could feel tighter or more intuitive
- **Cross-theme consistency** — ensure the same UI elements feel equivalent across Dark/Light/Glass/Retro
### Approach
1. **Color Audit** — For each theme, extract all unique colors actually used. Group by purpose (text, surface, border, accent). Flag duplicates that are close but not identical (e.g., two slightly different grays serving the same role).
2. **Token Consolidation** — Where colors are inconsistent, consolidate to existing tokens or define new ones. Goal: fewer unique colors, each with a clear semantic role.
3. **Per-Theme Walkthrough** — User provides screenshots. AI audits for:
- Color contrast issues (text readability)
- Inconsistent element styling across themes
- Visual weight imbalances
- Elements that "don't belong" visually
4. **Surgical Fixes** — Each fix is small, tested, and verified across all 4 themes.
---
## SESSION WORKFLOW
1. **Start with a color audit** — Use Explore agent to scan all theme CSS files for unique color values, group by semantic role
2. **User provides screenshots** of specific areas that feel "off"
3. **AI proposes fixes** with before/after reasoning
4. **Apply fixes surgically** — one logical change at a time
5. **User verifies visually** — confirms or adjusts
6. **Run tests** after any render/engine changes
---
## HARD CONSTRAINTS
1. DOM IDs are a contract — renaming breaks mobile sync (100+ pairs)
2. 254 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
3. All 4 themes must work after every change
4. No frameworks, no npm — vanilla only
5. Surgical changes only — read before editing
6. Sections IVVI are placeholders — do not activate
---
## THEME FILES
| Theme | File | Base |
|-------|------|------|
| Dark (default) | `SVS-MSP-Calculator-tokens.css` | All colors defined here |
| Light | `SVS-MSP-Calculator-light.css` | Overrides dark tokens |
| Glass | `SVS-MSP-Calculator-glass.css` | Glassmorphism overrides |
| Retro | `SVS-MSP-Calculator-70retro.css` | Cyberpunk overrides |
---
## VERIFICATION
```
node svsmspcalc/tests/test-quote-engine.js
```
254 tests, zero dependencies. Run after any pricing/engine/render changes.
---
## CONTEXT MANAGEMENT
After completing work:
- Update `docs/CHECKPOINT.md` with results
- If context is heavy, create `docs/STAGE11-SESSION-PROMPT.md` for the next chat

View File

@@ -1,100 +0,0 @@
# SVS MSP CALC — STAGE 11: BETA PREP
# Generated: 2026-03-15
---
## WHERE WE ARE
**Stages 110: COMPLETE.** Production-quality for Sections IIII across all 4 themes.
**Tests:** 254/254 passing.
### Stage 10 Completed Work
**Color Audit & Consolidation:**
- Full audit of all 4 theme CSS files — every unique color extracted, grouped by role
- Near-duplicates consolidated via aliases (e.g., 4 positive surfaces → 2 semantic tiers)
- On-accent text ladder collapsed from 5 → 3 levels
- Light theme: ~6 near-identical surfaces → 3 tiers, ~10 borders → 3 tiers
- Glass theme: 6 ice-white text values → 1 reference, 4 border opacities → 1 reference
- Retro theme: ~20 hardcoded hex in selectors → 7 `--retro-*` helper tokens
- 17 raw hex/rgba in components.css → new overlay/shadow tokens
- Retro `--sky` given distinct value (`#6366f1` indigo, was identical to `--green`)
- Unused `--text-on-accent-muted` removed
**Sidebar / Live Quote Polish:**
- Zone backgrounds strengthened + semantically tinted (value zone gets green tint)
- Line borders: last-child removed, totals get solid top border, dashed elsewhere
- Hover tint on sidebar lines for read-tracking
- `.export-wrap` background → transparent, border removed (all themes)
- Sidebar changed from sticky to static (scrolls with page)
**Section Grouping:**
- "MANAGED IT SERVICES (Sections I, II, III)" eyebrow label above section I
- Vertical accent bracket with top/bottom caps alongside sections IIII
- HR divider between Site Management (III) and Server Management (IV)
- Mobile: bracket hidden, sections IIII get accent left border instead
- Per-theme `--group-strip` token (blue/grey-teal/cyan/rose)
**Mobile:**
- Quote pill repositioned: `top: 12vh` at ≤600px (percentage-based, viewport-responsive)
---
## WHAT'S READY FOR BETA
- Sections IIII fully functional with live pricing, addons, admin fee logic
- All 4 themes visually polished and color-consistent
- Sidebar live quote with clear zone separation and clean borders
- Mobile panel sync working (100+ ID pairs)
- Print/PDF export functional
- JSON import/export functional
- 254 automated tests passing
## WHAT REMAINS (Sections IVVI)
Sections IV (Server Management), V (Zero Trust Networking), VI (VoIP) are **placeholders only** — basic stepper + collapsed body, no active pricing or addons wired up. Do not activate unless explicitly requested.
---
## HARD CONSTRAINTS
1. DOM IDs are a contract — renaming breaks mobile sync (100+ pairs)
2. 254 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
3. All 4 themes must work after every change
4. No frameworks, no npm — vanilla only
5. Surgical changes only — read before editing
6. Sections IVVI are placeholders — do not activate
---
## KEY FILES MODIFIED IN STAGE 10
| File | What Changed |
|------|-------------|
| `SVS-MSP-Calculator-tokens.css` | Semantic positive/overlay/sidebar/group tokens added; near-dupes aliased |
| `SVS-MSP-Calculator-light.css` | Surface/border consolidated; sidebar zone + group-strip tokens |
| `SVS-MSP-Calculator-glass.css` | Ice-whites + borders deduped; sidebar/group tokens; export-wrap cleaned |
| `SVS-MSP-Calculator-70retro.css` | 7 retro helper tokens; hardcoded hex → var(); distinct --sky; group-strip |
| `SVS-MSP-Calculator-components.css` | 17 raw colors tokenized; sidebar line/zone polish; group label + bracket |
| `SVS-MSP-Calculator-layout.css` | 1 raw #fff tokenized; sidebar position: static |
| `SVS-MSP-Calculator-responsive.css` | Mobile bracket → left border; group-divider; pill top: 12vh |
| `SVS-MSP-Calculator-print.css` | Group label/bracket/divider hidden in print |
| `SVS-MSP-Calculator.html` | Group label div + group divider HR added (decorative, no IDs) |
---
## VERIFICATION
```
node svsmspcalc/tests/test-quote-engine.js
```
254 tests, zero dependencies. Run after any pricing/engine/render changes.
---
## CONTEXT MANAGEMENT
After completing work:
- Update `docs/CHECKPOINT.md` with results
- If context is heavy, create `docs/STAGE12-SESSION-PROMPT.md` for the next chat

View File

@@ -1,176 +0,0 @@
# SVS MSP CALC — STAGE 3 SESSION PROMPT
# Post-Beta Hardening & Manual QA
# Generated: 2026-03-15
---
## WHERE WE ARE
**Beta build: COMPLETE.** Sections IIII are production-quality.
**Accessibility/performance audit: COMPLETE.** Phase 5 fixes applied.
**Tests:** 88/88 passing.
**Sections IVVI:** Intentionally deferred as placeholders — do not activate.
### Completed Stages
- **Stage 1** — Discovery audit (codebase mapping, doc generation)
- **Stage 2** — Beta build (Phases 14: bug fixes, visual polish, UX hardening, docs/QA)
- **Phase 5** — Accessibility/performance audit (aria-expanded, focus traps, stepper labels, glass scroll fix, mobile sync optimization)
---
## START EVERY SESSION BY READING
1. `svsmspcalc/docs/CHECKPOINT.md` — current status, all completed work
2. `svsmspcalc/docs/MASTER-SESSION-PROMPT.md` — full architecture, constraints, priorities
3. `svsmspcalc/docs/QUICK-REF.md` — compact file map, DOM IDs, pricing, danger zones
4. This file — session goals and context
---
## PROJECT SNAPSHOT
**App:** SVS MSP CALC — live quote/pricing calculator for SVS Managed Services
**Type:** Static HTML + Vanilla JS + Modular CSS (no frameworks, no build tools, no npm)
**Used by:** SVS sales team, live on screen during prospect calls
### Architecture
```
svsmspcalc/
├── SVS-MSP-Calculator.html # Stable HTML shell (65KB)
├── SVS-MSP-Calculator.js # Orchestration (350 lines)
├── quote-engine.js # Pure quote math (197 lines)
├── quote-pricing.js # Pricing defaults + CSV override (134 lines)
├── quote-render.js # DOM rendering + nudges (729 lines)
├── quote-persistence.js # localStorage save/restore (225 lines)
├── quote-export.js # Print/PDF + JSON export (299 lines)
├── theme-manager.js # 4-theme switching (121 lines)
├── mobile-sync.js # Mobile panel sync (275 lines)
├── SVS-MSP-Calculator.css # Manifest (@imports all CSS)
├── SVS-MSP-Calculator-tokens.css # Design tokens + CSS vars
├── SVS-MSP-Calculator-base.css # Global chrome
├── SVS-MSP-Calculator-layout.css # Grid, header, main/sidebar
├── SVS-MSP-Calculator-components.css # Section cards, controls, sidebar (66KB)
├── SVS-MSP-Calculator-responsive.css # Viewport/container overrides (16KB)
├── SVS-MSP-Calculator-print.css # Print-specific rules
├── SVS-MSP-Calculator-light.css # Light theme overrides
├── SVS-MSP-Calculator-glass.css # Glass theme (glassmorphism)
├── SVS-MSP-Calculator-70retro.css # 70s Retro theme overrides
├── package-prices.csv # Overrideable pricing (31 rows)
├── tests/
│ └── test-quote-engine.js # 88 tests (Node.js, zero deps)
└── docs/
├── CHECKPOINT.md # Build status checkpoint
├── MASTER-SESSION-PROMPT.md # Full architecture brief
├── QUICK-REF.md # Compact reference
├── README.md
├── code-verification.md # Known-good baseline
├── phase-roadmap.md # Phase status
├── quote-rules.md # Business logic rules
├── regression-checklist.md # Test procedures
└── STAGE3-SESSION-PROMPT.md # This file
```
---
## HARD CONSTRAINTS (NON-NEGOTIABLE)
1. DOM IDs are a contract — renaming breaks mobile sync (100+ pairs)
2. 88 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
3. localStorage keys unchanged: `svs-msp-quote-v1`, `svs-msp-quote-ref`
4. All 4 themes must work after every change
5. Mobile parity maintained
6. Print/PDF tested after CSS changes
7. No frameworks, no npm — vanilla only
8. Surgical changes only — read before editing
9. Sections IVVI are placeholders — do not activate or build out
---
## STAGE 3 GOALS — Choose priorities from this menu:
### Option A: Browser-Based Manual QA
Full visual/functional walkthrough at all breakpoints × 4 themes.
Requires user to open the app in a browser and share screenshots or describe issues.
**Breakpoint matrix:**
| Width | Context | Key checks |
|-------|---------|------------|
| 1800px+ | Wide desktop | Max-width constraint, sidebar positioning |
| 1400px | Standard desktop | 3:2 main/sidebar balance |
| 1100px | Tablet/narrow | Single column transition, pill appears |
| 900px | Small tablet | Tighter spacing, smaller numerals |
| 600px | Phone portrait | Stacked layout, no numeral gutter |
| 375px | Small phone | MRR pill position, bottom sheet |
| 780px landscape | Phone landscape | 2-col restored, sidebar visible |
**Theme matrix:** Dark (default), Light, Glass, 70s Retro
**Total test grid:** 7 breakpoints × 4 themes = 28 combinations
### Option B: Feature Work
- JSON quote import (load a previously exported .json file)
- Enhanced print/PDF with more layout control
- Sidebar focus mode refinements
- Additional nudge logic
- Any specific feature requests
### Option C: Code Quality Pass
- Dead code removal across all modules
- CSS deduplication audit
- Magic number → token migration in components.css
- Mobile sync map completeness verification
- Export template class reference audit
### Option D: Test Coverage Expansion
- Add DOM rendering tests (sidebar values match calculations)
- Add persistence round-trip tests
- Add export schema validation tests
- Increase from 88 to 120+ tests
---
## PRICING REFERENCE
```
Users: M365 $140 (m2m) / $130 (annual) | BYOL $110
ExtHrs +$25 | 1PWM +$9 | INKY +$8 | ZT +$55
Endpoints: $35/ea | USB +$4 | BMB +$25
Servers: $120/ea
ZT Net: $25/seat | $100/router
Admin: Floor $150 | Threshold $650 | ZT supplement +$250 | 1PWM 10%
VoIP: Basic $28 | Standard $35 | Premium $45 | Phone +$15 | Fax +$10
Discounts: m2m 0% | 12mo 3% + 50% off onboarding | 24mo 5% + complimentary
HST: 13% (Ontario)
```
---
## WHAT WAS DONE IN PHASE 5 (for context)
| Fix | Change |
|-----|--------|
| `aria-expanded` on toggles | 12 section/collapsible headers in HTML + JS dynamic update |
| Focus trap on reset modal | Tab cycles within modal when open |
| `aria-label` on steppers | All 12 +/- buttons have descriptive labels |
| Glass mobile scroll | `background-attachment: scroll` at ≤1100px |
| Mobile sync guard | Skips sync on desktop when panel closed; forces sync on open |
| Sync map gaps | Added `sidebarFocusClientName`, `sl-discount-detail`, `sl-value-onboarding-label` |
| FA icons invisible on `file://` | All 36 Sharp Solid SVG file refs → inline `data:image/svg+xml` URIs in components.css |
---
## VERIFICATION COMMAND
```
node svsmspcalc/tests/test-quote-engine.js
```
88 tests, zero dependencies. Run after any pricing/engine/render changes.
---
## CONTEXT MANAGEMENT
After completing work:
- Update `docs/CHECKPOINT.md` with results
- If context is heavy, create `docs/STAGE4-SESSION-PROMPT.md` for the next chat
- Keep this document chain as the canonical handoff mechanism

View File

@@ -1,186 +0,0 @@
# SVS MSP CALC — STAGE 4 SESSION PROMPT
# Post-Beta Feature & QA Sprint
# Generated: 2026-03-15
---
## WHERE WE ARE
**Beta build: COMPLETE.** Sections IIII are production-quality.
**Accessibility/performance audit: COMPLETE.** Phase 5 fixes applied.
**Code quality pass: COMPLETE.** Phase 6 — hardcoded colors tokenized, CSS deduped, dead code removed.
**Tests:** 254/254 passing.
**Sections IVVI:** Intentionally deferred as placeholders — do not activate.
### Completed Stages
- **Stage 1** — Discovery audit (codebase mapping, doc generation)
- **Stage 2** — Beta build (Phases 14: bug fixes, visual polish, UX hardening, docs/QA)
- **Phase 5** — Accessibility/performance audit (aria-expanded, focus traps, stepper labels, glass scroll fix, mobile sync optimization)
- **Phase 5b** — Font Awesome icon fix (inline data URIs for file:// protocol)
- **Stage 3 / Phase 6** — Code quality pass (new `--sky` + `--transition-fast` tokens, button CSS dedup, hardcoded colors → color-mix(), dead null-check removed)
---
## START EVERY SESSION BY READING
1. `svsmspcalc/docs/CHECKPOINT.md` — current status, all completed work
2. `svsmspcalc/docs/MASTER-SESSION-PROMPT.md` — full architecture, constraints, priorities
3. `svsmspcalc/docs/QUICK-REF.md` — compact file map, DOM IDs, pricing, danger zones
4. This file — session goals and context
---
## PROJECT SNAPSHOT
**App:** SVS MSP CALC — live quote/pricing calculator for SVS Managed Services
**Type:** Static HTML + Vanilla JS + Modular CSS (no frameworks, no build tools, no npm)
**Used by:** SVS sales team, live on screen during prospect calls
### Architecture
```
svsmspcalc/
├── SVS-MSP-Calculator.html # Stable HTML shell (65KB)
├── SVS-MSP-Calculator.js # Orchestration (350 lines)
├── quote-engine.js # Pure quote math (197 lines)
├── quote-pricing.js # Pricing defaults + CSV override (134 lines)
├── quote-render.js # DOM rendering + nudges (729 lines)
├── quote-persistence.js # localStorage save/restore (225 lines)
├── quote-export.js # Print/PDF + JSON export (299 lines)
├── quote-import.js # JSON quote import (50 lines)
├── theme-manager.js # 4-theme switching (121 lines)
├── mobile-sync.js # Mobile panel sync (275 lines)
├── SVS-MSP-Calculator.css # Manifest (@imports all CSS)
├── SVS-MSP-Calculator-tokens.css # Design tokens + CSS vars
├── SVS-MSP-Calculator-base.css # Global chrome
├── SVS-MSP-Calculator-layout.css # Grid, header, main/sidebar
├── SVS-MSP-Calculator-components.css # Section cards, controls, sidebar (66KB)
├── SVS-MSP-Calculator-responsive.css # Viewport/container overrides (16KB)
├── SVS-MSP-Calculator-print.css # Print-specific rules
├── SVS-MSP-Calculator-light.css # Light theme overrides
├── SVS-MSP-Calculator-glass.css # Glass theme (glassmorphism)
├── SVS-MSP-Calculator-70retro.css # 70s Retro theme overrides
├── package-prices.csv # Overrideable pricing (31 rows)
├── tests/
│ └── test-quote-engine.js # 254 tests (Node.js, zero deps)
└── docs/
├── CHECKPOINT.md # Build status checkpoint
├── MASTER-SESSION-PROMPT.md # Full architecture brief
├── QUICK-REF.md # Compact reference
├── README.md
├── code-verification.md # Known-good baseline
├── phase-roadmap.md # Phase status
├── quote-rules.md # Business logic rules
├── regression-checklist.md # Test procedures
├── STAGE3-SESSION-PROMPT.md # Previous stage prompt
└── STAGE4-SESSION-PROMPT.md # This file
```
---
## HARD CONSTRAINTS (NON-NEGOTIABLE)
1. DOM IDs are a contract — renaming breaks mobile sync (100+ pairs)
2. 254 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
3. localStorage keys unchanged: `svs-msp-quote-v1`, `svs-msp-quote-ref`
4. All 4 themes must work after every change
5. Mobile parity maintained
6. Print/PDF tested after CSS changes
7. No frameworks, no npm — vanilla only
8. Surgical changes only — read before editing
9. Sections IVVI are placeholders — do not activate or build out
---
## STAGE 4 GOALS — Choose priorities from this menu:
### Option A: Browser-Based Manual QA
Full visual/functional walkthrough at all breakpoints × 4 themes.
Requires user to open the app in a browser and share screenshots or describe issues.
**Breakpoint matrix:**
| Width | Context | Key checks |
|-------|---------|------------|
| 1800px+ | Wide desktop | Max-width constraint, sidebar positioning |
| 1400px | Standard desktop | 3:2 main/sidebar balance |
| 1100px | Tablet/narrow | Single column transition, pill appears |
| 900px | Small tablet | Tighter spacing, smaller numerals |
| 600px | Phone portrait | Stacked layout, no numeral gutter |
| 375px | Small phone | MRR pill position, bottom sheet |
| 780px landscape | Phone landscape | 2-col restored, sidebar visible |
**Theme matrix:** Dark (default), Light, Glass, 70s Retro
**Total test grid:** 7 breakpoints × 4 themes = 28 combinations
### Option B: Feature Work
- **JSON quote import** — load a previously exported .json file back into the calculator
- **Enhanced print/PDF** — more layout control, optional cover page, save-as-PDF flow
- **Sidebar focus mode refinements** — expand/collapse behavior, keyboard shortcuts
- **Additional nudge logic** — new contextual nudges based on quote configuration
- **Any specific feature requests**
### Option C: Test Coverage Expansion — DONE
- ~~Add persistence round-trip tests~~ ✓ (6 tests)
- ~~Add export schema validation tests~~ ✓ (18 tests)
- ~~Add pricing integrity + engine edge cases~~ ✓ (89 tests)
- ~~Add import mapping + invariant tests~~ ✓ (36 tests)
- **Result: 88 → 250 tests (162 new)**
- Remaining: DOM rendering tests (requires JSDOM), theme token tests
### Option D: Further Code Quality
- Spacing magic numbers → token migration (95+ instances of 14px/16px/20px)
- `--transition-fast` token adoption across components.css (remaining 0.15s instances)
- CSS selector specificity audit
- Print CSS hardening
---
## WHAT WAS DONE IN PHASE 6 (for context)
| Fix | Change |
|-----|--------|
| `--sky` token | New per-theme accent for import button: Dark `#38bdf8`, Light `#0e7490`, Glass `#7dd3fc`, Retro `#a34a14` |
| `--transition-fast` token | `0.15s` — used in layout.css button transitions |
| Button CSS dedup | `.btn-reset-quote` + `.btn-import-quote` merged into grouped selector (10 duplicate properties removed) |
| Hardcoded colors → tokens | 8 hardcoded rgba/hex values replaced with `var(--sky)`, `var(--amber)`, and `color-mix()` across 4 theme files |
| Dead null-check | `nudgeIndex == null ||` removed from quote-render.js (variable always initialized) |
**Audit documented (not acted on):**
- `fmt()` duplication in render/export (intentional IIFE isolation)
- Spacing magic numbers (too many touchpoints)
- `console.warn()` statements (intentional error reporting)
- No dead functions/exports found across all 8 JS modules
---
## PRICING REFERENCE
```
Users: M365 $140 (m2m) / $130 (annual) | BYOL $110
ExtHrs +$25 | 1PWM +$9 | INKY +$8 | ZT +$55
Endpoints: $35/ea | USB +$4 | BMB +$25
Servers: $120/ea
ZT Net: $25/seat | $100/router
Admin: Floor $150 | Threshold $650 | ZT supplement +$250 | 1PWM 10%
VoIP: Basic $28 | Standard $35 | Premium $45 | Phone +$15 | Fax +$10
Discounts: m2m 0% | 12mo 3% + 50% off onboarding | 24mo 5% + complimentary
HST: 13% (Ontario)
```
---
## VERIFICATION COMMAND
```
node svsmspcalc/tests/test-quote-engine.js
```
254 tests, zero dependencies. Run after any pricing/engine/render changes.
---
## CONTEXT MANAGEMENT
After completing work:
- Update `docs/CHECKPOINT.md` with results
- If context is heavy, create `docs/STAGE5-SESSION-PROMPT.md` for the next chat
- Keep this document chain as the canonical handoff mechanism

View File

@@ -1,193 +0,0 @@
# SVS MSP CALC — STAGE 5 SESSION PROMPT
# Post-Feature Sprint — QA, Polish & Next Features
# Generated: 2026-03-15
---
## WHERE WE ARE
**Beta build: COMPLETE.** Sections IIII are production-quality.
**Accessibility/performance audit: COMPLETE.** Phase 5 fixes applied.
**Code quality pass: COMPLETE.** Phase 6 — hardcoded colors tokenized, CSS deduped, dead code removed.
**Test expansion: COMPLETE.** Phase 7 — 88 → 254 tests (pricing integrity, edge cases, export schema, persistence, import round-trip, invariants).
**Enhanced Print/PDF: COMPLETE.** Phase 8 — rep name, notes field, validity date, page breaks, CYA "Not Included" section.
**Tests:** 254/254 passing.
**Sections IVVI:** Intentionally deferred as placeholders — do not activate.
### Completed Stages
- **Stage 1** — Discovery audit (codebase mapping, doc generation)
- **Stage 2** — Beta build (Phases 14: bug fixes, visual polish, UX hardening, docs/QA)
- **Phase 5** — Accessibility/performance audit (aria-expanded, focus traps, stepper labels, glass scroll fix, mobile sync optimization)
- **Phase 5b** — Font Awesome icon fix (inline data URIs for file:// protocol)
- **Stage 3 / Phase 6** — Code quality pass (new `--sky` + `--transition-fast` tokens, button CSS dedup, hardcoded colors → color-mix(), dead null-check removed)
- **Stage 4 / Phase 7** — Test expansion (88 → 254 tests across 6 categories)
- **Stage 4 / Phase 8** — Enhanced Print/PDF (rep name, notes, validity date, page breaks, CYA section, JSON schema v1.1)
---
## START EVERY SESSION BY READING
1. `svsmspcalc/docs/CHECKPOINT.md` — current status, all completed work
2. `svsmspcalc/docs/MASTER-SESSION-PROMPT.md` — full architecture, constraints, priorities
3. `svsmspcalc/docs/QUICK-REF.md` — compact file map, DOM IDs, pricing, danger zones
4. This file — session goals and context
---
## PROJECT SNAPSHOT
**App:** SVS MSP CALC — live quote/pricing calculator for SVS Managed Services
**Type:** Static HTML + Vanilla JS + Modular CSS (no frameworks, no build tools, no npm)
**Used by:** SVS sales team, live on screen during prospect calls
### Architecture
```
svsmspcalc/
├── SVS-MSP-Calculator.html # Stable HTML shell (65KB)
├── SVS-MSP-Calculator.js # Orchestration (350 lines)
├── quote-engine.js # Pure quote math (197 lines)
├── quote-pricing.js # Pricing defaults + CSV override (134 lines)
├── quote-render.js # DOM rendering + nudges (729 lines)
├── quote-persistence.js # localStorage save/restore (237 lines)
├── quote-export.js # Print/PDF + JSON export (320 lines)
├── quote-import.js # JSON quote import (166 lines)
├── theme-manager.js # 4-theme switching (121 lines)
├── mobile-sync.js # Mobile panel sync (275 lines)
├── SVS-MSP-Calculator.css # Manifest (@imports all CSS)
├── SVS-MSP-Calculator-tokens.css # Design tokens + CSS vars
├── SVS-MSP-Calculator-base.css # Global chrome
├── SVS-MSP-Calculator-layout.css # Grid, header, main/sidebar
├── SVS-MSP-Calculator-components.css # Section cards, controls, sidebar (67KB)
├── SVS-MSP-Calculator-responsive.css # Viewport/container overrides (16KB)
├── SVS-MSP-Calculator-print.css # Print-specific rules
├── SVS-MSP-Calculator-light.css # Light theme overrides
├── SVS-MSP-Calculator-glass.css # Glass theme (glassmorphism)
├── SVS-MSP-Calculator-70retro.css # 70s Retro theme overrides
├── package-prices.csv # Overrideable pricing (31 rows)
├── tests/
│ └── test-quote-engine.js # 254 tests (Node.js, zero deps)
└── docs/
├── CHECKPOINT.md # Build status checkpoint
├── MASTER-SESSION-PROMPT.md # Full architecture brief
├── QUICK-REF.md # Compact reference
├── README.md
├── code-verification.md # Known-good baseline
├── phase-roadmap.md # Phase status
├── quote-rules.md # Business logic rules
├── regression-checklist.md # Test procedures
├── STAGE3-SESSION-PROMPT.md # Stage 3 prompt
├── STAGE4-SESSION-PROMPT.md # Stage 4 prompt
└── STAGE5-SESSION-PROMPT.md # This file
```
### New in Phase 8 (Print/PDF Enhancements)
- **`id="repName"`** — text input below client name ("Prepared by"), persisted + exported
- **`id="quoteNotes"`** — textarea in sidebar before export buttons, persisted + exported
- **Print invoice** now includes: rep name in header/footer, notes section, computed "Valid until" date, page-break-safe tables, split config (included vs. "Not Included" CYA section)
- **JSON export schema** bumped to `v1.1` — new `repName` and `quoteNotes` fields
- **JSON import** handles new fields (backward-compatible with v1.0)
---
## HARD CONSTRAINTS (NON-NEGOTIABLE)
1. DOM IDs are a contract — renaming breaks mobile sync (100+ pairs)
2. 254 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
3. localStorage keys unchanged: `svs-msp-quote-v1`, `svs-msp-quote-ref`
4. All 4 themes must work after every change
5. Mobile parity maintained
6. Print/PDF tested after CSS changes
7. No frameworks, no npm — vanilla only
8. Surgical changes only — read before editing
9. Sections IVVI are placeholders — do not activate or build out
---
## STAGE 5 GOALS — Choose priorities from this menu:
### Option A: Browser-Based Manual QA
Full visual/functional walkthrough at all breakpoints × 4 themes.
Requires user to open the app in a browser and share screenshots or describe issues.
**Breakpoint matrix:**
| Width | Context | Key checks |
|-------|---------|------------|
| 1800px+ | Wide desktop | Max-width constraint, sidebar positioning |
| 1400px | Standard desktop | 3:2 main/sidebar balance |
| 1100px | Tablet/narrow | Single column transition, pill appears |
| 900px | Small tablet | Tighter spacing, smaller numerals |
| 600px | Phone portrait | Stacked layout, no numeral gutter |
| 375px | Small phone | MRR pill position, bottom sheet |
| 780px landscape | Phone landscape | 2-col restored, sidebar visible |
**Theme matrix:** Dark (default), Light, Glass, 70s Retro
**Total test grid:** 7 breakpoints × 4 themes = 28 combinations
### Option B: Feature Work
- **Sidebar keyboard shortcuts** — Ctrl+P print, Ctrl+E export, Ctrl+R reset, Escape close focus mode
- **Additional nudge logic** — new contextual nudges (e.g. "no endpoints but users set", "VoIP seats ≠ user count", high admin-to-MRR ratio)
- **Any specific feature requests**
### Option C: Further Code Quality
- Spacing magic numbers → token migration (95+ instances of 14px/16px/20px)
- `--transition-fast` token adoption across components.css (remaining 0.15s instances)
- CSS selector specificity audit
- Print CSS hardening
---
## WHAT WAS DONE IN PHASES 78 (for context)
### Phase 7: Test Expansion
| Group | Tests | Coverage |
|-------|-------|----------|
| Pricing DEFAULTS integrity | 34 | Key existence, types, spec values, frozen, ordering |
| Engine edge cases | 55 | Admin thresholds, 100-user scale, string coercion, invalid inputs, VoIP/ZT edge cases |
| Export JSON schema | 22 | Payload structure, field types, version, labels, pricing sub-object, new fields |
| Persistence state shape | 6 | JSON round-trip, engine compatibility, zero-state |
| Import payload mapping | 12 | Term reverse-map, export→import→engine round-trip |
| Quote output invariants | 24 | 6 configs × 4 invariants |
### Phase 8: Enhanced Print/PDF
| # | Enhancement | Details |
|---|------------|---------|
| P1 | Notes field | `<textarea id="quoteNotes">` — sidebar, persisted, exported, printed |
| P2 | Validity date | "Valid until [computed date]" in print footer |
| P3 | Page breaks | `page-break-inside:avoid` on tables, totals, notes |
| P4 | Rep name | `<input id="repName">` — persisted, exported, in print header + footer |
| P5 | CYA section | Active features prominent; excluded in muted "Services Not Included in This Quote" section |
---
## PRICING REFERENCE
```
Users: M365 $140 (m2m) / $130 (annual) | BYOL $110
ExtHrs +$25 | 1PWM +$9 | INKY +$8 | ZT +$55
Endpoints: $35/ea | USB +$4 | BMB +$25
Servers: $120/ea
ZT Net: $25/seat | $100/router
Admin: Floor $150 | Threshold $650 | ZT supplement +$250 | 1PWM 10%
VoIP: Basic $28 | Standard $35 | Premium $45 | Phone +$15 | Fax +$10
Discounts: m2m 0% | 12mo 3% + 50% off onboarding | 24mo 5% + complimentary
HST: 13% (Ontario)
```
---
## VERIFICATION COMMAND
```
node svsmspcalc/tests/test-quote-engine.js
```
254 tests, zero dependencies. Run after any pricing/engine/render changes.
---
## CONTEXT MANAGEMENT
After completing work:
- Update `docs/CHECKPOINT.md` with results
- If context is heavy, create `docs/STAGE6-SESSION-PROMPT.md` for the next chat
- Keep this document chain as the canonical handoff mechanism

View File

@@ -1,162 +0,0 @@
# SVS MSP CALC — STAGE 6 SESSION PROMPT
# Post-QA — Feature Work & Remaining Polish
# Generated: 2026-03-15
---
## WHERE WE ARE
**Beta build: COMPLETE.** Sections IIII are production-quality.
**Phases 18: COMPLETE.** Bug fixes, visual polish, UX hardening, docs/QA, a11y/perf, code quality, test expansion, print enhancements.
**Phase 9 / Stage 5: COMPLETE.** Visual QA across 3 breakpoints × 4 themes — Dark/Light/Glass all clean. Retro theme overhauled from muddy brown → warm paper + hot rose/teal cyberpunk accents.
**Tests:** 254/254 passing.
**Sections IVVI:** Intentionally deferred as placeholders — do not activate.
### Completed Stages
- **Stage 1** — Discovery audit (codebase mapping, doc generation)
- **Stage 2** — Beta build (Phases 14: bug fixes, visual polish, UX hardening, docs/QA)
- **Phase 5** — Accessibility/performance audit + Font Awesome icon fix
- **Stage 3 / Phase 6** — Code quality pass (tokens, CSS dedup, dead code)
- **Stage 4 / Phase 7** — Test expansion (88 → 254 tests)
- **Stage 4 / Phase 8** — Enhanced Print/PDF (rep name, notes, validity date, page breaks, CYA section)
- **Stage 5 / Phase 9** — Visual QA (3 breakpoints × 4 themes) + Retro theme overhaul
---
## START EVERY SESSION BY READING
1. `svsmspcalc/docs/CHECKPOINT.md` — current status, all completed work
2. `svsmspcalc/docs/MASTER-SESSION-PROMPT.md` — full architecture, constraints, priorities
3. `svsmspcalc/docs/QUICK-REF.md` — compact file map, DOM IDs, pricing, danger zones
4. This file — session goals and context
---
## PROJECT SNAPSHOT
**App:** SVS MSP CALC — live quote/pricing calculator for SVS Managed Services
**Type:** Static HTML + Vanilla JS + Modular CSS (no frameworks, no build tools, no npm)
**Used by:** SVS sales team, live on screen during prospect calls
### Architecture
```
svsmspcalc/
├── SVS-MSP-Calculator.html # Stable HTML shell (65KB)
├── SVS-MSP-Calculator.js # Orchestration (350 lines)
├── quote-engine.js # Pure quote math (197 lines)
├── quote-pricing.js # Pricing defaults + CSV override (134 lines)
├── quote-render.js # DOM rendering + nudges (729 lines)
├── quote-persistence.js # localStorage save/restore (237 lines)
├── quote-export.js # Print/PDF + JSON export (320 lines)
├── quote-import.js # JSON quote import (166 lines)
├── theme-manager.js # 4-theme switching (121 lines)
├── mobile-sync.js # Mobile panel sync (275 lines)
├── SVS-MSP-Calculator.css # Manifest (@imports all CSS)
├── SVS-MSP-Calculator-tokens.css # Design tokens + CSS vars
├── SVS-MSP-Calculator-base.css # Global chrome
├── SVS-MSP-Calculator-layout.css # Grid, header, main/sidebar
├── SVS-MSP-Calculator-components.css # Section cards, controls, sidebar (67KB)
├── SVS-MSP-Calculator-responsive.css # Viewport/container overrides (16KB)
├── SVS-MSP-Calculator-print.css # Print-specific rules
├── SVS-MSP-Calculator-light.css # Light theme overrides
├── SVS-MSP-Calculator-glass.css # Glass theme (glassmorphism)
├── SVS-MSP-Calculator-70retro.css # Retro Cyberpunk theme (paper + hot rose/teal)
├── package-prices.csv # Overrideable pricing (31 rows)
├── tests/
│ └── test-quote-engine.js # 254 tests (Node.js, zero deps)
└── docs/
├── CHECKPOINT.md # Build status checkpoint
├── MASTER-SESSION-PROMPT.md # Full architecture brief
├── QUICK-REF.md # Compact reference
├── README.md
├── code-verification.md # Known-good baseline
├── phase-roadmap.md # Phase status
├── quote-rules.md # Business logic rules
├── regression-checklist.md # Test procedures
└── STAGE6-SESSION-PROMPT.md # This file
```
### Retro Theme Status (Phase 9)
- Overhauled from 70s wood-panel brown → warm paper + cyberpunk neon-warm accents
- Accent: hot rose `#e11d48` | Teal: `#0d9488` | Header: warm charcoal `#1c1317`
- Logo SVG fix: `.top-bar-logo path { fill: #f0e4d0 }` overrides hardcoded black
- **User noted:** full design polish deferred — current version is functional placeholder
- Retro theme not yet QA'd at all 7 breakpoints
---
## HARD CONSTRAINTS (NON-NEGOTIABLE)
1. DOM IDs are a contract — renaming breaks mobile sync (100+ pairs)
2. 254 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
3. localStorage keys unchanged: `svs-msp-quote-v1`, `svs-msp-quote-ref`
4. All 4 themes must work after every change
5. Mobile parity maintained
6. Print/PDF tested after CSS changes
7. No frameworks, no npm — vanilla only
8. Surgical changes only — read before editing
9. Sections IVVI are placeholders — do not activate or build out
---
## STAGE 6 GOALS — Choose priorities from this menu:
### Option A: Remaining Visual QA
Complete the breakpoint matrix for retro theme + remaining breakpoints:
| Width | Context |
|-------|---------|
| 900px | Small tablet |
| 600px | Phone portrait |
| 375px | Small phone |
| 780px landscape | Phone landscape |
### Option B: Feature Work
- **Sidebar keyboard shortcuts** — Ctrl+P print, Ctrl+E export, Ctrl+R reset, Escape close focus mode
- **Additional nudge logic** — new contextual nudges (e.g. "no endpoints but users set", "VoIP seats ≠ user count", high admin-to-MRR ratio)
- **Any specific feature requests from the user**
### Option C: Further Code Quality
- Spacing magic numbers → token migration (95+ instances of 14px/16px/20px)
- `--transition-fast` token adoption across components.css
- CSS selector specificity audit
- Print CSS hardening
### Option D: Retro Theme Full Design Pass
- Complete cyberpunk aesthetic overhaul with user collaboration
- Color refinement, contrast tuning, component-level styling
- Full breakpoint QA after design is finalized
---
## PRICING REFERENCE
```
Users: M365 $140 (m2m) / $130 (annual) | BYOL $110
ExtHrs +$25 | 1PWM +$9 | INKY +$8 | ZT +$55
Endpoints: $35/ea | USB +$4 | BMB +$25
Servers: $120/ea
ZT Net: $25/seat | $100/router
Admin: Floor $150 | Threshold $650 | ZT supplement +$250 | 1PWM 10%
VoIP: Basic $28 | Standard $35 | Premium $45 | Phone +$15 | Fax +$10
Discounts: m2m 0% | 12mo 3% + 50% off onboarding | 24mo 5% + complimentary
HST: 13% (Ontario)
```
---
## VERIFICATION COMMAND
```
node svsmspcalc/tests/test-quote-engine.js
```
254 tests, zero dependencies. Run after any pricing/engine/render changes.
---
## CONTEXT MANAGEMENT
After completing work:
- Update `docs/CHECKPOINT.md` with results
- If context is heavy, create `docs/STAGE7-SESSION-PROMPT.md` for the next chat
- Keep this document chain as the canonical handoff mechanism

View File

@@ -1,166 +0,0 @@
# SVS MSP CALC — STAGE 7 SESSION PROMPT
# Post-Elastic — Feature Work & Polish
# Generated: 2026-03-15
---
## WHERE WE ARE
**Beta build: COMPLETE.** Sections IIII are production-quality.
**Phases 19: COMPLETE.** Bug fixes, visual polish, UX hardening, docs/QA, a11y/perf, code quality, test expansion, print enhancements, visual QA.
**Phase 10 / Stage 6: COMPLETE.** Elastic responsive foundation — fluid `clamp()` tokens replace 5 fixed breakpoints with 3 structural ones. Max width scales to 2400px for large monitors.
**Tests:** 254/254 passing.
**Sections IVVI:** Intentionally deferred as placeholders — do not activate.
### Completed Stages
- **Stage 1** — Discovery audit (codebase mapping, doc generation)
- **Stage 2** — Beta build (Phases 14: bug fixes, visual polish, UX hardening, docs/QA)
- **Phase 5** — Accessibility/performance audit + Font Awesome icon fix
- **Stage 3 / Phase 6** — Code quality pass (tokens, CSS dedup, dead code)
- **Stage 4 / Phase 7** — Test expansion (88 → 254 tests)
- **Stage 4 / Phase 8** — Enhanced Print/PDF (rep name, notes, validity date, page breaks, CYA section)
- **Stage 5 / Phase 9** — Visual QA (3 breakpoints × 4 themes) + Retro theme overhaul
- **Stage 6 / Phase 10** — Elastic responsive foundation (5 → 3 breakpoints, fluid clamp() tokens, max-width up to 2400px)
---
## START EVERY SESSION BY READING
1. `svsmspcalc/docs/CHECKPOINT.md` — current status, all completed work
2. `svsmspcalc/docs/MASTER-SESSION-PROMPT.md` — full architecture, constraints, priorities
3. `svsmspcalc/docs/QUICK-REF.md` — compact file map, DOM IDs, pricing, danger zones
4. This file — session goals and context
---
## PROJECT SNAPSHOT
**App:** SVS MSP CALC — live quote/pricing calculator for SVS Managed Services
**Type:** Static HTML + Vanilla JS + Modular CSS (no frameworks, no build tools, no npm)
**Used by:** SVS sales team, live on screen during prospect calls
### Architecture
```
svsmspcalc/
├── SVS-MSP-Calculator.html # Stable HTML shell (65KB)
├── SVS-MSP-Calculator.js # Orchestration (350 lines)
├── quote-engine.js # Pure quote math (197 lines)
├── quote-pricing.js # Pricing defaults + CSV override (134 lines)
├── quote-render.js # DOM rendering + nudges (729 lines)
├── quote-persistence.js # localStorage save/restore (237 lines)
├── quote-export.js # Print/PDF + JSON export (320 lines)
├── quote-import.js # JSON quote import (166 lines)
├── theme-manager.js # 4-theme switching (121 lines)
├── mobile-sync.js # Mobile panel sync (275 lines)
├── SVS-MSP-Calculator.css # Manifest (@imports all CSS)
├── SVS-MSP-Calculator-tokens.css # Design tokens + CSS vars (fluid clamp() layout)
├── SVS-MSP-Calculator-base.css # Global chrome
├── SVS-MSP-Calculator-layout.css # Grid, header, main/sidebar
├── SVS-MSP-Calculator-components.css # Section cards, controls, sidebar (67KB)
├── SVS-MSP-Calculator-responsive.css # 3 structural breakpoints (1100/600/780L)
├── SVS-MSP-Calculator-print.css # Print-specific rules
├── SVS-MSP-Calculator-light.css # Light theme overrides
├── SVS-MSP-Calculator-glass.css # Glass theme (glassmorphism)
├── SVS-MSP-Calculator-70retro.css # Retro Cyberpunk theme (paper + hot rose/teal)
├── package-prices.csv # Overrideable pricing (31 rows)
├── tests/
│ └── test-quote-engine.js # 254 tests (Node.js, zero deps)
└── docs/
├── CHECKPOINT.md # Build status checkpoint
├── MASTER-SESSION-PROMPT.md # Full architecture brief
├── QUICK-REF.md # Compact reference
├── README.md
├── code-verification.md # Known-good baseline
├── phase-roadmap.md # Phase status
├── quote-rules.md # Business logic rules
├── regression-checklist.md # Test procedures
└── STAGE7-SESSION-PROMPT.md # This file
```
### Responsive Architecture (Phase 10)
- **Fluid tokens** in `tokens.css``clamp()` drives all spacing/sizing continuously
- **3 structural breakpoints** in `responsive.css`:
- `≤1100px` — 2-col grid → 1-col, sidebar → mobile pill/panel
- `≤600px` — phone layout shifts (stacking, gutter collapse, touch targets)
- `≤780px landscape` — restore 2-col with sticky sidebar
- **Width scaling:** 1080p → ~1766px, 1440p → ~2355px, 4K → 2400px cap
- No more 1350px or 900px breakpoints — fluid tokens handle the range
### Retro Theme Status
- Overhauled from 70s wood-panel brown → warm paper + cyberpunk neon-warm accents
- Accent: hot rose `#e11d48` | Teal: `#0d9488` | Header: warm charcoal `#1c1317`
- **User noted:** full design polish deferred — current version is functional placeholder
- Retro theme not yet QA'd across all viewport widths
---
## HARD CONSTRAINTS (NON-NEGOTIABLE)
1. DOM IDs are a contract — renaming breaks mobile sync (100+ pairs)
2. 254 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
3. localStorage keys unchanged: `svs-msp-quote-v1`, `svs-msp-quote-ref`
4. All 4 themes must work after every change
5. Mobile parity maintained
6. Print/PDF tested after CSS changes
7. No frameworks, no npm — vanilla only
8. Surgical changes only — read before editing
9. Sections IVVI are placeholders — do not activate or build out
---
## STAGE 7 GOALS — Choose priorities from this menu:
### Option A: Feature Work
- **Sidebar keyboard shortcuts** — Ctrl+P print, Ctrl+E export, Ctrl+R reset, Escape close focus mode
- **Additional nudge logic** — new contextual nudges (e.g. "no endpoints but users set", "VoIP seats ≠ user count", high admin-to-MRR ratio)
- **Any specific feature requests from the user**
### Option B: Further Code Quality
- `--transition-fast` token adoption across components.css
- CSS selector specificity audit
- Print CSS hardening
### Option C: Retro Theme Full Design Pass
- Complete cyberpunk aesthetic overhaul with user collaboration
- Color refinement, contrast tuning, component-level styling
- Full viewport QA after design is finalized
### Option D: Visual QA Sweep
- Verify elastic responsive behavior across viewport range (resize from 375px → 2560px)
- Confirm all 4 themes render cleanly with new fluid tokens
- Test landscape orientation on tablet/phone sizes
---
## PRICING REFERENCE
```
Users: M365 $140 (m2m) / $130 (annual) | BYOL $110
ExtHrs +$25 | 1PWM +$9 | INKY +$8 | ZT +$55
Endpoints: $35/ea | USB +$4 | BMB +$25
Servers: $120/ea
ZT Net: $25/seat | $100/router
Admin: Floor $150 | Threshold $650 | ZT supplement +$250 | 1PWM 10%
VoIP: Basic $28 | Standard $35 | Premium $45 | Phone +$15 | Fax +$10
Discounts: m2m 0% | 12mo 3% + 50% off onboarding | 24mo 5% + complimentary
HST: 13% (Ontario)
```
---
## VERIFICATION COMMAND
```
node svsmspcalc/tests/test-quote-engine.js
```
254 tests, zero dependencies. Run after any pricing/engine/render changes.
---
## CONTEXT MANAGEMENT
After completing work:
- Update `docs/CHECKPOINT.md` with results
- If context is heavy, create `docs/STAGE8-SESSION-PROMPT.md` for the next chat
- Keep this document chain as the canonical handoff mechanism

View File

@@ -1,167 +0,0 @@
# SVS MSP CALC — STAGE 9 SESSION PROMPT
# Next Session
# Generated: 2026-03-15
---
## WHERE WE ARE
**Beta build: COMPLETE.** Sections IIII are production-quality.
**Phases 111 + Stage 8: COMPLETE.** Bug fixes, visual polish, UX hardening, docs/QA, a11y/perf, code quality I & II, test expansion, print enhancements, visual QA, elastic responsive, feature work.
**Stage 8 Feature Fixes: COMPLETE.**
- Fullscreen live quote view: Print button in sidebar header (Reset/Import/Export JSON hidden)
- Toggle switches: distinct off/on colors per theme (`--surface-switch-off`/`--surface-switch-on`)
- Pricing: migrated from CSV to JSON (`package-prices.json`)
- JSON export/import: already captures all form data including quote notes (schema v1.1)
**Tests:** 254/254 passing.
**Sections IVVI:** Intentionally deferred as placeholders — do not activate.
### Completed Stages
- **Stage 1** — Discovery audit (codebase mapping, doc generation)
- **Stage 2** — Beta build (Phases 14: bug fixes, visual polish, UX hardening, docs/QA)
- **Phase 5** — Accessibility/performance audit + Font Awesome icon fix
- **Stage 3 / Phase 6** — Code quality pass I (tokens, CSS dedup, dead code)
- **Stage 4 / Phase 7** — Test expansion (88 → 254 tests)
- **Stage 4 / Phase 8** — Enhanced Print/PDF (rep name, notes, validity date, page breaks, CYA section)
- **Stage 5 / Phase 9** — Visual QA (3 breakpoints × 4 themes) + Retro theme overhaul
- **Stage 6 / Phase 10** — Elastic responsive foundation (5 → 3 breakpoints, fluid clamp() tokens, max-width up to 2400px)
- **Stage 7 / Phase 11** — Feature work: keyboard shortcuts (Ctrl+P/E/R) + 4 new contextual nudges
- **Stage 8 / Phase 12** — Code quality pass II: transition tokens, CSS specificity audit, print CSS hardening
- **Stage 8 Feature Fixes** — Fullscreen print button fix, toggle switch 2-state themed colors, pricing CSV → JSON migration
---
## START EVERY SESSION BY READING
1. `svsmspcalc/docs/CHECKPOINT.md` — current status, all completed work
2. `svsmspcalc/docs/MASTER-SESSION-PROMPT.md` — full architecture, constraints, priorities
3. `svsmspcalc/docs/QUICK-REF.md` — compact file map, DOM IDs, pricing, danger zones
4. This file — session goals and context
---
## PROJECT SNAPSHOT
**App:** SVS MSP CALC — live quote/pricing calculator for SVS Managed Services
**Type:** Static HTML + Vanilla JS + Modular CSS (no frameworks, no build tools, no npm)
**Used by:** SVS sales team, live on screen during prospect calls
### Architecture
```
svsmspcalc/
├── SVS-MSP-Calculator.html # Stable HTML shell (65KB)
├── SVS-MSP-Calculator.js # Orchestration + keyboard shortcuts (375 lines)
├── quote-engine.js # Pure quote math (197 lines)
├── quote-pricing.js # Pricing defaults + JSON override (134 lines)
├── quote-render.js # DOM rendering + nudges (760 lines)
├── quote-persistence.js # localStorage save/restore (237 lines)
├── quote-export.js # Print/PDF + JSON export (320 lines)
├── quote-import.js # JSON quote import (166 lines)
├── theme-manager.js # 4-theme switching (121 lines)
├── mobile-sync.js # Mobile panel sync (275 lines)
├── SVS-MSP-Calculator.css # Manifest (@imports all CSS)
├── SVS-MSP-Calculator-tokens.css # Design tokens + CSS vars (fluid clamp() layout)
├── SVS-MSP-Calculator-base.css # Global chrome
├── SVS-MSP-Calculator-layout.css # Grid, header, main/sidebar
├── SVS-MSP-Calculator-components.css # Section cards, controls, sidebar (67KB)
├── SVS-MSP-Calculator-responsive.css # 3 structural breakpoints (1100/600/780L)
├── SVS-MSP-Calculator-print.css # Print-specific rules
├── SVS-MSP-Calculator-light.css # Light theme overrides
├── SVS-MSP-Calculator-glass.css # Glass theme (glassmorphism)
├── SVS-MSP-Calculator-70retro.css # Retro Cyberpunk theme (paper + hot rose/teal)
├── package-prices-data.js # Pricing data (edit this to change prices — loaded via <script>)
├── package-prices.json # Pricing reference (JSON format, not loaded at runtime)
├── package-prices.csv # Legacy pricing reference (not loaded at runtime)
├── tests/
│ └── test-quote-engine.js # 254 tests (Node.js, zero deps)
└── docs/
├── CHECKPOINT.md # Build status checkpoint
├── MASTER-SESSION-PROMPT.md # Full architecture brief
├── QUICK-REF.md # Compact reference
├── README.md
├── code-verification.md # Known-good baseline
├── phase-roadmap.md # Phase status
├── quote-rules.md # Business logic rules
├── regression-checklist.md # Test procedures
├── STAGE8-SESSION-PROMPT.md # Previous stage
└── STAGE9-SESSION-PROMPT.md # This file
```
### Pricing Configuration
Pricing is loaded from `package-prices-data.js` via `<script>` tag (works on `file://` — no web server needed). The loader also has a `fetch()` fallback for web server environments, then falls back to built-in defaults.
**To update pricing:** Edit `package-prices-data.js` — change the `value` field for any key:
```js
window.SVS_PRICING_DATA = {
"user_packages": {
"RATE_M365": { "value": 130, "description": "..." },
...
},
...
};
```
Categories: `user_packages`, `user_addons`, `endpoints`, `endpoint_addons`, `zero_trust_network`, `voip`, `site_admin`, `contract_discounts`, `tax`, `vs_comparison`.
---
## HARD CONSTRAINTS (NON-NEGOTIABLE)
1. DOM IDs are a contract — renaming breaks mobile sync (100+ pairs)
2. 254 tests must pass: `node svsmspcalc/tests/test-quote-engine.js`
3. localStorage keys unchanged: `svs-msp-quote-v1`, `svs-msp-quote-ref`
4. All 4 themes must work after every change
5. Mobile parity maintained
6. Print/PDF tested after CSS changes
7. No frameworks, no npm — vanilla only
8. Surgical changes only — read before editing
9. Sections IVVI are placeholders — do not activate or build out
---
## STAGE 9 GOALS
_To be determined by the user in the next session._
Potential areas for continued work:
- **Spacing token consolidation** — 150+ magic-number spacings in components.css (deferred from Stage 8)
- **Visual QA pass** — Retro theme at all viewport widths + landscape (noted as incomplete in Stage 5)
- **Additional feature work** — user-driven
- **Test coverage** — additional edge cases or integration-level tests
- **Documentation updates** — README, MASTER-SESSION-PROMPT alignment
---
## PRICING REFERENCE
```
Users: M365 $140 (m2m) / $130 (annual) | BYOL $110
ExtHrs +$25 | 1PWM +$9 | INKY +$8 | ZT +$55
Endpoints: $35/ea | USB +$4 | BMB +$25
Servers: $120/ea
ZT Net: $25/seat | $100/router
Admin: Floor $150 | Threshold $650 | ZT supplement +$250 | 1PWM 10%
VoIP: Basic $28 | Standard $35 | Premium $45 | Phone +$15 | Fax +$10
Discounts: m2m 0% | 12mo 3% + 50% off onboarding | 24mo 5% + complimentary
HST: 13% (Ontario)
```
---
## VERIFICATION COMMAND
```
node svsmspcalc/tests/test-quote-engine.js
```
254 tests, zero dependencies. Run after any pricing/engine/render changes.
---
## CONTEXT MANAGEMENT
After completing work:
- Update `docs/CHECKPOINT.md` with results
- If context is heavy, create `docs/STAGE10-SESSION-PROMPT.md` for the next chat
- Keep this document chain as the canonical handoff mechanism

View File

@@ -1,65 +0,0 @@
# AI Session Brief
## Mission
Operate like a senior engineering, UX/UI, QA, and sales-enablement team in one:
- Senior frontend engineer: minimal safe changes, clean code, strong regression awareness
- UX/UI lead: polish, readability, hierarchy, responsive quality, accessibility, and visual restraint
- Marketing manager: keep messaging clear, persuasive, professional, and sales-usable
## Post-startup reminder
After reading docs, always remind the user:
> You have an automated quote engine test suite (88 tests). Run anytime:
> `node svsmspcalc/tests/test-quote-engine.js`
> I can run it for you, or modify it to add new test scenarios.
## Non-negotiables
- Inspect existing code before editing
- Prefer the smallest safe change
- Preserve current behavior unless explicitly asked to change it
- Keep the HTML shell and DOM IDs stable unless approved
- Preserve calculations, exports, persistence, theme switching, and mobile parity
- Avoid broad rewrites, framework changes, or "cleanup for its own sake"
- Minimize token waste
- Read only the files needed for the task
- Treat code quality as production-grade: clear intent, low duplication, safe edge handling, and no casual hacks
- Always check for regressions, syntax issues, broken flows, and stale code before calling work done
- Run `node svsmspcalc/tests/test-quote-engine.js` after any pricing or engine changes
## Product bar
- Sales-facing and professional
- Clear, persuasive, and polished rather than flashy
- Responsive behavior should feel elastic and harmonious, not breakpoint-fragile
- Prefer tokenized or container-based layout fixes over piling on viewport hacks
- Light mode should feel comfortable, soft khaki/brown, and high-readability
- UI changes should improve both visual quality and sales clarity
- Copy should sound confident, concise, and client-facing
## Current scope guardrails
- All six sections (I-VI) are structurally active with unified `sec-controls-row` headers
- Sections default to collapsed on load; inner collapsibles default to collapsed
- Keep docs concise; only append notes that materially speed up future resume work
## Required validation mindset
- Run automated tests after pricing/engine changes
- Run syntax checks after JS edits
- Review for stale references, dead code, and duplicated logic
- Assume inline handlers and mobile clone sync can hide dependencies; verify before deleting code
- Call out any unverified areas, especially print/PDF, JSON export, persistence, and mobile parity
## Resume order
1. Read `docs/README.md`
2. Read `docs/phase-roadmap.md`
3. Read `docs/code-verification.md`
4. Use `docs/quote-rules.md` only if business-rule detail is needed
5. Use `docs/regression-checklist.md` only when validating behavior
6. Then inspect only the code files relevant to the request
7. **Remind user about the test suite** (see Post-startup reminder above)

View File

@@ -1,124 +0,0 @@
# Code Verification
## Latest checkpoint
- Date: March 15, 2026
- Verification: automated quote engine tests (88/88 passing) + visual review
- Status: Phases 13 complete (bug fixes, visual polish, UX hardening)
## Automated test suite
**Location:** `svsmspcalc/tests/test-quote-engine.js`
**Run:** `node svsmspcalc/tests/test-quote-engine.js`
**Result:** 88 tests passing across 21 test groups
### What's covered
| Area | Tests |
|------|-------|
| M365 vs BYOL rates | User base rates, rate switching |
| Admin fee scaling | Floor ($150), minimum ($650), ZT premium ($250), 1Password markup (10%) |
| Admin waiver | Fee calculated but excluded from MRR |
| User add-ons | Extended Hours, 1Password, INKY, Zero Trust per-user |
| Endpoint add-ons | USB Blocking, Bare Metal Backup |
| Server pricing | Per-server rate, inclusion in subtotals |
| Contract discounts | m2m (0%), 12mo (3%), 24mo (5%) |
| HST | 13% applied after discount, disabled = 0, combo with discount |
| VoIP | All 3 tiers, phone + fax add-ons |
| Zero Trust networking | Seats + routers |
| Edge cases | Zero users, per-user breakdowns |
| MRR integrity | MRR = sum of components across 5 different configs |
| Realistic scenario | Full 22-user quote with all features |
### How to extend
- Add `describe()` / `it()` blocks in the test file
- Uses `eq(actual, expected, label)` for exact assertions
- Uses `near(actual, expected, tolerance, label)` for floating-point
- Ask Claude to add tests for new scenarios
## What is currently known-good
### Section header redesign (completed this session)
- All 6 sections use unified `sec-controls-row` pattern (stepper + badge + price on row 2)
- Subtitles moved to row 3, visible only when expanded
- Section headers use CSS grid: numeral + title + chevron on row 1
- Container query at ≤520px and media query at ≤600px handle small-screen stacking
- Cascade order issues resolved (container query overrides placed after base rules)
### UI/UX changes (completed this session)
- ~15-20% spacing optimization across all tokens, layout, components, and responsive CSS
- All sections default to collapsed state on load
- Inner collapsibles (What's Included, Add-Ons) default to collapsed
- "What This Fee Supports" card removed from SEC-01
- "Protection First, Recovery Optional" card removed from SEC-03
- Progress bar always visible when SEC-01 is collapsed
- Floor note restyled to match title font (DM Mono, 12px, uppercase)
- Summary badges show price only (no counts — stepper already shows quantity)
### Print/export changes
- Print now respects user's HST toggle (Phase 1 fix — no longer forces HST on)
- JSON export includes `version: '1.0'` schema field (Phase 1 addition)
- Print CSS hides `sec-controls-row` instead of old `sec-collapsed-counter`
### Dead code removed
- `sec-collapsed-counter` CSS and HTML (replaced by `sec-controls-row`)
- `floorProgress` span (right-side threshold text)
## Runtime areas that must stay intact
- Quote calculations (now verified by automated tests)
- Exports
- Local persistence
- Theme switching
- Mobile quote sync/parity
### Phase 1 bug fixes (verified)
- ADDON_INKY default: $5 → $8 in quote-pricing.js (test expectations updated)
- Onboarding fee manual override preserved across term switches (data-manual-value attribute)
- VoIP fax CSV comment: "Flat/mo" → "Per seat/mo"
- Print HST: now respects user toggle instead of forcing HST on
- JSON export: schema version '1.0' added
- ZT admin supplement: amber nudge warning when ztActive
### Phase 2 visual polish (verified)
- Hardcoded danger icon color → `var(--text-danger)`
- Hardcoded pill-savings active color → `var(--text-pill-savings-active)` (new token in all 4 themes)
- All 4 themes fully token-covered for Sections IIII
- No remaining hardcoded colors in active section CSS
### Phase 3 UX hardening (verified)
- Smooth theme-switch transition (0.25s crossfade via `.theme-transitioning`)
- Nudge crossfade on rotation/nav (180ms fade-out → swap → fade-in)
- Summary badge fade-in on collapse (0.25s animation)
- Addon toggle micro-feedback (0.2s scale pulse)
- Touch targets ≥44px on mobile (close btn, nudge nav, section toggles)
- Mobile focus trap in panel (Tab cycles within, focus returns to pill on close)
- Safe-area insets for notch phones (pill position, panel padding)
### Phase 5 accessibility & performance fixes (verified)
- `aria-expanded` on all section toggles and collapsible headers (12 elements)
- Focus trap on reset confirm modal (Tab cycles within modal)
- `aria-label` on all stepper buttons (12 elements: "Decrease/Increase users/endpoints/etc.")
- Glass theme `background-attachment: scroll` on mobile (≤1100px) to prevent iOS scroll jank
- Mobile sync guard: skips 35+ element sync when panel closed on desktop
- Mobile sync gaps fixed: `sidebarFocusClientName`, `sl-discount-detail`, `sl-value-onboarding-label` added to sync map
- Full sync forced on `openMobilePanel()` to ensure fresh data
### Font Awesome icon fix (verified)
- All 36 FA Sharp Solid icon references in components.css converted from file paths to inline `data:image/svg+xml` URIs
- Icons now render on `file://` protocol without a server
- If adding new icons: use inline data URI format, not file path references
## What still needs caution
- DOM rendering tests (sidebar values, warnings, mobile sync) are not yet automated
- Export/persistence tests are not yet automated
## Required check standard for future edits
- After pricing/engine edits: run `node svsmspcalc/tests/test-quote-engine.js`
- After JS edits: run syntax checks
- After logic edits: review quote math, persistence, export, and mobile parity impact
- After sidebar changes: verify the mobile clone/sync map still covers the changed elements
- After copy or styling edits in sales-critical areas: check readability and messaging

View File

@@ -1,50 +0,0 @@
# Phase Roadmap
## Status
All phases complete. Project is in user-driven stage work.
| Stage | Phase | Work | Status |
|-------|-------|------|--------|
| 1 | 0 | Discovery audit, codebase mapping, doc generation | Complete |
| 2 | 1 | Bug fixes (6 issues: INKY $8, onboarding, VoIP CSV, print HST, JSON, ZT nudge) | Complete |
| 2 | 2 | Visual polish (hardcoded colors → tokens, 4 themes audited) | Complete |
| 2 | 3 | UX hardening (transitions, touch targets, focus traps, safe-area) | Complete |
| 2 | 4 | Documentation & QA (doc sync, regression walkthrough, beta DOD) | Complete |
| — | 5 | Accessibility/performance audit + Font Awesome icon fix | Complete |
| 3 | 6 | Code quality I (tokens, CSS dedup, dead code) | Complete |
| 4 | 7 | Test expansion (88 → 254 tests) | Complete |
| 4 | 8 | Enhanced Print/PDF (rep name, notes, validity, page breaks, CYA) | Complete |
| 5 | 9 | Visual QA (3 breakpoints × 4 themes) + Retro theme overhaul | Complete |
| 6 | 10 | Elastic responsive (5 → 3 breakpoints, fluid clamp() tokens) | Complete |
| 7 | 11 | Feature work: keyboard shortcuts (Ctrl+P/E/R) + 4 nudges | Complete |
| 8 | 12 | Code quality II: transition tokens, specificity, print CSS | Complete |
| 8 | — | Feature fixes: fullscreen print, toggle colors, CSV→JSON pricing | Complete |
| 9 | — | Spacing token consolidation, pricing label sync, Retro visual QA | Complete |
**Tests:** 254/254 passing. `node svsmspcalc/tests/test-quote-engine.js`
## Architecture baseline
- JS split into pricing, engine, render, persistence, export, import, theme, and mobile-sync modules
- CSS split into tokens, base, layout, components, responsive, print, and 3 theme override files
- HTML shell kept stable during the split
- Pricing loads from `package-prices-data.js` via `<script>` tag (no web server needed)
- Automated test suite at `tests/test-quote-engine.js` (254 tests, zero deps)
## Approved work
- Bug fixes
- CSS/UI polish, theme improvements
- Responsive cleanup and hardening
- Spacing, alignment, color consistency fixes
- Documentation cleanup
- Test coverage expansion
- Small dead-code cleanup (after usage verification)
## Do not do without explicit approval
- Broad rewrites or framework migration
- DOM ID or event-contract changes
- Reworking quote math or export structure without a clear bug
- Activating Sections IVVI beyond placeholder state

View File

@@ -0,0 +1,386 @@
# Spacing Token Consolidation — Implementation Plan
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Replace ~183 hardcoded px values across 6 CSS files with design tokens, improving maintainability and theme consistency.
**Architecture:** Three sequential phases — border widths, spacing token adoption, border-radius scale. Each phase touches all affected files, gets verified via Playwright + tests, and is committed independently.
**Tech Stack:** Vanilla CSS custom properties (no build tools). Playwright MCP for visual verification.
**Spec:** `docs/superpowers/specs/2026-03-16-spacing-token-consolidation-design.md`
---
## Pre-flight
- [ ] **Step 0a: Run baseline tests**
Run: `node tests/test-quote-engine.js`
Expected: `254/254 tests passing`
- [ ] **Step 0b: Capture baseline Playwright screenshots**
Use Playwright MCP to capture screenshots for pixel-diff comparison after each phase:
1. Navigate to `SVS-MSP-Calculator.html` at 1920x1080
2. Screenshot Dark theme (default) → save reference
3. Toggle to Light theme → screenshot → save reference
4. Toggle to Glass theme → screenshot → save reference
5. Resize to 375px width → screenshot mobile (Dark) → save reference
These are your "before" images. After each phase, re-capture and visually compare.
---
## Chunk 1: Phase 1 — Border Width Tokens
### Task 1: Add border width tokens to tokens.css
**Files:**
- Modify: `SVS-MSP-Calculator-tokens.css:66-68` (insert before `--radius-control`)
- [ ] **Step 1: Add new tokens**
Insert these 3 lines immediately before `--radius-control: 6px;` (line 67):
```css
--border-thin: 1px;
--border-medium: 2px;
--border-thick: 3px;
```
The `:root` block at this point should read:
```css
--space-4xl: 32px;
--border-thin: 1px;
--border-medium: 2px;
--border-thick: 3px;
--radius-control: 6px;
```
- [ ] **Step 2: Verify no syntax errors**
Run: `node tests/test-quote-engine.js`
Expected: `254/254 tests passing` (CSS-only change, tests should still pass)
---
### Task 2: Tokenize border widths in components.css
**Files:**
- Modify: `SVS-MSP-Calculator-components.css`
- [ ] **Step 1: Replace all `1px` border values**
Search for these patterns and replace. Apply ONLY to border properties (`border`, `border-top`, `border-bottom`, `border-left`, `border-right`, `border-width`, `border-*-width`). Include `solid`, `dashed`, and `dotted` styles.
**DO NOT replace** `1px` in:
- `calc()` expressions
- `box-shadow` values
- `outline` properties
- `pre-alpha/` directory
Replace pattern: `1px solid``var(--border-thin) solid`, `1px dashed``var(--border-thin) dashed`, `border-width: 1px``border-width: var(--border-thin)`, etc.
- [ ] **Step 2: Replace all `2px` border values**
Same pattern as Step 1 but for `2px``var(--border-medium)`. Only border properties — skip `padding: 2px` or `margin: 2px` (those are spacing).
- [ ] **Step 3: Replace all `3px` border values**
Same pattern for `3px``var(--border-thick)`. Only border properties.
- [ ] **Step 4: Verify — run tests**
Run: `node tests/test-quote-engine.js`
Expected: `254/254 tests passing`
---
### Task 3: Tokenize border widths in remaining files
**Files:**
- Modify: `SVS-MSP-Calculator-responsive.css`
- Modify: `SVS-MSP-Calculator-layout.css`
- Modify: `SVS-MSP-Calculator-print.css`
- Modify: `SVS-MSP-Calculator-base.css`
- Modify: `SVS-MSP-Calculator-glass.css`
- [ ] **Step 1: Replace border widths in responsive.css**
Same rules as Task 2. Replace `1px`/`2px`/`3px` in border properties only. Respect all exclusions (calc, box-shadow, outline).
- [ ] **Step 2: Replace border widths in layout.css**
Same rules.
- [ ] **Step 3: Replace border widths in print.css**
Same rules.
- [ ] **Step 4: Replace border widths in base.css**
Known instances:
- `border-bottom: 2px solid var(--top-bar-border)``border-bottom: var(--border-medium) solid var(--top-bar-border)`
- `border: 1px solid var(--theme-chip-border)``border: var(--border-thin) solid var(--theme-chip-border)`
- [ ] **Step 5: Replace border widths in glass.css**
Known instance:
- `border-bottom-width: 1px !important``border-bottom-width: var(--border-thin) !important`
- [ ] **Step 6: Verify — run tests**
Run: `node tests/test-quote-engine.js`
Expected: `254/254 tests passing`
---
### Task 4: Phase 1 visual verification
- [ ] **Step 1: Playwright screenshots — all 3 themes at 1920px**
Navigate to calculator. Screenshot Dark, Light, Glass at 1920x1080. Compare against baseline from Step 0b. Changes should be zero — this is a pure refactor.
- [ ] **Step 2: Playwright screenshots — mobile at 375px**
Resize to 375px. Verify floating MRR pill visible, mobile panel opens, values sync.
- [ ] **Step 3: Playwright — print emulation**
Enable print media emulation. Verify all sections expand, interactive controls hidden, no visual regressions.
- [ ] **Step 4: Commit Phase 1**
```bash
git add SVS-MSP-Calculator-tokens.css SVS-MSP-Calculator-components.css SVS-MSP-Calculator-responsive.css SVS-MSP-Calculator-layout.css SVS-MSP-Calculator-print.css SVS-MSP-Calculator-base.css SVS-MSP-Calculator-glass.css
git commit -m "css: tokenize border widths — consolidate 1px/2px/3px to --border-thin/medium/thick"
```
---
## Chunk 2: Phase 2 — Adopt Existing Spacing Tokens
### Task 5: Replace hardcoded spacing values in components.css
**Files:**
- Modify: `SVS-MSP-Calculator-components.css`
- [ ] **Step 1: Replace `4px` spacing → `var(--space-xs)`**
Search for `margin`, `padding`, and `gap` properties using hardcoded `4px`. Replace with `var(--space-xs)`.
**DO NOT replace** `4px` when used as:
- `border-radius` (Phase 3)
- `font-size`, `width`, `height`
- Inside `clamp()` or `calc()`
- Values that are already `var(--space-xs)`
- [ ] **Step 2: Replace `8px` spacing → `var(--space-sm)`**
Same rules — only `margin`, `padding`, `gap`. Skip `border-radius: 8px` (Phase 3).
- [ ] **Step 3: Replace `10px` spacing → `var(--space-stack-tight)`**
Same rules. Only spacing properties.
- [ ] **Step 4: Replace `12px` spacing → `var(--space-md)`**
Only spacing properties. Skip `border-radius: 12px` (already uses `--radius-card`).
- [ ] **Step 5: Replace `16px` spacing → `var(--space-stack-roomy)`**
Only spacing properties. Skip font-size and width/height uses.
- [ ] **Step 6: Replace `18px` spacing → `var(--space-lg)`**
Only spacing properties. Skip icon sizing.
- [ ] **Step 7: Replace `20px` spacing → `var(--space-xl)`**
Only spacing properties.
- [ ] **Step 8: Verify — run tests**
Run: `node tests/test-quote-engine.js`
Expected: `254/254 tests passing`
---
### Task 6: Replace hardcoded spacing in remaining files
**Files:**
- Modify: `SVS-MSP-Calculator-layout.css`
- Modify: `SVS-MSP-Calculator-responsive.css`
- Modify: `SVS-MSP-Calculator-print.css`
- Modify: `SVS-MSP-Calculator-base.css`
- [ ] **Step 1: Replace spacing values in layout.css**
Same rules as Task 5. Only `margin`, `padding`, `gap` properties. Skip values in `clamp()`.
- [ ] **Step 2: Replace spacing values in responsive.css**
Same rules.
- [ ] **Step 3: Replace spacing values in print.css**
Same rules.
- [ ] **Step 4: Replace spacing values in base.css**
Known instance: `gap: 16px``gap: var(--space-stack-roomy)`. Same exclusion rules apply.
- [ ] **Step 5: Verify — run tests**
Run: `node tests/test-quote-engine.js`
Expected: `254/254 tests passing`
---
### Task 7: Phase 2 visual verification
- [ ] **Step 1: Playwright screenshots — all 3 themes at 1920px**
Compare against Phase 1 screenshots. Changes should be zero.
- [ ] **Step 2: Playwright screenshots — mobile at 375px**
Verify pill, panel, sync all working.
- [ ] **Step 3: Playwright — print emulation**
Enable print media emulation. Verify no regressions from print.css spacing changes.
- [ ] **Step 4: Commit Phase 2**
```bash
git add SVS-MSP-Calculator-components.css SVS-MSP-Calculator-layout.css SVS-MSP-Calculator-responsive.css SVS-MSP-Calculator-print.css SVS-MSP-Calculator-base.css
git commit -m "css: adopt existing spacing tokens — replace hardcoded 4-20px with var(--space-*)"
```
---
## Chunk 3: Phase 3 — Border-Radius Scale
### Task 8: Add border-radius tokens to tokens.css
**Files:**
- Modify: `SVS-MSP-Calculator-tokens.css` (insert after `--border-thick`, before `--radius-control`)
- [ ] **Step 1: Add new radius tokens**
Insert into the radius section in **ascending px order**, interleaving with existing `--radius-control` (6px) and `--radius-card` (12px):
```css
--border-thick: 3px;
--radius-sm: 4px; /* NEW */
--radius-control: 6px; /* existing */
--radius-md: 8px; /* NEW */
--radius-lg: 10px; /* NEW */
--radius-card: 12px; /* existing */
--radius-pill: 999px; /* NEW */
```
This requires inserting at 3 positions:
1. `--radius-sm: 4px;` before `--radius-control`
2. `--radius-md: 8px;` and `--radius-lg: 10px;` between `--radius-control` and `--radius-card`
3. `--radius-pill: 999px;` after `--radius-card`
- [ ] **Step 2: Verify — run tests**
Run: `node tests/test-quote-engine.js`
Expected: `254/254 tests passing`
---
### Task 9: Tokenize border-radius in components.css
**Files:**
- Modify: `SVS-MSP-Calculator-components.css`
- [ ] **Step 1: Replace `border-radius: 4px` → `var(--radius-sm)`**
Search for all `border-radius` properties using `4px`. Replace with `var(--radius-sm)`. Skip values inside `calc()`.
- [ ] **Step 2: Replace `border-radius: 8px` → `var(--radius-md)`**
Same pattern.
- [ ] **Step 3: Replace `border-radius: 10px` → `var(--radius-lg)`**
Same pattern.
- [ ] **Step 4: Replace `border-radius: 999px` → `var(--radius-pill)`**
Same pattern.
- [ ] **Step 5: Verify — run tests**
Run: `node tests/test-quote-engine.js`
Expected: `254/254 tests passing`
---
### Task 10: Tokenize border-radius in remaining files
**Files:**
- Modify: `SVS-MSP-Calculator-responsive.css`
- Modify: `SVS-MSP-Calculator-base.css`
- [ ] **Step 1: Replace border-radius values in responsive.css**
Same rules as Task 9.
- [ ] **Step 2: Replace border-radius values in base.css**
Known instance: `border-radius: 8px``var(--radius-md)`
- [ ] **Step 3: Verify — run tests**
Run: `node tests/test-quote-engine.js`
Expected: `254/254 tests passing`
---
### Task 11: Phase 3 visual verification + final commit
- [ ] **Step 1: Playwright screenshots — all 3 themes at 1920px**
Compare against Phase 2 screenshots. Changes should be zero.
- [ ] **Step 2: Playwright screenshots — mobile at 375px**
Verify pill, panel, sync.
- [ ] **Step 3: Playwright — print emulation**
Final print check.
- [ ] **Step 4: Commit Phase 3**
```bash
git add SVS-MSP-Calculator-tokens.css SVS-MSP-Calculator-components.css SVS-MSP-Calculator-responsive.css SVS-MSP-Calculator-base.css
git commit -m "css: tokenize border-radius — consolidate to --radius-sm/md/lg/pill scale"
```
---
## Post-flight
- [ ] **Step final-a: Run full test suite**
Run: `node tests/test-quote-engine.js`
Expected: `254/254 tests passing`
- [ ] **Step final-b: Final Playwright verification — all themes, all viewports**
Full sweep: Dark/Light/Glass at 1920px + 375px mobile + print. Compare against original baseline screenshots.
- [ ] **Step final-c: Update SESSION-HANDOFF.md**
Record what was done, files modified, test status, and next steps.
- [ ] **Step final-d: Update KNOWN-ISSUES.md**
Mark "Spacing token consolidation — 150+ magic px values" as partially resolved (Phases 1-3 complete, Phases 4-6 remain).

View File

@@ -0,0 +1,202 @@
# Spacing Token Consolidation — Design Spec
**Date:** 2026-03-16
**Status:** Approved
**Scope:** Phases 1-3 (~183 instances across 5 CSS files)
**Approach:** Phase-by-phase across all files, one commit per phase
---
## Context
The SVS MSP Calculator CSS has ~420 hardcoded px values across 75+ unique values. About half already have tokens defined in `SVS-MSP-Calculator-tokens.css` but aren't being used. This consolidation pass addresses the highest-ROI phases: border widths, existing token adoption, and border-radius scale.
**Why now:** The project is moving from alpha to beta. Consolidating magic numbers improves maintainability, makes theme adjustments easier, and reduces regression risk from future CSS changes.
**What's NOT in scope:** Icon/button sizing tokens (Phase 4), typography scale (Phase 5), breakpoint tokens (Phase 6 — CSS vars can't be used in `@media` queries anyway).
---
## Phase 1: Border Width Tokens (~124 instances)
### New tokens (add to `SVS-MSP-Calculator-tokens.css`)
```css
--border-thin: 1px; /* structural borders */
--border-medium: 2px; /* accent borders, summary badges */
--border-thick: 3px; /* sidebar decorators, group strips */
```
### Replacement rules
| Pattern | Replacement |
|---------|-------------|
| `border: 1px solid ...` | `border: var(--border-thin) solid ...` |
| `border-top: 1px solid ...` | `border-top: var(--border-thin) solid ...` |
| `border-bottom: 1px solid ...` | `border-bottom: var(--border-thin) solid ...` |
| `border-left: 1px solid ...` | `border-left: var(--border-thin) solid ...` |
| `border-right: 1px solid ...` | `border-right: var(--border-thin) solid ...` |
| `border-width: 1px` | `border-width: var(--border-thin)` |
| Same patterns for `2px` | Use `var(--border-medium)` |
| `border: 1px dashed ...` | `border: var(--border-thin) dashed ...` |
| `border: 1px dotted ...` | `border: var(--border-thin) dotted ...` |
| `border-*-width: 1px` | `border-*-width: var(--border-thin)` |
| Same patterns for `2px` | Use `var(--border-medium)` |
| Same patterns for `3px` | Use `var(--border-thick)` |
### Files affected
1. `SVS-MSP-Calculator-components.css` (~86 instances)
2. `SVS-MSP-Calculator-responsive.css` (~19 instances)
3. `SVS-MSP-Calculator-layout.css` (~12 instances)
4. `SVS-MSP-Calculator-print.css` (~7 instances)
5. `SVS-MSP-Calculator-base.css` (~2 instances)
6. `SVS-MSP-Calculator-glass.css` (~1 instance)
### Global exclusions
- `pre-alpha/` directory — archived reference, do not modify
- `1px` in `calc()` expressions — visual micro-adjustments
- `1px` in `box-shadow` offsets — shadow geometry, not borders
- `1px` in `outline` — separate concern
- `2px` in non-border contexts (e.g., `padding: 2px`) — that's spacing, not border width
### Commit message
`css: tokenize border widths — consolidate 1px/2px/3px to --border-thin/medium/thick`
---
## Phase 2: Adopt Existing Spacing Tokens (~37 instances)
### No new tokens — use what already exists
| Hardcoded | Token | Count |
|-----------|-------|-------|
| `4px` (spacing) | `var(--space-xs)` | ~12 |
| `8px` (spacing) | `var(--space-sm)` | ~11 |
| `10px` (spacing) | `var(--space-stack-tight)` | ~5 |
| `12px` (spacing) | `var(--space-md)` | ~2 |
| `16px` (spacing) | `var(--space-stack-roomy)` | ~2 |
| `18px` (spacing) | `var(--space-lg)` | ~2 |
| `20px` (spacing) | `var(--space-xl)` | ~3 |
### Replacement rules
Only replace `margin`, `padding`, and `gap` properties. Do NOT replace:
- `border-radius` values (Phase 3)
- `font-size` values (out of scope)
- `width`/`height` values (out of scope)
- Values in `clamp()` or `calc()` expressions
- Intentional off-grid values (`5px`, `9px`, `7px`) — these are deliberate tweaks
### Files affected
1. `SVS-MSP-Calculator-components.css` (~30 instances)
2. `SVS-MSP-Calculator-layout.css` (~4 instances)
3. `SVS-MSP-Calculator-responsive.css` (~2 instances)
4. `SVS-MSP-Calculator-print.css` (~1 instance)
### Commit message
`css: adopt existing spacing tokens — replace hardcoded 4-20px with var(--space-*)`
---
## Phase 3: Border-Radius Scale (~20 instances)
### New tokens (add to `SVS-MSP-Calculator-tokens.css`)
```css
--radius-sm: 4px; /* small corners: pills, badges, small buttons */
--radius-md: 8px; /* medium corners: cards, modals, containers */
--radius-lg: 10px; /* large corners: pill buttons, modal dialogs */
--radius-pill: 999px; /* perfect pill shape */
```
Existing tokens remain unchanged:
- `--radius-control: 6px` — input controls
- `--radius-card: 12px` — section cards
### Replacement rules
| Hardcoded | Token | Count |
|-----------|-------|-------|
| `border-radius: 4px` | `var(--radius-sm)` | ~12 |
| `border-radius: 8px` | `var(--radius-md)` | ~8 |
| `border-radius: 10px` | `var(--radius-lg)` | ~4 |
| `border-radius: 999px` | `var(--radius-pill)` | ~1 |
### Exclusions
- `border-radius` values inside `clamp()` or `calc()`
- Outlier values (`2px`, `3px`, `7px`) — keep as hardcoded, too unique to tokenize
### Files affected
1. `SVS-MSP-Calculator-components.css` (~18 instances)
2. `SVS-MSP-Calculator-responsive.css` (~2 instances)
3. `SVS-MSP-Calculator-base.css` (~1 instance)
### Commit message
`css: tokenize border-radius — consolidate to --radius-sm/md/lg/pill scale`
---
## Verification (per phase)
1. **Tests:** `node tests/test-quote-engine.js` — 254/254 must pass
2. **Visual:** Playwright screenshots at 1920px — Dark, Light, Glass themes
3. **Mobile:** Playwright at 375px — floating MRR pill, bottom sheet, sync
4. **Print:** Playwright print media emulation — no regressions
5. **Diff:** Pixel-compare screenshots before/after — changes should be zero (pure refactor)
---
## Risk Assessment
| Risk | Mitigation |
|------|------------|
| Accidental visual change | Playwright pixel-diff before/after each phase |
| Theme-specific breakage | Light/Glass only override colors/shadows, not border-widths or spacing — low risk |
| Mobile sync breakage | No DOM ID changes — zero risk |
| Print regression | Print CSS uses same tokens — verify with print emulation |
| Token name collision | Checked: no existing `--border-thin/medium/thick` or `--radius-sm/md/lg/pill` tokens |
| `!important` in theme overrides | Tokenizing `1px !important` in glass.css is valid CSS; border widths don't vary by theme so low risk |
---
## Token Summary (final state after all 3 phases)
### New tokens added to `SVS-MSP-Calculator-tokens.css`
```css
/* Border widths */
--border-thin: 1px;
--border-medium: 2px;
--border-thick: 3px;
/* Border radius (additions to existing scale) */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 10px;
--radius-pill: 999px;
```
### Existing tokens (no changes)
```css
/* Spacing (already defined, now actually used) */
--space-xs: 4px;
--space-sm: 8px;
--space-stack-tight: 10px;
--space-md: 12px;
--space-stack-roomy: 16px;
--space-lg: 18px;
--space-xl: 20px;
/* Radius (already defined, unchanged) */
--radius-control: 6px;
--radius-card: 12px;
```