8.3 KiB
Reading all eight files for an independent review across the listed defect categories. Searching for em dashes, side-stripe patterns, and verifying assets exist.
Verdict
[OK] Ship-ready demo with one real handoff gap and a few UX/mobile nits. Nav/aria-current are correct per page, calculator math matches published rates, assets resolve, and the normal left-click estimate flow works. I found no broken internal links and no user-facing em dashes or side-stripe accent borders.
Confidence: high on nav/math/links; medium on narrow-mobile layout (CSS-only inference, not browser-tested).
1. Cross-page header / footer / nav
[OK] Header structure is consistent across all six pages: same brand, six nav links, phone, theme toggle, mobile menu (navLinks, navToggle, themeToggle).
[OK] aria-current="page" is correct on every page:
| Page | Active link |
|---|---|
index.html:38 |
Home |
services.html:26 |
Services |
pricing.html:27 |
Pricing |
calculator.html:28 |
Estimate |
about.html:29 |
About |
contact.html:33 |
Contact |
[WARN] Footer disclaimer copy drifts (not broken, but inconsistent):
index.html:152— longest (estimator + tailored quote)about.html:112— no pricing disclaimer at allcontact.html:106— form-not-wired note only
[INFO] Cosmetic head-script formatting differs (index.html expanded vs minified on other pages). No functional impact.
2. Calculator → contact estimate handoff
[OK] Normal left-click path works. Flow:
var sendBtn = $("#sendEstimate");
if (sendBtn) {
sendBtn.addEventListener("click", function () {
// ... builds summary from #ledgerLines ...
try { sessionStorage.setItem("acg-estimate", summary); } catch (e) {}
});
}
var msg = $("#cf-msg");
if (msg) {
try {
var est = sessionStorage.getItem("acg-estimate");
if (est) {
msg.value = est;
sessionStorage.removeItem("acg-estimate");
#sendEstimateis an<a href="contact.html#contact">(calculator.html:124)- Click handler is synchronous before navigation —
sessionStorageis set in time app.jsis at end of<body>oncontact.html:109, so#cf-msgexists before prefill runs- Anchor target
#contactexists (contact.html:43)
[OK] JS-before/after hash jump is not a data bug. Script runs after DOM parse; hash scroll does not block prefill.
[ERROR] Middle-click / “Open in new tab” drops the estimate. #sendEstimate is a plain link; middle-click and some modifier opens navigate without firing the click listener, so acg-estimate is never written. Left-click only.
[WARN] Hash lands on section top, not the form. contact.html#contact scrolls to the section (id="contact"), not #cf-msg / #contactForm. On desktop two-column layout, the prefilled textarea can be off-screen right; user may not see the handoff without scrolling.
[WARN] No focus management after prefill — textarea is filled but never focused/scrolled into view.
[INFO] One-shot storage — reload clears the estimate (removeItem on read). Intentional, but worth knowing.
3. Calculator math and stepper clamp
[OK] Math matches pricing page rates:
| Input | Rate | Source |
|---|---|---|
| GPS tiers | $19 / $26 / $39 | app.js:99, pricing.html:56-82 |
| Equipment | $25 flat | app.js:78, pricing.html:94 |
| Support | $200–$850 | app.js:94, pricing.html:108-111 |
| M365 | $14/user | app.js:78, pricing.html:154 |
| VoIP | $28/user | app.js:78, pricing.html:166 |
| Hosting | $15 / $35 / $65 | app.js:97, pricing.html:141-143 |
Default (22 endpoints, Pro, Standard support): $952/mo — arithmetic checks out.
[OK] Stepper clamp — clampInt 0–500 (app.js:80), HTML min="0" max="500", buttons use clamp + recalc().
[WARN] Typing invalid numbers — recalc() uses clamped value on input, but the visible field can still show garbage until change blur clamp (app.js:136). Totals can disagree with displayed digits briefly.
4. Links, assets, duplicate IDs
[OK] All internal page links resolve — index, services, pricing, calculator, about, contact, tel:, mailto:.
[OK] Referenced images exist under assets/images/: hero.png, about.png, services.png, story.png, contact.png.
[WARN] Duplicate id="cta-h" on four pages (index.html:137, services.html:88, pricing.html:179, about.html:97). Harmless in multipage navigation; invalid if pages were ever combined.
[WARN] Shared IDs across pages (year, navLinks, themeToggle, etc.) — same note; one page at a time so no runtime collision.
5. Light + dark contrast (new components)
[OK] Token design is deliberate — --ink-3 annotated AA-safe on light paper (styles.css:14); dark tokens mirrored (styles.css:32-44).
[OK] New multipage components (active nav underline, page-head eyebrow, contact labels, CTA band, office figure) use --ink, --ink-2, --ink-3, --accent-ink — consistent with the system.
[WARN] .calc__foot at 0.78rem / --ink-3 (styles.css:374) — likely fine in light mode; dark-mode small muted text is the usual borderline case. Worth a quick contrast pass in browser, not a confirmed fail.
[OK] Primary buttons — --on-accent on --accent is an intentional pairing (styles.css:18-19, 118).
6. Em dashes and side-stripe accents
[OK] No em dashes in user-facing HTML across any of the six pages.
[WARN] Em dashes only in CSS comments (styles.css:2, 17) — violates house style in source, not visible to visitors.
[OK] No forbidden side-stripe accent borders. Only structural rules: .calc__inputs { border-right: 1px solid var(--rule) } (styles.css:310), .tier--pop top inset accent (styles.css:263). Matches DESIGN intent (no colored left/right accent stripes).
7. Mobile
[OK] Major breakpoints covered — nav drawer @880px, hero/story/calc/contact/CTA stack @760–820px, trust 2-col @720px, tables scroll via .table-wrap.
[WARN] Calculator rows stay two-column on all widths — .calc__row { grid-template-columns: 1fr auto } (styles.css:311-312) with no narrow override. Stepper is ~152px fixed; on ~320px viewports labels + controls may crush or overflow.
[WARN] .svc__meta { white-space: nowrap } (styles.css:251) — on very narrow screens, right-aligned meta could overflow when not yet in stacked mobile layout.
[OK] Mobile nav — aria-expanded toggles, Escape closes, link click closes drawer (app.js:57-69).
[ERROR] Progressive enhancement gap — .reveal { opacity: 0 } (styles.css:427) until app.js adds .in. If JS fails, large sections stay invisible sitewide.
Ranked top fixes
| Rank | Severity | Issue | Where |
|---|---|---|---|
| 1 | [ERROR] | Estimate handoff fails on middle-click / open-in-new-tab because storage write is click-only on an <a> |
calculator.html:124, app.js:140-153 |
| 2 | [ERROR] | Content hidden without JS (.reveal starts at opacity: 0) |
styles.css:427-428, all pages |
| 3 | [WARN] | After handoff, user may not see prefilled message (hash → section, not form; no focus/scroll) | calculator.html:124, contact.html:43-76, app.js:192-201 |
| 4 | [WARN] | Calculator input rows don’t stack on very narrow phones | styles.css:311-312, 375-378 |
| 5 | [WARN] | Invalid stepper text vs calculated total until blur | app.js:136-137, 156-157 |
| 6 | [INFO] | Footer disclaimer text inconsistent | all footers |
| 7 | [INFO] | Duplicate id="cta-h" across four pages |
see §4 |
| 8 | [INFO] | Em dashes in CSS comments only | styles.css:2, 17 |
Bottom line: The multipage shell is coherent and the primary estimate workflow is sound. Fix #1 (handoff reliability) and #2 (no-JS visibility) first; the rest are polish. I did not modify any files per your instruction.