sync: auto-sync from GURU-5070 at 2026-06-14 20:04:14

Author: Mike Swanson
Machine: GURU-5070
Timestamp: 2026-06-14 20:04:14
This commit is contained in:
2026-06-14 20:05:02 -07:00
parent 30933bd35d
commit c5d4d3527c
119 changed files with 5204 additions and 1 deletions

View File

@@ -0,0 +1,35 @@
# Arizona Computer Guru — Website Showcase
Single-page, hand-built static site (HTML/CSS/vanilla JS). A local "what we can do" showcase.
## Art direction: SONORAN LEDGER
Chosen by Grok+Gemini design panel (both ranked #1), confirmed by Mike (human review 2026-06-14).
Concept: a trusted local bookkeeper's ledger for the web. Pricing, the calculator, and the
concierge story are treated as a permanent, transparent record — the design embodiment of
"honest numbers, no games." Warm paper, precise rules, mono numerals.
## Design system (locked)
- **Light:** paper `#F7F3EB`, surface `#EDE6D9`, ink `#2A2521`, ink-2 `#5A5148`.
- **Dark:** paper `#1C1814`, surface `#2A2520`, ink `#E8DFCE`, ink-2 `#C4B8A3`.
- **Accent (the "ink"):** amber `#F2922E` for marks/underlines/button fill (dark text on it).
- Orange-on-cream TEXT fails AA at the bright value → orange text/links on light use `#BD5A00`.
- On dark, accent text uses `#F2A24E` (passes AA on `#1C1814`).
- **Type:** Barlow Condensed (600700) display + "since 2001" lockup; Lexend body;
JetBrains Mono for ALL prices, calculator output, endpoint counts.
- **Baseline:** strict 24px vertical rhythm. Faint ledger rulings align to Lexend line-height.
- **Radius:** 02px only. Sharp = precise/manual.
## Anti-slop rules (Gemini, non-negotiable)
1. Orange = highlighting ink — active data, underlines, primary CTA only. Never big blocks/generic icons.
2. Zero generic iconography (no FontAwesome/Heroicons). CSS-drawn precision marks / hardware photos only.
3. Ledger grid — everything snaps to 24px baseline; rulings align to body line-height.
4. Data as design — calculator is the centerpiece, not a popup. Large mono "stamped" totals.
5. No over-smoothed radius — sharp corners.
## Pipeline
Grok (imagery + direction input) → Gemini (design weight) → human pick → build →
multi-AI design+code review → human-touch review → publish locally.
## Content integrity
No fabricated named testimonials (prior site flagged this as a launch blocker). Proof is built
on verifiable facts; any testimonial slot is clearly marked representative.

View File

@@ -0,0 +1,50 @@
# Arizona Computer Guru — Website Showcase
A self-contained, hand-built single-page marketing site for Arizona Computer Guru,
produced as a local "what we can do" test piece. No framework, no build step.
**Art direction:** "Sonoran Ledger" — warm paper, a bookkeeper's-ledger rhythm,
mono numerals, orange used only as sparse accent "ink." Chosen by a Grok + Gemini
design panel and confirmed by Mike. See `DESIGN.md`.
## View it
From this folder:
```powershell
powershell -ExecutionPolicy Bypass -File serve.ps1 # serves :4327 and opens a browser
```
or any static server, e.g.:
```bash
py -m http.server 4327 # then open http://localhost:4327/
```
It also works opened directly as `index.html` (file://), though serving is cleaner
(Google Fonts and JSON-LD behave best over http).
## What's inside
| Path | Purpose |
|---|---|
| `index.html` | The page — semantic, accessible, all sections |
| `css/styles.css` | Sonoran Ledger design system (light + dark themes) |
| `js/app.js` | Theme toggle, IT-cost calculator, FAQ, mobile nav, reveal |
| `assets/images/` | Grok-generated photography (hero, story, trust) + paper texture |
| `design/` | Pipeline artifacts: AI design directions, reviews, render screenshots |
| `DESIGN.md` | Locked design system + anti-slop rules |
## Features
- Light **and** dark themes (system-aware, remembered, no flash-of-wrong-theme).
- Live **IT cost calculator** — endpoints, GPS tier, support plan, M365, phones,
hosting → monthly/annual/all-in-per-endpoint. "Send me this estimate" hands the
built statement to the contact form.
- Published GPS pricing, services, FAQ, and concierge story from ACG's own materials.
- WCAG-AA contrast in both themes; keyboard-accessible nav, FAQ, and forms.
## Build pipeline (how it was made)
Grok (art-direction input + image generation) → Gemini (design-weight decision) →
human pick → hand-built static site → Gemini + Grok design & code review → fixes →
Gemini vision final design gate → polish → local publish.
## Not production
This is a demonstration build. The contact form is not wired to a mailbox, pricing
is illustrative of published GPS rates, and photography is representational. The live
site project lives at `clients/azcomputerguru.com/` (Astro).

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

View File

@@ -0,0 +1,424 @@
/* ===========================================================================
ARIZONA COMPUTER GURU — "Sonoran Ledger"
Hand-built. Warm paper, precise rules, mono numerals. No framework.
Baseline grid: 24px. Radius: 02px. Orange = ink, used sparingly.
=========================================================================== */
/* ---- Tokens ------------------------------------------------------------- */
:root {
--paper: #F7F3EB;
--surface: #EDE6D9;
--surface-2: #E4DACA;
--ink: #2A2521;
--ink-2: #5A5148;
--ink-3: #6D6456; /* AA-safe for small text on cream (~4.6:1) */
--rule: rgba(90, 81, 72, 0.16);
--rule-soft: rgba(90, 81, 72, 0.09);
--accent: #F2922E; /* the amber "ink" — fills, marks, underlines */
--accent-ink: #BD5A00; /* orange TEXT on light paper (AA-safe ~4.9:1) */
--on-accent: #2A2521; /* text sitting on an amber fill */
--good: #4F7A3F;
--maxw: 1140px;
--base: 24px; /* baseline unit */
--f-display: "Barlow Condensed", "Arial Narrow", system-ui, sans-serif;
--f-body: "Lexend", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
--f-mono: "JetBrains Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;
--shadow: 0 1px 0 var(--rule), 0 18px 40px -28px rgba(40, 33, 25, 0.45);
}
[data-theme="dark"] {
--paper: #1C1814;
--surface: #2A2520;
--surface-2: #322B24;
--ink: #E8DFCE;
--ink-2: #C4B8A3;
--ink-3: #9A8C77;
--rule: rgba(196, 184, 163, 0.16);
--rule-soft: rgba(196, 184, 163, 0.08);
--accent: #F2A24E;
--accent-ink: #F4A85C; /* orange text on dark paper (AA-safe) */
--on-accent: #1C1814;
--good: #8FB97E;
--shadow: 0 1px 0 var(--rule), 0 22px 48px -30px rgba(0, 0, 0, 0.7);
}
/* ---- Reset -------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
* { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}
body {
background: var(--paper);
color: var(--ink);
font-family: var(--f-body);
font-size: 1rem;
line-height: 1.5; /* 24px at 16px → on-grid */
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
transition: background 0.4s ease, color 0.4s ease;
}
img { display: block; max-width: 100%; height: auto; }
a { color: var(--accent-ink); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--ink); }
::selection { background: var(--accent); color: var(--on-accent); }
.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
/* ---- Layout primitives -------------------------------------------------- */
.wrap { width: min(100% - 3rem, var(--maxw)); margin-inline: auto; }
.wrap--narrow { width: min(100% - 3rem, 760px); margin-inline: auto; }
section { padding-block: calc(var(--base) * 3.5); }
.section-tag {
font-family: var(--f-mono);
font-size: 0.72rem;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--accent-ink);
display: flex; align-items: center; gap: 0.75rem;
margin-bottom: var(--base);
}
.section-tag::before { /* precision mark, not an icon font */
content: ""; width: 28px; height: 0;
border-top: 2px solid var(--accent);
}
.section-tag span { color: var(--ink-3); }
h1, h2, h3, h4 { font-family: var(--f-display); font-weight: 600; line-height: 1.04;
letter-spacing: -0.01em; color: var(--ink); }
h2 { font-size: clamp(2rem, 4.5vw, 3.25rem); margin-bottom: var(--base); }
h3 { font-size: 1.5rem; font-weight: 600; }
.lead { font-size: 1.18rem; color: var(--ink-2); max-width: 60ch; }
.muted { color: var(--ink-2); }
.mono { font-family: var(--f-mono); font-variant-numeric: tabular-nums; }
/* underline "ink" used on key phrases */
.ul { background-image: linear-gradient(var(--accent), var(--accent));
background-size: 100% 2px; background-position: 0 92%; background-repeat: no-repeat;
padding-bottom: 1px; }
/* ---- Buttons ------------------------------------------------------------ */
.btn {
font-family: var(--f-display); font-weight: 600; font-size: 1.05rem;
letter-spacing: 0.01em;
display: inline-flex; align-items: center; gap: 0.6rem;
padding: 0.7rem 1.4rem; border: 1px solid transparent; border-radius: 2px;
cursor: pointer; text-decoration: none; transition: transform 0.15s ease, background 0.2s ease;
}
.btn:active { transform: translateY(1px); }
.btn--primary { background: var(--accent); color: var(--on-accent); }
.btn--primary:hover { color: var(--on-accent); filter: brightness(1.05); }
.btn--ghost { background: transparent; color: var(--ink); border-color: var(--rule); }
.btn--ghost:hover { border-color: var(--accent); color: var(--ink); }
.btn .arrow { font-family: var(--f-mono); }
/* ---- Header ------------------------------------------------------------- */
.site-header {
position: sticky; top: 0; z-index: 50;
background: color-mix(in srgb, var(--paper) 86%, transparent);
backdrop-filter: blur(8px);
border-bottom: 1px solid var(--rule);
}
.site-header .wrap { display: flex; align-items: center; gap: 1.5rem;
min-height: calc(var(--base) * 3); }
.brand { display: flex; align-items: baseline; gap: 0.6rem; text-decoration: none; color: var(--ink); }
.brand__mark { /* CSS-drawn monogram, not an image icon */
font-family: var(--f-display); font-weight: 700; font-size: 1.05rem;
letter-spacing: 0.05em; color: var(--on-accent); background: var(--accent);
padding: 0.15rem 0.5rem; border-radius: 2px; align-self: center;
}
.brand__name { font-family: var(--f-display); font-weight: 600; font-size: 1.35rem;
letter-spacing: 0.005em; }
.brand__since { font-family: var(--f-mono); font-size: 0.66rem; letter-spacing: 0.18em;
text-transform: uppercase; color: var(--ink-3); }
.nav { margin-left: auto; display: flex; align-items: center; gap: 1.2rem; }
.nav__links { display: flex; align-items: center; gap: 1.6rem; }
.nav a { font-family: var(--f-body); font-size: 0.92rem; color: var(--ink-2);
text-decoration: none; }
.nav a:hover { color: var(--ink); }
.nav__phone { font-family: var(--f-mono); color: var(--ink) !important; font-weight: 500; }
.theme-toggle, .nav__toggle {
background: transparent; border: 1px solid var(--rule); border-radius: 2px;
width: 44px; height: 44px; cursor: pointer; color: var(--ink);
display: grid; place-items: center; font-size: 1.1rem; transition: border-color 0.2s;
}
.theme-toggle:hover, .nav__toggle:hover { border-color: var(--accent); }
.nav__toggle { display: none; font-family: var(--f-mono); line-height: 1; }
@media (max-width: 880px) {
.nav__toggle { display: grid; }
.nav__links {
position: absolute; top: 100%; left: 0; right: 0;
flex-direction: column; align-items: stretch; gap: 0;
background: var(--paper); border-bottom: 1px solid var(--rule);
box-shadow: var(--shadow); padding: 0.5rem 0;
max-height: 0; overflow: hidden; visibility: hidden;
transition: max-height 0.25s ease, visibility 0.25s ease;
}
.site-header.nav-open .nav__links { max-height: 60vh; visibility: visible; }
.nav__links a { padding: 0.85rem 1.5rem; font-size: 1.05rem;
border-top: 1px solid var(--rule-soft); }
}
/* ---- Hero --------------------------------------------------------------- */
.hero { padding-block: calc(var(--base) * 3) calc(var(--base) * 4); position: relative; }
.hero .wrap { display: grid; grid-template-columns: 1.05fr 0.95fr; gap: calc(var(--base) * 2);
align-items: center; }
.hero__eyebrow { font-family: var(--f-mono); font-size: 0.78rem; letter-spacing: 0.2em;
text-transform: uppercase; color: var(--accent-ink); margin-bottom: var(--base); }
.hero h1 { font-size: clamp(2.6rem, 6vw, 4.6rem); font-weight: 700; }
.hero h1 .amp { color: var(--accent-ink); white-space: nowrap; }
.hero__sub { font-size: 1.3rem; color: var(--ink-2); margin-top: var(--base);
max-width: 38ch; }
.hero__cta { display: flex; gap: 1rem; margin-top: calc(var(--base) * 1.5); flex-wrap: wrap; }
.hero__note { font-family: var(--f-mono); font-size: 0.78rem; color: var(--ink-3);
margin-top: var(--base); }
.hero__frame {
border: 1px solid var(--rule); border-radius: 2px; overflow: hidden;
box-shadow: var(--shadow); background: var(--surface); position: relative;
}
.hero__frame img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }
.hero__caption {
font-family: var(--f-mono); font-size: 0.72rem; color: var(--ink-3);
padding: 0.6rem 0.9rem; border-top: 1px solid var(--rule);
display: flex; justify-content: space-between; background: var(--surface);
}
@media (max-width: 860px) {
.hero .wrap { grid-template-columns: 1fr; }
.hero__frame { order: -1; }
}
/* ---- Ledger ruling background (signature) ------------------------------- */
.ledger {
background-image: repeating-linear-gradient(
to bottom, transparent, transparent 23px, var(--rule-soft) 23px, var(--rule-soft) 24px);
background-position: 0 0;
}
/* ---- Trust strip -------------------------------------------------------- */
.trust { border-block: 1px solid var(--rule); background: var(--surface); }
.trust .wrap { display: grid; grid-template-columns: repeat(4, 1fr);
gap: 1px; background: var(--rule); }
.trust__cell { background: var(--surface); padding: calc(var(--base) * 1.25) 1.25rem; }
.trust__num { font-family: var(--f-mono); font-size: 1.9rem; font-weight: 600;
color: var(--ink); line-height: 1; }
.trust__num .u { color: var(--accent-ink); }
.trust__label { font-size: 0.86rem; color: var(--ink-2); margin-top: 0.5rem; }
@media (max-width: 720px) { .trust .wrap { grid-template-columns: repeat(2, 1fr); } }
/* ---- Concierge story ---------------------------------------------------- */
.story .wrap { display: grid; grid-template-columns: 1fr 1fr; gap: calc(var(--base) * 2.2);
align-items: center; }
.story__img { border: 1px solid var(--rule); border-radius: 2px; overflow: hidden;
box-shadow: var(--shadow); }
.story__img img { aspect-ratio: 3 / 2; object-fit: cover; width: 100%; }
.story p + p { margin-top: var(--base); }
.story blockquote {
font-family: var(--f-display); font-weight: 500; font-size: 1.6rem; line-height: 1.15;
color: var(--ink); border-left: 2px solid var(--accent); padding-left: 1.1rem;
margin-top: calc(var(--base) * 1.2);
}
@media (max-width: 820px) {
.story .wrap { grid-template-columns: 1fr; }
.story__img { order: -1; }
}
/* ---- Services (continuous ledger list, not cards) ----------------------- */
.svc-list { border-top: 1px solid var(--rule); margin-top: var(--base); }
.svc {
display: grid; grid-template-columns: 2.4rem 1fr auto; gap: 1.25rem;
align-items: baseline; padding-block: calc(var(--base) * 0.9);
border-bottom: 1px solid var(--rule); transition: background 0.2s ease;
}
.svc:hover { background: var(--surface); }
.svc__no { font-family: var(--f-mono); font-size: 0.8rem; color: var(--ink-3); }
.svc__name { font-family: var(--f-display); font-size: 1.5rem; font-weight: 600; }
.svc__desc { color: var(--ink-2); font-size: 0.98rem; margin-top: 0.25rem; max-width: 60ch; }
.svc__meta { font-family: var(--f-mono); font-size: 0.82rem; color: var(--accent-ink);
text-align: right; white-space: nowrap; }
@media (max-width: 680px) {
.svc { grid-template-columns: 1.8rem 1fr; }
.svc__meta { grid-column: 2; text-align: left; }
}
/* ---- Pricing rate card -------------------------------------------------- */
.pricing { background: var(--surface); border-block: 1px solid var(--rule); }
.rate-card { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
background: var(--rule); border: 1px solid var(--rule); border-radius: 2px; overflow: hidden; }
.tier { background: var(--paper); padding: calc(var(--base) * 1.5) 1.5rem; position: relative;
align-self: stretch; }
.tier--pop { background: var(--surface-2); box-shadow: inset 0 3px 0 var(--accent); }
.tier__flag { position: absolute; top: 0; right: 0; font-family: var(--f-mono);
font-size: 0.64rem; letter-spacing: 0.16em; text-transform: uppercase;
background: var(--accent); color: var(--on-accent); padding: 0.25rem 0.6rem; }
.tier__name { font-family: var(--f-display); font-size: 1.7rem; font-weight: 600; }
.tier__price { font-family: var(--f-mono); font-size: 2.6rem; font-weight: 700;
color: var(--ink); margin-top: 0.5rem; line-height: 1; }
.tier__price .per { font-size: 0.9rem; color: var(--ink-3); font-weight: 400; }
.tier__blurb { color: var(--ink-2); font-size: 0.92rem; margin-top: 0.75rem;
padding-bottom: var(--base); border-bottom: 1px solid var(--rule); }
.tier ul { list-style: none; padding: 0; margin-top: var(--base); display: grid; gap: 0.5rem; }
.tier li { font-size: 0.92rem; color: var(--ink-2); padding-left: 1.4rem; position: relative; }
.tier li::before { content: "+"; position: absolute; left: 0; color: var(--accent-ink);
font-family: var(--f-mono); font-weight: 700; }
.tier li.is-base::before { content: "\2713"; }
@media (max-width: 820px) { .rate-card { grid-template-columns: 1fr; } }
.plans { margin-top: calc(var(--base) * 2); }
.plan-table { width: 100%; border-collapse: collapse; font-size: 0.95rem; }
.plan-table caption { text-align: left; font-family: var(--f-mono); font-size: 0.74rem;
letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-3);
padding-bottom: 0.75rem; }
.plan-table th, .plan-table td { text-align: left; padding: 0.7rem 0.9rem;
border-bottom: 1px solid var(--rule); }
.plan-table thead th { font-family: var(--f-mono); font-size: 0.72rem; letter-spacing: 0.1em;
text-transform: uppercase; color: var(--ink-3); font-weight: 500; }
.plan-table td.num, .plan-table th.num { font-family: var(--f-mono); text-align: right;
font-variant-numeric: tabular-nums; }
.plan-table tr:hover td { background: var(--surface); }
.plan-table .pop td { color: var(--ink); }
.plan-table .pop td:first-child::after { content: " \25C6"; color: var(--accent-ink); }
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.plan-table { min-width: 460px; }
/* visible keyboard focus everywhere interactive */
.btn:focus-visible, a:focus-visible, button:focus-visible, .faq__q:focus-visible,
.theme-toggle:focus-visible, .nav__toggle:focus-visible {
outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px;
}
/* ---- Calculator (centerpiece) ------------------------------------------ */
.calc { position: relative; }
.calc__shell {
border: 1px solid var(--rule); border-radius: 2px; box-shadow: var(--shadow);
background: var(--paper); overflow: hidden;
}
.calc__grid { display: grid; grid-template-columns: 1.15fr 0.85fr; }
.calc__inputs { padding: calc(var(--base) * 1.5); border-right: 1px solid var(--rule); }
.calc__row { display: grid; grid-template-columns: 1fr auto; align-items: center;
gap: 1rem; padding-block: calc(var(--base) * 0.6); border-bottom: 1px dashed var(--rule); }
.calc__row label { font-size: 0.98rem; color: var(--ink); }
.calc__row .hint { display: block; font-size: 0.78rem; color: var(--ink-3); margin-top: 0.15rem; }
.calc__control { display: flex; align-items: center; gap: 0.5rem; }
.stepper { display: inline-flex; align-items: stretch; border: 1px solid var(--rule);
border-radius: 2px; overflow: hidden; }
.stepper button { width: 44px; min-height: 44px; border: 0; background: var(--surface);
color: var(--ink); font-family: var(--f-mono); font-size: 1.2rem; cursor: pointer; }
.stepper button:hover { background: var(--accent); color: var(--on-accent); }
.stepper input { width: 64px; height: 44px; border: 0; text-align: center; background: var(--paper);
color: var(--ink); font-family: var(--f-mono); font-size: 1.05rem; font-weight: 600;
appearance: textfield; -moz-appearance: textfield; }
.stepper input::-webkit-outer-spin-button, .stepper input::-webkit-inner-spin-button {
-webkit-appearance: none; margin: 0; }
select, .calc__control select {
font-family: var(--f-body); font-size: 0.95rem; color: var(--ink);
background: var(--paper); border: 1px solid var(--rule); border-radius: 2px;
padding: 0.5rem 2rem 0.5rem 0.7rem; cursor: pointer;
appearance: none;
background-image: linear-gradient(45deg, transparent 50%, var(--ink-3) 50%),
linear-gradient(135deg, var(--ink-3) 50%, transparent 50%);
background-position: calc(100% - 16px) 55%, calc(100% - 11px) 55%;
background-size: 5px 5px, 5px 5px; background-repeat: no-repeat;
}
select:focus, .stepper input:focus, input:focus { outline: 2px solid var(--accent);
outline-offset: 1px; }
.toggle-row { display: flex; align-items: center; gap: 0.6rem; }
.switch { position: relative; width: 44px; height: 24px; flex: none; }
.switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.switch .track { position: absolute; inset: 0; background: var(--surface-2);
border: 1px solid var(--rule); border-radius: 2px; transition: background 0.2s; }
.switch .knob { position: absolute; top: 2px; left: 2px; width: 18px; height: 18px;
background: var(--ink-3); border-radius: 1px; transition: transform 0.2s, background 0.2s; }
.switch input:checked + .track { background: color-mix(in srgb, var(--accent) 35%, var(--surface)); }
.switch input:checked + .track + .knob, .switch input:checked ~ .knob { transform: translateX(20px);
background: var(--accent); }
/* ledger output side */
.calc__out { padding: calc(var(--base) * 1.5); background: var(--surface);
display: flex; flex-direction: column; }
.calc__out h3 { font-size: 1.15rem; }
.ledger-lines { margin-top: var(--base); flex: 1; }
.lline { display: grid; grid-template-columns: 1fr auto; align-items: baseline;
font-family: var(--f-mono); font-size: 0.9rem; padding-block: 0.45rem;
color: var(--ink-2); }
.lline .lcost { color: var(--ink); font-variant-numeric: tabular-nums; }
.lline.dots .lname { position: relative; }
.lline.is-zero { display: none; }
.calc__divider { border: 0; border-top: 1px solid var(--ink-3); margin-block: 0.75rem; }
.calc__total { display: grid; grid-template-columns: 1fr auto; align-items: end; }
.calc__total .tlabel { font-family: var(--f-display); font-size: 1.1rem; letter-spacing: 0.02em;
text-transform: uppercase; color: var(--ink-2); }
.calc__total .tnum { font-family: var(--f-mono); font-size: 2.9rem; font-weight: 700;
color: var(--ink); line-height: 1; }
.calc__annual { font-family: var(--f-mono); font-size: 0.82rem; color: var(--ink-3);
text-align: right; margin-top: 0.4rem; }
.calc__perep { font-family: var(--f-mono); font-size: 0.82rem; color: var(--accent-ink);
margin-top: 0.2rem; text-align: right; }
.calc__cta { margin-top: var(--base); }
.calc__foot { font-size: 0.78rem; color: var(--ink-3); margin-top: 1rem; }
@media (max-width: 820px) {
.calc__grid { grid-template-columns: 1fr; }
.calc__inputs { border-right: 0; border-bottom: 1px solid var(--rule); }
}
/* ---- FAQ ---------------------------------------------------------------- */
.faq { background: var(--surface); border-block: 1px solid var(--rule); }
.faq__item { border-bottom: 1px solid var(--rule); }
.faq__q { width: 100%; text-align: left; background: transparent; border: 0; cursor: pointer;
padding: calc(var(--base) * 0.9) 0; display: flex; justify-content: space-between;
gap: 1.5rem; align-items: center; color: var(--ink); font-family: var(--f-display);
font-size: 1.25rem; font-weight: 600; }
.faq__q .pm { font-family: var(--f-mono); color: var(--accent-ink); font-size: 1.4rem;
flex: none; transition: transform 0.2s; }
.faq__q[aria-expanded="true"] .pm { transform: rotate(45deg); }
.faq__a { overflow: hidden; max-height: 0; visibility: hidden;
transition: max-height 0.3s ease, visibility 0.3s ease; }
.faq__q[aria-expanded="true"] + .faq__a { visibility: visible; }
.faq__a-inner { padding-bottom: calc(var(--base) * 1.1); color: var(--ink-2); max-width: 70ch; }
/* ---- Contact ------------------------------------------------------------ */
.contact { padding-block: calc(var(--base) * 4.5); }
.contact .wrap { display: grid; grid-template-columns: 1fr 1fr; gap: calc(var(--base) * 2.5);
align-items: start; }
.contact__lines { font-family: var(--f-mono); margin-top: var(--base); display: grid; gap: 0.6rem; }
.contact__lines .k { color: var(--ink-3); font-size: 0.72rem; letter-spacing: 0.14em;
text-transform: uppercase; }
.contact__lines .v { color: var(--ink); font-size: 1.05rem; }
.contact__lines a { color: var(--ink); }
.contact__lines a:hover { color: var(--accent-ink); }
.form-field { margin-bottom: var(--base); }
.form-field label { display: block; font-size: 0.85rem; color: var(--ink-2);
margin-bottom: 0.4rem; font-family: var(--f-mono); letter-spacing: 0.06em;
text-transform: uppercase; }
.form-field input, .form-field textarea {
width: 100%; font-family: var(--f-body); font-size: 1rem; color: var(--ink);
background: var(--paper); border: 1px solid var(--rule); border-radius: 2px;
padding: 0.65rem 0.8rem; }
.form-field textarea { min-height: calc(var(--base) * 4); resize: vertical; }
.form-note { font-size: 0.82rem; color: var(--ink-3); margin-top: 0.5rem; }
@media (max-width: 820px) { .contact .wrap { grid-template-columns: 1fr; } }
/* ---- Footer ------------------------------------------------------------- */
.site-footer { border-top: 1px solid var(--rule); padding-block: calc(var(--base) * 1.5);
background: var(--paper); }
.site-footer .wrap { display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between;
align-items: center; }
.site-footer p { font-size: 0.84rem; color: var(--ink-3); font-family: var(--f-mono); }
.site-footer .disclaimer { width: 100%; font-size: 0.74rem; color: var(--ink-3);
border-top: 1px dashed var(--rule); padding-top: 0.9rem; margin-top: 0.5rem; }
/* reveal-on-scroll */
.reveal { opacity: 0; transform: translateY(12px); transition: opacity 0.6s ease, transform 0.6s ease; }
.reveal.in { opacity: 1; transform: none; }

View File

@@ -0,0 +1 @@
at-written.

View File

@@ -0,0 +1,34 @@
You are the LEAD design authority making the final call on art direction for a website. A second model (Grok) has proposed three directions as INPUT. Your job is to weigh them and decide.
PROJECT: Flagship single-page marketing website for "Arizona Computer Guru" (ACG), a Tucson MSP/IT-services company since 2001. Differentiator: "concierge IT" — go far beyond typical IT companies, genuine client relationships, transparent honest pricing, kind+direct (never condescending), local Tucson presence. Brand cues: warm amber-orange accent oklch(0.70 0.18 55), display font Barlow Condensed, body font Lexend.
HARD REQUIREMENTS: (1) must NOT look like generic AI-slop SaaS template; (2) genuinely beautiful + distinctive; (3) ALL text readable in BOTH light and dark themes (WCAG AA contrast, ~4.5:1 body); (4) interactive IT-cost calculator; (5) sections: hero, concierge story, services, GPS pricing tiers, trust/proof, FAQ, contact. Built as hand-crafted static HTML/CSS/vanilla-JS.
GROK'S THREE PROPOSED DIRECTIONS:
== 1. SONORAN LEDGER ==
Concept: page feels like a trusted local bookkeeper's ledger for the web — warm paper surfaces, precise horizontal rules, restrained type; pricing/calculator/story treated as permanent records, no marketing theater.
Light: bg cream #F7F3EB, surface #EDE6D9, text #2A2521, secondary #5A5148, orange accent only on active numbers/underlines/primary button. Dark: bg #1C1814, surface #2A2520, text #E8DFCE, secondary #C4B8A3, desaturated orange.
Type: Barlow Condensed 600-700 headlines + "Since 2001" lockup; Lexend body; JetBrains Mono for ALL prices/calculator output/endpoint counts (ledger precision).
Layout: contained hero, long single-column story with subtle rules, services as continuous text with orange underlines (not cards), pricing as a wide ruled rate card, calculator = the largest break (full-width "open ledger page" with live totals), quiet FAQ/contact.
Signature: faint continuous warm-gray ledger rulings (0.5-1px) behind story/pricing/calculator on a shared baseline grid so the whole scroll reads as one book.
== 2. DESERT THRESHOLD ==
Concept: site experienced as a sequence of architectural thresholds — moving from bright desert into shaded well-kept rooms; thick walls, deep reveals, Tucson 5pm light.
Light: bg plaster #F4F0E8, surface sand #E6DFD2, text #24201C, secondary #51463C, orange on door-like details + calculator action. Dark: bg umber #1A1612, surface #25211C, text #EDE3D3, secondary #B8A998.
Type: Barlow Condensed section titles + big "concierge" word; Lexend body; a quiet condensed geometric (Inter) for nav/buttons/calculator controls only.
Layout: tall framed hero, story = wide text column vs full-height "wall" image, services as a stepped vertical "hallway" list, pricing tiers as three recessed "openings in a thick wall" (not cards), calculator in its own deep niche, tighter FAQ/contact.
Signature: every section divider is a thick horizontal "beam/lintel" (CSS border+shadow) like moving under architecture; calculator has the deepest reveal, sits lower.
== 3. THE WORKBENCH ==
Concept: everything arranged on one long solid workbench; honest materials (wood, metal, paper, warm light); visitor sits across from the craftsman; no theater.
Light: bg warm gray #F1EDE5, surface wood #D9CDB8, text #2C2621, secondary #5C5146. Dark: bg #1D1914, surface #2E2720, text #E6D9C7, secondary #A99A85.
Type: Barlow Condensed headlines + pricing tier names only; Lexend everything else; no third face.
Layout: low wide hero "across the bench top", story two tight columns above a horizontal work-surface band, services as running inventory down left edge, pricing + calculator on widest "continuous bench surface" (tiers left, live calculator right), trust as small object photos.
Signature: pricing tiers + calculator rendered as physical objects resting on one continuous wooden plane with realistic soft shadows.
YOUR TASK:
1. For EACH direction: assess (a) how distinctive/anti-generic it truly is, (b) light+dark contrast/readability risk given the proposed palettes — call out ANY pairs that fail ~4.5:1 and give corrected hex, (c) build feasibility in static HTML/CSS, (d) the single biggest risk.
2. Make the CALL: which direction to build (you may recommend a refined hybrid). Justify against "concierge, premium, Tucson-local, anti-generic" and against readability in both themes.
3. Give the build 4-5 concrete, non-negotiable design rules to guarantee it's beautiful and NOT AI-slop.
Keep it tight and decisive, under ~650 words.

View File

@@ -0,0 +1,45 @@
[OK] VERDICT: Build a refined version of **SONORAN LEDGER**.
This direction captures the "Concierge" and "Since 2001" essence through the lens of precision, longevity, and transparency. It is the strongest antidote to SaaS-slop because it trades "shiny tech" for "trusted record."
---
### 1. DIRECTION ASSESSMENT
**== 1. SONORAN LEDGER ==**
* **(a) Distinctiveness:** Very High. It reframes IT as a meticulous craft rather than an abstract cloud service. It feels established and high-end.
* **(b) Contrast Risk:** Light Theme: Orange accent `oklch(0.70 0.18 55)` (~#FF8D2E) on Cream `#F7F3EB` is ~2.3:1. **[ERROR]** This fails for text. *Correction:* Darken orange to `#BD5A00` for text links/active numbers. Dark Theme: Orange on `#1C1814` is ~6.2:1 (Pass).
* **(c) Build Feasibility:** Extreme. CSS Grid + `repeating-linear-gradient` for ledger lines.
* **(d) Biggest Risk:** Looking like a dry accounting firm. Needs high-quality typography and motion to feel "alive."
**== 2. DESERT THRESHOLD ==**
* **(a) Distinctiveness:** Medium. Atmospheric but risks feeling like an architecture portfolio.
* **(b) Contrast Risk:** Solid. Secondary `#51463C` on `#F4F0E8` is ~6.3:1 (Pass).
* **(c) Build Feasibility:** Moderate. Requires careful box-shadow management to maintain "depth" without performance lag.
* **(d) Biggest Risk:** Heavy "beams" and "walls" might make the content feel cramped on mobile.
**== 3. THE WORKBENCH ==**
* **(a) Distinctiveness:** High, but dangerously nostalgic.
* **(b) Contrast Risk:** Solid. Secondary `#5C5146` on `#F1EDE5` is ~5.7:1 (Pass).
* **(c) Build Feasibility:** Low. Success depends entirely on the quality of "wood" assets/textures. If they look like stock photos, the brand dies.
* **(d) Biggest Risk:** Falling into the "2012 Skeuomorphism" trap. Hard to execute without looking dated.
---
### 2. THE CALL: SONORAN LEDGER (REFINED)
I am choosing **SONORAN LEDGER** with a hybrid infusion of **DESERT THRESHOLDs** depth.
**Justification:** ACG is a concierge service. A ledger represents the "Permanent Record" of a 25-year relationship. It communicates that every endpoint and every dollar is accounted for. It is the ultimate expression of "transparent, honest pricing." By using JetBrains Mono for data and Lexend for the story, we create a "High-End Workshop" feel that is professional yet approachable.
---
### 3. NON-NEGOTIABLE DESIGN RULES (ANTI-SLOP)
1. **THE INK RULE:** The orange accent is treated as "highlighting ink." Use it ONLY for active data points, underlines, and the primary CTA. Never use it for large background blocks or generic icons.
2. **ZERO GENERIC ICONOGRAPHY:** Do not use FontAwesome or Heroicons. If a section needs an icon, use a CSS-drawn geometric shape, a simplified technical schematic, or a high-resolution photo of actual hardware. AI-slop relies on "Friendly 3D People" and "Outline Icons"; we use "Precision Marks."
3. **THE LEDGER GRID:** Every element on the page must snap to a strict 24px vertical baseline grid. The "faint rulings" are not just decoration; they must align perfectly with the `line-height` of the Lexend body text.
4. **DATA AS DESIGN:** The IT-cost calculator is the centerpiece. Do not hide it in a "Get a Quote" pop-up. Treat the calculator's output with the same typographic dignity as the Hero headline. Large, mono-spaced numbers that feel "stamped" onto the page.
5. **NO OVER-SMOOTHED RADIUS:** Standard SaaS uses 12px-16px border-radius. We use 0px (sharp) or a maximum of 2px. Sharp corners communicate "Precise" and "Manual."
[INFO] This direction provides the highest "Trust-per-Pixel" for a local MSP. Proceed with static build.

View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# Generate Sonoran Ledger imagery with Grok. Photographic, anti-AI-slop, warm Tucson palette.
set -u
ROOT=/d/ClaudeTools
GROK="$ROOT/.claude/skills/grok/scripts/ask-grok.sh"
OUT="$ROOT/projects/acg-website-showcase/assets/images"
cd "$ROOT"
echo "=== [1/4] hero ==="
bash "$GROK" image "Photorealistic editorial photograph, late afternoon golden hour. A warm light-wood desk in a calm Tucson office. On the desk: an open paper accounting ledger with neat handwritten columns of numbers, a modern matte-aluminum laptop with no logos open beside it, a plain white ceramic coffee cup, and a brass fountain pen. Behind the desk a large window frames the distant Santa Catalina mountains and a single saguaro cactus in warm hazy desert light. Natural raking side light, soft long shadows, subtle fine film grain, gentle shallow depth of field. No people. Calm, premium, trustworthy mood. Warm cream, tan and soft amber tones, restrained and not orange-saturated. Wide 3:2 landscape composition." "$OUT/hero.png"
echo "=== [2/4] story windowsill ==="
bash "$GROK" image "Photorealistic architectural close-up. Warm desert golden-hour light rakes across the corner where a smooth hand-troweled white stucco wall meets a clean light-wood windowsill. Minimal, calm, textural, quiet shadow play. Tucson afternoon light, soft fine film grain, warm cream and tan palette. No text, no people, no logos. Wide 3:2 landscape." "$OUT/story.png"
echo "=== [3/4] trust still life ==="
bash "$GROK" image "Photorealistic editorial still life on a warm wooden desk in soft afternoon window light. In focus: an open paper notebook with a date neatly hand-written in ink, and a well-worn keyboard. In the soft out-of-focus background, a small potted barrel cactus sits on a windowsill. Honest, lived-in, premium and human. Warm cream and charcoal palette, fine film grain. No logos, no visible faces. Wide 3:2 landscape." "$OUT/trust.png"
echo "=== [4/4] paper texture ==="
bash "$GROK" image "Seamless subtle warm cream paper texture, very faint fibers and tone variation, almost flat, high resolution, soft and clean, suitable as a quiet website background. No text, no objects, evenly lit. Square." "$OUT/paper.png"
echo "=== DONE ==="
ls -la "$OUT"

View File

@@ -0,0 +1,24 @@
Design-direction consultation (text only, no image generation). You are an opinionated senior art director.
CONTEXT: Art-directing a flagship single-page marketing website for "Arizona Computer Guru" (ACG), a Tucson MSP / IT-services company operating since 2001. Core differentiator: "concierge IT" — they go far beyond typical IT companies, build genuine relationships with clients, transparent honest pricing, kind + direct (never condescending), strong local Tucson presence. Brand voice from their own materials: anti-sales-games, transparent, premium-but-grounded, "we built it the way we'd want to be treated."
Existing brand cues to respect (but you may evolve): accent orange ~oklch(0.70 0.18 55) (a warm amber-orange), display font Barlow Condensed, body font Lexend.
HARD REQUIREMENTS:
- Must NOT look like a generic AI-slop SaaS template (no default gradient-blob hero, no purple, no generic 3-card row clichés).
- Must be genuinely beautiful, distinctive, memorable.
- ALL text readable in BOTH a light theme and a dark theme.
- Includes an interactive IT-cost calculator.
- Sections to convey: hero, concierge story/differentiator, services, GPS pricing tiers ($19/$26/$39 per endpoint + support plans), trust/proof, FAQ, contact.
DELIVERABLE: Give me 3 DISTINCT, concrete, BUILDABLE art directions. For each include:
1. Name
2. Core visual concept (one tight paragraph)
3. Color strategy — explicit for LIGHT theme and DARK theme (name the background, surface, text, accent roles)
4. Typography approach (how Barlow Condensed + Lexend are used; any third face)
5. Layout / section rhythm (how the long scroll is paced)
6. Imagery treatment — exactly what hero image + 2-3 section images Grok should generate (describe each image so I can prompt it)
7. ONE signature distinctive element that makes it memorable
8. What specifically keeps it from looking generic
Then RANK the 3 by which best expresses "concierge, premium, Tucson-local, anti-generic" and say why in 2 sentences. Be specific and opinionated. Keep under ~750 words total.

View File

@@ -0,0 +1,54 @@
**Sonoran Ledger**
Core visual concept: The page feels like a trusted local bookkeepers ledger updated for the web — precise horizontal rules, warm paper surfaces, and restrained typography carrying every number and promise with the same weight as the desert light. No marketing theater; the design treats pricing, the calculator, and the story as permanent records.
Color strategy — LIGHT: Background warm cream #F7F3EB; surface soft paper #EDE6D9; primary text deep warm charcoal #2A2521; secondary #5A5148; accent remains the specified oklch(0.70 0.18 55) orange used only for active numbers, underlines, and the single “Get the estimate” button. DARK: Background warm deep charcoal #1C1814; surface muted slate-brown #2A2520; primary text warm parchment #E8DFCE; secondary #C4B8A3; accent orange desaturated slightly for contrast on dark.
Typography approach: Barlow Condensed 600700 for every headline, subhead, and the “Since 2001” lockup with tight tracking and generous line height. Lexend for all body, story, and form labels at comfortable reading sizes. JetBrains Mono (or system mono) used exclusively for every price, the calculator output, and endpoint counts — this single-face restriction gives the whole page its ledger precision.
Layout / section rhythm: Hero (contained, not full-bleed), then a long single-column “page” for the concierge story broken only by subtle rules. Services read as continuous justified text with occasional orange accent underlines instead of cards. Pricing sits as a wide ruled rate card. The calculator becomes the largest break in rhythm — a full-width interactive panel treated as an open ledger page with live totals. Trust and FAQ stay quiet and narrow; contact closes with one clear phone/email block.
Imagery treatment: Hero — late-afternoon Tucson interior, wooden desk with an open paper ledger showing neat columns, a modern but unbranded laptop, a white coffee cup, and a window framing distant Catalina mountains and one saguaro; natural side light, slight film grain, no people. Section image 1 (concierge story) — close crop of a stucco wall meeting a clean wooden windowsill with desert light raking across it. Section image 2 (trust) — horizontal strip of three quiet details: a hand writing a date in a notebook, a well-worn keyboard on a desk, a small potted barrel cactus on a windowsill.
Signature distinctive element: Extremely faint warm-gray ledger rulings (0.51 px) that run continuously behind the story, pricing, and calculator, aligned to a consistent baseline grid so the entire scroll reads as one continuous book.
What specifically keeps it from looking generic: The entire information architecture is subordinated to the ledger metaphor; there are no floating cards, no gradient blobs, no three-up grids, and the calculator is not an afterthought widget but the central “open page.”
**Desert Threshold**
Core visual concept: The site is experienced as a sequence of thresholds — moving from the bright desert into shaded, well-kept rooms where real work and real conversations happen. Architecture, not abstraction: thick walls, deep reveals, and light that feels like Tucson at 5 p.m.
Color strategy — LIGHT: Background off-white plaster #F4F0E8; surface warmer sand #E6DFD2; primary text near-black #24201C; secondary #51463C; accent orange used for door-like details and the calculators primary action. DARK: Background deep warm umber #1A1612; surface #25211C; primary text soft warm #EDE3D3; secondary #B8A998; accent orange kept but never glowing.
Typography approach: Barlow Condensed for all section titles and the large “concierge” word in the story. Lexend for body and most UI. A quiet, slightly condensed geometric (Inter or similar) for navigation, buttons, and calculator controls only — keeps the display face special.
Layout / section rhythm: Short, tall hero with a literal framed view. Concierge story uses a wide left text column against a right full-height “wall” image. Services use a stepped vertical list that feels like walking down a hallway. Pricing tiers are presented as three equal “openings” in a thick wall (subtle recessed panels, not cards). The calculator sits in its own deep niche after pricing. Trust, FAQ, and contact fold back into tighter, lower-ceiling passages.
Imagery treatment: Hero — exterior of a simple Tucson adobe or stucco building at golden hour, shot straight-on through a deep shaded portal or window frame so the viewer is literally looking “in”; one clean saguaro and mountain ridge visible beyond; no people. Section image 1 — interior corner where white plaster meets thick wooden lintel and a slice of bright desert is visible through an open door. Section image 2 — tight shot of a real wooden threshold with a worn doormat, a pair of clean work boots, and a small brass house number plate.
Signature distinctive element: Every major section divider is a thick horizontal “beam” or lintel (CSS border + shadow) that creates the sensation of moving under architectural elements; the calculator panel has the deepest reveal and sits lower, like stepping down into the workroom.
What specifically keeps it from looking generic: The visual system is built from real Tucson residential and small commercial architecture rather than UI patterns; the pacing feels like walking through a cared-for building, not scrolling a marketing deck.
**The Workbench**
Core visual concept: Everything is arranged on a single long, solid workbench. Materials are honest (wood, metal, paper, warm light). The visitor sits across from the craftsman; pricing, the calculator, and proof are all laid out at working height with no theater.
Color strategy — LIGHT: Background soft warm gray #F1EDE5; primary surface #D9CDB8 (actual wood tone); text #2C2621; secondary #5C5146; accent orange only on active elements and the calculator total. DARK: Background #1D1914; surface #2E2720; text #E6D9C7; secondary #A99A85; accent orange held at the same hue but lowered lightness for legibility.
Typography approach: Barlow Condensed for headlines and the pricing tier names only. Lexend for everything else. No third face — the two fonts plus the physical surface treatment carry all distinction.
Layout / section rhythm: Hero is low and wide, like looking across the bench top. The concierge story is two tight columns of text above a long horizontal “work surface” band. Services are listed as a single running inventory down the left edge. Pricing and the calculator occupy the widest, most physical-feeling section — a continuous bench surface that holds the three tiers on the left and the live calculator on the right. Trust uses small object photos set directly into the surface. FAQ and contact sit at the far end of the bench.
Imagery treatment: Hero — straight-down or 3/4 view of a real wooden workbench in a Tucson garage or workshop; on the bench: a clean laptop, a paper work order, a tape measure, a small orange-handled tool, and a cup; strong side light from a window, visible concrete or stucco wall behind. Section image 1 — macro of hands (one older, one younger) plugging a clean network cable into a small switch on the same bench. Section image 2 — close detail of the bench edge with a few honest scuffs, a brass ruler, and a printed invoice showing the $19 / $26 / $39 numbers.
Signature distinctive element: The pricing tiers and calculator are rendered as objects actually resting on the same continuous wooden plane; subtle material texture, soft realistic shadows, and a consistent “edge” treatment make the interactive elements feel physical rather than overlaid.
What specifically keeps it from looking generic: The metaphor is resolutely physical and local (a real bench in a real Tucson workshop) instead of any digital “surface”; the calculator is not a module but part of the same working surface as the prices.
**Ranking**
1. Sonoran Ledger (best)
2. Desert Threshold
3. The Workbench
Sonoran Ledger best expresses concierge, premium, Tucson-local, and anti-generic because the entire visual language is built around transparent record-keeping rather than marketing decoration — the calculator and pricing literally sit inside the “books,” which directly embodies the client promise of honest numbers and no games. The other two are still strong and local, but Threshold risks feeling more like a boutique hotel and Workbench slightly more tradesman than the relationship-first concierge positioning.

View File

@@ -0,0 +1,29 @@
### index.html
[ERROR] Accessibility: FAQ accordion buttons lack `aria-controls` and panels lack `id`s (line 217). Verified by checking markup. Confidence: 100%.
[WARN] Accessibility: Calculator `aria-live="polite"` (line 197) contains the entire output ledger. Refutation attempt: Maybe `app.js` debounces input? No, `app.js:125` fires `recalc()` and rewrites DOM immediately on every `input` event, causing severe screen reader spam while typing. Confidence: 100%.
[OK] Form labels (line 261): Explicitly linked via `for`/`id`.
[OK] Focus visibility: Custom outlines exist for inputs, and default browser outlines remain intact for buttons since no global reset exists. Confidence: 100%.
### styles.css
[ERROR] WCAG Contrast (Light): `--ink-3` (`#837868`) on `--paper` (`#F7F3EB`) yields ~3.8:1, failing AA 4.5:1. Refutation attempt: Is it only used for large text? No, it's used for small body text like `.hero__note` and `.calc__foot`. Corrected hex: `#6D6456`. Confidence: 100%.
[ERROR] Accessibility: Collapsed FAQ answers `.faq__a` (line 327) hide visually via `max-height: 0` but lack `visibility: hidden`. Screen readers will inappropriately read collapsed answers. Confidence: 100%.
[ERROR] Responsive: Mobile nav links are `display: none` < 880px (line 127). Refutation attempt: Because it is a single-page site, users can still scroll. However, removing primary wayfinding without a hamburger fallback completely breaks standard user expectation. Confidence: High.
[WARN] Responsive: Pricing tables lack a horizontal overflow wrapper. A 5-column table will break 320px mobile viewports. Confidence: 100%.
[OK] WCAG Contrast (Dark): `--ink-3` (`#9A8C77`) on `#1C1814` is 5.3:1. Both light/dark `--accent-ink` and button texts pass AA comfortably. Confidence: 100%.
### app.js
[WARN] Calculator Edge Case: Typing an empty string `""` evaluates safely to `0` mathematically (line 64). Refutation attempt: Does this break the math? No. Does it break UI state? Yes, `input.value` is not overwritten in the UI during typing, leaving the input visually blank while the calculator computes `0`. Confidence: 100%.
[OK] Calculator Math: Divide-by-zero is handled safely via `endpoints > 0` condition (line 112). `NaN` correctly defaults to `0`. Confidence: 100%.
[OK] Theme Toggle: Properly sets `aria-pressed` and respects system preferences dynamically (line 16). Confidence: 100%.
### Design & Art Direction
[OK] AI-slop: None detected. The "Sonoran Ledger" constraints (strict 24px baseline, tabular-nums, sparse orange accent) are mathematically and visually respected. Copy is direct and avoids generic SaaS bloat words.
### PRIORITIZED FIX LIST
1. **[CRITICAL]** Update `--ink-3` in `styles.css:12` to `#6D6456` to meet WCAG AA contrast.
2. **[CRITICAL]** Add `visibility: hidden` to `.faq__a` (`styles.css:327`) and `visibility: visible` to its expanded state to prevent screen reader leakage.
3. **[HIGH]** Implement a mobile menu fallback for viewports `< 880px` rather than purely hiding `.nav__link` (`styles.css:127`).
4. **[HIGH]** Wrap `.plan-table` in an `overflow-x: auto` container to prevent layout breakage on narrow screens (`index.html:120`).
5. **[MED]** Add `id`s to FAQ panels and `aria-controls` to FAQ buttons (`index.html:217`).
6. **[MED]** Scope `aria-live` to specific total spans or debounce the `input` listener to prevent screen reader spam (`index.html:197`, `app.js:125`).
7. **[LOW]** Add a `blur` listener to calculator inputs to visually sync empty/invalid inputs back to `"0"` (`app.js:61`).

View File

@@ -0,0 +1,62 @@
**VERDICT**
Hand-built static site that largely executes the "Sonoran Ledger" direction (warm paper #F7F3EB/#1C1814, sparse --accent orange as ink only, mono tabular numerals for money, 2px sharp corners, repeating-linear-gradient ledger rulings at 24px baseline). It is not generic AI-slop in the visual layer — custom tokens, no framework, hand-rolled selects/switches/steppers, sectional rulings, split trust numerals, and explicit "open book" copy differentiate it. However, the information architecture (hero/trust/story/services/pricing/calc/faq/contact/footer) is the standard MSP marketing skeleton, and several real defects exist, especially in JS behavior and mobile.
Prioritized by the requested hunt areas. All analysis from the three full file reads. No modifications performed.
**index.html**
[ERROR] Mobile navigation breakage. At <=880px, `.nav .nav__link` are hidden via CSS with no hamburger, no remaining in-nav anchors, and no alternative (e.g., footer or select). Only the phone number and theme toggle survive in the header. Sections #services, #pricing, #calculator, #faq become unreachable without scrolling the entire page on phones/tablets. (cross-ref css/styles.css:154)
[ERROR] "Send me this estimate" (id="sendEstimate") is a plain `<a href="#contact">`. It performs scroll only; calculator state (ledger lines, total) is never injected into #cf-msg or any hidden field. The contact form and estimator are completely disconnected. (index.html:362)
[WARN] Schema.org LocalBusiness "image" and the two `<img>` src values point to "assets/images/hero.png" / "assets/images/story.png" (relative, no root guarantee). If served from a subpath the images 404 and JSON-LD is invalid. Width/height present on imgs (good). (index.html:20,82,124)
[WARN] Accordion (FAQ) buttons carry aria-expanded correctly, but the answer containers lack role="region", id, or aria-labelledby wiring back to the button. Content remains in DOM (good for search), but grouping is weak for assistive tech. (index.html:377-407)
[INFO] Semantics otherwise strong: `<main>`, skip link, section+aria-labelledby, table+caption+scope="col", label+for on all form controls (including the custom switch and number inputs), tel/mailto, descriptive alts on the two photos, novalidate + custom validation on contact. Trust strip uses a labeled section + grid of cells (not a list or dl, but acceptable). Inline style tweaks exist on a few containers for spacing (minor hygiene).
[INFO] No obvious duplicate IDs, broken heading order, or missing lang/color-scheme. The `<span id="top">` inside main is harmless but odd.
**css/styles.css**
[INFO] Tokens and dark override correctly implement the spec: --paper/--surface warm, --rule/--rule-soft for lines, --accent/#BD5A00 (light) and #F2A24E/#F4A85C (dark) used only for marks, flags, underlines (.ul), section-tag::before rules, brand mark, hover states, and selection. No heavy rounded corners (2px max), no large blurs except header. .mono + font-variant-numeric: tabular-nums on all money. Ledger ruling via precise repeating-linear-gradient (23px transparent + 1px at 24px) on .ledger sections only. (css/styles.css:9-46,184-189,254, etc.)
[WARN] Contrast. Tokens comment claims AA for --accent-ink. Core --ink on --paper is high-contrast. --ink-2 muted (#5A5148 light / #C4B8A3 dark) on body copy and small labels is borderline, especially at 0.78-0.92rem. Rule lines (rgba 0.16 / 0.08) and ledger rulings are intentionally faint; in dark mode or outdoors they become nearly invisible. This undercuts the "premium ledger" readability claim on real devices. (css/styles.css:13,38,101,186)
[WARN] Mobile risks beyond nav. Stepper buttons fixed at 34px wide (sub-44px touch minimum). Many breakpoints (880/860/820/720/680) but no container queries; tables and grids can squeeze (right-aligned .num columns in plan-table). Header sticky + backdrop-filter is modern but the surviving mobile nav content (phone + 40px toggle) can feel cramped. No focus-visible beyond the form-control outline rule. (css/styles.css:154,288-292,264-269,346-349,382)
[INFO] Distinctive execution, not templated. Custom chevron-less selects (bg-image linear gradients), custom .switch, custom .stepper (spinners explicitly killed), grid-gap + background-rule trick for trust separators, reveal using IntersectionObserver, section tags with accent rule, .tier--pop flag, .svc continuous ruled list instead of cards. The visual system fights generic. However, the overall page skeleton, "Most chosen" ribbon, trust strip, live two-pane estimator, and +/x FAQ are still recognizable marketing patterns.
[OK] Reduced-motion, box-sizing, and basic reset present. Body transitions limited to bg/color.
**js/app.js**
[ERROR] Stepper bounds + direct edit desync (core bug). Stepper click path (js/app.js:139) does `Math.max(0, Math.min(500, v + dir))` and writes back. But `intVal` (67-72) only clamps the *read* value for calculations (`if (v > 500) v = 500; return v;`) and never mutates `el.value`. Typing 600 (or -5) into an endpoints/m365/voip field leaves the input showing the out-of-range number while every recalc, line, total, and per-endpoint uses the clamped 500/0. Subsequent stepper clicks read the lying input.value. (js/app.js:67-72,136-143)
[ERROR] Calculator "per endpoint" math/presentation. `per = endpoints > 0 ? total / endpoints : 0` (125) then unconditionally renders "X per endpoint / mo". Total includes large fixed adders (support plan 200-850, equip 25, m365, voip, hosting). The displayed per-endpoint figure therefore amortizes the support plan etc. across seats. This contradicts the page claim "the same math we'd walk you through in person" and "per-endpoint pricing". Arithmetic is correct; the semantics of the output are misleading when any non-endpoint item is selected. (js/app.js:119,125-128)
[WARN] Support-plan line exactly as flagged. `var support = numSelect("support");` (94) pulls the raw `<option value="380">` price. It is passed verbatim as the `cost` argument to `lineRow` (114): `lineRow((supportName || "Support plan") + " support", ..., support)`. The same raw value is added directly into total (119). For the "None" case (value 0) this produces the label "Support plan support" (supportName falls to "") with cost 0 (hidden by .is-zero). Functionally the total is correct (flat fee), but it is the only line that does not compute cost from a rate; it trusts the select value as both identifier and dollar amount. (js/app.js:94,105-107,114,119)
[WARN] FAQ max-height on resize is present but fragile. Click handler (159) sets `panel.style.maxHeight = (inner.offsetHeight + 8) + "px"`. Resize listener (163-171) re-queries every open panel and does the same. Issues: magic +8, only updates *currently open* panels, measures at event time (font loading, subpixel, zoom, or post-open reflow can still clip or leave extra space), no removal of style on close beyond hard "0px", no MutationObserver for content changes. Works for static text on this page but is a classic source of cut-off answers. (js/app.js:152-171)
[INFO] Theme persistence. STORE="acg-theme", localStorage read on load, write on toggle, OS prefers listener that only applies when no explicit saved value (40-48). Logic is correct. Weaknesses: initial `<html data-theme="light">` + script at very end of body guarantees a flash of the wrong theme on first paint for users with dark saved or OS dark. No early inline script or critical CSS. (js/app.js:12-49)
[INFO] Contact validation (demo level). Trims name + cf-contact only (181). No format check on the "phone or email" field. On error sets accent color + message + focus. On success sets good color + thanks + reset(). Note text/color survives the reset until next submit. No aria-live on #formNote (calc side has aria-live="polite" on the output shell). Correct for a static demo; insufficient for production. (js/app.js:173-192)
[OK] Remaining calculator paths: money() rounds, no NaN escapes in the hot path, form submit prevented, input+change both trigger recalc, steppers call recalc after write. Ledger hides zero lines. No other arithmetic defects found.
**Short ranked list of the top fixes that matter (real defects only, highest impact first)**
1. Mobile nav: .nav__link {display:none} at 880px with no menu or persistent anchors — primary navigation is lost on phones.
2. Stepper direct-edit desync: intVal clamps for math only; input.value can show values outside 0-500 while all outputs use the clamped number.
3. Per-endpoint display math: total (with fixed support/equip/etc.) is divided by endpoints and labeled "per endpoint / mo" — misrepresents the per-endpoint claim.
4. FAQ max-height: offsetHeight + 8 px snapshots on resize only for open items; content can clip after reflow, font load, or width change.
5. Theme flash + late application: hardcoded light on <html>, script at bottom; dark users see wrong theme before JS runs.
6. Touch targets: stepper +/- buttons locked at 34px wide.
7. Estimate-to-contact handoff missing: the CTA only scrolls; no data transfer occurs.
8. Support line label: raw price passed as cost produces "Support plan support" (value 0 case) and couples the select value to both identity and dollars.
9. Contact status region has no aria-live; results are invisible to screen readers.
10. Faint ledger rulings + muted text contrast: especially in dark mode; undercuts premium paper/ink readability.
The rest are nits (inline styles, brittle asset paths in JSON-LD, missing focus-visible on .btn, etc.). The site is closer to a deliberate craft artifact than a generic template, but the defects above are functional or accessibility issues that affect real use.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

View File

@@ -0,0 +1,481 @@
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Arizona Computer Guru — Concierge IT for Tucson, since 2001</title>
<meta name="description" content="Arizona Computer Guru is Tucson's concierge IT and managed security partner since 2001. Transparent per-endpoint pricing, local people, month-to-month terms. Estimate your IT cost in seconds." />
<meta name="color-scheme" content="light dark" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@500;600;700&family=Lexend:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="css/styles.css" />
<!-- Set theme before first paint to avoid a flash of the wrong theme. -->
<script>
(function () {
try {
var s = localStorage.getItem("acg-theme");
var m = s || (window.matchMedia &&
window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
document.documentElement.setAttribute("data-theme", m);
} catch (e) {}
})();
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "LocalBusiness",
"name": "Arizona Computer Guru",
"image": "assets/images/hero.png",
"description": "Concierge managed IT and cybersecurity for Tucson businesses since 2001.",
"address": {
"@type": "PostalAddress",
"streetAddress": "7437 E. 22nd St",
"addressLocality": "Tucson",
"addressRegion": "AZ",
"postalCode": "85710",
"addressCountry": "US"
},
"telephone": "+1-520-304-8300",
"url": "https://azcomputerguru.com",
"foundingDate": "2001",
"areaServed": "Tucson, Arizona",
"openingHours": "Mo-Fr 09:00-17:00"
}
</script>
</head>
<body>
<a href="#main" class="sr-only">Skip to content</a>
<!-- ======================= HEADER ======================= -->
<header class="site-header">
<div class="wrap">
<a class="brand" href="#top" aria-label="Arizona Computer Guru home">
<span class="brand__mark" aria-hidden="true">ACG</span>
<span>
<span class="brand__name">Arizona Computer Guru</span><br />
<span class="brand__since">Concierge IT &middot; Tucson &middot; since 2001</span>
</span>
</a>
<nav class="nav" aria-label="Primary">
<div class="nav__links" id="navLinks">
<a class="nav__link" href="#services">Services</a>
<a class="nav__link" href="#pricing">Pricing</a>
<a class="nav__link" href="#calculator">Estimate</a>
<a class="nav__link" href="#faq">FAQ</a>
</div>
<a class="nav__phone" href="tel:+15203048300">520.304.8300</a>
<button class="theme-toggle" id="themeToggle" type="button"
aria-pressed="false" aria-label="Switch to dark theme" title="Toggle light / dark">
<span aria-hidden="true" data-theme-icon>&#9728;</span>
</button>
<button class="nav__toggle" id="navToggle" type="button"
aria-expanded="false" aria-controls="navLinks" aria-label="Open menu">&#8801;</button>
</nav>
</div>
</header>
<main id="main">
<span id="top"></span>
<!-- ======================= HERO ======================= -->
<section class="hero ledger" aria-labelledby="hero-h">
<div class="wrap">
<div class="hero__copy reveal">
<p class="hero__eyebrow">Tucson &middot; Managed IT &amp; Security &middot; Est. 2001</p>
<h1 id="hero-h">Concierge IT for Tucson&mdash;<br />every number <span class="amp">on the table.</span></h1>
<p class="hero__sub">We go further than your last IT company, build a real relationship, and never hide the math&mdash;honest pricing, local people, problems fixed before you feel them.</p>
<div class="hero__cta">
<a class="btn btn--primary" href="#calculator">Estimate your cost <span class="arrow" aria-hidden="true">&rarr;</span></a>
<a class="btn btn--ghost" href="#contact">Talk to a human</a>
</div>
<p class="hero__note mono">// Month-to-month &middot; no lock-in &middot; no offshore call centers</p>
</div>
<figure class="hero__frame reveal">
<img src="assets/images/hero.png" width="1248" height="832"
alt="A wooden desk at golden hour with an open paper ledger, a laptop, and a coffee cup; the Santa Catalina mountains and a saguaro visible through the window." />
<figcaption class="hero__caption">
<span>Tucson, AZ</span><span>open book, nothing hidden</span>
</figcaption>
</figure>
</div>
</section>
<!-- ======================= TRUST STRIP ======================= -->
<section class="trust" aria-label="Why businesses stay with us" style="padding-block:0;">
<div class="wrap">
<div class="trust__cell">
<div class="trust__num">20<span class="u">01</span></div>
<div class="trust__label">Serving Tucson businesses since</div>
</div>
<div class="trust__cell">
<div class="trust__num">1&ndash;2<span class="u">hr</span></div>
<div class="trust__label">Typical onsite emergency response</div>
</div>
<div class="trust__cell">
<div class="trust__num">100<span class="u">%</span></div>
<div class="trust__label">Local team &mdash; never offshore</div>
</div>
<div class="trust__cell">
<div class="trust__num">30<span class="u">day</span></div>
<div class="trust__label">Cancel anytime, no penalty</div>
</div>
</div>
</section>
<!-- ======================= CONCIERGE STORY ======================= -->
<section class="story" id="concierge" aria-labelledby="story-h">
<div class="wrap">
<div class="story__copy reveal">
<p class="section-tag">Our difference <span>/ concierge</span></p>
<h2 id="story-h">What "concierge" actually means</h2>
<p class="muted">Most IT companies wait for things to break, route you to a call center three time zones away, and lock you into a three-year contract. We built Arizona Computer Guru to be the opposite of that.</p>
<p class="muted">Concierge means we learn your business, not just your network. We pick up the phone. We translate in plain English instead of talking down to you. We&rsquo;ll drive across town when it&rsquo;s faster than a remote session. And because we charge the same whether your systems break or not, our incentive is simple: <span class="ul">keep them running</span>.</p>
<blockquote>We built our service the way we&rsquo;d want to be treated if we were the customer.</blockquote>
</div>
<figure class="story__img reveal">
<img src="assets/images/story.png" width="1248" height="832"
alt="Warm afternoon desert light raking across a white stucco wall meeting a clean wooden windowsill." />
</figure>
</div>
</section>
<!-- ======================= SERVICES ======================= -->
<section class="services ledger" id="services" aria-labelledby="svc-h">
<div class="wrap">
<p class="section-tag">Everything you need <span>/ one local partner</span></p>
<h2 id="svc-h">Services, laid out plainly</h2>
<div class="svc-list">
<article class="svc reveal">
<div class="svc__no">01</div>
<div><div class="svc__name">Managed IT (GPS)</div><p class="svc__desc">24/7 monitoring, automated patching, and a help desk that knows your name. Guru Protection Services keeps your endpoints healthy and accounted for.</p></div>
<div class="svc__meta">from&nbsp;$19&nbsp;/endpoint</div>
</article>
<article class="svc reveal">
<div class="svc__no">02</div>
<div><div class="svc__name">Cybersecurity</div><p class="svc__desc">Advanced EDR, email security, dark-web monitoring, and security-awareness training that catch what plain antivirus misses.</p></div>
<div class="svc__meta">in GPS-Pro &amp; up</div>
</article>
<article class="svc reveal">
<div class="svc__no">03</div>
<div><div class="svc__name">Backup &amp; Recovery</div><p class="svc__desc">Tested backups, offsite copies, and ransomware rollback&mdash;so a bad day is an inconvenience, not a closure.</p></div>
<div class="svc__meta">in GPS-Pro &amp; up</div>
</article>
<article class="svc reveal">
<div class="svc__no">04</div>
<div><div class="svc__name">Microsoft 365 &amp; Email</div><p class="svc__desc">Migrations, Business Standard/Premium, or budget-friendly hosted email&mdash;configured, secured, and supported.</p></div>
<div class="svc__meta">from&nbsp;$2&nbsp;/mailbox</div>
</article>
<article class="svc reveal">
<div class="svc__no">05</div>
<div><div class="svc__name">Business Phones (GPS-Voice)</div><p class="svc__desc">Cloud phone systems with mobile and desktop apps, auto-attendants, and porting. Four tiers, no usage surprises.</p></div>
<div class="svc__meta">from&nbsp;$22&nbsp;/user</div>
</article>
<article class="svc reveal">
<div class="svc__no">06</div>
<div><div class="svc__name">Web &amp; Email Hosting</div><p class="svc__desc">Managed hosting with free SSL, daily backups, and real humans&mdash;not a ticket robot&mdash;when something needs a hand.</p></div>
<div class="svc__meta">from&nbsp;$15&nbsp;/mo</div>
</article>
<article class="svc reveal">
<div class="svc__no">07</div>
<div><div class="svc__name">Projects &amp; Block Time</div><p class="svc__desc">Migrations, network buildouts, and one-off work&mdash;billed against pre-paid hours that never expire.</p></div>
<div class="svc__meta">from&nbsp;$100&nbsp;/hr</div>
</article>
<article class="svc reveal">
<div class="svc__no">08</div>
<div><div class="svc__name">vCIO &amp; Compliance</div><p class="svc__desc">Strategy, budgeting, and cyber-insurance readiness. We help you check the boxes auditors and insurers ask for.</p></div>
<div class="svc__meta">in GPS-Advanced</div>
</article>
</div>
</div>
</section>
<!-- ======================= PRICING ======================= -->
<section class="pricing" id="pricing" aria-labelledby="price-h">
<div class="wrap">
<p class="section-tag">Published, not "call for quote" <span>/ per endpoint</span></p>
<h2 id="price-h">GPS monitoring &amp; protection</h2>
<p class="lead">You pay for exactly the computers you have&mdash;no rounding you up into a bigger package tier.</p>
<div class="rate-card" role="list" style="margin-top:var(--base);">
<div class="tier" role="listitem">
<div class="tier__name">GPS-Basic</div>
<div class="tier__price">$19 <span class="per">/ endpoint / mo</span></div>
<p class="tier__blurb">Essential monitoring for small, simple environments.</p>
<ul>
<li class="is-base">Remote monitoring &amp; management</li>
<li class="is-base">Automated patch management</li>
<li class="is-base">Business-grade antivirus</li>
<li class="is-base">8&times;5 help desk</li>
<li class="is-base">Monthly health reports</li>
</ul>
</div>
<div class="tier tier--pop" role="listitem">
<div class="tier__flag">Most chosen</div>
<div class="tier__name">GPS-Pro</div>
<div class="tier__price">$26 <span class="per">/ endpoint / mo</span></div>
<p class="tier__blurb">Comprehensive protection for growing businesses.</p>
<ul>
<li class="is-base">Everything in Basic</li>
<li>24&times;7 help desk</li>
<li>Advanced EDR &amp; email security</li>
<li>Dark-web monitoring</li>
<li>Backup &amp; disaster recovery</li>
<li>Security-awareness training</li>
</ul>
</div>
<div class="tier" role="listitem">
<div class="tier__name">GPS-Advanced</div>
<div class="tier__price">$39 <span class="per">/ endpoint / mo</span></div>
<p class="tier__blurb">Enterprise-grade security &amp; compliance.</p>
<ul>
<li class="is-base">Everything in Pro</li>
<li>Compliance management (HIPAA/SOC)</li>
<li>Ransomware rollback</li>
<li>Virtual CIO services</li>
<li>Priority response SLA</li>
<li>Dedicated account manager</li>
</ul>
</div>
</div>
<p class="muted" style="margin-top:1rem;font-size:0.9rem;">Add equipment monitoring (routers, switches, printers, NAS) for <span class="mono">$25/mo</span> up to 10 devices.</p>
<div class="plans">
<div class="table-wrap">
<table class="plan-table">
<caption>Support plans &mdash; bundled labor at a lower effective rate</caption>
<thead>
<tr><th scope="col">Plan</th><th class="num" scope="col">Monthly</th><th class="num" scope="col">Hours</th><th class="num" scope="col">Effective&nbsp;rate</th><th scope="col">Response</th></tr>
</thead>
<tbody>
<tr><td>Essential</td><td class="num">$200</td><td class="num">2</td><td class="num">$100/hr</td><td>Next business day</td></tr>
<tr class="pop"><td>Standard</td><td class="num">$380</td><td class="num">4</td><td class="num">$95/hr</td><td>8 hours</td></tr>
<tr><td>Premium</td><td class="num">$540</td><td class="num">6</td><td class="num">$90/hr</td><td>4 hours</td></tr>
<tr><td>Priority</td><td class="num">$850</td><td class="num">10</td><td class="num">$85/hr</td><td>2 hours, 24/7</td></tr>
</tbody>
</table>
</div>
<div class="table-wrap">
<table class="plan-table" style="margin-top:var(--base);">
<caption>Block time &mdash; pre-paid hours that never expire</caption>
<thead>
<tr><th scope="col">Block</th><th class="num" scope="col">Price</th><th class="num" scope="col">Effective&nbsp;rate</th><th scope="col">Notes</th></tr>
</thead>
<tbody>
<tr><td>10 hours</td><td class="num">$1,500</td><td class="num">$150/hr</td><td>Use anytime, no expiry</td></tr>
<tr><td>20 hours</td><td class="num">$2,600</td><td class="num">$130/hr</td><td>Bank &amp; draw down</td></tr>
<tr><td>30 hours</td><td class="num">$3,000</td><td class="num">$100/hr</td><td>Best per-hour value</td></tr>
</tbody>
</table>
</div>
<p class="muted" style="margin-top:1rem;font-size:0.9rem;">Off-plan labor is <span class="mono">$175/hr</span>. Plans and block time bring that down&mdash;and there&rsquo;s never a charge to find out what something will cost.</p>
</div>
</div>
</section>
<!-- ======================= CALCULATOR ======================= -->
<section class="calc ledger" id="calculator" aria-labelledby="calc-h">
<div class="wrap">
<p class="section-tag">No email wall, no sales call <span>/ live estimate</span></p>
<h2 id="calc-h">Your IT, accounted for</h2>
<p class="lead">Move the numbers. Watch the total. This is the same math we&rsquo;d walk you through in person&mdash;nothing hidden behind a &ldquo;contact sales&rdquo; button.</p>
<div class="calc__shell" style="margin-top:var(--base);">
<div class="calc__grid">
<!-- INPUTS -->
<form class="calc__inputs" id="calcForm" aria-label="IT cost estimator">
<div class="calc__row">
<label for="endpoints">Computers &amp; servers <span class="hint">Every monitored endpoint</span></label>
<div class="calc__control">
<div class="stepper" data-step="endpoints">
<button type="button" data-dir="-1" aria-label="Fewer endpoints">&minus;</button>
<input id="endpoints" name="endpoints" type="number" inputmode="numeric" min="0" max="500" value="22" />
<button type="button" data-dir="1" aria-label="More endpoints">+</button>
</div>
</div>
</div>
<div class="calc__row">
<label for="gpsTier">Protection tier <span class="hint">Per-endpoint monitoring level</span></label>
<div class="calc__control">
<select id="gpsTier" name="gpsTier">
<option value="19">GPS-Basic &mdash; $19</option>
<option value="26" selected>GPS-Pro &mdash; $26</option>
<option value="39">GPS-Advanced &mdash; $39</option>
</select>
</div>
</div>
<div class="calc__row">
<label for="equip">Equipment monitoring <span class="hint">Network gear, printers, NAS &mdash; $25/mo</span></label>
<div class="calc__control toggle-row">
<span class="switch">
<input type="checkbox" id="equip" name="equip" />
<span class="track" aria-hidden="true"></span><span class="knob" aria-hidden="true"></span>
</span>
</div>
</div>
<div class="calc__row">
<label for="support">Support plan <span class="hint">Bundled labor hours each month</span></label>
<div class="calc__control">
<select id="support" name="support">
<option value="0">None &mdash; pay as you go</option>
<option value="200">Essential &mdash; $200 (2 hr)</option>
<option value="380" selected>Standard &mdash; $380 (4 hr)</option>
<option value="540">Premium &mdash; $540 (6 hr)</option>
<option value="850">Priority &mdash; $850 (10 hr)</option>
</select>
</div>
</div>
<div class="calc__row">
<label for="m365">Microsoft 365 users <span class="hint">Business Standard, $14/user</span></label>
<div class="calc__control">
<div class="stepper" data-step="m365">
<button type="button" data-dir="-1" aria-label="Fewer Microsoft 365 users">&minus;</button>
<input id="m365" name="m365" type="number" inputmode="numeric" min="0" max="500" value="0" />
<button type="button" data-dir="1" aria-label="More Microsoft 365 users">+</button>
</div>
</div>
</div>
<div class="calc__row">
<label for="voip">Business phone users <span class="hint">GPS-Voice Standard, $28/user</span></label>
<div class="calc__control">
<div class="stepper" data-step="voip">
<button type="button" data-dir="-1" aria-label="Fewer phone users">&minus;</button>
<input id="voip" name="voip" type="number" inputmode="numeric" min="0" max="500" value="0" />
<button type="button" data-dir="1" aria-label="More phone users">+</button>
</div>
</div>
</div>
<div class="calc__row" style="border-bottom:0;">
<label for="hosting">Web hosting <span class="hint">Managed, with SSL &amp; backups</span></label>
<div class="calc__control">
<select id="hosting" name="hosting">
<option value="0" selected>None</option>
<option value="15">Starter &mdash; $15</option>
<option value="35">Business &mdash; $35</option>
<option value="65">Commerce &mdash; $65</option>
</select>
</div>
</div>
</form>
<!-- OUTPUT (ledger page) -->
<div class="calc__out">
<h3>Monthly statement</h3>
<div class="ledger-lines" id="ledgerLines"><!-- JS fills --></div>
<hr class="calc__divider" />
<div class="calc__total" aria-live="polite">
<span class="tlabel">Per month</span>
<span class="tnum mono" id="totalMonthly">$0</span>
</div>
<div class="calc__annual mono" id="totalAnnual">$0 / year</div>
<div class="calc__perep mono" id="perEndpoint">&mdash; all-in, per endpoint / mo</div>
<a class="btn btn--primary calc__cta" href="#contact" id="sendEstimate">Send me this estimate <span class="arrow" aria-hidden="true">&rarr;</span></a>
<p class="calc__foot">Estimate only &mdash; your real quote is tailored to your setup. No card, no commitment, and we&rsquo;ll tell you if you&rsquo;re buying more than you need.</p>
</div>
</div>
</div>
</div>
</section>
<!-- ======================= FAQ ======================= -->
<section class="faq" id="faq" aria-labelledby="faq-h">
<div class="wrap wrap--narrow">
<p class="section-tag">Straight answers <span>/ ask us anything</span></p>
<h2 id="faq-h">Questions worth asking any MSP</h2>
<div class="faq__list" id="faqList">
<div class="faq__item">
<button class="faq__q" aria-expanded="false"><span>Will I get a real person, or an offshore call center?</span><span class="pm" aria-hidden="true">+</span></button>
<div class="faq__a"><div class="faq__a-inner">A real, local person. Our team works out of our Tucson office at 7437 E. 22nd St. You can visit, and after hours you reach an on-call tech who is also local&mdash;not a queue overseas.</div></div>
</div>
<div class="faq__item">
<button class="faq__q" aria-expanded="false"><span>What&rsquo;s your contract term?</span><span class="pm" aria-hidden="true">+</span></button>
<div class="faq__a"><div class="faq__a-inner">Month-to-month. Cancel with 30 days&rsquo; notice, no early-termination fee. We&rsquo;d rather earn your business every month than trap you in a three-year agreement.</div></div>
</div>
<div class="faq__item">
<button class="faq__q" aria-expanded="false"><span>What happens if I use more support hours than my plan includes?</span><span class="pm" aria-hidden="true">+</span></button>
<div class="faq__a"><div class="faq__a-inner">Plan hours are used first. After that we draw from any pre-paid block time you&rsquo;ve banked ($100&ndash;150/hr), which never expires. No block time? Overage is billed at $175/hr&mdash;and we&rsquo;ll always flag it before the meter runs.</div></div>
</div>
<div class="faq__item">
<button class="faq__q" aria-expanded="false"><span>How fast do you respond to an emergency?</span><span class="pm" aria-hidden="true">+</span></button>
<div class="faq__a"><div class="faq__a-inner">By plan: Standard is 8 hours, Premium 4 hours, Priority 2 hours around the clock. A total outage is escalated immediately regardless of plan, and for local clients we&rsquo;re typically onsite within 1&ndash;2 hours.</div></div>
</div>
<div class="faq__item">
<button class="faq__q" aria-expanded="false"><span>What security is actually included?</span><span class="pm" aria-hidden="true">+</span></button>
<div class="faq__a"><div class="faq__a-inner">GPS-Pro and above include advanced EDR, email security, dark-web monitoring, and monthly security-awareness training. GPS-Advanced adds compliance tooling and ransomware rollback. Basic antivirus alone isn&rsquo;t enough anymore, so we don&rsquo;t pretend it is.</div></div>
</div>
<div class="faq__item">
<button class="faq__q" aria-expanded="false"><span>Can you help me meet cyber-insurance requirements?</span><span class="pm" aria-hidden="true">+</span></button>
<div class="faq__a"><div class="faq__a-inner">Yes. GPS-Pro covers most of what insurers ask for&mdash;MFA enforcement, EDR, training, and cloud backups&mdash;and we&rsquo;ll provide documentation for your agent. We work with several Tucson insurance agents regularly.</div></div>
</div>
<div class="faq__item">
<button class="faq__q" aria-expanded="false"><span>What if my team grows or shrinks?</span><span class="pm" aria-hidden="true">+</span></button>
<div class="faq__a"><div class="faq__a-inner">Billing is per endpoint, so it just follows your headcount. Add three people, add three endpoints; downsize and it drops the next month. No contract amendments, no penalties.</div></div>
</div>
<div class="faq__item">
<button class="faq__q" aria-expanded="false"><span>Do you really publish your pricing?</span><span class="pm" aria-hidden="true">+</span></button>
<div class="faq__a"><div class="faq__a-inner">You&rsquo;re looking at it. Our rates are on this page and in our Buyers Guide. We think hiding prices is a red flag&mdash;in any MSP, including us.</div></div>
</div>
</div>
</div>
</section>
<!-- ======================= CONTACT ======================= -->
<section class="contact ledger" id="contact" aria-labelledby="contact-h">
<div class="wrap">
<div class="contact__intro reveal">
<p class="section-tag">No pressure, no pitch <span>/ let&rsquo;s talk</span></p>
<h2 id="contact-h">Talk to a human</h2>
<p class="muted">Tell us what&rsquo;s frustrating you, or send over an estimate from the calculator. We&rsquo;ll give you honest feedback&mdash;even if that&rsquo;s &ldquo;your current setup is fine, here&rsquo;s one thing to fix.&rdquo;</p>
<div class="contact__lines">
<div><span class="k">Phone</span><br /><a class="v" href="tel:+15203048300">520.304.8300</a></div>
<div><span class="k">Email</span><br /><a class="v" href="mailto:info@azcomputerguru.com">info@azcomputerguru.com</a></div>
<div><span class="k">Office</span><br /><span class="v">7437 E. 22nd St, Tucson, AZ 85710</span></div>
<div><span class="k">Hours</span><br /><span class="v">Mon&ndash;Fri 9&ndash;5 &middot; 24/7 emergency for Priority clients</span></div>
</div>
</div>
<form class="contact__form reveal" id="contactForm" novalidate>
<div class="form-field">
<label for="cf-name">Your name</label>
<input id="cf-name" name="name" type="text" autocomplete="name" required />
</div>
<div class="form-field">
<label for="cf-biz">Business</label>
<input id="cf-biz" name="business" type="text" autocomplete="organization" />
</div>
<div class="form-field">
<label for="cf-contact">Phone or email</label>
<input id="cf-contact" name="contact" type="text" autocomplete="email" required />
</div>
<div class="form-field">
<label for="cf-msg">What can we help with?</label>
<textarea id="cf-msg" name="message"></textarea>
</div>
<button class="btn btn--primary" type="submit">Send message <span class="arrow" aria-hidden="true">&rarr;</span></button>
<p class="form-note" id="formNote" aria-live="polite">We reply the same business day. (Demo build &mdash; this form is not wired to a mailbox.)</p>
</form>
</div>
</section>
</main>
<!-- ======================= FOOTER ======================= -->
<footer class="site-footer">
<div class="wrap">
<p>&copy; <span id="year">2026</span> Arizona Computer Guru &middot; Protecting Tucson businesses since 2001</p>
<p><a href="tel:+15203048300">520.304.8300</a> &middot; <a href="mailto:info@azcomputerguru.com">info@azcomputerguru.com</a></p>
<p class="disclaimer">Local demonstration build of a proposed azcomputerguru.com. Pricing shown reflects published GPS rates and is illustrative for the estimator; a real quote is tailored to your environment. Photography is representational.</p>
</div>
</footer>
<script src="js/app.js"></script>
</body>
</html>

View File

@@ -0,0 +1,261 @@
/* ===========================================================================
Arizona Computer Guru — Sonoran Ledger
Vanilla JS. Theme, calculator, FAQ, steppers, reveal. No dependencies.
=========================================================================== */
(function () {
"use strict";
var $ = function (s, c) { return (c || document).querySelector(s); };
var $$ = function (s, c) { return Array.prototype.slice.call((c || document).querySelectorAll(s)); };
/* ---- Theme ------------------------------------------------------------ */
var root = document.documentElement;
var toggle = $("#themeToggle");
var icon = $("[data-theme-icon]");
var STORE = "acg-theme";
function applyTheme(mode) {
root.setAttribute("data-theme", mode);
var dark = mode === "dark";
if (toggle) {
toggle.setAttribute("aria-pressed", String(dark));
toggle.setAttribute("aria-label", dark ? "Switch to light theme" : "Switch to dark theme");
}
if (icon) icon.innerHTML = dark ? "&#9790;" : "&#9728;"; // moon / sun
}
var saved = null;
try { saved = localStorage.getItem(STORE); } catch (e) {}
var prefersDark = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
applyTheme(saved || (prefersDark ? "dark" : "light"));
if (toggle) {
toggle.addEventListener("click", function () {
var next = root.getAttribute("data-theme") === "dark" ? "light" : "dark";
applyTheme(next);
try { localStorage.setItem(STORE, next); } catch (e) {}
});
}
// Follow the OS only while the user hasn't explicitly chosen.
if (window.matchMedia) {
var mq = window.matchMedia("(prefers-color-scheme: dark)");
var onChange = function (e) {
var explicit = null;
try { explicit = localStorage.getItem(STORE); } catch (err) {}
if (!explicit) applyTheme(e.matches ? "dark" : "light");
};
if (mq.addEventListener) mq.addEventListener("change", onChange);
else if (mq.addListener) mq.addListener(onChange);
}
/* ---- Dynamic year ----------------------------------------------------- */
var yr = $("#year");
if (yr) yr.textContent = String(new Date().getFullYear());
/* ---- Mobile nav ------------------------------------------------------- */
var header = $(".site-header");
var navToggle = $("#navToggle");
if (navToggle && header) {
var closeNav = function () {
header.classList.remove("nav-open");
navToggle.setAttribute("aria-expanded", "false");
navToggle.setAttribute("aria-label", "Open menu");
};
navToggle.addEventListener("click", function () {
var open = header.classList.toggle("nav-open");
navToggle.setAttribute("aria-expanded", String(open));
navToggle.setAttribute("aria-label", open ? "Close menu" : "Open menu");
});
$$("#navLinks a").forEach(function (a) { a.addEventListener("click", closeNav); });
document.addEventListener("keydown", function (e) {
if (e.key === "Escape") closeNav();
});
}
/* ---- Money helper ----------------------------------------------------- */
function money(n) {
return "$" + Math.round(n).toLocaleString("en-US");
}
/* ---- Calculator ------------------------------------------------------- */
var form = $("#calcForm");
if (form) {
var EQUIP = 25;
var M365_RATE = 14; // Business Standard
var VOIP_RATE = 28; // GPS-Voice Standard
function intVal(id) {
var el = $("#" + id);
var v = parseInt(el && el.value, 10);
if (isNaN(v) || v < 0) v = 0;
if (v > 500) v = 500;
return v;
}
function numSelect(id) {
var el = $("#" + id);
var v = parseFloat(el && el.value);
return isNaN(v) ? 0 : v;
}
function lineRow(name, detail, cost) {
var zero = cost <= 0 ? " is-zero" : "";
return (
'<div class="lline' + zero + '">' +
'<span class="lname">' + name + (detail ? ' <span class="muted">' + detail + "</span>" : "") + "</span>" +
'<span class="lcost">' + money(cost) + "</span>" +
"</div>"
);
}
function recalc() {
var endpoints = intVal("endpoints");
var tier = numSelect("gpsTier");
var equip = $("#equip").checked;
var support = numSelect("support");
var m365 = intVal("m365");
var voip = intVal("voip");
var hosting = numSelect("hosting");
var gpsCost = endpoints * tier;
var equipCost = equip ? EQUIP : 0;
var m365Cost = m365 * M365_RATE;
var voipCost = voip * VOIP_RATE;
var tierName = tier === 19 ? "GPS-Basic" : tier === 39 ? "GPS-Advanced" : "GPS-Pro";
var supportName = support === 0 ? "" :
support === 200 ? "Essential" : support === 380 ? "Standard" :
support === 540 ? "Premium" : "Priority";
var hostingName = hosting === 0 ? "" :
hosting === 15 ? "Starter" : hosting === 35 ? "Business" : "Commerce";
var lines = "";
lines += lineRow(tierName + " monitoring", endpoints + " &times; " + money(tier), gpsCost);
lines += lineRow("Equipment monitoring", "up to 10 devices", equipCost);
lines += lineRow((supportName || "Support plan") + " support", supportName ? "bundled labor" : "none", support);
lines += lineRow("Microsoft 365", m365 + " &times; " + money(M365_RATE), m365Cost);
lines += lineRow("Business phones", voip + " &times; " + money(VOIP_RATE), voipCost);
lines += lineRow((hostingName || "Web hosting") + " hosting", hostingName ? "managed" : "none", hosting);
var total = gpsCost + equipCost + support + m365Cost + voipCost + hosting;
$("#ledgerLines").innerHTML = lines;
$("#totalMonthly").textContent = money(total);
$("#totalAnnual").textContent = money(total * 12) + " / year";
var per = endpoints > 0 ? total / endpoints : 0;
$("#perEndpoint").innerHTML = endpoints > 0
? money(per) + " all-in, per endpoint / mo"
: "&mdash; add endpoints to see per-seat cost";
}
function clampInt(v) {
if (isNaN(v) || v < 0) return 0;
return v > 500 ? 500 : v;
}
// Steppers
$$(".stepper").forEach(function (st) {
var input = $("input", st);
$$("button", st).forEach(function (b) {
b.addEventListener("click", function () {
var dir = parseInt(b.getAttribute("data-dir"), 10);
input.value = clampInt(parseInt(input.value, 10) + dir);
recalc();
});
});
// On commit (blur / Enter), snap a typed value back into range so the
// field never shows a number the math has silently clamped.
input.addEventListener("change", function () {
input.value = clampInt(parseInt(input.value, 10));
});
});
// Hand the built estimate to the contact form when "Send me this estimate"
// is clicked (the anchor still scrolls to #contact).
var sendBtn = $("#sendEstimate");
if (sendBtn) {
sendBtn.addEventListener("click", function () {
var msg = $("#cf-msg");
if (!msg) return;
var lines = $$("#ledgerLines .lline")
.filter(function (l) { return !l.classList.contains("is-zero"); })
.map(function (l) {
var name = $(".lname", l).textContent.replace(/\s+/g, " ").trim();
return "- " + name + ": " + $(".lcost", l).textContent.trim();
});
msg.value = "Here is the estimate I built:\n" + lines.join("\n") +
"\n\nMonthly: " + $("#totalMonthly").textContent +
" (" + $("#totalAnnual").textContent + ")\n\nI'd like to talk it through.";
});
}
form.addEventListener("input", recalc);
form.addEventListener("change", recalc);
form.addEventListener("submit", function (e) { e.preventDefault(); });
recalc();
}
/* ---- FAQ accordion ---------------------------------------------------- */
$$(".faq__q").forEach(function (q, i) {
var panel = q.nextElementSibling;
var inner = panel ? panel.firstElementChild : null;
// Wire ARIA relationships (button <-> answer region).
if (panel) {
var pid = "faq-a-" + (i + 1);
var qid = "faq-q-" + (i + 1);
panel.id = pid; q.id = qid;
panel.setAttribute("role", "region");
panel.setAttribute("aria-labelledby", qid);
q.setAttribute("aria-controls", pid);
}
q.addEventListener("click", function () {
var open = q.getAttribute("aria-expanded") === "true";
q.setAttribute("aria-expanded", String(!open));
if (panel) panel.style.maxHeight = open ? "0px" : (inner.scrollHeight + 8) + "px";
});
});
// Recompute open panel heights on resize (text reflow)
window.addEventListener("resize", function () {
$$(".faq__q").forEach(function (q) {
if (q.getAttribute("aria-expanded") === "true") {
var panel = q.nextElementSibling;
var inner = panel ? panel.firstElementChild : null;
if (panel && inner) panel.style.maxHeight = (inner.scrollHeight + 8) + "px";
}
});
});
/* ---- Contact form (demo) --------------------------------------------- */
var contact = $("#contactForm");
if (contact) {
contact.addEventListener("submit", function (e) {
e.preventDefault();
var name = $("#cf-name");
var contactField = $("#cf-contact");
var note = $("#formNote");
if (!name.value.trim() || !contactField.value.trim()) {
note.textContent = "Please add your name and a phone or email so we can reach you.";
note.style.color = "var(--accent-ink)";
(name.value.trim() ? contactField : name).focus();
return;
}
note.textContent = "Thanks, " + name.value.trim().split(" ")[0] +
" — in a live build this reaches our Tucson team. (Demo: nothing was sent.)";
note.style.color = "var(--good)";
contact.reset();
});
}
/* ---- Reveal on scroll ------------------------------------------------- */
var reveals = $$(".reveal");
if ("IntersectionObserver" in window && reveals.length) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting) { en.target.classList.add("in"); io.unobserve(en.target); }
});
}, { rootMargin: "0px 0px -8% 0px", threshold: 0.08 });
reveals.forEach(function (el) { io.observe(el); });
} else {
reveals.forEach(function (el) { el.classList.add("in"); });
}
})();

View File

@@ -0,0 +1,51 @@
# DESIGN.md — "Sonoran Ledger" (multipage)
The locked design language for the ACG site. A trusted local bookkeeper's ledger,
rendered for the web: warm paper, precise rules, mono numerals. Pricing, the
calculator, and the concierge story are treated as a transparent, permanent record.
## Color strategy: Committed (warm paper carries the surface), one accent as "ink"
Use OKLCH; neutrals tinted toward the brand hue (~30 hue). Never pure #000/#fff.
- **Light:** paper `#F7F3EB`, surface `#EDE6D9`, surface-2 `#E4DACA`,
ink `#2A2521`, ink-2 `#5A5148`, ink-3 `#6D6456` (AA on cream).
- **Dark:** paper `#1C1814`, surface `#2A2520`, surface-2 `#322B24`,
ink `#E8DFCE`, ink-2 `#C4B8A3`, ink-3 `#9A8C77`.
- **Accent (the "ink"):** amber `#F2922E` (light) / `#F2A24E` (dark) for fills,
underlines, marks, the single primary CTA. Orange TEXT on light must use
`#BD5A00` (AA); on dark `#F4A85C`.
- Accent is ≤ ~10% of any view. It marks active data, never fills big blocks.
## Theme
Both light and dark are first-class. Scene: a Tucson business owner reading at a
sunlit desk by day, or late at night deciding on a provider. Warm in both. System
preference is honored and remembered, set before first paint (no flash).
## Typography
- Display: **Barlow Condensed** 600/700 (headlines, tier names, the "since 2001" lockup).
- Body: **Lexend** 300/400/500 (body capped 6575ch).
- Mono: **JetBrains Mono**, tabular-nums, for ALL money, the calculator, endpoint
counts, eyebrows, and ledger micro-labels.
- Scale steps keep ≥1.25 contrast; hierarchy via scale + weight.
## Layout & rhythm
- Strict 24px baseline. Faint continuous ledger rulings align to body line-height.
- Vary spacing per page; do not repeat one padding everywhere. Avoid identical card
grids. Cards only where they are the right affordance (pricing tiers).
- Radius 02px only. Sharp = precise, manual, honest.
## Motion
- Reveal on scroll (opacity + small translate), ease-out only, respects reduced-motion.
- Never animate layout properties.
## House laws (impeccable)
- **No em dashes** anywhere in copy. Use commas, colons, semicolons, periods, parentheses.
- **No side-stripe accent borders** (no border-left/right > 1px as a colored accent).
Use full borders, leading numerals/marks, background tints, or nothing.
- No gradient text, no decorative glassmorphism, no hero-metric template, no AI slop.
## Multipage specifics
- Shared sticky header (brand + nav + theme toggle + mobile menu) and footer on every page.
- Active nav state per page (`aria-current="page"`).
- Pages: Home, Services, Pricing, Calculator, About, Contact. Each has its own
opening rhythm so the set never feels templated.

View File

@@ -0,0 +1,44 @@
# PRODUCT.md — Arizona Computer Guru (ACG) website
register: brand
## Product purpose
A multipage marketing website for Arizona Computer Guru, a Tucson managed-IT and
cybersecurity provider operating since 2001. The site sells trust before it sells
services: the goal is for a small-business owner to feel they have found honest,
local people who will go further than the last IT company, then to reach out or
build an estimate. Design IS the product here.
## Users
- Tucson / southern-Arizona small-business owners and office managers (roughly
5 to 100 employees): law firms, medical practices, accounting, construction,
property management, retail, manufacturing.
- Often burned before: surprise bills, offshore call centers, three-year lock-ins,
"unlimited support" that wasn't. Skeptical, value-conscious, not deeply technical.
- They want a real person, fast local help, and to know what it costs before a call.
## Brand & tone
- The differentiator is **concierge IT**: genuine relationships, going much further
than typical IT companies, at reasonable prices.
- Voice: kind, direct, transparent, plain-spoken, confident without arrogance.
Never condescending, never sales-pressure, never hype. "We built it the way we'd
want to be treated." Publishes its prices on purpose.
- Local and rooted: Tucson since 2001, physical office at 7437 E. 22nd St, onsite
in 1 to 2 hours, month-to-month terms.
## Anti-references (do NOT look like these)
- Generic SaaS/MSP slop: blue/purple gradients, floating 3D isometrics, server-rack
clip art, glowing shields, "hero metric" stat templates, identical icon-card grids.
- Hard-sell landing pages: countdown timers, fake scarcity, manufactured testimonials.
- Cold enterprise-security navy-and-gold. Cold healthcare white-and-teal.
## Strategic principles
- Transparency is the aesthetic. Show real numbers, real pricing, real process.
- Warmth and place: Tucson light, paper, ledgers, honest materials. Photographic,
not illustrated.
- Every word earns its place. No restated headings, no filler.
## Integrity rules
- No fabricated named client testimonials. Proof is built from verifiable facts.
- The contact form is a demo (not wired to a mailbox). Pricing is illustrative of
published GPS rates. Photography is representational.

View File

@@ -0,0 +1,118 @@
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>About | Arizona Computer Guru</title>
<meta name="description" content="A Tucson IT company since 2001. We started as a break-fix shop, then built GPS to align our incentives with our clients. Concierge IT, the way we'd want to be treated." />
<meta name="color-scheme" content="light dark" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@500;600;700&family=Lexend:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500;600;700&family=Fraunces:wght@400;500;600;700&family=Source+Sans+3:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500;600;700&family=Anton&family=Hanken+Grotesk:wght@400;500;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="css/styles.css" />
<script>(function(){try{var s=localStorage.getItem("acg-theme");var m=s||(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light");document.documentElement.setAttribute("data-theme",m);document.documentElement.setAttribute("data-skin",(localStorage.getItem("acg-skin")||"ledger"));document.documentElement.classList.add("js");}catch(e){}})();</script>
</head>
<body>
<a href="#main" class="sr-only">Skip to content</a>
<header class="site-header">
<div class="wrap">
<a class="brand" href="index.html" aria-label="Arizona Computer Guru home">
<span class="brand__mark" aria-hidden="true">ACG</span>
<span><span class="brand__name">Arizona Computer Guru</span><br /><span class="brand__since">Concierge IT &middot; Tucson &middot; since 2001</span></span>
</a>
<nav class="nav" aria-label="Primary">
<div class="nav__links" id="navLinks">
<a class="nav__link" href="index.html">Home</a>
<a class="nav__link" href="services.html">Services</a>
<a class="nav__link" href="pricing.html">Pricing</a>
<a class="nav__link" href="calculator.html">Estimate</a>
<a class="nav__link" href="about.html" aria-current="page">About</a>
<a class="nav__link" href="contact.html">Contact</a>
</div>
<a class="nav__phone" href="tel:+15203048300">520.304.8300</a>
<button class="skin-toggle" id="skinToggle" type="button" aria-label="Switch visual skin (Paper, Midnight, Verdigris, or Bold)" title="Switch skin: Paper / Midnight / Verdigris / Bold"><span class="sw" aria-hidden="true"></span></button>
<button class="theme-toggle" id="themeToggle" type="button" aria-pressed="false" aria-label="Switch to dark theme" title="Toggle light / dark"><span aria-hidden="true" data-theme-icon>&#9728;</span></button>
<button class="nav__toggle" id="navToggle" type="button" aria-expanded="false" aria-controls="navLinks" aria-label="Open menu">&#8801;</button>
</nav>
</div>
</header>
<main id="main">
<section class="page-head page-head--img ledger">
<div class="wrap">
<div class="reveal">
<p class="eyebrow">Tucson, since 2001</p>
<h1>The IT partner that actually picks up</h1>
<p class="lead">We are not a national chain and we are not venture-backed. We are a local Tucson team that has been here for two decades because we do right by the businesses we serve.</p>
</div>
<figure class="reveal">
<img src="assets/images/about.png" width="1248" height="832" alt="A friendly handshake over a wooden desk with a laptop and a handwritten notebook, an Arizona map framed on the wall." />
</figure>
</div>
</section>
<!-- Origin story -->
<section class="story" aria-labelledby="origin-h">
<div class="wrap">
<div class="story__copy reveal">
<p class="section-tag">Why we work this way <span>/ the short version</span></p>
<h2 id="origin-h">We used to be the company we now warn you about</h2>
<p class="muted">We started in 2001 as a break-fix shop. We charged by the hour, showed up when things broke, and quietly made more money when our clients had more problems. That did not sit right.</p>
<p class="muted">So we built Guru Protection Services: proactive monitoring, transparent published pricing, and month-to-month terms. The point was to put our incentives on the same side of the table as yours. We make more when your systems stay healthy, not when they fail.</p>
<p class="muted">Today we look after businesses across Tucson, from small offices to busy practices and job sites. We have stopped ransomware before a single file was encrypted, replaced failing drives before anyone noticed, and helped owners meet the cyber-insurance requirements that used to keep them up at night.</p>
<blockquote>We built our service the way we'd want to be treated if we were the customer.</blockquote>
</div>
<figure class="story__img reveal">
<img src="assets/images/story.png" width="1248" height="832" alt="Warm afternoon desert light raking across a white stucco wall meeting a clean wooden windowsill." loading="lazy" />
</figure>
</div>
</section>
<!-- Philosophy -->
<section class="home-services ledger" aria-labelledby="phil-h">
<div class="wrap">
<p class="section-tag">What concierge means, concretely <span>/ five choices</span></p>
<h2 id="phil-h">The promises behind the work</h2>
<div class="svc-list">
<article class="svc reveal"><div class="svc__no">01</div><div><h3 class="svc__name">Transparent pricing</h3><p class="svc__desc">Published per-endpoint rates. You know what you pay before you call us. No games, no "call for quote," no fees that appear at the bottom of an invoice.</p></div><div class="svc__meta">$19&ndash;$39 /endpoint</div></article>
<article class="svc reveal"><div class="svc__no">02</div><div><h3 class="svc__name">Local Tucson team</h3><p class="svc__desc">A real office at 7437 E. 22nd St since 2001. When your server dies at 3pm you do not want a ticket, you want someone at your door by 3:45. We can usually be onsite within an hour or two.</p></div><div class="svc__meta">onsite 1&ndash;2 hr</div></article>
<article class="svc reveal"><div class="svc__no">03</div><div><h3 class="svc__name">Proactive, not reactive</h3><p class="svc__desc">24/7 monitoring, automated patching, and alerts that reach us before they reach you. The goal is that you never have to call because something broke.</p></div><div class="svc__meta">24/7 monitoring</div></article>
<article class="svc reveal"><div class="svc__no">04</div><div><h3 class="svc__name">Month-to-month terms</h3><p class="svc__desc">No three-year lock-ins, no early-termination fees. If we stop earning your business, you can leave with 30 days' notice. We would rather keep you by choice.</p></div><div class="svc__meta">cancel in 30 days</div></article>
<article class="svc reveal"><div class="svc__no">05</div><div><h3 class="svc__name">Plain English, always</h3><p class="svc__desc">Many IT people are dismissive or condescending. That is never tolerated here. We are kind, direct, and honest, and we will never make you feel small for asking a question.</p></div><div class="svc__meta">no jargon walls</div></article>
</div>
</div>
</section>
<!-- Big quote band -->
<section aria-labelledby="bq-h">
<div class="wrap" style="max-width:900px;">
<h2 id="bq-h" class="sr-only">Our standard</h2>
<p class="bigquote reveal">You might not choose us. That's okay. But you'll make a better decision because we told you the truth.</p>
<p class="signature reveal">// Arizona Computer Guru, Tucson</p>
</div>
</section>
<section class="cta-band" aria-labelledby="cta-h">
<div class="wrap">
<div class="reveal">
<h2 id="cta-h">Come see for yourself</h2>
<p>Stop by the office, or have us out to look at the server closet that runs hot and the printer that jams every Tuesday. The first conversation is free, and there is no pitch.</p>
</div>
<div class="cta-band__actions reveal">
<a class="btn btn--primary" href="contact.html">Talk to a human <span class="arrow" aria-hidden="true">&rarr;</span></a>
<a class="btn btn--ghost" href="calculator.html">Estimate your cost</a>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="wrap">
<p>&copy; <span id="year">2026</span> Arizona Computer Guru &middot; Protecting Tucson businesses since 2001</p>
<p><a href="tel:+15203048300">520.304.8300</a> &middot; <a href="mailto:info@azcomputerguru.com">info@azcomputerguru.com</a></p>
<p class="disclaimer">Local demonstration build of a proposed azcomputerguru.com (multipage). Pricing reflects published GPS rates and is illustrative for the estimator; a real quote is tailored to your environment. The contact form is not wired to a mailbox. Photography is representational.</p>
</div>
</footer>
<script src="js/app.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

View File

@@ -0,0 +1,143 @@
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Estimate your IT cost | Arizona Computer Guru</title>
<meta name="description" content="Build a transparent IT estimate in seconds: endpoints, protection tier, support plan, Microsoft 365, phones, and hosting. No email wall, no sales call." />
<meta name="color-scheme" content="light dark" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@500;600;700&family=Lexend:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500;600;700&family=Fraunces:wght@400;500;600;700&family=Source+Sans+3:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500;600;700&family=Anton&family=Hanken+Grotesk:wght@400;500;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="css/styles.css" />
<script>(function(){try{var s=localStorage.getItem("acg-theme");var m=s||(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light");document.documentElement.setAttribute("data-theme",m);document.documentElement.setAttribute("data-skin",(localStorage.getItem("acg-skin")||"ledger"));document.documentElement.classList.add("js");}catch(e){}})();</script>
</head>
<body>
<a href="#main" class="sr-only">Skip to content</a>
<header class="site-header">
<div class="wrap">
<a class="brand" href="index.html" aria-label="Arizona Computer Guru home">
<span class="brand__mark" aria-hidden="true">ACG</span>
<span><span class="brand__name">Arizona Computer Guru</span><br /><span class="brand__since">Concierge IT &middot; Tucson &middot; since 2001</span></span>
</a>
<nav class="nav" aria-label="Primary">
<div class="nav__links" id="navLinks">
<a class="nav__link" href="index.html">Home</a>
<a class="nav__link" href="services.html">Services</a>
<a class="nav__link" href="pricing.html">Pricing</a>
<a class="nav__link" href="calculator.html" aria-current="page">Estimate</a>
<a class="nav__link" href="about.html">About</a>
<a class="nav__link" href="contact.html">Contact</a>
</div>
<a class="nav__phone" href="tel:+15203048300">520.304.8300</a>
<button class="skin-toggle" id="skinToggle" type="button" aria-label="Switch visual skin (Paper, Midnight, Verdigris, or Bold)" title="Switch skin: Paper / Midnight / Verdigris / Bold"><span class="sw" aria-hidden="true"></span></button>
<button class="theme-toggle" id="themeToggle" type="button" aria-pressed="false" aria-label="Switch to dark theme" title="Toggle light / dark"><span aria-hidden="true" data-theme-icon>&#9728;</span></button>
<button class="nav__toggle" id="navToggle" type="button" aria-expanded="false" aria-controls="navLinks" aria-label="Open menu">&#8801;</button>
</nav>
</div>
</header>
<main id="main">
<section class="page-head ledger">
<div class="wrap reveal">
<p class="eyebrow">Live estimate &middot; no email wall</p>
<h1>Your IT, accounted for</h1>
<p class="lead">Move the numbers, watch the total. This is the same math we would walk you through in person, with nothing hidden behind a "contact sales" button.</p>
</div>
</section>
<section class="calc ledger" id="calculator" aria-labelledby="calc-h" style="padding-top:calc(var(--base)*2);">
<div class="wrap">
<h2 id="calc-h" class="sr-only">IT cost estimator</h2>
<div class="calc__shell">
<div class="calc__grid">
<form class="calc__inputs" id="calcForm" aria-label="IT cost estimator">
<div class="calc__row">
<label for="endpoints">Computers &amp; servers <span class="hint">Every monitored endpoint</span></label>
<div class="calc__control"><div class="stepper" data-step="endpoints">
<button type="button" data-dir="-1" aria-label="Fewer endpoints">&minus;</button>
<input id="endpoints" name="endpoints" type="number" inputmode="numeric" min="0" max="500" value="22" />
<button type="button" data-dir="1" aria-label="More endpoints">+</button>
</div></div>
</div>
<div class="calc__row">
<label for="gpsTier">Protection tier <span class="hint">Per-endpoint monitoring level</span></label>
<div class="calc__control"><select id="gpsTier" name="gpsTier">
<option value="19">GPS-Basic &middot; $19</option>
<option value="26" selected>GPS-Pro &middot; $26</option>
<option value="39">GPS-Advanced &middot; $39</option>
</select></div>
</div>
<div class="calc__row">
<label for="equip">Equipment monitoring <span class="hint">Network gear, printers, NAS. $25/mo</span></label>
<div class="calc__control toggle-row"><span class="switch">
<input type="checkbox" id="equip" name="equip" />
<span class="track" aria-hidden="true"></span><span class="knob" aria-hidden="true"></span>
</span></div>
</div>
<div class="calc__row">
<label for="support">Support plan <span class="hint">Bundled labor hours each month</span></label>
<div class="calc__control"><select id="support" name="support">
<option value="0">None, pay as you go</option>
<option value="200">Essential &middot; $200 (2 hr)</option>
<option value="380" selected>Standard &middot; $380 (4 hr)</option>
<option value="540">Premium &middot; $540 (6 hr)</option>
<option value="850">Priority &middot; $850 (10 hr)</option>
</select></div>
</div>
<div class="calc__row">
<label for="m365">Microsoft 365 users <span class="hint">Business Standard, $14/user</span></label>
<div class="calc__control"><div class="stepper" data-step="m365">
<button type="button" data-dir="-1" aria-label="Fewer Microsoft 365 users">&minus;</button>
<input id="m365" name="m365" type="number" inputmode="numeric" min="0" max="500" value="0" />
<button type="button" data-dir="1" aria-label="More Microsoft 365 users">+</button>
</div></div>
</div>
<div class="calc__row">
<label for="voip">Business phone users <span class="hint">GPS-Voice Standard, $28/user</span></label>
<div class="calc__control"><div class="stepper" data-step="voip">
<button type="button" data-dir="-1" aria-label="Fewer phone users">&minus;</button>
<input id="voip" name="voip" type="number" inputmode="numeric" min="0" max="500" value="0" />
<button type="button" data-dir="1" aria-label="More phone users">+</button>
</div></div>
</div>
<div class="calc__row" style="border-bottom:0;">
<label for="hosting">Web hosting <span class="hint">Managed, with SSL &amp; backups</span></label>
<div class="calc__control"><select id="hosting" name="hosting">
<option value="0" selected>None</option>
<option value="15">Starter &middot; $15</option>
<option value="35">Business &middot; $35</option>
<option value="65">Commerce &middot; $65</option>
</select></div>
</div>
</form>
<div class="calc__out">
<h3>Monthly statement</h3>
<div class="ledger-lines" id="ledgerLines"></div>
<hr class="calc__divider" />
<div class="calc__total" aria-live="polite">
<span class="tlabel">Per month</span>
<span class="tnum mono" id="totalMonthly">$0</span>
</div>
<div class="calc__annual mono" id="totalAnnual">$0 / year</div>
<div class="calc__perep mono" id="perEndpoint">all-in, per endpoint / mo</div>
<a class="btn btn--primary calc__cta" href="contact.html#contact" id="sendEstimate">Send me this estimate <span class="arrow" aria-hidden="true">&rarr;</span></a>
<p class="calc__foot">Estimate only, your real quote is tailored to your setup. No card, no commitment, and we will tell you if you are buying more than you need.</p>
</div>
</div>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="wrap">
<p>&copy; <span id="year">2026</span> Arizona Computer Guru &middot; Protecting Tucson businesses since 2001</p>
<p><a href="tel:+15203048300">520.304.8300</a> &middot; <a href="mailto:info@azcomputerguru.com">info@azcomputerguru.com</a></p>
<p class="disclaimer">Local demonstration build of a proposed azcomputerguru.com (multipage). Pricing reflects published GPS rates and is illustrative for the estimator; a real quote is tailored to your environment. The contact form is not wired to a mailbox. Photography is representational.</p>
</div>
</footer>
<script src="js/app.js"></script>
</body>
</html>

View File

@@ -0,0 +1,112 @@
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Contact | Arizona Computer Guru</title>
<meta name="description" content="Talk to a human in Tucson. Call 520.304.8300, email info@azcomputerguru.com, or visit 7437 E. 22nd St. No pressure, no pitch." />
<meta name="color-scheme" content="light dark" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@500;600;700&family=Lexend:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500;600;700&family=Fraunces:wght@400;500;600;700&family=Source+Sans+3:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500;600;700&family=Anton&family=Hanken+Grotesk:wght@400;500;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="css/styles.css" />
<script>(function(){try{var s=localStorage.getItem("acg-theme");var m=s||(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light");document.documentElement.setAttribute("data-theme",m);document.documentElement.setAttribute("data-skin",(localStorage.getItem("acg-skin")||"ledger"));document.documentElement.classList.add("js");}catch(e){}})();</script>
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"LocalBusiness","name":"Arizona Computer Guru","image":"assets/images/contact.png","address":{"@type":"PostalAddress","streetAddress":"7437 E. 22nd St","addressLocality":"Tucson","addressRegion":"AZ","postalCode":"85710","addressCountry":"US"},"telephone":"+1-520-304-8300","email":"info@azcomputerguru.com","url":"https://azcomputerguru.com","openingHours":"Mo-Fr 09:00-17:00"}
</script>
</head>
<body>
<a href="#main" class="sr-only">Skip to content</a>
<header class="site-header">
<div class="wrap">
<a class="brand" href="index.html" aria-label="Arizona Computer Guru home">
<span class="brand__mark" aria-hidden="true">ACG</span>
<span><span class="brand__name">Arizona Computer Guru</span><br /><span class="brand__since">Concierge IT &middot; Tucson &middot; since 2001</span></span>
</a>
<nav class="nav" aria-label="Primary">
<div class="nav__links" id="navLinks">
<a class="nav__link" href="index.html">Home</a>
<a class="nav__link" href="services.html">Services</a>
<a class="nav__link" href="pricing.html">Pricing</a>
<a class="nav__link" href="calculator.html">Estimate</a>
<a class="nav__link" href="about.html">About</a>
<a class="nav__link" href="contact.html" aria-current="page">Contact</a>
</div>
<a class="nav__phone" href="tel:+15203048300">520.304.8300</a>
<button class="skin-toggle" id="skinToggle" type="button" aria-label="Switch visual skin (Paper, Midnight, Verdigris, or Bold)" title="Switch skin: Paper / Midnight / Verdigris / Bold"><span class="sw" aria-hidden="true"></span></button>
<button class="theme-toggle" id="themeToggle" type="button" aria-pressed="false" aria-label="Switch to dark theme" title="Toggle light / dark"><span aria-hidden="true" data-theme-icon>&#9728;</span></button>
<button class="nav__toggle" id="navToggle" type="button" aria-expanded="false" aria-controls="navLinks" aria-label="Open menu">&#8801;</button>
</nav>
</div>
</header>
<main id="main">
<section class="contact ledger" id="contact" aria-labelledby="contact-h" style="padding-top:calc(var(--base)*3);">
<div class="wrap">
<div class="contact__intro reveal">
<p class="section-tag">No pressure, no pitch <span>/ let's talk</span></p>
<h1 id="contact-h">Talk to a human</h1>
<p class="muted">Tell us what is frustrating you, or send over an estimate from the calculator. We will give you honest feedback, even if that is "your current setup is fine, here is one thing to fix."</p>
<div class="contact__lines">
<div><span class="k">Phone</span><br /><a class="v" href="tel:+15203048300">520.304.8300</a></div>
<div><span class="k">Email</span><br /><a class="v" href="mailto:info@azcomputerguru.com">info@azcomputerguru.com</a></div>
<div><span class="k">Office</span><br /><span class="v">7437 E. 22nd St, Tucson, AZ 85710</span></div>
<div><span class="k">Hours</span><br /><span class="v">Mon&ndash;Fri 9&ndash;5 &middot; 24/7 emergency for Priority clients</span></div>
</div>
<figure class="office-figure">
<img src="assets/images/contact.png" width="1248" height="832" alt="A warm southwestern stucco storefront at golden hour with a saguaro and the Santa Catalina mountains in the distance, Tucson." loading="lazy" />
<figcaption><span>East side, Tucson</span><span>come say hello</span></figcaption>
</figure>
</div>
<form class="contact__form reveal" id="contactForm" novalidate>
<div class="form-field">
<label for="cf-name">Your name</label>
<input id="cf-name" name="name" type="text" autocomplete="name" required />
</div>
<div class="form-field">
<label for="cf-biz">Business</label>
<input id="cf-biz" name="business" type="text" autocomplete="organization" />
</div>
<div class="form-field">
<label for="cf-contact">Phone or email</label>
<input id="cf-contact" name="contact" type="text" autocomplete="email" required />
</div>
<div class="form-field">
<label for="cf-msg">What can we help with?</label>
<textarea id="cf-msg" name="message"></textarea>
</div>
<button class="btn btn--primary" type="submit">Send message <span class="arrow" aria-hidden="true">&rarr;</span></button>
<p class="form-note" id="formNote" aria-live="polite">We reply the same business day. (Demo build: this form is not wired to a mailbox.)</p>
</form>
</div>
</section>
<section class="faq" id="faq" aria-labelledby="faq-h">
<div class="wrap wrap--narrow">
<p class="section-tag">Straight answers <span>/ ask us anything</span></p>
<h2 id="faq-h">Questions worth asking any IT company</h2>
<div class="faq__list" id="faqList">
<div class="faq__item"><button class="faq__q" aria-expanded="false"><span>Will I get a real person, or an offshore call center?</span><span class="pm" aria-hidden="true">+</span></button><div class="faq__a"><div class="faq__a-inner">A real, local person. Our team works out of our Tucson office at 7437 E. 22nd St. You can visit, and after hours you reach an on-call tech who is also local, not a queue overseas.</div></div></div>
<div class="faq__item"><button class="faq__q" aria-expanded="false"><span>What's your contract term?</span><span class="pm" aria-hidden="true">+</span></button><div class="faq__a"><div class="faq__a-inner">Month-to-month. Cancel with 30 days' notice, no early-termination fee. We would rather earn your business every month than trap you in a three-year agreement.</div></div></div>
<div class="faq__item"><button class="faq__q" aria-expanded="false"><span>What happens if I use more support hours than my plan includes?</span><span class="pm" aria-hidden="true">+</span></button><div class="faq__a"><div class="faq__a-inner">Plan hours are used first. After that we draw from any pre-paid block time you have banked ($100 to $150/hr), which never expires. No block time? Overage is billed at $175/hr, and we will always flag it before the meter runs.</div></div></div>
<div class="faq__item"><button class="faq__q" aria-expanded="false"><span>How fast do you respond to an emergency?</span><span class="pm" aria-hidden="true">+</span></button><div class="faq__a"><div class="faq__a-inner">By plan: Standard is 8 hours, Premium 4 hours, Priority 2 hours around the clock. A total outage is escalated immediately regardless of plan, and for local clients we are typically onsite within 1 to 2 hours.</div></div></div>
<div class="faq__item"><button class="faq__q" aria-expanded="false"><span>What security is actually included?</span><span class="pm" aria-hidden="true">+</span></button><div class="faq__a"><div class="faq__a-inner">GPS-Pro and above include advanced EDR, email security, dark-web monitoring, and monthly security-awareness training. GPS-Advanced adds compliance tooling and ransomware rollback. Basic antivirus alone is not enough anymore, so we do not pretend it is.</div></div></div>
<div class="faq__item"><button class="faq__q" aria-expanded="false"><span>Can you help me meet cyber-insurance requirements?</span><span class="pm" aria-hidden="true">+</span></button><div class="faq__a"><div class="faq__a-inner">Yes. GPS-Pro covers most of what insurers ask for: MFA enforcement, EDR, training, and cloud backups, and we will provide documentation for your agent. We work with several Tucson insurance agents regularly.</div></div></div>
<div class="faq__item"><button class="faq__q" aria-expanded="false"><span>What if my team grows or shrinks?</span><span class="pm" aria-hidden="true">+</span></button><div class="faq__a"><div class="faq__a-inner">Billing is per endpoint, so it just follows your headcount. Add three people, add three endpoints; downsize and it drops the next month. No contract amendments, no penalties.</div></div></div>
<div class="faq__item"><button class="faq__q" aria-expanded="false"><span>Do you really publish your pricing?</span><span class="pm" aria-hidden="true">+</span></button><div class="faq__a"><div class="faq__a-inner">You are looking at it. Our rates are on the pricing page and in the estimator. We think hiding prices is a red flag, in any IT company, including us.</div></div></div>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="wrap">
<p>&copy; <span id="year">2026</span> Arizona Computer Guru &middot; Protecting Tucson businesses since 2001</p>
<p><a href="tel:+15203048300">520.304.8300</a> &middot; <a href="mailto:info@azcomputerguru.com">info@azcomputerguru.com</a></p>
<p class="disclaimer">Local demonstration build of a proposed azcomputerguru.com (multipage). Pricing reflects published GPS rates and is illustrative for the estimator; a real quote is tailored to your environment. The contact form is not wired to a mailbox. Photography is representational.</p>
</div>
</footer>
<script src="js/app.js"></script>
</body>
</html>

View File

@@ -0,0 +1,206 @@
/* ===========================================================================
ARIZONA COMPUTER GURU - "BLOWOUT" (radical direction)
Poster brutalism: ultra-condensed display, drenched signal-orange,
scrolling marquee, giant numerals, hard edges, zero ledger calm.
Self-contained (does not use styles.css).
=========================================================================== */
:root {
--ink: #0C0A09; /* near-black canvas */
--ink-2: #16120F; /* raised black panel */
--bone: #F4EDE1; /* primary text */
--bone-2: #B8AE9E; /* secondary */
--hot: #FF5A1F; /* signal orange, used BIG */
--hot-2: #FF8A4D; /* lighter orange for fine text */
--line: rgba(244, 237, 225, 0.16);
--maxw: 1280px;
--f-poster: "Anton", "Arial Narrow", system-ui, sans-serif;
--f-body: "Hanken Grotesk", system-ui, -apple-system, Segoe UI, sans-serif;
--f-mono: "JetBrains Mono", ui-monospace, Consolas, monospace;
}
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
*, *::before, *::after { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
.marquee__track { animation: none !important; }
}
body {
background: var(--ink); color: var(--bone);
font-family: var(--f-body); font-size: 1.0625rem; line-height: 1.5;
-webkit-font-smoothing: antialiased; overflow-x: hidden;
}
img { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
::selection { background: var(--hot); color: var(--ink); }
.sr-only { position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0; }
.wrap { width: min(100% - 3rem, var(--maxw)); margin-inline: auto; }
.hot { color: var(--hot); }
/* ---- Header ------------------------------------------------------------- */
.rx-header {
position: sticky; top: 0; z-index: 50;
background: var(--ink); border-bottom: 2px solid var(--bone);
}
.rx-header .wrap { display: flex; align-items: center; gap: 1.5rem;
height: 68px; }
.rx-brand { font-family: var(--f-poster); font-size: 1.6rem; letter-spacing: 0.02em;
text-transform: uppercase; line-height: 1; }
.rx-brand b { color: var(--hot); }
.rx-nav { margin-left: auto; display: flex; align-items: center; gap: 1.75rem; }
.rx-nav a { font-family: var(--f-mono); font-size: 0.8rem; letter-spacing: 0.08em;
text-transform: uppercase; color: var(--bone-2); }
.rx-nav a:hover { color: var(--hot); }
.rx-cta-sm { border: 2px solid var(--hot); color: var(--bone) !important;
padding: 0.45rem 0.9rem; }
.rx-cta-sm:hover { background: var(--hot); color: var(--ink) !important; }
@media (max-width: 760px) { .rx-nav a:not(.rx-cta-sm) { display: none; } }
/* ---- Hero --------------------------------------------------------------- */
.rx-hero { padding-block: clamp(2.5rem, 7vw, 6rem) clamp(2rem, 5vw, 4rem);
border-bottom: 2px solid var(--bone); position: relative; overflow: hidden; }
.rx-hero__eyebrow { font-family: var(--f-mono); font-size: 0.8rem; letter-spacing: 0.25em;
text-transform: uppercase; color: var(--hot); margin-bottom: 1.5rem; }
.rx-hero h1 {
font-family: var(--f-poster); font-weight: 400; text-transform: uppercase;
font-size: clamp(3.5rem, 16vw, 13rem); line-height: 0.86; letter-spacing: -0.01em;
margin-left: -0.06em;
}
.rx-hero h1 .stroke {
color: transparent; -webkit-text-stroke: 2px var(--bone); text-stroke: 2px var(--bone);
}
.rx-hero__sub { font-size: clamp(1.1rem, 2vw, 1.5rem); color: var(--bone-2);
max-width: 30ch; margin-top: 2rem; }
.rx-hero__cta { display: flex; gap: 1rem; flex-wrap: wrap; margin-top: 2.25rem; }
.rx-hero__art {
position: absolute; right: -4%; top: 50%; transform: translateY(-50%);
width: 42vw; max-width: 620px; aspect-ratio: 3/2; pointer-events: none;
filter: grayscale(1) contrast(1.15) brightness(0.62);
-webkit-mask-image: linear-gradient(90deg, transparent, #000 35%);
mask-image: linear-gradient(90deg, transparent, #000 35%);
opacity: 0.55; z-index: -1;
}
.rx-hero__art::after { content: ""; position: absolute; inset: 0; background: var(--hot);
mix-blend-mode: overlay; }
.rx-hero__art img { width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 900px) { .rx-hero__art { display: none; } }
/* ---- Buttons ------------------------------------------------------------ */
.rx-btn { font-family: var(--f-poster); text-transform: uppercase; letter-spacing: 0.02em;
font-size: 1.15rem; display: inline-flex; align-items: center; gap: 0.6rem;
padding: 0.85rem 1.6rem; border: 2px solid transparent; cursor: pointer; }
.rx-btn--hot { background: var(--hot); color: var(--ink); }
.rx-btn--hot:hover { background: var(--bone); }
.rx-btn--out { border-color: var(--bone); color: var(--bone); }
.rx-btn--out:hover { background: var(--bone); color: var(--ink); }
.rx-btn .x { font-family: var(--f-mono); }
:focus-visible { outline: 3px solid var(--hot); outline-offset: 3px; }
/* ---- Marquee ------------------------------------------------------------ */
.marquee { background: var(--hot); color: var(--ink); border-bottom: 2px solid var(--bone);
overflow: hidden; padding-block: 0.7rem; }
.marquee__track { display: inline-flex; white-space: nowrap; will-change: transform;
animation: marquee 26s linear infinite; }
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee span { font-family: var(--f-poster); text-transform: uppercase;
font-size: 1.4rem; letter-spacing: 0.03em; padding-inline: 1.25rem; }
.marquee span::after { content: "\273F"; margin-left: 2.5rem; opacity: 0.6; }
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
/* ---- Section scaffold --------------------------------------------------- */
.rx-sec { padding-block: clamp(3rem, 7vw, 6.5rem); border-bottom: 2px solid var(--bone); position: relative; }
.rx-kicker { font-family: var(--f-mono); font-size: 0.8rem; letter-spacing: 0.22em;
text-transform: uppercase; color: var(--hot); display: flex; gap: 1rem; align-items: center; }
.rx-kicker::before { content: ""; width: 40px; height: 2px; background: var(--hot); }
.rx-h2 { font-family: var(--f-poster); text-transform: uppercase; font-weight: 400;
font-size: clamp(2.4rem, 6vw, 4.5rem); line-height: 0.92; margin-top: 1rem; }
.rx-ghost { position: absolute; right: 2%; top: 0.5rem; font-family: var(--f-poster);
font-size: clamp(6rem, 18vw, 16rem); line-height: 1; color: transparent;
-webkit-text-stroke: 1px var(--line); pointer-events: none; user-select: none; z-index: 0; }
.rx-sec > .wrap { position: relative; z-index: 1; }
/* ---- Services (giant rows) ---------------------------------------------- */
.rx-svc { display: grid; grid-template-columns: 4.5rem 1fr auto; align-items: baseline;
gap: 1.5rem; padding-block: 1.4rem; border-top: 2px solid var(--line);
transition: background 0.18s, padding-left 0.18s; }
.rx-svc:last-child { border-bottom: 2px solid var(--line); }
.rx-svc:hover { background: var(--ink-2); padding-left: 1rem; }
.rx-svc__n { font-family: var(--f-mono); color: var(--hot); font-size: 0.95rem; }
.rx-svc__name { font-family: var(--f-poster); text-transform: uppercase;
font-size: clamp(1.6rem, 3.6vw, 2.6rem); line-height: 0.95; }
.rx-svc__d { color: var(--bone-2); font-size: 0.98rem; max-width: 52ch; margin-top: 0.4rem; }
.rx-svc__meta { font-family: var(--f-mono); color: var(--hot-2); font-size: 0.85rem;
white-space: nowrap; text-align: right; }
@media (max-width: 640px) {
.rx-svc { grid-template-columns: 2.5rem 1fr; }
.rx-svc__meta { grid-column: 2; text-align: left; }
}
/* ---- Pricing (blowout numerals) ----------------------------------------- */
.rx-prices { display: grid; grid-template-columns: repeat(3, 1fr); margin-top: 2.5rem;
border: 2px solid var(--bone); }
.rx-price { padding: clamp(1.5rem, 3vw, 2.5rem); border-right: 2px solid var(--bone); }
.rx-price:last-child { border-right: 0; }
.rx-price--pop { background: var(--hot); color: var(--ink); }
.rx-price__tier { font-family: var(--f-mono); text-transform: uppercase; letter-spacing: 0.1em;
font-size: 0.85rem; }
.rx-price__big { font-family: var(--f-poster); font-size: clamp(4rem, 11vw, 8.5rem);
line-height: 0.8; margin-top: 0.75rem; }
.rx-price__u { font-family: var(--f-mono); font-size: 0.8rem; color: var(--bone-2); display: block;
margin-top: 0.75rem; }
.rx-price--pop .rx-price__u { color: var(--ink); opacity: 0.7; }
.rx-price__list { list-style: none; padding: 0; margin-top: 1.25rem; display: grid; gap: 0.4rem;
font-size: 0.9rem; color: var(--bone-2); }
.rx-price--pop .rx-price__list { color: var(--ink); }
.rx-price__list li::before { content: "+ "; font-family: var(--f-mono); }
@media (max-width: 760px) {
.rx-prices { grid-template-columns: 1fr; }
.rx-price { border-right: 0; border-bottom: 2px solid var(--bone); }
.rx-price:last-child { border-bottom: 0; }
}
/* ---- Calculator teaser -------------------------------------------------- */
.rx-calc { background: var(--ink-2); }
.rx-calc .wrap { display: grid; grid-template-columns: 1.4fr auto; gap: 2rem; align-items: center; }
.rx-calc__big { font-family: var(--f-poster); text-transform: uppercase;
font-size: clamp(2.2rem, 6vw, 4.5rem); line-height: 0.9; }
.rx-calc p { color: var(--bone-2); margin-top: 1rem; max-width: 46ch; }
@media (max-width: 760px) { .rx-calc .wrap { grid-template-columns: 1fr; } }
/* ---- CTA drench --------------------------------------------------------- */
.rx-cta { background: var(--hot); color: var(--ink); border-bottom: 0; text-align: center; }
.rx-cta__big { font-family: var(--f-poster); text-transform: uppercase;
font-size: clamp(3rem, 13vw, 10rem); line-height: 0.85; }
.rx-cta__row { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; margin-top: 2rem; }
.rx-cta .rx-btn--hot { background: var(--ink); color: var(--hot); }
.rx-cta .rx-btn--hot:hover { background: var(--bone); color: var(--ink); }
.rx-cta .rx-btn--out { border-color: var(--ink); color: var(--ink); }
.rx-cta .rx-btn--out:hover { background: var(--ink); color: var(--hot); }
/* ---- Footer ------------------------------------------------------------- */
.rx-footer { padding-block: 2.5rem; }
.rx-footer .wrap { display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; }
.rx-footer p { font-family: var(--f-mono); font-size: 0.8rem; color: var(--bone-2); }
.rx-footer a { color: var(--bone); }
.rx-footer a:hover { color: var(--hot); }
.rx-note { width: 100%; font-size: 0.72rem; color: var(--bone-2); opacity: 0.7;
border-top: 2px solid var(--line); padding-top: 1rem; margin-top: 0.5rem; }
/* reveal */
.rx-up { opacity: 0; transform: translateY(18px); transition: opacity 0.6s cubic-bezier(0.2,0.7,0.2,1), transform 0.6s cubic-bezier(0.2,0.7,0.2,1); }
.rx-up.in { opacity: 1; transform: none; }
/* ---- narrow-screen polish ---------------------------------------------- */
@media (max-width: 480px) {
.rx-header .wrap { gap: 0.75rem; }
.rx-brand { font-size: 1.15rem; }
.rx-cta-sm { font-size: 0.72rem; padding: 0.4rem 0.6rem; }
.rx-hero__cta { flex-direction: column; align-items: stretch; }
.rx-hero__cta .rx-btn { justify-content: center; }
.marquee span { font-size: 1.1rem; }
.rx-calc .rx-btn { justify-self: start; }
}

View File

@@ -0,0 +1,694 @@
/* ===========================================================================
ARIZONA COMPUTER GURU - "Sonoran Ledger"
Hand-built. Warm paper, precise rules, mono numerals. No framework.
Baseline grid: 24px. Radius: 02px. Orange = ink, used sparingly.
=========================================================================== */
/* ---- Tokens ------------------------------------------------------------- */
:root {
--paper: #F7F3EB;
--surface: #EDE6D9;
--surface-2: #E4DACA;
--ink: #2A2521;
--ink-2: #5A5148;
--ink-3: #6D6456; /* AA-safe for small text on cream (~4.6:1) */
--rule: rgba(90, 81, 72, 0.16);
--rule-soft: rgba(90, 81, 72, 0.09);
--accent: #F2922E; /* the amber "ink" - fills, marks, underlines */
--accent-ink: #BD5A00; /* orange TEXT on light paper (AA-safe ~4.9:1) */
--on-accent: #2A2521; /* text sitting on an amber fill */
--good: #4F7A3F;
--maxw: 1140px;
--base: 24px; /* baseline unit */
--f-display: "Barlow Condensed", "Arial Narrow", system-ui, sans-serif;
--f-body: "Lexend", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
--f-mono: "JetBrains Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;
--shadow: 0 1px 0 var(--rule), 0 18px 40px -28px rgba(40, 33, 25, 0.45);
}
[data-theme="dark"] {
--paper: #1C1814;
--surface: #2A2520;
--surface-2: #322B24;
--ink: #E8DFCE;
--ink-2: #C4B8A3;
--ink-3: #9A8C77;
--rule: rgba(196, 184, 163, 0.16);
--rule-soft: rgba(196, 184, 163, 0.08);
--accent: #F2A24E;
--accent-ink: #F4A85C; /* orange text on dark paper (AA-safe) */
--on-accent: #1C1814;
--good: #8FB97E;
--shadow: 0 1px 0 var(--rule), 0 22px 48px -30px rgba(0, 0, 0, 0.7);
}
/* ---- Skin: Midnight Concierge (cool dark-premium) ----------------------- */
html[data-skin="midnight"] { /* Midnight, light mode (cool slate-grey) */
--paper: #F2F4F7;
--surface: #E6EAEF;
--surface-2: #DBE1E9;
--ink: #1A222E;
--ink-2: #46505E;
--ink-3: #5F6977; /* AA on cool light (~5:1) */
--rule: rgba(40, 55, 75, 0.16);
--rule-soft: rgba(40, 55, 75, 0.07);
--accent: #E4831A; /* amber, deepened a touch for cool light fills */
--accent-ink: #A8530A; /* amber TEXT on cool light (AA ~5:1) */
--on-accent: #14202C;
--good: #2F7D55;
--shadow: 0 1px 0 var(--rule), 0 18px 40px -28px rgba(20, 30, 45, 0.35);
}
html[data-skin="midnight"][data-theme="dark"] { /* Midnight, dark (the primary mood) */
--paper: #10151C;
--surface: #1B2430;
--surface-2: #232E3C;
--ink: #E7ECF2;
--ink-2: #AAB4C2;
--ink-3: #7E8A9A;
--rule: rgba(180, 200, 225, 0.15);
--rule-soft: rgba(180, 200, 225, 0.07);
--accent: #F2A24E;
--accent-ink: #F4AC62; /* amber text on deep slate (AA) */
--on-accent: #10151C;
--good: #6FC08C;
--shadow: 0 1px 0 var(--rule), 0 24px 50px -30px rgba(0, 0, 0, 0.8);
}
/* ---- Skin: Verdigris Gate (Grok-designed) ------------------------------- */
html[data-skin="verdigris"] { /* fonts (both modes) + light palette */
--f-display: "Fraunces", Georgia, "Times New Roman", serif;
--f-body: "Source Sans 3", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
--f-mono: "IBM Plex Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;
--grain: rgba(0, 0, 0, 0.035);
--paper: #E9EDEA;
--surface: #FFFFFF;
--surface-2: #DFE5E1;
--ink: #1C2421;
--ink-2: #3F4F49;
--ink-3: #4A5853; /* AA on surface-2 band too (~5.8:1) */
--rule: rgba(28, 36, 33, 0.14);
--rule-soft: rgba(28, 36, 33, 0.07);
--accent: #2F7A6B; /* verdigris */
--accent-ink: #1F5F52; /* verdigris TEXT on plaster (AA) */
--on-accent: #FFFFFF; /* white on the teal fill clears AA (button label)*/
--good: #2F7A6B;
--shadow: 0 1px 0 var(--rule), 0 18px 40px -28px rgba(28, 40, 36, 0.4);
}
html[data-skin="verdigris"][data-theme="dark"] { /* Verdigris, dark (wet courtyard) */
--grain: rgba(255, 255, 255, 0.05);
--paper: #141A18;
--surface: #1C2421;
--surface-2: #25302C;
--ink: #E4EBE8;
--ink-2: #A8B5AF;
--ink-3: #8FA098; /* AA on surface-2 band in dark (~5:1) */
--rule: rgba(228, 235, 232, 0.12);
--rule-soft: rgba(228, 235, 232, 0.06);
--accent: #4DA896;
--accent-ink: #6BC4B0; /* verdigris text on dark (AA) */
--on-accent: #0D1614;
--good: #6BC4B0;
--shadow: 0 1px 0 var(--rule), 0 24px 50px -30px rgba(0, 0, 0, 0.8);
}
/* Verdigris signature treatments */
html[data-skin="verdigris"] .site-header {
background-color: var(--surface-2);
background-image: repeating-linear-gradient(90deg, transparent, transparent 2px,
var(--grain) 2px, var(--grain) 3px);
backdrop-filter: none;
}
html[data-skin="verdigris"] .calc__shell,
html[data-skin="verdigris"] .rate-card,
html[data-skin="verdigris"] .contact__form {
box-shadow: inset 0 0 0 4px var(--paper), inset 0 0 0 5px var(--rule), var(--shadow);
}
html[data-skin="verdigris"] .contact__form {
border: 1px solid var(--rule); background: var(--surface);
padding: calc(var(--base) * 1.25);
}
html[data-skin="verdigris"] .nav__link:hover {
background-image: linear-gradient(var(--accent), var(--accent));
background-size: 100% 2px; background-position: 0 100%; background-repeat: no-repeat;
padding-bottom: 3px; color: var(--ink);
}
html[data-skin="verdigris"] h1, html[data-skin="verdigris"] h2 { letter-spacing: -0.005em; }
/* ---- Skin: Bold (dialed-back radical) ----------------------------------- */
html[data-skin="bold"] { /* fonts (both modes) + LIGHT palette */
--f-display: "Anton", "Arial Narrow Bold", system-ui, sans-serif;
--f-body: "Hanken Grotesk", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
--f-mono: "JetBrains Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;
--paper: #F4EDE1;
--surface: #FBF7F0;
--surface-2: #EAE2D4;
--ink: #16120F;
--ink-2: #46403A;
--ink-3: #6B6359;
--rule: rgba(22, 18, 15, 0.22);
--rule-soft: rgba(22, 18, 15, 0.10);
--accent: #E24A12; /* signal orange, deepened for bone (button fill) */
--accent-ink: #C23A0A; /* orange TEXT on bone (AA ~5.3:1) */
--on-accent: #FBF7F0;
--good: #1F7A4A;
--shadow: 0 2px 0 var(--ink), 0 24px 44px -30px rgba(22, 18, 15, 0.4);
}
html[data-skin="bold"][data-theme="dark"] { /* Bold, dark (the primary mood) */
--paper: #0C0A09;
--surface: #16120F;
--surface-2: #1E1814;
--ink: #F4EDE1;
--ink-2: #C9C1B5;
--ink-3: #9A9082;
--rule: rgba(244, 237, 225, 0.20);
--rule-soft: rgba(244, 237, 225, 0.08);
--accent: #FF5A1F;
--accent-ink: #FF8A4D; /* orange text on near-black (AA) */
--on-accent: #0C0A09;
--good: #8FD0A0;
--shadow: 0 2px 0 var(--ink), 0 24px 50px -30px rgba(0, 0, 0, 0.85);
}
/* Bold signature treatments: poster type, hard ink borders, mono photos */
html[data-skin="bold"] .ledger { background-image: none; } /* drop ledger rulings */
html[data-skin="bold"] h1, html[data-skin="bold"] h2,
html[data-skin="bold"] .tier__name, html[data-skin="bold"] .svc__name,
html[data-skin="bold"] .faq__q, html[data-skin="bold"] .brand__name {
text-transform: uppercase; letter-spacing: 0.005em; font-weight: 400;
}
html[data-skin="bold"] .hero h1 { font-size: clamp(2.8rem, 7vw, 5.5rem); line-height: 0.9; }
html[data-skin="bold"] .hero h1 .amp { -webkit-text-stroke: 0; }
html[data-skin="bold"] h2 { line-height: 0.95; }
/* hard borders */
html[data-skin="bold"] .site-header { border-bottom: 2px solid var(--ink); backdrop-filter: none;
background: var(--paper); }
html[data-skin="bold"] .trust { border-block: 2px solid var(--ink); }
html[data-skin="bold"] .rate-card, html[data-skin="bold"] .calc__shell { border: 2px solid var(--ink); }
html[data-skin="bold"] .pricing, html[data-skin="bold"] .faq,
html[data-skin="bold"] .home-services, html[data-skin="bold"] .cta-band { border-block: 2px solid var(--ink); }
/* poster numerals via Anton */
html[data-skin="bold"] .tier__price { font-family: var(--f-display); font-weight: 400;
font-size: clamp(2.8rem, 6vw, 4.25rem); letter-spacing: 0.01em; }
html[data-skin="bold"] .trust__num { font-family: var(--f-display); font-weight: 400; font-size: 2.6rem; }
html[data-skin="bold"] .calc__total .tnum { font-family: var(--f-display); font-weight: 400; }
/* sharp corners everywhere */
html[data-skin="bold"] .btn, html[data-skin="bold"] .tier, html[data-skin="bold"] .calc__shell,
html[data-skin="bold"] .rate-card, html[data-skin="bold"] select, html[data-skin="bold"] input,
html[data-skin="bold"] textarea, html[data-skin="bold"] .stepper, html[data-skin="bold"] .theme-toggle,
html[data-skin="bold"] .nav__toggle, html[data-skin="bold"] .skin-toggle,
html[data-skin="bold"] .brand__mark { border-radius: 0; }
/* monochrome documentary photos to match the palette */
html[data-skin="bold"] img { filter: grayscale(1) contrast(1.08); }
html[data-skin="bold"] .nav__link[aria-current="page"] {
background-image: linear-gradient(var(--accent), var(--accent)); }
/* ---- Reset -------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
* { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}
body {
background: var(--paper);
color: var(--ink);
font-family: var(--f-body);
font-size: 1rem;
line-height: 1.5; /* 24px at 16px → on-grid */
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
transition: background 0.4s ease, color 0.4s ease;
}
img { display: block; max-width: 100%; height: auto; }
a { color: var(--accent-ink); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--ink); }
::selection { background: var(--accent); color: var(--on-accent); }
.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
}
/* ---- Layout primitives -------------------------------------------------- */
.wrap { width: min(100% - 3rem, var(--maxw)); margin-inline: auto; }
.wrap--narrow { width: min(100% - 3rem, 760px); margin-inline: auto; }
section { padding-block: calc(var(--base) * 3.5); }
.section-tag {
font-family: var(--f-mono);
font-size: 0.72rem;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--accent-ink);
display: flex; align-items: center; gap: 0.75rem;
margin-bottom: var(--base);
}
.section-tag::before { /* precision mark, not an icon font */
content: ""; width: 28px; height: 0;
border-top: 2px solid var(--accent);
}
.section-tag span { color: var(--ink-3); }
h1, h2, h3, h4 { font-family: var(--f-display); font-weight: 600; line-height: 1.04;
letter-spacing: -0.01em; color: var(--ink); }
h2 { font-size: clamp(2rem, 4.5vw, 3.25rem); margin-bottom: var(--base); }
h3 { font-size: 1.5rem; font-weight: 600; }
.lead { font-size: 1.18rem; color: var(--ink-2); max-width: 60ch; }
.muted { color: var(--ink-2); }
.mono { font-family: var(--f-mono); font-variant-numeric: tabular-nums; }
/* underline "ink" used on key phrases */
.ul { background-image: linear-gradient(var(--accent), var(--accent));
background-size: 100% 2px; background-position: 0 92%; background-repeat: no-repeat;
padding-bottom: 1px; }
/* ---- Buttons ------------------------------------------------------------ */
.btn {
font-family: var(--f-display); font-weight: 600; font-size: 1.05rem;
letter-spacing: 0.01em;
display: inline-flex; align-items: center; gap: 0.6rem;
padding: 0.7rem 1.4rem; border: 1px solid transparent; border-radius: 2px;
cursor: pointer; text-decoration: none; transition: transform 0.15s ease, background 0.2s ease;
}
.btn:active { transform: translateY(1px); }
.btn--primary { background: var(--accent); color: var(--on-accent); }
.btn--primary:hover { color: var(--on-accent); filter: brightness(1.05); }
.btn--ghost { background: transparent; color: var(--ink); border-color: var(--rule); }
.btn--ghost:hover { border-color: var(--accent); color: var(--ink); }
.btn .arrow { font-family: var(--f-mono); }
/* ---- Header ------------------------------------------------------------- */
.site-header {
position: sticky; top: 0; z-index: 50;
background: color-mix(in srgb, var(--paper) 86%, transparent);
backdrop-filter: blur(8px);
border-bottom: 1px solid var(--rule);
}
.site-header .wrap { display: flex; align-items: center; gap: 1.5rem;
min-height: calc(var(--base) * 3); }
.brand { display: flex; align-items: baseline; gap: 0.6rem; text-decoration: none; color: var(--ink); }
.brand__mark { /* CSS-drawn monogram, not an image icon */
font-family: var(--f-display); font-weight: 700; font-size: 1.05rem;
letter-spacing: 0.05em; color: var(--on-accent); background: var(--accent);
padding: 0.15rem 0.5rem; border-radius: 2px; align-self: center;
}
.brand__name { font-family: var(--f-display); font-weight: 600; font-size: 1.35rem;
letter-spacing: 0.005em; }
.brand__since { font-family: var(--f-mono); font-size: 0.66rem; letter-spacing: 0.18em;
text-transform: uppercase; color: var(--ink-3); }
.nav { margin-left: auto; display: flex; align-items: center; gap: 1.2rem; }
.nav__links { display: flex; align-items: center; gap: 1.6rem; }
.nav a { font-family: var(--f-body); font-size: 0.92rem; color: var(--ink-2);
text-decoration: none; }
.nav a:hover { color: var(--ink); }
.nav__phone { font-family: var(--f-mono); color: var(--ink) !important; font-weight: 500; }
.theme-toggle, .nav__toggle {
background: transparent; border: 1px solid var(--rule); border-radius: 2px;
width: 44px; height: 44px; cursor: pointer; color: var(--ink);
display: grid; place-items: center; font-size: 1.1rem; transition: border-color 0.2s;
}
.theme-toggle:hover, .nav__toggle:hover { border-color: var(--accent); }
.nav__toggle { display: none; font-family: var(--f-mono); line-height: 1; }
@media (max-width: 880px) {
.nav__toggle { display: grid; }
.nav__links {
position: absolute; top: 100%; left: 0; right: 0;
flex-direction: column; align-items: stretch; gap: 0;
background: var(--paper); border-bottom: 1px solid var(--rule);
box-shadow: var(--shadow); padding: 0.5rem 0;
max-height: 0; overflow: hidden; visibility: hidden;
transition: max-height 0.25s ease, visibility 0.25s ease;
}
.site-header.nav-open .nav__links { max-height: 80vh; overflow-y: auto; visibility: visible; }
.nav__links a { padding: 0.85rem 1.5rem; font-size: 1.05rem;
border-top: 1px solid var(--rule-soft); }
}
/* ---- Hero --------------------------------------------------------------- */
.hero { padding-block: calc(var(--base) * 3) calc(var(--base) * 4); position: relative; }
.hero .wrap { display: grid; grid-template-columns: 1.05fr 0.95fr; gap: calc(var(--base) * 2);
align-items: center; }
.hero__eyebrow { font-family: var(--f-mono); font-size: 0.78rem; letter-spacing: 0.2em;
text-transform: uppercase; color: var(--accent-ink); margin-bottom: var(--base); }
.hero h1 { font-size: clamp(2.6rem, 6vw, 4.6rem); font-weight: 700; }
.hero h1 .amp { color: var(--accent-ink); }
.hero__sub { font-size: 1.3rem; color: var(--ink-2); margin-top: var(--base);
max-width: 38ch; }
.hero__cta { display: flex; gap: 1rem; margin-top: calc(var(--base) * 1.5); flex-wrap: wrap; }
.hero__note { font-family: var(--f-mono); font-size: 0.78rem; color: var(--ink-3);
margin-top: var(--base); }
.hero__frame {
border: 1px solid var(--rule); border-radius: 2px; overflow: hidden;
box-shadow: var(--shadow); background: var(--surface); position: relative;
}
.hero__frame img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }
.hero__caption {
font-family: var(--f-mono); font-size: 0.72rem; color: var(--ink-2);
padding: 0.6rem 0.9rem; border-top: 1px solid var(--rule);
display: flex; justify-content: space-between; background: var(--surface);
}
@media (max-width: 860px) {
.hero .wrap { grid-template-columns: 1fr; }
.hero__frame { order: -1; }
}
/* ---- Ledger ruling background (signature) ------------------------------- */
.ledger {
background-image: repeating-linear-gradient(
to bottom, transparent, transparent 23px, var(--rule-soft) 23px, var(--rule-soft) 24px);
background-position: 0 0;
}
/* ---- Trust strip -------------------------------------------------------- */
.trust { border-block: 1px solid var(--rule); background: var(--surface); }
.trust .wrap { display: grid; grid-template-columns: repeat(4, 1fr);
gap: 1px; background: var(--rule); }
.trust__cell { background: var(--surface); padding: calc(var(--base) * 1.25) 1.25rem; }
.trust__num { font-family: var(--f-mono); font-size: 1.9rem; font-weight: 600;
color: var(--ink); line-height: 1; }
.trust__num .u { color: var(--accent-ink); }
.trust__label { font-size: 0.86rem; color: var(--ink-2); margin-top: 0.5rem; }
@media (max-width: 720px) { .trust .wrap { grid-template-columns: repeat(2, 1fr); } }
/* ---- Concierge story ---------------------------------------------------- */
.story .wrap { display: grid; grid-template-columns: 1fr 1fr; gap: calc(var(--base) * 2.2);
align-items: center; }
.story__img { border: 1px solid var(--rule); border-radius: 2px; overflow: hidden;
box-shadow: var(--shadow); }
.story__img img { aspect-ratio: 3 / 2; object-fit: cover; width: 100%; }
.story p + p { margin-top: var(--base); }
.story blockquote {
font-family: var(--f-display); font-weight: 500; font-size: 1.7rem; line-height: 1.18;
color: var(--ink); margin-top: calc(var(--base) * 1.5);
padding-block: var(--base); border-block: 1px solid var(--rule);
}
.story blockquote::before {
content: "\201C"; font-family: var(--f-display); color: var(--accent-ink);
font-size: 2.4rem; line-height: 0; vertical-align: -0.4em; margin-right: 0.2rem;
}
@media (max-width: 820px) {
.story .wrap { grid-template-columns: 1fr; }
.story__img { order: -1; }
}
/* ---- Services (continuous ledger list, not cards) ----------------------- */
.svc-list { border-top: 1px solid var(--rule); margin-top: var(--base); }
.svc {
display: grid; grid-template-columns: 2.4rem 1fr auto; gap: 1.25rem;
align-items: baseline; padding-block: calc(var(--base) * 0.9);
border-bottom: 1px solid var(--rule); transition: background 0.2s ease;
}
.svc:hover { background: var(--surface); }
.svc__no { font-family: var(--f-mono); font-size: 0.8rem; color: var(--ink-3); }
.svc__name { font-family: var(--f-display); font-size: 1.5rem; font-weight: 600; }
.svc__desc { color: var(--ink-2); font-size: 0.98rem; margin-top: 0.25rem; max-width: 60ch; }
.svc__meta { font-family: var(--f-mono); font-size: 0.82rem; color: var(--accent-ink);
text-align: right; white-space: nowrap; }
@media (max-width: 680px) {
.svc { grid-template-columns: 1.8rem 1fr; }
.svc__meta { grid-column: 2; text-align: left; }
}
/* ---- Pricing rate card -------------------------------------------------- */
.pricing { background: var(--surface); border-block: 1px solid var(--rule); }
.rate-card { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
background: var(--rule); border: 1px solid var(--rule); border-radius: 2px; overflow: hidden; }
.tier { background: var(--paper); padding: calc(var(--base) * 1.5) 1.5rem; position: relative;
align-self: stretch; }
.tier--pop { background: var(--surface-2); box-shadow: inset 0 3px 0 var(--accent); }
.tier__flag { position: absolute; top: 0; right: 0; font-family: var(--f-mono);
font-size: 0.64rem; letter-spacing: 0.16em; text-transform: uppercase;
background: var(--accent); color: var(--on-accent); padding: 0.25rem 0.6rem; }
.tier__name { font-family: var(--f-display); font-size: 1.7rem; font-weight: 600; }
.tier__price { font-family: var(--f-mono); font-size: 2.6rem; font-weight: 700;
color: var(--ink); margin-top: 0.5rem; line-height: 1; }
.tier__price .per { font-size: 0.9rem; color: var(--ink-3); font-weight: 400; }
.tier__blurb { color: var(--ink-2); font-size: 0.92rem; margin-top: 0.75rem;
padding-bottom: var(--base); border-bottom: 1px solid var(--rule); }
.tier ul { list-style: none; padding: 0; margin-top: var(--base); display: grid; gap: 0.5rem; }
.tier li { font-size: 0.92rem; color: var(--ink-2); padding-left: 1.4rem; position: relative; }
.tier li::before { content: "+"; position: absolute; left: 0; color: var(--accent-ink);
font-family: var(--f-mono); font-weight: 700; }
.tier li.is-base::before { content: "\2713"; }
@media (max-width: 820px) { .rate-card { grid-template-columns: 1fr; } }
.plans { margin-top: calc(var(--base) * 2); }
.plan-table { width: 100%; border-collapse: collapse; font-size: 0.95rem; }
.plan-table caption { text-align: left; font-family: var(--f-mono); font-size: 0.74rem;
letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-3);
padding-bottom: 0.75rem; }
.plan-table th, .plan-table td { text-align: left; padding: 0.7rem 0.9rem;
border-bottom: 1px solid var(--rule); }
.plan-table thead th { font-family: var(--f-mono); font-size: 0.72rem; letter-spacing: 0.1em;
text-transform: uppercase; color: var(--ink-3); font-weight: 500; }
.plan-table td.num, .plan-table th.num { font-family: var(--f-mono); text-align: right;
font-variant-numeric: tabular-nums; }
.plan-table tr:hover td { background: var(--surface); }
.plan-table .pop td { color: var(--ink); }
.plan-table .pop td:first-child::after { content: " \25C6"; color: var(--accent-ink); }
.table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.plan-table { min-width: 460px; }
/* visible keyboard focus everywhere interactive */
.btn:focus-visible, a:focus-visible, button:focus-visible, .faq__q:focus-visible,
.theme-toggle:focus-visible, .nav__toggle:focus-visible {
outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 2px;
}
/* ---- Calculator (centerpiece) ------------------------------------------ */
.calc { position: relative; }
.calc__shell {
border: 1px solid var(--rule); border-radius: 2px; box-shadow: var(--shadow);
background: var(--paper); overflow: hidden;
}
.calc__grid { display: grid; grid-template-columns: 1.15fr 0.85fr; }
.calc__inputs { padding: calc(var(--base) * 1.5); border-right: 1px solid var(--rule); }
.calc__row { display: grid; grid-template-columns: 1fr auto; align-items: center;
gap: 1rem; padding-block: calc(var(--base) * 0.6); border-bottom: 1px dashed var(--rule); }
.calc__row label { font-size: 0.98rem; color: var(--ink); }
.calc__row .hint { display: block; font-size: 0.78rem; color: var(--ink-3); margin-top: 0.15rem; }
.calc__control { display: flex; align-items: center; gap: 0.5rem; }
.stepper { display: inline-flex; align-items: stretch; border: 1px solid var(--rule);
border-radius: 2px; overflow: hidden; }
.stepper button { width: 44px; min-height: 44px; border: 0; background: var(--surface);
color: var(--ink); font-family: var(--f-mono); font-size: 1.2rem; cursor: pointer; }
.stepper button:hover { background: var(--accent); color: var(--on-accent); }
.stepper input { width: 64px; height: 44px; border: 0; text-align: center; background: var(--paper);
color: var(--ink); font-family: var(--f-mono); font-size: 1.05rem; font-weight: 600;
appearance: textfield; -moz-appearance: textfield; }
.stepper input::-webkit-outer-spin-button, .stepper input::-webkit-inner-spin-button {
-webkit-appearance: none; margin: 0; }
select, .calc__control select {
font-family: var(--f-body); font-size: 0.95rem; color: var(--ink);
background: var(--paper); border: 1px solid var(--rule); border-radius: 2px;
padding: 0.5rem 2rem 0.5rem 0.7rem; cursor: pointer;
appearance: none;
background-image: linear-gradient(45deg, transparent 50%, var(--ink-3) 50%),
linear-gradient(135deg, var(--ink-3) 50%, transparent 50%);
background-position: calc(100% - 16px) 55%, calc(100% - 11px) 55%;
background-size: 5px 5px, 5px 5px; background-repeat: no-repeat;
}
select:focus, .stepper input:focus, input:focus { outline: 2px solid var(--accent);
outline-offset: 1px; }
.toggle-row { display: flex; align-items: center; gap: 0.6rem; }
.switch { position: relative; width: 44px; height: 24px; flex: none; }
.switch input { position: absolute; opacity: 0; width: 100%; height: 100%; margin: 0; cursor: pointer; }
.switch .track { position: absolute; inset: 0; background: var(--surface-2);
border: 1px solid var(--rule); border-radius: 2px; transition: background 0.2s; }
.switch .knob { position: absolute; top: 2px; left: 2px; width: 18px; height: 18px;
background: var(--ink-3); border-radius: 1px; transition: transform 0.2s, background 0.2s; }
.switch input:checked + .track { background: color-mix(in srgb, var(--accent) 35%, var(--surface)); }
.switch input:checked + .track + .knob, .switch input:checked ~ .knob { transform: translateX(20px);
background: var(--accent); }
/* ledger output side */
.calc__out { padding: calc(var(--base) * 1.5); background: var(--surface);
display: flex; flex-direction: column; }
.calc__out h3 { font-size: 1.15rem; }
.ledger-lines { margin-top: var(--base); flex: 1; }
.lline { display: grid; grid-template-columns: 1fr auto; align-items: baseline;
font-family: var(--f-mono); font-size: 0.9rem; padding-block: 0.45rem;
color: var(--ink-2); }
.lline .lcost { color: var(--ink); font-variant-numeric: tabular-nums; }
.lline.dots .lname { position: relative; }
.lline.is-zero { display: none; }
.calc__divider { border: 0; border-top: 1px solid var(--ink-3); margin-block: 0.75rem; }
.calc__total { display: grid; grid-template-columns: 1fr auto; align-items: end; }
.calc__total .tlabel { font-family: var(--f-display); font-size: 1.1rem; letter-spacing: 0.02em;
text-transform: uppercase; color: var(--ink-2); }
.calc__total .tnum { font-family: var(--f-mono); font-size: 2.9rem; font-weight: 700;
color: var(--ink); line-height: 1; }
.calc__annual { font-family: var(--f-mono); font-size: 0.82rem; color: var(--ink-2);
text-align: right; margin-top: 0.4rem; }
.calc__perep { font-family: var(--f-mono); font-size: 0.82rem; color: var(--accent-ink);
margin-top: 0.2rem; text-align: right; }
.calc__cta { margin-top: var(--base); }
.calc__foot { font-size: 0.78rem; color: var(--ink-2); margin-top: 1rem; }
@media (max-width: 820px) {
.calc__grid { grid-template-columns: 1fr; }
.calc__inputs { border-right: 0; border-bottom: 1px solid var(--rule); }
}
/* ---- FAQ ---------------------------------------------------------------- */
.faq { background: var(--surface); border-block: 1px solid var(--rule); }
.faq__item { border-bottom: 1px solid var(--rule); }
.faq__q { width: 100%; text-align: left; background: transparent; border: 0; cursor: pointer;
padding: calc(var(--base) * 0.9) 0; display: flex; justify-content: space-between;
gap: 1.5rem; align-items: center; color: var(--ink); font-family: var(--f-display);
font-size: 1.25rem; font-weight: 600; }
.faq__q .pm { font-family: var(--f-mono); color: var(--accent-ink); font-size: 1.4rem;
flex: none; transition: transform 0.2s; }
.faq__q[aria-expanded="true"] .pm { transform: rotate(45deg); }
.faq__a { overflow: hidden; max-height: 0; visibility: hidden;
transition: max-height 0.3s ease, visibility 0.3s ease; }
.faq__q[aria-expanded="true"] + .faq__a { visibility: visible; }
.faq__a-inner { padding-bottom: calc(var(--base) * 1.1); color: var(--ink-2); max-width: 70ch; }
/* ---- Contact ------------------------------------------------------------ */
.contact { padding-block: calc(var(--base) * 4.5); }
.contact .wrap { display: grid; grid-template-columns: 1fr 1fr; gap: calc(var(--base) * 2.5);
align-items: start; }
.contact__lines { font-family: var(--f-mono); margin-top: var(--base); display: grid; gap: 0.6rem; }
.contact__lines .k { color: var(--ink-3); font-size: 0.72rem; letter-spacing: 0.14em;
text-transform: uppercase; }
.contact__lines .v { color: var(--ink); font-size: 1.05rem; }
.contact__lines a { color: var(--ink); }
.contact__lines a:hover { color: var(--accent-ink); }
.form-field { margin-bottom: var(--base); }
.form-field label { display: block; font-size: 0.85rem; color: var(--ink-2);
margin-bottom: 0.4rem; font-family: var(--f-mono); letter-spacing: 0.06em;
text-transform: uppercase; }
.form-field input, .form-field textarea {
width: 100%; font-family: var(--f-body); font-size: 1rem; color: var(--ink);
background: var(--paper); border: 1px solid var(--rule); border-radius: 2px;
padding: 0.65rem 0.8rem; }
.form-field textarea { min-height: calc(var(--base) * 4); resize: vertical; }
.form-note { font-size: 0.82rem; color: var(--ink-3); margin-top: 0.5rem; }
@media (max-width: 820px) { .contact .wrap { grid-template-columns: 1fr; } }
/* ---- Footer ------------------------------------------------------------- */
.site-footer { border-top: 1px solid var(--rule); padding-block: calc(var(--base) * 1.5);
background: var(--paper); }
.site-footer .wrap { display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between;
align-items: center; }
.site-footer p { font-size: 0.84rem; color: var(--ink-3); font-family: var(--f-mono); }
.site-footer .disclaimer { width: 100%; font-size: 0.74rem; color: var(--ink-3);
border-top: 1px dashed var(--rule); padding-top: 0.9rem; margin-top: 0.5rem; }
/* reveal-on-scroll (gated on .js so content is visible if JS fails to run) */
.js .reveal { opacity: 0; transform: translateY(12px); transition: opacity 0.6s ease, transform 0.6s ease; }
.js .reveal.in { opacity: 1; transform: none; }
/* ===========================================================================
MULTIPAGE additions
=========================================================================== */
/* active nav */
.nav__link[aria-current="page"] {
color: var(--ink);
background-image: linear-gradient(var(--accent-ink), var(--accent-ink));
background-size: 100% 2px; background-position: 0 100%; background-repeat: no-repeat;
padding-bottom: 3px;
}
@media (max-width: 880px) {
.nav__links a[aria-current="page"] { background: var(--surface);
background-image: none; padding-bottom: 0.85rem; }
}
/* page header band (inner pages) */
.page-head { padding-block: calc(var(--base) * 2.75) calc(var(--base) * 1.75);
border-bottom: 1px solid var(--rule); }
.page-head.ledger { background-position: 0 0; }
.page-head .eyebrow { font-family: var(--f-mono); font-size: 0.76rem; letter-spacing: 0.2em;
text-transform: uppercase; color: var(--accent-ink); margin-bottom: 0.75rem; }
.page-head h1 { font-size: clamp(2.3rem, 5vw, 3.7rem); font-weight: 700; max-width: 18ch; }
.page-head .lead { margin-top: var(--base); }
.page-head--img .wrap { display: grid; grid-template-columns: 1.1fr 0.9fr;
gap: calc(var(--base) * 2); align-items: center; }
.page-head--img figure { border: 1px solid var(--rule); border-radius: 2px; overflow: hidden;
box-shadow: var(--shadow); }
.page-head--img img { aspect-ratio: 3 / 2; object-fit: cover; width: 100%; }
@media (max-width: 820px) {
.page-head--img .wrap { grid-template-columns: 1fr; }
.page-head--img figure { order: -1; }
}
/* breadcrumb-ish kicker reused; group titles in services */
.svc-group { margin-top: calc(var(--base) * 2); }
.svc-group:first-of-type { margin-top: var(--base); }
.svc-group__title { font-family: var(--f-mono); font-size: 0.78rem; letter-spacing: 0.18em;
text-transform: uppercase; color: var(--ink-3); display: flex; align-items: center; gap: 0.75rem; }
.svc-group__title::after { content: ""; flex: 1; border-top: 1px solid var(--rule); }
/* principle list (About) reuses .svc-list; quote callout */
.bigquote { font-family: var(--f-display); font-weight: 500;
font-size: clamp(1.6rem, 3.5vw, 2.4rem); line-height: 1.2; color: var(--ink);
max-width: 24ch; }
.bigquote::before { content: "\201C"; color: var(--accent-ink); margin-right: 0.15rem; }
.signature { font-family: var(--f-mono); font-size: 0.85rem; color: var(--ink-2);
margin-top: var(--base); }
/* simple price tables grid for hosting / voip / email on pricing page */
.price-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
gap: calc(var(--base) * 1.5); margin-top: var(--base); }
.price-block h3 { font-size: 1.3rem; }
.price-block .table-wrap { margin-top: 0.75rem; }
/* CTA band */
.cta-band { background: var(--surface); border-block: 1px solid var(--rule); }
.cta-band .wrap { display: grid; grid-template-columns: 1.3fr auto; gap: var(--base);
align-items: center; }
.cta-band h2 { margin-bottom: 0.4rem; }
.cta-band p { color: var(--ink-2); max-width: 52ch; }
.cta-band__actions { display: flex; gap: 1rem; flex-wrap: wrap; justify-content: flex-end; }
@media (max-width: 760px) {
.cta-band .wrap { grid-template-columns: 1fr; }
.cta-band__actions { justify-content: flex-start; }
}
/* teaser link */
.more-link { font-family: var(--f-display); font-weight: 600; font-size: 1.05rem;
color: var(--accent-ink); text-decoration: none; display: inline-flex; gap: 0.4rem;
align-items: center; margin-top: var(--base); }
.more-link:hover { color: var(--ink); }
.more-link .arrow { font-family: var(--f-mono); transition: transform 0.15s ease; }
.more-link:hover .arrow { transform: translateX(3px); }
/* home section spacing variety so pages don't feel templated */
.home-services { background: var(--surface); border-block: 1px solid var(--rule); }
.home-pricing .rate-card { margin-top: var(--base); }
/* contact: office figure */
.office-figure { border: 1px solid var(--rule); border-radius: 2px; overflow: hidden;
box-shadow: var(--shadow); margin-top: var(--base); }
.office-figure img { width: 100%; aspect-ratio: 3 / 2; object-fit: cover; }
.office-figure figcaption { font-family: var(--f-mono); font-size: 0.74rem; color: var(--ink-2);
padding: 0.6rem 0.9rem; border-top: 1px solid var(--rule); background: var(--surface);
display: flex; justify-content: space-between; }
/* utilities + small-screen calculator stacking */
.inline-actions { display: flex; gap: 1.5rem; flex-wrap: wrap; margin-top: var(--base); }
.contact__intro h1 { font-family: var(--f-display); font-weight: 700;
font-size: clamp(2.2rem, 4.5vw, 3.2rem); line-height: 1.04; }
@media (max-width: 440px) {
.calc__row { grid-template-columns: 1fr; gap: 0.5rem; }
.calc__row .calc__control { justify-self: start; }
}
/* skin switcher (Paper / Midnight) */
.skin-toggle { background: transparent; border: 1px solid var(--rule); border-radius: 2px;
width: 44px; height: 44px; cursor: pointer; display: grid; place-items: center;
transition: border-color 0.2s; padding: 0; }
.skin-toggle:hover { border-color: var(--accent); }
.skin-toggle .sw { width: 18px; height: 18px; border: 1px solid var(--rule); border-radius: 1px;
background: linear-gradient(135deg, var(--surface-2) 0 50%, var(--accent) 50% 100%); }
.skin-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (max-width: 560px) { .nav__phone { display: none; } }

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

View File

@@ -0,0 +1 @@
Error: bundled detector not found.

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -u
ROOT=/d/ClaudeTools
GROK="$ROOT/.claude/skills/grok/scripts/ask-grok.sh"
OUT="$ROOT/projects/acg-website-showcase/multipage/assets/images"
cd "$ROOT"
echo "=== [1/3] services / hardware ==="
bash "$GROK" image "Photorealistic editorial close-up of a tidy small-office network setup in warm afternoon light: a clean wall-mounted patch panel and a small network switch with neatly dressed cables on a light wooden shelf against a smooth white stucco wall. Orderly, honest, premium, no logos, no text. Warm cream, tan and soft amber palette, gentle film grain, shallow depth of field. No people. Wide 3:2 landscape." "$OUT/services.png"
echo "=== [2/3] about / relationship ==="
bash "$GROK" image "Photorealistic editorial photograph, warm afternoon window light: a friendly handshake across a light-wood desk in a small Tucson office, mid-shot focusing on the two hands meeting above the desk, with a modern laptop and an open paper notebook nearby. Genuine, human, welcoming. Warm cream and tan palette, soft fine film grain, shallow depth of field. No logos, no visible faces, no text. Wide 3:2 landscape." "$OUT/about.png"
echo "=== [3/3] contact / Tucson exterior ==="
bash "$GROK" image "Photorealistic golden-hour photograph: the exterior of a modest single-story southwestern commercial building with warm hand-troweled stucco walls and a clean simple storefront, a saguaro cactus and the soft silhouette of the Santa Catalina mountains in the distance, Tucson Arizona. Calm, local, welcoming. No signage text, no people, no logos. Warm cream and amber palette, soft film grain. Wide 3:2 landscape." "$OUT/contact.png"
echo "=== DONE ==="
ls -la "$OUT"

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -u
ROOT=/d/ClaudeTools
GROK="$ROOT/.claude/skills/grok/scripts/ask-grok.sh"
OUT="$ROOT/projects/acg-website-showcase/multipage/assets/images/verdigris"
mkdir -p "$OUT"
cd "$ROOT"
echo "=== [1/4] hero: copper gate latch ==="
bash "$GROK" image "Photorealistic documentary close-up: a verdigris-patinated copper gate latch on a whitewashed stucco wall, soft cool morning side light, the latch in sharp focus and the plaster softly blurred behind it. Neutral-to-cool white balance, desaturated palette, the verdigris blue-green patina is the only saturated hue in frame, shallow depth of field, fine grain. No people, no text, no lens flare. Wide 3:2 landscape." "$OUT/hero.png"
echo "=== [2/4] services: patch panel labeling ==="
bash "$GROK" image "Photorealistic documentary photo: a pair of hands labeling a network patch panel with small neat handwritten tags, careful and human, cool neutral light, shallow depth of field, desaturated palette with a faint hint of verdigris green. Industrial, orderly, transparent process. No faces, no logos, no text on screen. Wide 3:2 landscape." "$OUT/services.png"
echo "=== [3/4] about: weathered enamel storefront sign ==="
bash "$GROK" image "Photorealistic documentary photo: a weathered hand-painted enamel storefront service sign mounted on a whitewashed Tucson stucco wall, cared-for but not glossy, the lettering soft and partly out of focus, partial sidewalk and plaster visible. Cool neutral white balance, side light, desaturated palette with verdigris-green and aged-metal accents. No people. Wide 3:2 landscape." "$OUT/about.png"
echo "=== [4/4] contact: cool Tucson storefront ==="
bash "$GROK" image "Photorealistic documentary photo: the exterior of a tidy 1950s single-story Tucson stucco storefront in cool overcast morning light, whitewashed plaster walls, a weathered copper-patina door pull and trim, clean simple windows. Calm, local, cared-for. Cool neutral white balance, desaturated palette, verdigris-green the only saturated hue. No signage text, no people. Wide 3:2 landscape." "$OUT/contact.png"
echo "=== DONE ==="; ls -la "$OUT"

View File

@@ -0,0 +1,20 @@
You are the lead designer. Originate ONE complete, opinionated, buildable art direction for a website. I will implement it exactly, then put it through an independent design critique, so be concrete and commit.
THE SITE: A multipage marketing website for "Arizona Computer Guru" (ACG), a Tucson managed-IT and cybersecurity company since 2001. Differentiator: "concierge IT" (genuine relationships, going far beyond typical IT companies, transparent honest pricing, kind and direct, local). Audience: Tucson small-business owners, often burned before by surprise bills, offshore call centers, three-year lock-ins. The site sells trust first. Pages: Home, Services, Pricing, Calculator (interactive IT-cost estimator), About, Contact.
HARD CONSTRAINTS:
- Hand-built static HTML/CSS/vanilla-JS, driven by CSS custom-property design tokens. So give me CONCRETE values.
- Must be genuinely premium and distinctive, and must NOT look like generic AI-slop SaaS (no blue/purple gradient blobs, no floating 3D isometrics, no glowing shields, no hero-metric template, no identical icon-card grids).
- ALL text must be readable (WCAG AA, ~4.5:1 body) in BOTH a light theme AND a dark theme.
- It must be DISTINCT from the two skins this site already has: (1) "Sonoran Ledger" = warm cream paper, ledger rulings, mono numerals, burnt-amber accent; (2) "Midnight Concierge" = cool slate dark-premium with amber accent. Do not give me a third variation on warm-paper or cool-slate. Go somewhere new.
DELIVER, in this structure:
1. NAME.
2. CONCEPT: one tight paragraph. Name a real-world reference object/aesthetic so it is not generic.
3. COLOR TOKENS, explicit hex, for BOTH modes. Give exactly these roles for LIGHT and for DARK: paper (bg), surface, surface-2, ink (primary text), ink-2 (secondary), ink-3 (muted), rule (hairline rgba), accent (the one signal color used <=10%), accent-ink (the accent as TEXT, AA-safe on that mode's bg), on-accent (text on an accent fill). Keep chroma sane at extremes. State the accent hue you chose and why (you may keep ACG's amber or pick a new brand-justified color).
4. TYPOGRAPHY: display + body + mono families (real Google Fonts). You MAY change from the current Barlow Condensed / Lexend / JetBrains Mono if your concept needs it; justify. Note weights and the hierarchy approach.
5. SIGNATURE MOVES: 2-4 concrete, distinctive treatments that define the look (what replaces ledger rulings, corner radius, dividers, the way pricing/the calculator are framed, motion). Be specific enough to build.
6. IMAGERY: describe the hero image + 2 section images you want (photographic, so I can generate them with an image model), and the overall art-direction of photography (light, palette, mood). Say if existing warm golden-hour Tucson photos (desk-with-ledger, handshake, stucco storefront) should be reused or replaced.
7. ANTI-SLOP: 3 specific reasons this will not read as AI-generated, and name the single biggest risk that it DOES, plus how to avoid it.
Keep it tight and decisive, under ~750 words. Commit to specifics; do not hedge with ranges.

View File

@@ -0,0 +1,96 @@
# 1. NAME
**Verdigris Gate**
---
# 2. CONCEPT
Reference object: the **oxidized copper hardware on a maintained 1950s Tucson stucco storefront** — whitewashed plaster, brushed aluminum door pulls weathered to verdigris, hand-painted enamel service signage. Premium through material honesty, not dashboard gloss. Concierge IT is the gate you walk through: labeled, direct, locally kept. The site should feel like that buildings fascia and service counter, not a SaaS landing template.
---
# 3. COLOR TOKENS
**Accent hue: Verdigris (#2F7A6B light / #4DA896 dark).** Copper oxidation is Tucson-native (pipes, roof flashings, desert hardware) and signals stewardship and longevity — not alarm-red security or fintech amber. Used ≤10%: primary CTA, active nav rule, calculator totals, one pull-quote bar.
### Light (plaster day)
| Role | Value |
|---|---|
| paper | `#E9EDEA` |
| surface | `#FFFFFF` |
| surface-2 | `#DFE5E1` |
| ink | `#1C2421` |
| ink-2 | `#3F4F49` |
| ink-3 | `#5C6B65` |
| rule | `rgba(28, 36, 33, 0.12)` |
| accent | `#2F7A6B` |
| accent-ink | `#1F5F52` |
| on-accent | `#F0F7F5` |
### Dark (wet courtyard night)
| Role | Value |
|---|---|
| paper | `#141A18` |
| surface | `#1C2421` |
| surface-2 | `#25302C` |
| ink | `#E4EBE8` |
| ink-2 | `#A8B5AF` |
| ink-3 | `#7A8A84` |
| rule | `rgba(228, 235, 232, 0.10)` |
| accent | `#4DA896` |
| accent-ink | `#6BC4B0` |
| on-accent | `#0D1614` |
All body pairs (`ink` on `paper`, `ink-2` on `surface`) exceed 4.5:1. `accent-ink` is for text links and inline emphasis on backgrounds, never large accent fills.
---
# 4. TYPOGRAPHY
**Display: Fraunces** (Google Fonts) — weights 500 (H1), 400 (H2H3). Soft, old-metal signage energy; breaks SaaS sans uniformity without ledger nostalgia.
**Body: Source Sans 3** — 400 body, 600 labels/nav. Humanist, plainspoken; reads kind and direct at 18px/1.6.
**Mono: IBM Plex Mono** — 400 inputs, 500 calculator totals and pricing figures. Clinical where money is discussed.
**Hierarchy:** One Fraunces H1 per page (max 48px). Sections lead with Source Sans 600 uppercase tracked labels (11px, `letter-spacing: 0.08em`, `ink-3`) — enamel caption style. No hero metrics; trust copy is body-scale, not billboard numbers.
---
# 5. SIGNATURE MOVES
1. **Enamel double-rule frames** — Pricing tiers, calculator shell, and contact form sit inside a 1px `rule` border with a second 1px border 4px inset (no outer glow). `border-radius: 2px` everywhere. Max corner radius site-wide: 4px on buttons only.
2. **Gate-rail header** — Nav lives on a full-width `surface-2` band with a CSS brushed-metal grain (`repeating-linear-gradient(90deg, transparent, transparent 2px, rgba(0,0,0,0.03) 2px, rgba(0,0,0,0.03) 3px)` in light; `0.06` opacity in dark). Not floating, not sticky glass.
3. **Verdigris underscore navigation** — Links are `ink`; hover/active = 2px `accent` bottom border. One filled `accent` button per viewport (primary CTA). Secondary actions are outlined `rule` borders, never ghost gradients.
4. **Calculator as meter tape** — The estimator is a vertical stack on `surface-2`, top edge perforated via `radial-gradient(circle, paper 30%, transparent 31%)` spaced 8px. Live total in IBM Plex Mono 28px `accent-ink` on `paper` strip — receipt, not widget card. Sliders are 2px track + square 12px thumb (no pill toggles).
**Motion:** 180ms ease on border-color and underscore only. No parallax, no blob drift. Theme toggle cross-fades `color`/`background-color` 200ms.
---
# 6. IMAGERY
**Photography direction:** Documentary maintenance photography — neutral-to-cool white balance, side light, shallow depth of field, desaturated greens and plaster whites, verdigris as the only saturated hue in frame. No lens flare, no neon, no server-room blue wash.
- **Hero:** Close-up of a **verdigris-patinated copper gate latch** on white stucco, morning side light, latch sharp / stucco soft blur. Conveys “local gatekeeper,” not “cyber shield.”
- **Services section:** **Hands labeling a network patch panel** with handwritten tags — neat, human, transparent process.
- **About section:** **Weathered hand-painted enamel “Computer Guru” sign** on a Tucson storefront, cared-for not glossy, partial stucco and sidewalk visible.
**Reuse verdict:** **Replace** golden-hour desk-with-ledger (too Sonoran Ledger), **replace** generic handshake (stock trust cliché). **Adapt** stucco storefront only if re-shot cooler/plaster-toned with enamel-sign framing; do not reuse warm golden-hour grade.
---
# 7. ANTI-SLOP
**Why this wont read AI-generated:**
1. Verdigris-on-plaster palette — neither blue-purple SaaS nor amber concierge variants already in the repo.
2. Fraunces serif + enamel double-rules — explicitly rejects icon-card grids and rounded-16px component libraries.
3. Calculator-as-meter-tape and gate-rail nav are site-specific behaviors, not interchangeable hero-metric templates.
**Biggest risk:** Fraunces + muted green reads **boutique wellness spa**, not MSP.
**Avoid it:** Keep photography industrial (patch panels, hardware, signage). Use IBM Plex Mono and plain-dollar copy on Pricing. No leaf motifs, no “wellness” whitespace — use `surface-2` bands and double-rules to maintain civic/commercial weight. Fraunces only on headlines; body stays Source Sans at 18px minimum.

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 796 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 842 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB

View File

@@ -0,0 +1,45 @@
**index.html**
- [WARN] Cross-page consistency: The inline theme `<script>` in the `<head>` is unminified, whereas it is a minified one-liner in the other 5 pages (line 12).
- [WARN] Cross-page consistency: Footer `<p class="disclaimer">` text heavily drifts/diverges from the strings used on other pages (line 109).
- [WARN] Inline styling: Uses a hardcoded `<div style="display:flex;gap:1.5rem;flex-wrap:wrap;margin-top:var(--base);">` instead of a CSS utility class (line 94).
**services.html**
- [ERROR] Accessibility: Missing semantic headings. Item titles use `<div class="svc__name">` inside `<article>` tags instead of heading elements (line 46). Group titles use `<p class="svc-group__title">` instead of headings (line 44).
- [WARN] Cross-page consistency: Footer disclaimer text drifts from other pages (line 78).
**pricing.html**
- [ERROR] Accessibility: Missing semantic headings. The rate cards use `<div class="tier__name">` instead of `<h3>` elements (line 37).
- [WARN] Cross-page consistency: Footer disclaimer text drifts from other pages (line 107).
**calculator.html**
- [OK] Accessibility: Form controls are properly labeled, input modes are specified, and `aria-live` is correctly placed on the total output.
- [WARN] Cross-page consistency: Footer disclaimer text drifts from other pages (line 90).
**about.html**
- [ERROR] Accessibility: Missing semantic headings. Similar to the services page, item names inside the `.svc-list` are structured as `<div class="svc__name">` instead of headings (line 51).
- [WARN] Cross-page consistency: Footer disclaimer text drifts from other pages (line 81).
**contact.html**
- [WARN] Inline styling: The main page heading uses heavy inline styles `<h1 id="contact-h" style="...">` rather than pulling from the stylesheet (line 39).
- [WARN] Cross-page consistency: Footer disclaimer text drifts from other pages (line 80).
**styles.css**
- [ERROR] House Rules: An em dash (`—`) is used in the header comment string block, violating the strict no em dash rule (line 2).
- [ERROR] WCAG AA Contrast: The `active nav underline` uses `var(--accent)` (orange) on `--paper` (cream), which fails the 3:1 non-text contrast requirement in light mode (line 348).
- [ERROR] WCAG AA Contrast: The `.bigquote::before` quotation mark uses `var(--accent)` on `--paper`, failing the 3:1 ratio for large text in light mode (line 364).
- [ERROR] WCAG AA Contrast: The `.office-figure figcaption` uses `var(--ink-3)` on `var(--surface)`. While `--ink-3` passes on `--paper`, the darker `--surface` background causes this to fail the 4.5:1 ratio for small text in light mode (line 386).
- [WARN] Responsive risk: The mobile nav `.nav__links` relies on `max-height: 60vh; overflow: hidden;`. On short landscape viewports, overflowed links will clip and become entirely inaccessible because it lacks `overflow-y: auto` (line 103).
- [OK] House Rules: Verified no side-stripe accent borders (`border-left` / `border-right`) are used on components.
**app.js**
- [OK] Calculator handoff logic: Cleanly captures `.lname` and `.lcost` strings, utilizes `.textContent` to safely strip nested spans/HTML entities (like `&times;`), and successfully targets the contact textarea via `sessionStorage`.
- [INFO] Handoff edge case: The `acg-estimate` payload is *only* saved when clicking the specific `#sendEstimate` button (line 125). If a user builds an estimate and manually clicks the "Contact" header link, their built estimate is lost.
### Prioritized Fix List
1. **WCAG Contrast Check:** In `styles.css`, darken `--accent` (or swap to `--accent-ink`) for active nav underlines and `.bigquote` marks in light mode. Change the `.office-figure figcaption` text color to `--ink-2` to pass 4.5:1 contrast on `--surface`.
2. **Semantic Hierarchy:** Convert `<div class="svc__name">`, `<div class="tier__name">`, and `<p class="svc-group__title">` elements to appropriate heading tags (`<h3>`, `<h4>`) across `services.html`, `pricing.html`, and `about.html`.
3. **Mobile Nav Overflow Risk:** Add `overflow-y: auto` to `.nav__links` within the `@media (max-width: 880px)` query in `styles.css` to prevent clipping.
4. **Markup Unification:** Standardize the varying footer `.disclaimer` text into a single unified string across all 6 HTML pages to resolve cross-page drift. Do the same for the `<head>` theme inline `<script>`.
5. **CSS Cleanup:** Relocate the inline styles from `index.html` (line 94) and `contact.html` (line 39) into utility or block classes in `styles.css`.
6. **House Rules:** Delete the em dash on line 2 of `styles.css`.
7. **Handoff Improvement (Optional):** Bind the `sessionStorage.setItem` call to the `recalc` function in `app.js` so the estimate auto-saves, preventing data loss if a user navigates to the contact page manually.

View File

@@ -0,0 +1,48 @@
[INFO] INDEPENDENT DESIGN & CODE REVIEW
Verdict: The 'Blowout' concept effectively shatters boring MSP templates, but heavily overshoots "trust" into "retail clearance" territory. It requires strategic dialing back to balance radical boldness with B2B stability.
---
### 1. DIAL-BACK STRATEGY (Ranked)
1. [ERROR] Overshoot: 13rem poster-typography (`radical.css:54`). Screams "event poster", refuting stability.
> Alternative: Cap clamp at ~5-6rem. Keep Anton, but size it for authority, not alarm.
2. [ERROR] Overshoot: Drenched signal-orange background blocks (`radical.css:80`, `radical.css:143`). Triggers urgency/warning semantics.
> Alternative: Restrict orange exclusively to interactive elements/accents. Use `--ink` for section backgrounds.
3. [WARN] Overshoot: 8.5rem blowout price numerals (`radical.css:128`). Reads as a discount warehouse.
> Alternative: Scale down to 3-4rem. Keep the transparent grid, but refine the typography.
4. [WARN] Overshoot: Infinite scrolling marquee (`radical.html:36`). Associated with aggressive retail.
> Alternative: Replace with a static, horizontal trust bar (logos or key features) below the hero.
5. [INFO] Overshoot: 16rem transparent ghost numerals (`radical.css:102`). Adds meaningless visual noise.
> Alternative: Remove entirely. Rely on the stark grid and whitespace.
### 2. WHAT TO KEEP
[OK] The `--ink` and `--bone` palette foundation: highly premium and tech-forward.
[OK] The brutalist structural borders (`2px solid var(--bone)`): communicates transparency and zero-BS.
[OK] Direct, no-fluff copywriting ("Everything, handled", "No games").
[OK] JetBrains Mono for metadata: adds legitimate engineering credibility.
### 3. WCAG AA CONTRAST
[ERROR] Orange text on `--ink`: `#FF5A1F` on `#0C0A09` yields ~4.3:1. Fails AA for body text.
> Corrected hex: Use `--hot-2` (`#FF8A4D`) for all orange text on dark backgrounds. Confidence: High.
[ERROR] Black on `#FF5A1F` (`rx-price--pop` unit text): `#0C0A09` on `#FF5A1F` at `opacity: 0.7` (`radical.css:131`). Fails functional contrast.
> Corrected hex: Remove opacity. Use solid `#0C0A09`. Confidence: High.
[ERROR] Stroke headline (`radical.css:57`): Transparent fill with 2px stroke fails functional contrast for low-vision users. Shape is lost.
> Corrected hex: Replace `color: transparent;` with solid `color: #F4EDE1;` (`var(--bone)`). Confidence: High.
[WARN] Bone-2 on `--ink`: `#B8AE9E` on `#0C0A09`. Technically passes (~7.3:1), but thin font (`radical.css:13`) makes it functionally weak.
> Corrected hex: Lighten to `#C9C1B5` or increase font-weight to 500. Confidence: Medium.
### 4. ACCESSIBILITY
[ERROR] Marquee data loss (`radical.html:35`): Claim: `aria-hidden="true"` is fine for decorative elements. Refutation: The values ("Since 2001", "No offshore") are not exposed to screen readers *anywhere else* in the DOM. Core value propositions are completely lost. Confidence: High.
[WARN] Focus management (`radical.css:76`): Claim: Global `:focus-visible` outline is visible. Refutation: Uses `--hot`. For `.rx-btn--hot` placed on a `--hot` background (like the CTA section), the outline relies entirely on `outline-offset`, becoming invisible. Confidence: High.
[OK] `prefers-reduced-motion` (`radical.css:21`): Correctly zeroes out animations.
[OK] Decorative image (`radical.html:26`): `alt=""` and `aria-hidden="true"` correctly implemented.
### 5. RESPONSIVE RISKS
[ERROR] Layout break (`radical.css:122`): 3-column pricing grid persists down to 761px. At ~761px-950px, 8.5rem text will overflow column bounds.
[WARN] Hover reflow (`radical.css:112`): `.rx-svc:hover` increases `padding-left: 1rem;` without initial padding compensation, causing text to abruptly jump right.
[WARN] Viewport overflow (`radical.css:54`): `clamp(3.5rem, ...)` sets a 56px minimum. The word "picks" may exceed a 320px width viewport, triggering horizontal scroll.
### 6. REAL CODE / MARKUP BUGS
[ERROR] Negative margin clip (`radical.css:55`): `margin-left: -0.06em;` on `h1` risks clipping the first letter ("I") outside the scrollable area.
[WARN] Inline styles (`radical.html:46`): `<div class="rx-svc-list" style="margin-top:2.5rem;">` violates structural separation.
[INFO] Legacy prefixing (`radical.css:58`): `text-stroke` is non-standard. `-webkit-text-stroke` is sufficient for all modern engines.

View File

@@ -0,0 +1,25 @@
[VERDICT] Verdigris Gate is structurally safe, cleanly scoped, and transitions reliably. However, it requires a minor contrast fix and a critical font-weight correction to prevent text degradation.
### 1. WCAG AA Contrast
- [ERROR] Light Primary Button: `--on-accent` (#F0F7F5) on `--accent` (#2F7A6B) yields ~4.4:1 contrast. At 1.05rem (16.8px) 600-weight, this fails the 4.5:1 requirement for normal-sized text.
*Fix: Lighten `--on-accent` to `#FFFFFF` (yields 4.9:1) or darken `--accent` to `#2B7364`.*
- [OK] Light Mode Base: Body text (10.9:1), muted text (6.4:1), and accent-ink (#1F5F52, 5.6:1) on #E9EDEA all comfortably pass.
- [OK] Dark Mode Base: Body text, muted text (7.6:1), accent-ink (#6BC4B0, 8.5:1), and primary button (#0D1614 on #4DA896, 6.5:1) on #141A18 all pass.
### 2. Typography & Faux-Bold Risk
- [ERROR] Missing Font Weights: Fraunces and IBM Plex Mono are loaded at 400;500 but are heavily applied at 600 and 700 across headings, buttons, and pricing tiers. This will force destructive, browser-synthesized faux-bolding.
*Fix: Append `600;700` to both Fraunces and IBM Plex Mono in the `<link>` Google Fonts payload.*
### 3. app.js Logic & Skin Swapping
- [OK] 3-Way Cycle: Modulo logic (`(cur + 1) % SKINS.length`) safely iterates `ledger -> midnight -> verdigris` and gracefully fails to index 0 (`ledger`) on null or corrupt local storage states.
- [OK] Image Swapping: `swapSkinImages` uses `getAttribute("src")` to correctly capture the un-resolved relative string, strictly matching `VERDIGRIS_IMG` keys. Reversal logic using `data-orig-src` is sound.
- [WARN] Double-Download (LCP impact): Because image `src` is hardcoded in HTML, direct loads into the Verdigris skin will fetch the `orig` image first, then immediately request the Verdigris variant.
### 4. Specificity & Token Leaks
- [OK] Token Scoping: No token leakage. `html[data-skin="verdigris"]` (0,1,1) securely overrides `:root` (0,1,0) and detaches entirely when the attribute changes.
- [OK] Signature Elements: Override rules like `html[data-skin="verdigris"] .site-header` (0,2,1) successfully trump base class styles `.site-header` (0,1,0) without relying on `!important`.
### 5. Accessibility Regressions
- [OK] Focus Navigation: Verdigris `--accent` maintains strong visibility as an outline color (4.47:1 against paper), preserving keyboard interaction cues.
- [INFO] Stale Static Tooltip: The static HTML `title` and `aria-label` for `#skinToggle` only declare "Paper / Midnight".
*Fix: Update the initial static HTML to "Paper / Midnight / Verdigris" so pre-JS screen readers announce it accurately.*

View File

@@ -0,0 +1,161 @@
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 all
- `contact.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:
```140:153:D:\ClaudeTools\projects\acg-website-showcase\multipage\js\app.js
var sendBtn = $("#sendEstimate");
if (sendBtn) {
sendBtn.addEventListener("click", function () {
// ... builds summary from #ledgerLines ...
try { sessionStorage.setItem("acg-estimate", summary); } catch (e) {}
});
}
```
```192:201:D:\ClaudeTools\projects\acg-website-showcase\multipage\js\app.js
var msg = $("#cf-msg");
if (msg) {
try {
var est = sessionStorage.getItem("acg-estimate");
if (est) {
msg.value = est;
sessionStorage.removeItem("acg-estimate");
```
- `#sendEstimate` is an `<a href="contact.html#contact">` (`calculator.html:124`)
- Click handler is synchronous before navigation — `sessionStorage` is set in time
- `app.js` is at end of `<body>` on `contact.html:109`, so `#cf-msg` exists before prefill runs
- Anchor target `#contact` exists (`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` 0500 (`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 @760820px, 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 dont 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.

View File

@@ -0,0 +1,58 @@
**VERDICT**
[INFO] Radical "Blowout" poster concept (Anton 13rem, #FF5A1F drench, marquee, stroke outline, giant pricing) succeeds at raw visual arrest and hierarchy but oversteps for a Tucson MSP selling long-term trust and concierge reliability. It reads like a one-day sale flyer more than a stable partner. radical.html + radical.css reviewed in full. No modifications performed.
**(1) DIAL-BACK list (ranked 1 = most damaging for trust sale)**
1. [HIGH] Hero headline scale + Anton ultra-condensed at clamp(3.5rem, 16vw, 13rem) (radical.html:36, radical.css:70). 13rem on desktop and still 7-9rem on tablets feels like carnival signage, not enterprise IT. Tamer: cap at clamp(2.75rem, 7vw, 5.75rem); keep Anton for one word only or add 0.03-0.04em tracking and switch subheads to Hanken Grotesk 700.
2. [HIGH] Full-bleed #FF5A1F CTA section + multiple hot buttons and pop pricing tier (radical.html:112, radical.css:175, 148, 96). Orange overload + "Talk to a human" at 10rem screams urgency/deal, erodes calm authority. Tamer: limit full-bleed hot to one primary CTA band only; use bone/ink inverted or bone-bordered buttons elsewhere; reserve solid hot for the single highest-value action.
3. [MED] 26s continuous marquee on full #FF5A1F bar (radical.html:46-50, radical.css:104, 107). Perpetual motion + saturated strip reads gimmicky for a 2001-established MSP. Tamer: 48-60s duration or convert to static centered pills row; keep hot only for text if bar must remain.
4. [MED] Transparent-fill -webkit-text-stroke headline on "up." (radical.html:36, radical.css:73-75). 2px bone outline with no interior fill creates thin, haloed, low-density letters that feel unfinished and hard to parse at speed. Tamer: solid bone or hot fill + 1px hot or bone underline/shadow; drop stroke entirely for body copy sizes.
5. [MED] Volume and placement of hot / hot-2 accents (kickers, eyebrow, svc__n, svc__meta, price pop, cta) (radical.css:42, 67, 117, 136, 148, 175). Too many saturated hits dilute focus and hierarchy. Tamer: restrict #FF5A1F to 1-2 CTAs + one price highlight; use bone-2 or ink-2 for meta; replace most hot-2 with bone or a darker orange variant.
6. [LOW] Giant ghost section numerals (16rem clamp) + 8.5rem pricing numerals (radical.css:121, 151). Poster numerals compete with actual content and make pricing feel theatrical. Tamer: ghost max 5-6rem; pricing bigs to clamp(2.5rem, 5vw, 4rem) or switch to weighted Hanken Grotesk for numerals while keeping Anton for true headlines.
**(2) KEEP (moves that are genuinely strong)**
- Direct "no email wall" calculator teaser and published per-endpoint grid with clear tiering and "most chosen" callout (radical.html:100-107, 76-95). Honest pricing posture is the single biggest trust signal here.
- Service row hover + IntersectionObserver reveal (radical.html:134-140, radical.css:194-195, 131). Subtle progressive disclosure without heavy JS.
- Mono + poster font pairing for scannability and brand lockup in header (radical.css:18-20, 51-56).
- Sticky header, phone CTA, and immediate human contact paths without friction.
- Self-contained file, proper lang/meta, skip link, and existing reduced-motion rule (radical.html:15, 8-12, radical.css:26-30).
**(3) WCAG AA contrast failures**
- Black #0C0A09 on #FF5A1F (rx-btn--hot, marquee text, rx-cta hot buttons, price pop): estimated 8-10:1+. Passes AA/AAA for large text and UI components. [OK]
- #FF5A1F on #0C0A09 (`.hot`, rx-kicker, rx-hero__eyebrow, rx-svc__n): ~3.6-4.0:1. FAILS AA for normal text (needs 4.5:1). Passes only if treated strictly as large text. Corrected hex for reliable AA: #FF7A4D or #FF8F60 (brighter) or switch accent text to #F4EDE1 bone on dark.
- #FF8A4D on #0C0A09 (svc__meta, some price units): ~2.8-3.2:1. FAILS AA. Corrected: #FFB57A or replace with #F4EDE1 / #CFC6B4.
- #B8AE9E (bone-2) on #0C0A09 (rx-hero__sub, rx-svc__d, rx-price__list, footer, calc p): ~4.1-4.4:1. FAILS AA 4.5:1 for body text under ~18pt/14pt bold. Corrected: #CFC6B4 or #D8D0C0 (lighten ~10-12%).
- -webkit-text-stroke headline (transparent fill + 2px bone): effective contrast fails. Thin outline only allows background bleed between strokes; no solid interior. Not reliably readable per WCAG for text. Fails even large-text criteria in practice. Corrected approach: solid #F4EDE1 fill + optional 1px #FF5A1F stroke or 2-3px text-shadow; never rely on stroke-only for critical headlines.
**(4) Accessibility**
- Marquee: aria-hidden="true" present (radical.html:46). Reduced-motion rule already kills the animation (radical.css:29). [OK] Gaps: hover-only pause (css:108) is useless on touch; content is duplicated for seamlessness but animation still runs for keyboard/screen-reader users who do not use hover. Recommend static fallback or `prefers-reduced-motion` + `aria-hidden` already good; add `role="presentation"` or convert to non-animated row for production.
- focus-visible: global rule exists (radical.css:101) with 3px hot outline. [OK] Risk: hot outline on #FF5A1F backgrounds has insufficient contrast. No legacy :focus fallback. Test on hot CTAs; consider conditional darker/bone outline for hot elements.
- Decorative hero image: container aria-hidden="true", img alt="", masked/grayscale + overlay (radical.html:33, radical.css:79-88). [OK] Correct for pure decoration. Mid-width (760-900px) absolute positioning could cause visual layering surprises if image ever gains meaning.
**(5) Responsive risks (ranked)**
- [CRITICAL] Nav collapse at 760px hides all links except phone CTA with no mobile menu or fallback (radical.css:61, radical.html:20-26). On phones/tablets users lose Work/Pricing/Estimate/Contact access.
- [HIGH] 16vw hero type + 13vw CTA type still produce 6-9rem sizes on 700-900px widths; risk of clipping, tall first fold, and poor line wrapping (radical.css:70, 177).
- [MED] Marquee text 1.4rem only shrinks at 480px; separators and long phrases can overflow or crowd on 320-360px (radical.css:204, 110).
- [MED] Pricing 3-col grid + service grid changes at 760/640px are clean but the orange pop tier loses visual weight when stacked.
- [LOW] Absolute hero art (42vw) + mask can cause text overlap or empty right gutter on 900-1100px before hiding (radical.css:90, 80-84). Body overflow-x:hidden can mask real scroll bugs.
**(6) Real code bugs (severity-tagged, ranked)**
- [ERROR] Stroke-only "up." headline uses non-standard `text-stroke` (radical.css:74). Only `-webkit-text-stroke` is reliable; other engines leave color:transparent text invisible or barely stroked.
- [ERROR] Marquee relies on exact duplicate span blocks for infinite loop (radical.html:48-49). Any content edit, font timing, or resize breaks the seam.
- [HIGH] `.marquee:hover .marquee__track` pause (radical.css:108) has no keyboard, focus, or touch equivalent. Animation cannot be stopped on mobile or by non-mouse users.
- [MED] Service hover applies `padding-left:1rem` (radical.css:131). Causes layout shift on every hover; better to use transform or background change only.
- [MED] Global `* { margin:0 }` + late :focus-visible (radical.css:24, 101) + reduced-motion rules are broad; some transitions on .rx-up and .rx-svc may still leak on older reduced-motion implementations.
- [LOW] rx-ghost and stroke elements use pointer-events:none + user-select:none but remain in tab order or selection edge cases in some engines (radical.css:123, 74).
- [LOW] Year script and IO observer are safe (radical.html:132-140). No console errors obvious from static source. Assets path and missing calculator.html are out-of-scope for this file pair.
[OK] End of review. All analysis derived strictly from the two read files.

View File

@@ -0,0 +1,94 @@
Reading all four files for an independent review of the Verdigris Gate skin and skin system.
Computing WCAG contrast ratios for Verdigris token pairs.
Checking image paths across pages and additional contrast edge cases (small text, hover states).
**Verdict:** Verdigris Gate is cleanly scoped and does not break ledger/midnight. Primary button and accent-text pass AA in both modes at rest. Real issues: small muted text on `surface-2` (including the gate-rail header), light-mode button hover, missing font weights 600/700, and stale skin-switcher copy. Confidence: **high** on tokens/contrast/JS; **medium** on live rendering (no browser pass).
---
### Ranked fix list
**1. [ERROR] `--ink-3` fails AA normal text on `surface-2` (both modes)**
`styles.css:91,107` — Header uses `surface-2` (`styles.css:118-119`); `.brand__since` is `ink-3` at 0.66rem (`styles.css:233-234`). Also hits 0.720.78rem hints/captions on `surface-2`.
| Mode | Pair | Ratio | Need |
|------|------|-------|------|
| Light | `#5C6B65` / `#DFE5E1` | 4.39:1 | 4.5:1 |
| Dark | `#7A8A84` / `#25302C` | 3.77:1 | 4.5:1 |
**Corrected hex:** light `--ink-3: #4A5853` (5.84:1 on `#DFE5E1`, 6.32:1 on paper); dark `--ink-3: #8FA098` (4.97:1 on `#25302C`, 5.78:1 on surface).
---
**2. [ERROR] Font loading vs CSS weights — faux-bold on Verdigris**
`index.html:11` loads `Fraunces:wght@400;500` and `IBM Plex Mono:wght@400;500`, but CSS uses **600700**:
- Fraunces: `h1h4`, `.btn`, `.brand__mark` at 600700 (`styles.css:188-191,203,227,270`)
- IBM Plex Mono: `.tier__price`, `.calc__total .tnum`, `.trust__num` at 600700 (`styles.css:304,360,459`)
Source Sans 3 loads 400;600 but `.nav__phone` uses `font-weight: 500` (`styles.css:240`) → synthesized medium.
**Fix:** load `Fraunces:wght@400;500;600;700`, `IBM Plex Mono:wght@400;500;600;700`, add `500` to Source Sans 3 (or change phone to 600).
---
**3. [WARN] Light primary button hover drops below AA**
`styles.css:211``filter: brightness(1.05)` on `#2F7A6B``#F0F7F5` contrast **4.34:1** (need 4.5:1). Dark hover stays fine (7.05:1).
**At-rest button passes:** light `#F0F7F5` / `#2F7A6B` = 4.69:1; dark `#0D1614` / `#4DA896` = 6.44:1.
**Fix options:** drop hover brighten; darken hover fill to `#2A6F61` (5.45:1); or light `--on-accent: #FFFFFF` (5.10:1 rest, 4.60:1 hover).
---
**4. [WARN] `swapSkinImages` — path fragility + duplicate art**
`app.js:63-69`
- Lookup is exact string on `data-orig-src`; resolved URLs, relative variants, or `?cache` params miss the map and stay on ledger art.
- `story.png``verdigris/hero.png` (`app.js:61`) duplicates hero latch on about (`about.html:66`) — likely unintentional; no dedicated `story` asset.
- `$$("img")` is broad but safe: non-mapped images restore via `orig`; `data-orig-src` caching is correct for the 3-skin cycle.
- Invalid/missing `data-skin``indexOf` = -1 → cycles to `ledger` (`app.js:86-87`) — OK but silent.
---
**5. [WARN] Skin switcher HTML still 2-skin**
`index.html:46`, `calculator.html:33``title="Switch skin: Paper / Midnight"` while JS cycles three skins (`app.js:53-54`). `aria-label` updates at runtime; static `title` does not.
---
**6. [INFO] Double-rule inset frames — mostly fine; rate-card frame weak**
`styles.css:124-127``inset 4px paper + 5px rule` on `.calc__shell`, `.rate-card`, `.contact__form`.
- `.calc__shell` / `.rate-card` have `overflow: hidden` (`styles.css:352,399`) — no scroll bleed.
- `.contact__form` has no overflow rule; inset shadows are paint-only — low risk.
- On `.rate-card`, child tiers fill the grid and likely **cover** most of the inset ring — frame may barely show. Calculator/contact are the visible wins.
---
**7. [INFO] Gate-rail header grain — no overflow concern**
`styles.css:118-122` — Opaque `surface-2` + 3px repeating gradient; `backdrop-filter: none` correctly drops ledger glass. Subtle moiré possible on low-DPI; not a layout issue.
---
**8. [OK] Accent-as-text and muted body copy**
Light: `--accent-ink #1F5F52` 6.31:1 on paper; `--ink-2 #3F4F49` 7.32:1 on paper.
Dark: `--accent-ink #6BC4B0` 8.52:1 on paper; `--ink-2 #A8B5AF` 8.31:1 on paper.
`::selection` accent/on-accent: 4.69:1 light, 6.44:1 dark.
---
**9. [OK] Ledger / Midnight isolation**
Verdigris tokens and treatments are scoped to `html[data-skin="verdigris"]` (`styles.css:81-138`). `ledger` falls through to `:root`; `midnight` overrides stay separate (`styles.css:49-78`). Extra font families in the shared Google Fonts URL add weight for all skins but do not alter their palettes or typography tokens. Image swap only runs for `verdigris`; ledger/midnight always get `orig` src.
---
### Summary table (requested checks)
| Check | Result |
|-------|--------|
| Primary button AA | Pass at rest; light hover fail |
| Accent-as-text | Pass both modes |
| Muted text | `--ink-2` pass; `--ink-3` fail on `surface-2` |
| Font weights | Fail — load 600/700 (and 500 for Source Sans) |
| Image swap | Logic sound; path/exact-match + story duplicate |
| Double-rule / grain | No overflow; rate-card frame may be invisible |
| Other skins | Not broken |

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 795 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 411 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 841 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 837 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 892 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 918 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 KiB

View File

@@ -0,0 +1,40 @@
{"rect": [861, 145, 874, 481], "text": "Migrations, licensing, and email that is configured, secured, and\nsupported."},
{"rect": [850, 808, 854, 895], "text": "from $2 /mailbox"},
{"rect": [894, 104, 899, 114], "text": "05"},
{"rect": [895, 145, 904, 283], "text": "Business Phones"},
{"rect": [909, 145, 922, 481], "text": "Cloud phone systems with mobile and desktop apps, porting, and no\nusage surprises."},
{"rect": [898, 819, 903, 895], "text": "from $22 /user"},
{"rect": [943, 104, 947, 114], "text": "06"},
{"rect": [943, 145, 952, 311], "text": "Web & Email Hosting"},
{"rect": [957, 145, 971, 481], "text": "Managed hosting with free SSL, daily backups, and real humans on\nsupport."},
{"rect": [946, 830, 951, 895], "text": "from $15 /mo"},
{"rect": [989, 104, 995, 199], "text": "See all services →"}, "text": "See all services →"},
{"rect": [1050, 131, 1054, 434], "text": "PUBLISHED, NOT \"CALL FOR QUOTE\" / PER ENDPOINT"},
{"rect": [1067, 104, 1088, 581], "text": "Pricing with nothing hidden"},
{"rect": [1098, 104, 1118, 481], "text": "You pay for exactly the computers you have. No rounding you up into a\nbigger package tier."},
{"rect": [1147, 120, 1157, 214], "text": "GPS-Basic"},
{"rect": [1160, 120, 1180, 175], "text": "$19"},
{"rect": [1170, 191, 1175, 280], "text": "/ endpoint / mo"},
{"rect": [1189, 120, 1195, 340], "text": "Essential monitoring for small, simple environments."},
{"rect": [1135, 564, 1140, 626], "text": "MOST CHOSEN"},
{"rect": [1147, 384, 1157, 463], "text": "GPS-Pro"},
{"rect": [1160, 384, 1180, 439], "text": "$26"},
{"rect": [1170, 455, 1175, 544], "text": "/ endpoint / mo"},
{"rect": [1189, 384, 1195, 602], "text": "Comprehensive protection for growing businesses."},
{"rect": [1147, 648, 1157, 781], "text": "GPS-Advanced"},
{"rect": [1160, 648, 1180, 703], "text": "$39"},
{"rect": [1170, 719, 1175, 808], "text": "/ endpoint / mo"},
{"rect": [1189, 648, 1195, 829], "text": "Enterprise-grade security and compliance."},
{"rect": [1235, 104, 1240, 246], "text": "Full pricing breakdown →"},
{"rect": [1235, 261, 1240, 370], "text": "Build an estimate →"},
{"rect": [1309, 104, 1330, 426], "text": "Let's talk, no pitch."},
{"rect": [1340, 104, 1360, 481], "text": "Tell us what is frustrating you, and we'll give you honest\nfeedback. Even if that is \"your current setup is fine, here is one\nthing to fix.\""},
{"rect": [1365, 652, 1370, 757], "text": "Talk to a human →"},
{"rect": [1365, 801, 1370, 880], "text": "520.304.8300"},
{"rect": [1412, 104, 1417, 497], "text": "© 2026 Arizona Computer Guru . Protecting Tucson businesses since 2001"},
{"rect": [1412, 683, 1417, 750], "text": "520.304.8300"},
{"rect": [1412, 757, 1417, 762], "text": "."},
{"rect": [1412, 768, 1417, 895], "text": "info@azcomputerguru.com"},
{"rect": [1433, 104, 1445, 895], "text": "Local demonstration build of a proposed azcomputerguru.com (multipage). Pricing reflects published GPS rates and is illustrative for the estimator; a real quote\nis tailored to your environment. The contact form is not wired to a mailbox. Photography is representational."}
]
```

View File

@@ -0,0 +1,158 @@
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Arizona Computer Guru | Concierge IT for Tucson, since 2001</title>
<meta name="description" content="Tucson's concierge managed IT and cybersecurity partner since 2001. Transparent per-endpoint pricing, local people, month-to-month terms. Build an estimate in seconds." />
<meta name="color-scheme" content="light dark" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@500;600;700&family=Lexend:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500;600;700&family=Fraunces:wght@400;500;600;700&family=Source+Sans+3:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500;600;700&family=Anton&family=Hanken+Grotesk:wght@400;500;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="css/styles.css" />
<script>
(function () { try {
var s = localStorage.getItem("acg-theme");
var m = s || (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");
document.documentElement.setAttribute("data-theme",m);document.documentElement.setAttribute("data-skin",(localStorage.getItem("acg-skin")||"ledger"));document.documentElement.classList.add("js");
} catch (e) {} })();
</script>
<script type="application/ld+json">
{"@context":"https://schema.org","@type":"LocalBusiness","name":"Arizona Computer Guru","image":"assets/images/hero.png","description":"Concierge managed IT and cybersecurity for Tucson businesses since 2001.","address":{"@type":"PostalAddress","streetAddress":"7437 E. 22nd St","addressLocality":"Tucson","addressRegion":"AZ","postalCode":"85710","addressCountry":"US"},"telephone":"+1-520-304-8300","url":"https://azcomputerguru.com","foundingDate":"2001","areaServed":"Tucson, Arizona","openingHours":"Mo-Fr 09:00-17:00"}
</script>
</head>
<body>
<a href="#main" class="sr-only">Skip to content</a>
<header class="site-header">
<div class="wrap">
<a class="brand" href="index.html" aria-label="Arizona Computer Guru home">
<span class="brand__mark" aria-hidden="true">ACG</span>
<span>
<span class="brand__name">Arizona Computer Guru</span><br />
<span class="brand__since">Concierge IT &middot; Tucson &middot; since 2001</span>
</span>
</a>
<nav class="nav" aria-label="Primary">
<div class="nav__links" id="navLinks">
<a class="nav__link" href="index.html" aria-current="page">Home</a>
<a class="nav__link" href="services.html">Services</a>
<a class="nav__link" href="pricing.html">Pricing</a>
<a class="nav__link" href="calculator.html">Estimate</a>
<a class="nav__link" href="about.html">About</a>
<a class="nav__link" href="contact.html">Contact</a>
</div>
<a class="nav__phone" href="tel:+15203048300">520.304.8300</a>
<button class="skin-toggle" id="skinToggle" type="button" aria-label="Switch visual skin (Paper, Midnight, Verdigris, or Bold)" title="Switch skin: Paper / Midnight / Verdigris / Bold"><span class="sw" aria-hidden="true"></span></button>
<button class="theme-toggle" id="themeToggle" type="button" aria-pressed="false" aria-label="Switch to dark theme" title="Toggle light / dark"><span aria-hidden="true" data-theme-icon>&#9728;</span></button>
<button class="nav__toggle" id="navToggle" type="button" aria-expanded="false" aria-controls="navLinks" aria-label="Open menu">&#8801;</button>
</nav>
</div>
</header>
<main id="main">
<!-- HERO -->
<section class="hero ledger" aria-labelledby="hero-h">
<div class="wrap">
<div class="hero__copy reveal">
<p class="hero__eyebrow">Tucson &middot; Managed IT &amp; Security &middot; Est. 2001</p>
<h1 id="hero-h">Concierge IT for Tucson,<br /><span class="amp">every number on the table.</span></h1>
<p class="hero__sub">We go further than your last IT company, build a real relationship, and never hide the math. Honest pricing, local people, problems fixed before you feel them.</p>
<div class="hero__cta">
<a class="btn btn--primary" href="calculator.html">Estimate your cost <span class="arrow" aria-hidden="true">&rarr;</span></a>
<a class="btn btn--ghost" href="contact.html">Talk to a human</a>
</div>
<p class="hero__note mono">// Month-to-month. No lock-in. No offshore call centers.</p>
</div>
<figure class="hero__frame reveal">
<img src="assets/images/hero.png" width="1248" height="832" alt="A wooden desk at golden hour with an open paper ledger, a laptop, and a coffee cup; the Santa Catalina mountains and a saguaro through the window." />
<figcaption class="hero__caption"><span>Tucson, AZ</span><span>open book, nothing hidden</span></figcaption>
</figure>
</div>
</section>
<!-- TRUST -->
<section class="trust" aria-label="Why businesses stay with us" style="padding-block:0;">
<div class="wrap">
<div class="trust__cell"><div class="trust__num">20<span class="u">01</span></div><div class="trust__label">Serving Tucson businesses since</div></div>
<div class="trust__cell"><div class="trust__num">1&ndash;2<span class="u">hr</span></div><div class="trust__label">Typical onsite emergency response</div></div>
<div class="trust__cell"><div class="trust__num">100<span class="u">%</span></div><div class="trust__label">Local team, never offshore</div></div>
<div class="trust__cell"><div class="trust__num">30<span class="u">day</span></div><div class="trust__label">Cancel anytime, no penalty</div></div>
</div>
</section>
<!-- CONCIERGE TEASER -->
<section class="story" aria-labelledby="story-h">
<div class="wrap">
<div class="story__copy reveal">
<p class="section-tag">Our difference <span>/ concierge</span></p>
<h2 id="story-h">What "concierge" actually means</h2>
<p class="muted">Most IT companies wait for things to break, route you to a call center three time zones away, and lock you into a three-year contract. We built Arizona Computer Guru to be the opposite. We learn your business, pick up the phone, and explain things in plain English. Because we charge the same whether your systems break or not, our only incentive is to keep them running.</p>
<a class="more-link" href="about.html">Read our story <span class="arrow" aria-hidden="true">&rarr;</span></a>
</div>
<figure class="story__img reveal">
<img src="assets/images/about.png" width="1248" height="832" alt="A friendly handshake over a wooden desk with a laptop and a handwritten notebook, an Arizona map framed on the wall." loading="lazy" />
</figure>
</div>
</section>
<!-- SERVICES TEASER -->
<section class="home-services ledger" aria-labelledby="hsvc-h">
<div class="wrap">
<p class="section-tag">Everything you need <span>/ one local partner</span></p>
<h2 id="hsvc-h">What we do</h2>
<div class="svc-list">
<article class="svc reveal"><div class="svc__no">01</div><div><h3 class="svc__name">Managed IT (GPS)</h3><p class="svc__desc">24/7 monitoring, automated patching, and a help desk that knows your name.</p></div><div class="svc__meta">from&nbsp;$19&nbsp;/endpoint</div></article>
<article class="svc reveal"><div class="svc__no">02</div><div><h3 class="svc__name">Cybersecurity</h3><p class="svc__desc">EDR, email security, dark-web monitoring, and training that catch what antivirus misses.</p></div><div class="svc__meta">in GPS-Pro &amp; up</div></article>
<article class="svc reveal"><div class="svc__no">03</div><div><h3 class="svc__name">Backup &amp; Recovery</h3><p class="svc__desc">Tested backups, offsite copies, and ransomware rollback.</p></div><div class="svc__meta">in GPS-Pro &amp; up</div></article>
<article class="svc reveal"><div class="svc__no">04</div><div><h3 class="svc__name">Microsoft 365 &amp; Email</h3><p class="svc__desc">Migrations, licensing, and email that is configured, secured, and supported.</p></div><div class="svc__meta">from&nbsp;$2&nbsp;/mailbox</div></article>
<article class="svc reveal"><div class="svc__no">05</div><div><h3 class="svc__name">Business Phones</h3><p class="svc__desc">Cloud phone systems with mobile and desktop apps, porting, and no usage surprises.</p></div><div class="svc__meta">from&nbsp;$22&nbsp;/user</div></article>
<article class="svc reveal"><div class="svc__no">06</div><div><h3 class="svc__name">Web &amp; Email Hosting</h3><p class="svc__desc">Managed hosting with free SSL, daily backups, and real humans on support.</p></div><div class="svc__meta">from&nbsp;$15&nbsp;/mo</div></article>
</div>
<a class="more-link" href="services.html">See all services <span class="arrow" aria-hidden="true">&rarr;</span></a>
</div>
</section>
<!-- PRICING TEASER -->
<section class="home-pricing" aria-labelledby="hp-h">
<div class="wrap">
<p class="section-tag">Published, not "call for quote" <span>/ per endpoint</span></p>
<h2 id="hp-h">Pricing with nothing hidden</h2>
<p class="lead">You pay for exactly the computers you have. No rounding you up into a bigger package tier.</p>
<div class="rate-card" role="list">
<div class="tier" role="listitem"><h3 class="tier__name">GPS-Basic</h3><div class="tier__price">$19 <span class="per">/ endpoint / mo</span></div><p class="tier__blurb" style="border-bottom:0;">Essential monitoring for small, simple environments.</p></div>
<div class="tier tier--pop" role="listitem"><div class="tier__flag">Most chosen</div><h3 class="tier__name">GPS-Pro</h3><div class="tier__price">$26 <span class="per">/ endpoint / mo</span></div><p class="tier__blurb" style="border-bottom:0;">Comprehensive protection for growing businesses.</p></div>
<div class="tier" role="listitem"><h3 class="tier__name">GPS-Advanced</h3><div class="tier__price">$39 <span class="per">/ endpoint / mo</span></div><p class="tier__blurb" style="border-bottom:0;">Enterprise-grade security and compliance.</p></div>
</div>
<div class="inline-actions">
<a class="more-link" href="pricing.html">Full pricing breakdown <span class="arrow" aria-hidden="true">&rarr;</span></a>
<a class="more-link" href="calculator.html">Build an estimate <span class="arrow" aria-hidden="true">&rarr;</span></a>
</div>
</div>
</section>
<!-- CTA -->
<section class="cta-band" aria-labelledby="cta-h">
<div class="wrap">
<div class="reveal">
<h2 id="cta-h">Let's talk, no pitch.</h2>
<p>Tell us what is frustrating you, and we'll give you honest feedback. Even if that is "your current setup is fine, here is one thing to fix."</p>
</div>
<div class="cta-band__actions reveal">
<a class="btn btn--primary" href="contact.html">Talk to a human <span class="arrow" aria-hidden="true">&rarr;</span></a>
<a class="btn btn--ghost" href="tel:+15203048300">520.304.8300</a>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="wrap">
<p>&copy; <span id="year">2026</span> Arizona Computer Guru &middot; Protecting Tucson businesses since 2001</p>
<p><a href="tel:+15203048300">520.304.8300</a> &middot; <a href="mailto:info@azcomputerguru.com">info@azcomputerguru.com</a></p>
<p class="disclaimer">Local demonstration build of a proposed azcomputerguru.com (multipage). Pricing reflects published GPS rates and is illustrative for the estimator; a real quote is tailored to your environment. The contact form is not wired to a mailbox. Photography is representational.</p>
</div>
</footer>
<script src="js/app.js"></script>
</body>
</html>

View File

@@ -0,0 +1,283 @@
/* ===========================================================================
Arizona Computer Guru, Sonoran Ledger (multipage)
Shared vanilla JS across all pages. Each block guards on its own DOM.
=========================================================================== */
(function () {
"use strict";
var $ = function (s, c) { return (c || document).querySelector(s); };
var $$ = function (s, c) { return Array.prototype.slice.call((c || document).querySelectorAll(s)); };
/* ---- Theme ------------------------------------------------------------ */
var root = document.documentElement;
var toggle = $("#themeToggle");
var icon = $("[data-theme-icon]");
var STORE = "acg-theme";
function applyTheme(mode) {
root.setAttribute("data-theme", mode);
var dark = mode === "dark";
if (toggle) {
toggle.setAttribute("aria-pressed", String(dark));
toggle.setAttribute("aria-label", dark ? "Switch to light theme" : "Switch to dark theme");
}
if (icon) icon.innerHTML = dark ? "&#9790;" : "&#9728;";
}
var saved = null;
try { saved = localStorage.getItem(STORE); } catch (e) {}
var prefersDark = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
applyTheme(saved || (prefersDark ? "dark" : "light"));
if (toggle) {
toggle.addEventListener("click", function () {
var next = root.getAttribute("data-theme") === "dark" ? "light" : "dark";
applyTheme(next);
try { localStorage.setItem(STORE, next); } catch (e) {}
});
}
if (window.matchMedia) {
var mq = window.matchMedia("(prefers-color-scheme: dark)");
var onChange = function (e) {
var explicit = null;
try { explicit = localStorage.getItem(STORE); } catch (err) {}
if (!explicit) applyTheme(e.matches ? "dark" : "light");
};
if (mq.addEventListener) mq.addEventListener("change", onChange);
else if (mq.addListener) mq.addListener(onChange);
}
/* ---- Skin (Paper / Midnight / Verdigris) ----------------------------- */
var skinToggle = $("#skinToggle");
var SKIN = "acg-skin";
var SKINS = ["ledger", "midnight", "verdigris", "bold"];
var SKIN_NAME = { ledger: "Paper", midnight: "Midnight", verdigris: "Verdigris", bold: "Bold" };
// Verdigris uses its own cooler documentary photography.
var VERDIGRIS_IMG = {
"assets/images/hero.png": "assets/images/verdigris/hero.png",
"assets/images/about.png": "assets/images/verdigris/about.png",
"assets/images/services.png": "assets/images/verdigris/services.png",
"assets/images/contact.png": "assets/images/verdigris/contact.png",
"assets/images/story.png": "assets/images/verdigris/contact.png"
};
function swapSkinImages(skin) {
$$("img").forEach(function (img) {
var orig = img.getAttribute("data-orig-src");
if (orig === null) { orig = img.getAttribute("src"); img.setAttribute("data-orig-src", orig); }
if (skin === "verdigris" && VERDIGRIS_IMG[orig]) img.setAttribute("src", VERDIGRIS_IMG[orig]);
else img.setAttribute("src", orig);
});
}
function applySkin(skin) {
if (SKINS.indexOf(skin) < 0) skin = "ledger";
root.setAttribute("data-skin", skin);
swapSkinImages(skin);
if (skinToggle) {
var name = SKIN_NAME[skin];
skinToggle.setAttribute("aria-label", "Current skin: " + name + ". Switch.");
skinToggle.setAttribute("title", "Skin: " + name + " (click to switch)");
}
}
var savedSkin = "ledger";
try { savedSkin = localStorage.getItem(SKIN) || "ledger"; } catch (e) {}
applySkin(savedSkin);
if (skinToggle) {
skinToggle.addEventListener("click", function () {
var cur = SKINS.indexOf(root.getAttribute("data-skin"));
var next = SKINS[(cur + 1) % SKINS.length];
applySkin(next);
try { localStorage.setItem(SKIN, next); } catch (e) {}
});
}
/* ---- Dynamic year ----------------------------------------------------- */
var yr = $("#year");
if (yr) yr.textContent = String(new Date().getFullYear());
/* ---- Mobile nav ------------------------------------------------------- */
var header = $(".site-header");
var navToggle = $("#navToggle");
if (navToggle && header) {
var closeNav = function () {
header.classList.remove("nav-open");
navToggle.setAttribute("aria-expanded", "false");
navToggle.setAttribute("aria-label", "Open menu");
};
navToggle.addEventListener("click", function () {
var open = header.classList.toggle("nav-open");
navToggle.setAttribute("aria-expanded", String(open));
navToggle.setAttribute("aria-label", open ? "Close menu" : "Open menu");
});
$$("#navLinks a").forEach(function (a) { a.addEventListener("click", closeNav); });
document.addEventListener("keydown", function (e) { if (e.key === "Escape") closeNav(); });
}
/* ---- Money helper ----------------------------------------------------- */
function money(n) { return "$" + Math.round(n).toLocaleString("en-US"); }
/* ---- Calculator (calculator.html) ------------------------------------ */
var form = $("#calcForm");
if (form) {
var EQUIP = 25, M365_RATE = 14, VOIP_RATE = 28;
function clampInt(v) { if (isNaN(v) || v < 0) return 0; return v > 500 ? 500 : v; }
function intVal(id) { return clampInt(parseInt(($("#" + id) || {}).value, 10)); }
function numSelect(id) { var v = parseFloat(($("#" + id) || {}).value); return isNaN(v) ? 0 : v; }
function lineRow(name, detail, cost) {
return '<div class="lline' + (cost <= 0 ? " is-zero" : "") + '">' +
'<span class="lname">' + name + (detail ? ' <span class="muted">' + detail + "</span>" : "") + "</span>" +
'<span class="lcost">' + money(cost) + "</span></div>";
}
function recalc() {
var endpoints = intVal("endpoints");
var tier = numSelect("gpsTier");
var equip = $("#equip").checked;
var support = numSelect("support");
var m365 = intVal("m365");
var voip = intVal("voip");
var hosting = numSelect("hosting");
var gpsCost = endpoints * tier;
var equipCost = equip ? EQUIP : 0;
var m365Cost = m365 * M365_RATE;
var voipCost = voip * VOIP_RATE;
var tierName = tier === 19 ? "GPS-Basic" : tier === 39 ? "GPS-Advanced" : "GPS-Pro";
var supportName = support === 0 ? "" : support === 200 ? "Essential" :
support === 380 ? "Standard" : support === 540 ? "Premium" : "Priority";
var hostingName = hosting === 0 ? "" : hosting === 15 ? "Starter" :
hosting === 35 ? "Business" : "Commerce";
var lines = "";
lines += lineRow(tierName + " monitoring", endpoints + " &times; " + money(tier), gpsCost);
lines += lineRow("Equipment monitoring", "up to 10 devices", equipCost);
lines += lineRow((supportName || "Support plan") + " support", supportName ? "bundled labor" : "none", support);
lines += lineRow("Microsoft 365", m365 + " &times; " + money(M365_RATE), m365Cost);
lines += lineRow("Business phones", voip + " &times; " + money(VOIP_RATE), voipCost);
lines += lineRow((hostingName || "Web hosting") + " hosting", hostingName ? "managed" : "none", hosting);
var total = gpsCost + equipCost + support + m365Cost + voipCost + hosting;
$("#ledgerLines").innerHTML = lines;
$("#totalMonthly").textContent = money(total);
$("#totalAnnual").textContent = money(total * 12) + " / year";
$("#perEndpoint").innerHTML = endpoints > 0
? money(total / endpoints) + " all-in, per endpoint / mo"
: "add endpoints to see per-seat cost";
}
$$(".stepper").forEach(function (st) {
var input = $("input", st);
$$("button", st).forEach(function (b) {
b.addEventListener("click", function () {
input.value = clampInt(parseInt(input.value, 10) + parseInt(b.getAttribute("data-dir"), 10));
recalc();
});
});
input.addEventListener("change", function () { input.value = clampInt(parseInt(input.value, 10)); });
});
// Carry the built estimate across to the contact page.
var sendBtn = $("#sendEstimate");
if (sendBtn) {
var storeEstimate = function () {
var lines = $$("#ledgerLines .lline")
.filter(function (l) { return !l.classList.contains("is-zero"); })
.map(function (l) {
return "- " + $(".lname", l).textContent.replace(/\s+/g, " ").trim() +
": " + $(".lcost", l).textContent.trim();
});
var summary = "Here is the estimate I built:\n" + lines.join("\n") +
"\n\nMonthly: " + $("#totalMonthly").textContent +
" (" + $("#totalAnnual").textContent + ")\n\nI'd like to talk it through.";
try { sessionStorage.setItem("acg-estimate", summary); } catch (e) {}
};
// 'click' covers keyboard + left-click; 'pointerdown' also catches
// middle-click / cmd-click / open-in-new-tab, which never fire 'click'.
sendBtn.addEventListener("click", storeEstimate);
sendBtn.addEventListener("pointerdown", storeEstimate);
}
form.addEventListener("input", recalc);
form.addEventListener("change", recalc);
form.addEventListener("submit", function (e) { e.preventDefault(); });
recalc();
}
/* ---- FAQ accordion (contact.html) ------------------------------------ */
$$(".faq__q").forEach(function (q, i) {
var panel = q.nextElementSibling;
var inner = panel ? panel.firstElementChild : null;
if (panel) {
var pid = "faq-a-" + (i + 1), qid = "faq-q-" + (i + 1);
panel.id = pid; q.id = qid;
panel.setAttribute("role", "region");
panel.setAttribute("aria-labelledby", qid);
q.setAttribute("aria-controls", pid);
}
q.addEventListener("click", function () {
var open = q.getAttribute("aria-expanded") === "true";
q.setAttribute("aria-expanded", String(!open));
if (panel) panel.style.maxHeight = open ? "0px" : (inner.scrollHeight + 8) + "px";
});
});
window.addEventListener("resize", function () {
$$(".faq__q").forEach(function (q) {
if (q.getAttribute("aria-expanded") === "true") {
var panel = q.nextElementSibling, inner = panel ? panel.firstElementChild : null;
if (panel && inner) panel.style.maxHeight = (inner.scrollHeight + 8) + "px";
}
});
});
/* ---- Contact form (contact.html) ------------------------------------- */
var contact = $("#contactForm");
if (contact) {
// Prefill from a calculator estimate, if one was just built.
var msg = $("#cf-msg");
if (msg) {
try {
var est = sessionStorage.getItem("acg-estimate");
if (est) {
msg.value = est;
sessionStorage.removeItem("acg-estimate");
var fn = $("#formNote");
if (fn) fn.textContent = "Your estimate is attached below. Add your name and we'll take it from there.";
// Bring the form into view and focus it so the handoff is visible.
if (contact.scrollIntoView) contact.scrollIntoView({ behavior: "smooth", block: "start" });
var nameField = $("#cf-name");
if (nameField) nameField.focus({ preventScroll: true });
}
} catch (e) {}
}
contact.addEventListener("submit", function (e) {
e.preventDefault();
var name = $("#cf-name"), contactField = $("#cf-contact"), note = $("#formNote");
if (!name.value.trim() || !contactField.value.trim()) {
note.textContent = "Please add your name and a phone or email so we can reach you.";
note.style.color = "var(--accent-ink)";
(name.value.trim() ? contactField : name).focus();
return;
}
note.textContent = "Thanks, " + name.value.trim().split(" ")[0] +
". In a live build this reaches our Tucson team. (Demo: nothing was sent.)";
note.style.color = "var(--good)";
contact.reset();
});
}
/* ---- Reveal on scroll ------------------------------------------------- */
var reveals = $$(".reveal");
if ("IntersectionObserver" in window && reveals.length) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting) { en.target.classList.add("in"); io.unobserve(en.target); }
});
}, { rootMargin: "0px 0px -8% 0px", threshold: 0.08 });
reveals.forEach(function (el) { io.observe(el); });
} else {
reveals.forEach(function (el) { el.classList.add("in"); });
}
})();

View File

@@ -0,0 +1,200 @@
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pricing | Arizona Computer Guru</title>
<meta name="description" content="Published, transparent IT pricing: GPS monitoring tiers, support plans, non-expiring block time, hosting, Microsoft 365, and business phones." />
<meta name="color-scheme" content="light dark" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@500;600;700&family=Lexend:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500;600;700&family=Fraunces:wght@400;500;600;700&family=Source+Sans+3:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500;600;700&family=Anton&family=Hanken+Grotesk:wght@400;500;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="css/styles.css" />
<script>(function(){try{var s=localStorage.getItem("acg-theme");var m=s||(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light");document.documentElement.setAttribute("data-theme",m);document.documentElement.setAttribute("data-skin",(localStorage.getItem("acg-skin")||"ledger"));document.documentElement.classList.add("js");}catch(e){}})();</script>
</head>
<body>
<a href="#main" class="sr-only">Skip to content</a>
<header class="site-header">
<div class="wrap">
<a class="brand" href="index.html" aria-label="Arizona Computer Guru home">
<span class="brand__mark" aria-hidden="true">ACG</span>
<span><span class="brand__name">Arizona Computer Guru</span><br /><span class="brand__since">Concierge IT &middot; Tucson &middot; since 2001</span></span>
</a>
<nav class="nav" aria-label="Primary">
<div class="nav__links" id="navLinks">
<a class="nav__link" href="index.html">Home</a>
<a class="nav__link" href="services.html">Services</a>
<a class="nav__link" href="pricing.html" aria-current="page">Pricing</a>
<a class="nav__link" href="calculator.html">Estimate</a>
<a class="nav__link" href="about.html">About</a>
<a class="nav__link" href="contact.html">Contact</a>
</div>
<a class="nav__phone" href="tel:+15203048300">520.304.8300</a>
<button class="skin-toggle" id="skinToggle" type="button" aria-label="Switch visual skin (Paper, Midnight, Verdigris, or Bold)" title="Switch skin: Paper / Midnight / Verdigris / Bold"><span class="sw" aria-hidden="true"></span></button>
<button class="theme-toggle" id="themeToggle" type="button" aria-pressed="false" aria-label="Switch to dark theme" title="Toggle light / dark"><span aria-hidden="true" data-theme-icon>&#9728;</span></button>
<button class="nav__toggle" id="navToggle" type="button" aria-expanded="false" aria-controls="navLinks" aria-label="Open menu">&#8801;</button>
</nav>
</div>
</header>
<main id="main">
<section class="page-head ledger">
<div class="wrap reveal">
<p class="eyebrow">Published, not "call for quote"</p>
<h1>Pricing with nothing hidden</h1>
<p class="lead">We think hiding prices is a red flag, in any IT company, including us. Here is the whole list. You pay for exactly what you have.</p>
</div>
</section>
<!-- GPS tiers -->
<section aria-labelledby="gps-h">
<div class="wrap">
<p class="section-tag">Per endpoint <span>/ monitoring &amp; protection</span></p>
<h2 id="gps-h">GPS monitoring tiers</h2>
<div class="rate-card" role="list" style="margin-top:var(--base);">
<div class="tier" role="listitem">
<h3 class="tier__name">GPS-Basic</h3>
<div class="tier__price">$19 <span class="per">/ endpoint / mo</span></div>
<p class="tier__blurb">Essential monitoring for small, simple environments.</p>
<ul>
<li class="is-base">Remote monitoring &amp; management</li>
<li class="is-base">Automated patch management</li>
<li class="is-base">Business-grade antivirus</li>
<li class="is-base">8&times;5 help desk</li>
<li class="is-base">Monthly health reports</li>
</ul>
</div>
<div class="tier tier--pop" role="listitem">
<div class="tier__flag">Most chosen</div>
<h3 class="tier__name">GPS-Pro</h3>
<div class="tier__price">$26 <span class="per">/ endpoint / mo</span></div>
<p class="tier__blurb">Comprehensive protection for growing businesses.</p>
<ul>
<li class="is-base">Everything in Basic</li>
<li>24&times;7 help desk</li>
<li>Advanced EDR &amp; email security</li>
<li>Dark-web monitoring</li>
<li>Backup &amp; disaster recovery</li>
<li>Security-awareness training</li>
</ul>
</div>
<div class="tier" role="listitem">
<h3 class="tier__name">GPS-Advanced</h3>
<div class="tier__price">$39 <span class="per">/ endpoint / mo</span></div>
<p class="tier__blurb">Enterprise-grade security and compliance.</p>
<ul>
<li class="is-base">Everything in Pro</li>
<li>Compliance management (HIPAA/SOC)</li>
<li>Ransomware rollback</li>
<li>Virtual CIO services</li>
<li>Priority response SLA</li>
<li>Dedicated account manager</li>
</ul>
</div>
</div>
<p class="muted" style="margin-top:1rem;font-size:0.9rem;">Add equipment monitoring (routers, switches, printers, NAS) for <span class="mono">$25/mo</span> up to 10 devices.</p>
</div>
</section>
<!-- Support + block -->
<section class="pricing" aria-labelledby="sup-h">
<div class="wrap">
<p class="section-tag">Labor <span>/ predictable rates</span></p>
<h2 id="sup-h">Support plans &amp; block time</h2>
<div class="table-wrap">
<table class="plan-table">
<caption>Support plans, bundled labor at a lower effective rate</caption>
<thead><tr><th scope="col">Plan</th><th class="num" scope="col">Monthly</th><th class="num" scope="col">Hours</th><th class="num" scope="col">Effective&nbsp;rate</th><th scope="col">Response</th></tr></thead>
<tbody>
<tr><td>Essential</td><td class="num">$200</td><td class="num">2</td><td class="num">$100/hr</td><td>Next business day</td></tr>
<tr class="pop"><td>Standard</td><td class="num">$380</td><td class="num">4</td><td class="num">$95/hr</td><td>8 hours</td></tr>
<tr><td>Premium</td><td class="num">$540</td><td class="num">6</td><td class="num">$90/hr</td><td>4 hours</td></tr>
<tr><td>Priority</td><td class="num">$850</td><td class="num">10</td><td class="num">$85/hr</td><td>2 hours, 24/7</td></tr>
</tbody>
</table>
</div>
<div class="table-wrap">
<table class="plan-table" style="margin-top:var(--base);">
<caption>Block time, pre-paid hours that never expire</caption>
<thead><tr><th scope="col">Block</th><th class="num" scope="col">Price</th><th class="num" scope="col">Effective&nbsp;rate</th><th scope="col">Notes</th></tr></thead>
<tbody>
<tr><td>10 hours</td><td class="num">$1,500</td><td class="num">$150/hr</td><td>Use anytime, no expiry</td></tr>
<tr><td>20 hours</td><td class="num">$2,600</td><td class="num">$130/hr</td><td>Bank &amp; draw down</td></tr>
<tr><td>30 hours</td><td class="num">$3,000</td><td class="num">$100/hr</td><td>Best per-hour value</td></tr>
</tbody>
</table>
</div>
<p class="muted" style="margin-top:1rem;font-size:0.9rem;">Off-plan labor is <span class="mono">$175/hr</span>. Plans and block time bring that down, and there is never a charge to find out what something will cost.</p>
</div>
</section>
<!-- Hosting / 365 / Phones -->
<section aria-labelledby="more-h">
<div class="wrap">
<p class="section-tag">Hosting, email &amp; phones <span>/ the rest of the stack</span></p>
<h2 id="more-h">Everything else, also published</h2>
<div class="price-cols">
<div class="price-block reveal">
<h3>Web hosting</h3>
<div class="table-wrap"><table class="plan-table">
<thead><tr><th scope="col">Tier</th><th class="num" scope="col">Monthly</th><th scope="col">Includes</th></tr></thead>
<tbody>
<tr><td>Starter</td><td class="num">$15</td><td>5&nbsp;GB, 1 site</td></tr>
<tr class="pop"><td>Business</td><td class="num">$35</td><td>25&nbsp;GB, 5 sites</td></tr>
<tr><td>Commerce</td><td class="num">$65</td><td>50&nbsp;GB, unlimited</td></tr>
</tbody>
</table></div>
</div>
<div class="price-block reveal">
<h3>Microsoft 365 &amp; email</h3>
<div class="table-wrap"><table class="plan-table">
<thead><tr><th scope="col">Plan</th><th class="num" scope="col">Per user</th><th scope="col">For</th></tr></thead>
<tbody>
<tr><td>Hosted email</td><td class="num">$2&ndash;$10</td><td>IMAP/POP, by storage</td></tr>
<tr><td>M365 Basic</td><td class="num">$7</td><td>Web apps, Teams</td></tr>
<tr class="pop"><td>M365 Standard</td><td class="num">$14</td><td>Desktop Office</td></tr>
<tr><td>M365 Premium</td><td class="num">$24</td><td>Security &amp; device mgmt</td></tr>
</tbody>
</table></div>
<p class="muted" style="font-size:0.84rem;margin-top:0.6rem;">Email security add-on: <span class="mono">$3</span>/mailbox.</p>
</div>
<div class="price-block reveal">
<h3>Business phones</h3>
<div class="table-wrap"><table class="plan-table">
<thead><tr><th scope="col">Tier</th><th class="num" scope="col">Per user</th><th scope="col">Highlights</th></tr></thead>
<tbody>
<tr><td>Basic</td><td class="num">$22</td><td>Unlimited US, app, e911</td></tr>
<tr class="pop"><td>Standard</td><td class="num">$28</td><td>Ring groups, recording</td></tr>
<tr><td>Pro</td><td class="num">$35</td><td>SMS, analytics, CRM</td></tr>
<tr><td>Call Center</td><td class="num">$55</td><td>Queues, supervisor tools</td></tr>
</tbody>
</table></div>
</div>
</div>
</div>
</section>
<section class="cta-band" aria-labelledby="cta-h">
<div class="wrap">
<div class="reveal">
<h2 id="cta-h">Add it up for your business</h2>
<p>Put in your numbers and watch the total. No email wall, no sales call required.</p>
</div>
<div class="cta-band__actions reveal">
<a class="btn btn--primary" href="calculator.html">Build an estimate <span class="arrow" aria-hidden="true">&rarr;</span></a>
<a class="btn btn--ghost" href="contact.html">Ask a question</a>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="wrap">
<p>&copy; <span id="year">2026</span> Arizona Computer Guru &middot; Protecting Tucson businesses since 2001</p>
<p><a href="tel:+15203048300">520.304.8300</a> &middot; <a href="mailto:info@azcomputerguru.com">info@azcomputerguru.com</a></p>
<p class="disclaimer">Local demonstration build of a proposed azcomputerguru.com (multipage). Pricing reflects published GPS rates and is illustrative for the estimator; a real quote is tailored to your environment. The contact form is not wired to a mailbox. Photography is representational.</p>
</div>
</footer>
<script src="js/app.js"></script>
</body>
</html>

View File

@@ -0,0 +1,143 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Arizona Computer Guru | IT that picks up</title>
<meta name="description" content="Concierge IT for Tucson since 2001. We go further, answer the phone, and fix it before you feel it. Transparent per-endpoint pricing, month-to-month." />
<meta name="color-scheme" content="dark" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Anton&family=Hanken+Grotesk:wght@400;500;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="css/radical.css" />
</head>
<body>
<a href="#main" class="sr-only">Skip to content</a>
<header class="rx-header">
<div class="wrap">
<a class="rx-brand" href="radical.html">ARIZONA <b>COMPUTER</b> GURU</a>
<nav class="rx-nav" aria-label="Primary">
<a href="#work">Work</a>
<a href="#pricing">Pricing</a>
<a href="#calc">Estimate</a>
<a href="#contact">Contact</a>
<a class="rx-cta-sm" href="tel:+15203048300">520.304.8300</a>
</nav>
</div>
</header>
<main id="main">
<!-- HERO -->
<section class="rx-hero">
<div class="rx-hero__art" aria-hidden="true"><img src="assets/images/hero.png" alt="" /></div>
<div class="wrap">
<p class="rx-hero__eyebrow">Tucson &middot; Managed IT &amp; Security &middot; Est. 2001</p>
<h1>IT that<br /><span class="hot">picks</span> <span class="stroke">up.</span></h1>
<p class="rx-hero__sub">Concierge IT for Tucson. We go further, we answer the phone, and we fix it before you feel it.</p>
<div class="rx-hero__cta">
<a class="rx-btn rx-btn--hot" href="#calc">Estimate your cost <span class="x" aria-hidden="true">&rarr;</span></a>
<a class="rx-btn rx-btn--out" href="#contact">Talk to a human</a>
</div>
</div>
</section>
<!-- MARQUEE -->
<div class="marquee" aria-hidden="true">
<div class="marquee__track">
<span>Since 2001</span><span>Local Tucson team</span><span>Month to month</span><span>No offshore</span><span>Onsite in 1-2 hrs</span><span>Published pricing</span>
<span>Since 2001</span><span>Local Tucson team</span><span>Month to month</span><span>No offshore</span><span>Onsite in 1-2 hrs</span><span>Published pricing</span>
</div>
</div>
<!-- WORK / SERVICES -->
<section class="rx-sec" id="work">
<span class="rx-ghost" aria-hidden="true">01</span>
<div class="wrap">
<p class="rx-kicker">What we run</p>
<h2 class="rx-h2">Everything, <span class="hot">handled.</span></h2>
<div class="rx-svc-list" style="margin-top:2.5rem;">
<article class="rx-svc rx-up"><div class="rx-svc__n">01</div><div><div class="rx-svc__name">Managed IT</div><p class="rx-svc__d">24/7 monitoring, automated patching, and a help desk that already knows your setup.</p></div><div class="rx-svc__meta">from $19/endpoint</div></article>
<article class="rx-svc rx-up"><div class="rx-svc__n">02</div><div><div class="rx-svc__name">Cybersecurity</div><p class="rx-svc__d">EDR, email security, dark-web monitoring, and training that catch what antivirus misses.</p></div><div class="rx-svc__meta">in GPS-Pro &amp; up</div></article>
<article class="rx-svc rx-up"><div class="rx-svc__n">03</div><div><div class="rx-svc__name">Backup &amp; Recovery</div><p class="rx-svc__d">Tested backups, offsite copies, ransomware rollback. A bad day stays a bad day, not a closure.</p></div><div class="rx-svc__meta">in GPS-Pro &amp; up</div></article>
<article class="rx-svc rx-up"><div class="rx-svc__n">04</div><div><div class="rx-svc__name">Microsoft 365</div><p class="rx-svc__d">Migrations, licensing, and email configured, secured, and supported by people you can call.</p></div><div class="rx-svc__meta">from $2/mailbox</div></article>
<article class="rx-svc rx-up"><div class="rx-svc__n">05</div><div><div class="rx-svc__name">Business Phones</div><p class="rx-svc__d">Cloud phone systems, mobile and desktop apps, free porting. No per-minute surprises.</p></div><div class="rx-svc__meta">from $22/user</div></article>
<article class="rx-svc rx-up"><div class="rx-svc__n">06</div><div><div class="rx-svc__name">Web &amp; Hosting</div><p class="rx-svc__d">Managed hosting with free SSL, daily backups, and a real human when something needs a hand.</p></div><div class="rx-svc__meta">from $15/mo</div></article>
</div>
</div>
</section>
<!-- PRICING -->
<section class="rx-sec" id="pricing">
<span class="rx-ghost" aria-hidden="true">$</span>
<div class="wrap">
<p class="rx-kicker">Published, not "call for quote"</p>
<h2 class="rx-h2">Per endpoint.<br />Per month. <span class="hot">No games.</span></h2>
<div class="rx-prices rx-up">
<div class="rx-price">
<div class="rx-price__tier">GPS-Basic</div>
<div class="rx-price__big">$19</div>
<span class="rx-price__u">/ endpoint / month</span>
<ul class="rx-price__list"><li>Monitoring &amp; patching</li><li>Antivirus</li><li>8x5 help desk</li></ul>
</div>
<div class="rx-price rx-price--pop">
<div class="rx-price__tier">GPS-Pro &mdash; most chosen</div>
<div class="rx-price__big">$26</div>
<span class="rx-price__u">/ endpoint / month</span>
<ul class="rx-price__list"><li>Everything in Basic</li><li>EDR + email security</li><li>Dark-web monitoring</li><li>Backup &amp; recovery</li></ul>
</div>
<div class="rx-price">
<div class="rx-price__tier">GPS-Advanced</div>
<div class="rx-price__big">$39</div>
<span class="rx-price__u">/ endpoint / month</span>
<ul class="rx-price__list"><li>Everything in Pro</li><li>Compliance (HIPAA/SOC)</li><li>Ransomware rollback</li><li>Virtual CIO</li></ul>
</div>
</div>
</div>
</section>
<!-- CALCULATOR TEASER -->
<section class="rx-sec rx-calc" id="calc">
<div class="wrap">
<div>
<p class="rx-kicker">No email wall</p>
<div class="rx-calc__big">Do the math <span class="hot">yourself.</span></div>
<p>Move the numbers, watch the total. The same math we would walk you through in person, with nothing hidden behind a "contact sales" button.</p>
</div>
<a class="rx-btn rx-btn--hot" href="calculator.html">Open the estimator <span class="x" aria-hidden="true">&rarr;</span></a>
</div>
</section>
<!-- CTA -->
<section class="rx-sec rx-cta" id="contact">
<div class="wrap">
<div class="rx-cta__big">Talk to a<br />human.</div>
<div class="rx-cta__row">
<a class="rx-btn rx-btn--hot" href="tel:+15203048300">520.304.8300</a>
<a class="rx-btn rx-btn--out" href="mailto:info@azcomputerguru.com">info@azcomputerguru.com</a>
</div>
</div>
</section>
</main>
<footer class="rx-footer">
<div class="wrap">
<p>&copy; <span id="yr">2026</span> Arizona Computer Guru &middot; Tucson since 2001</p>
<p><a href="tel:+15203048300">520.304.8300</a> &middot; <a href="mailto:info@azcomputerguru.com">info@azcomputerguru.com</a></p>
<p class="rx-note">Radical concept build of a proposed azcomputerguru.com. One dark, poster-scale direction; pricing reflects published GPS rates and is illustrative. This is a separate experiment from the Paper / Midnight / Verdigris skin set.</p>
</div>
</footer>
<script>
document.getElementById("yr").textContent = String(new Date().getFullYear());
(function () {
var els = [].slice.call(document.querySelectorAll(".rx-up"));
if (!("IntersectionObserver" in window) || !els.length) { els.forEach(function(e){e.classList.add("in");}); return; }
var io = new IntersectionObserver(function (en) {
en.forEach(function (e) { if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); } });
}, { threshold: 0.12, rootMargin: "0px 0px -6% 0px" });
els.forEach(function (e) { io.observe(e); });
})();
</script>
</body>
</html>

View File

@@ -0,0 +1,9 @@
# Serve the ACG multipage showcase locally, then open it.
# Usage: powershell -ExecutionPolicy Bypass -File serve.ps1 [port]
param([int]$Port = 4328)
$ErrorActionPreference = "Stop"
Set-Location -Path $PSScriptRoot
Write-Host "Arizona Computer Guru - showcase (multipage)" -ForegroundColor Yellow
Write-Host "Serving $PSScriptRoot at http://localhost:$Port/ (Ctrl+C to stop)"
Start-Process "http://localhost:$Port/"
if (Get-Command py -ErrorAction SilentlyContinue) { py -m http.server $Port } else { python -m http.server $Port }

View File

@@ -0,0 +1,108 @@
<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Services | Arizona Computer Guru</title>
<meta name="description" content="Managed IT, cybersecurity, backup, Microsoft 365, business phones, and hosting for Tucson businesses. One local partner, plainly laid out." />
<meta name="color-scheme" content="light dark" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Barlow+Condensed:wght@500;600;700&family=Lexend:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500;600;700&family=Fraunces:wght@400;500;600;700&family=Source+Sans+3:wght@400;500;600&family=IBM+Plex+Mono:wght@400;500;600;700&family=Anton&family=Hanken+Grotesk:wght@400;500;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="css/styles.css" />
<script>(function(){try{var s=localStorage.getItem("acg-theme");var m=s||(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light");document.documentElement.setAttribute("data-theme",m);document.documentElement.setAttribute("data-skin",(localStorage.getItem("acg-skin")||"ledger"));document.documentElement.classList.add("js");}catch(e){}})();</script>
</head>
<body>
<a href="#main" class="sr-only">Skip to content</a>
<header class="site-header">
<div class="wrap">
<a class="brand" href="index.html" aria-label="Arizona Computer Guru home">
<span class="brand__mark" aria-hidden="true">ACG</span>
<span><span class="brand__name">Arizona Computer Guru</span><br /><span class="brand__since">Concierge IT &middot; Tucson &middot; since 2001</span></span>
</a>
<nav class="nav" aria-label="Primary">
<div class="nav__links" id="navLinks">
<a class="nav__link" href="index.html">Home</a>
<a class="nav__link" href="services.html" aria-current="page">Services</a>
<a class="nav__link" href="pricing.html">Pricing</a>
<a class="nav__link" href="calculator.html">Estimate</a>
<a class="nav__link" href="about.html">About</a>
<a class="nav__link" href="contact.html">Contact</a>
</div>
<a class="nav__phone" href="tel:+15203048300">520.304.8300</a>
<button class="skin-toggle" id="skinToggle" type="button" aria-label="Switch visual skin (Paper, Midnight, Verdigris, or Bold)" title="Switch skin: Paper / Midnight / Verdigris / Bold"><span class="sw" aria-hidden="true"></span></button>
<button class="theme-toggle" id="themeToggle" type="button" aria-pressed="false" aria-label="Switch to dark theme" title="Toggle light / dark"><span aria-hidden="true" data-theme-icon>&#9728;</span></button>
<button class="nav__toggle" id="navToggle" type="button" aria-expanded="false" aria-controls="navLinks" aria-label="Open menu">&#8801;</button>
</nav>
</div>
</header>
<main id="main">
<section class="page-head page-head--img ledger">
<div class="wrap">
<div class="reveal">
<p class="eyebrow">What we do</p>
<h1>Services, laid out plainly</h1>
<p class="lead">One local partner for the whole stack: the computers, the security, the phones, the email, and the people who keep it all running. No bundles you do not need.</p>
</div>
<figure class="reveal">
<img src="assets/images/services.png" width="1248" height="832" alt="A tidy wall-mounted patch panel and small network switch with neatly dressed cables on a wooden shelf in warm light." />
</figure>
</div>
</section>
<section aria-label="All services">
<div class="wrap">
<div class="svc-group">
<h2 class="svc-group__title">Run your systems</h2>
<div class="svc-list">
<article class="svc reveal"><div class="svc__no">01</div><div><h3 class="svc__name">Managed IT (GPS)</h3><p class="svc__desc">Guru Protection Services keeps every endpoint healthy: 24/7 monitoring, automated patching, and a help desk that already knows your setup. You get monthly health reports showing what we fixed before it broke.</p></div><div class="svc__meta">from&nbsp;$19&nbsp;/endpoint</div></article>
<article class="svc reveal"><div class="svc__no">02</div><div><h3 class="svc__name">Backup &amp; Recovery</h3><p class="svc__desc">Tested backups, offsite copies, and ransomware rollback. A bad day becomes an inconvenience instead of a closure, and we prove the restores actually work.</p></div><div class="svc__meta">in GPS-Pro &amp; up</div></article>
<article class="svc reveal"><div class="svc__no">03</div><div><h3 class="svc__name">Projects &amp; Block Time</h3><p class="svc__desc">Migrations, network buildouts, new-office setups, and one-off work, billed against pre-paid hours that never expire. Bank the hours, draw them down when you need them.</p></div><div class="svc__meta">from&nbsp;$100&nbsp;/hr</div></article>
</div>
</div>
<div class="svc-group">
<h2 class="svc-group__title">Protect your business</h2>
<div class="svc-list">
<article class="svc reveal"><div class="svc__no">04</div><div><h3 class="svc__name">Cybersecurity</h3><p class="svc__desc">Advanced EDR, email security, dark-web monitoring, and security-awareness training. Basic antivirus alone is not enough anymore, so we do not pretend it is.</p></div><div class="svc__meta">in GPS-Pro &amp; up</div></article>
<article class="svc reveal"><div class="svc__no">05</div><div><h3 class="svc__name">vCIO &amp; Compliance</h3><p class="svc__desc">Strategy, budgeting, and cyber-insurance readiness. We help you check the boxes auditors and insurers ask for (HIPAA, SOC, MFA, documented backups) and hand you the paperwork.</p></div><div class="svc__meta">in GPS-Advanced</div></article>
</div>
</div>
<div class="svc-group">
<h2 class="svc-group__title">Connect your people</h2>
<div class="svc-list">
<article class="svc reveal"><div class="svc__no">06</div><div><h3 class="svc__name">Microsoft 365 &amp; Email</h3><p class="svc__desc">Migrations, Business Standard or Premium licensing, or budget-friendly hosted email. Configured, secured, and supported by people you can call.</p></div><div class="svc__meta">from&nbsp;$2&nbsp;/mailbox</div></article>
<article class="svc reveal"><div class="svc__no">07</div><div><h3 class="svc__name">Business Phones (GPS-Voice)</h3><p class="svc__desc">Cloud phone systems with mobile and desktop apps, auto-attendants, and free number porting for managed clients. Four tiers, no per-minute surprises.</p></div><div class="svc__meta">from&nbsp;$22&nbsp;/user</div></article>
<article class="svc reveal"><div class="svc__no">08</div><div><h3 class="svc__name">Web &amp; Email Hosting</h3><p class="svc__desc">Managed hosting with free SSL, daily backups, and a real person (not a ticket robot) when something needs a hand.</p></div><div class="svc__meta">from&nbsp;$15&nbsp;/mo</div></article>
</div>
</div>
</div>
</section>
<section class="cta-band" aria-labelledby="cta-h">
<div class="wrap">
<div class="reveal">
<h2 id="cta-h">See what it costs</h2>
<p>Every service above is on our published price list. Build your own estimate, or read the full breakdown.</p>
</div>
<div class="cta-band__actions reveal">
<a class="btn btn--primary" href="calculator.html">Estimate your cost <span class="arrow" aria-hidden="true">&rarr;</span></a>
<a class="btn btn--ghost" href="pricing.html">Full pricing</a>
</div>
</div>
</section>
</main>
<footer class="site-footer">
<div class="wrap">
<p>&copy; <span id="year">2026</span> Arizona Computer Guru &middot; Protecting Tucson businesses since 2001</p>
<p><a href="tel:+15203048300">520.304.8300</a> &middot; <a href="mailto:info@azcomputerguru.com">info@azcomputerguru.com</a></p>
<p class="disclaimer">Local demonstration build of a proposed azcomputerguru.com (multipage). Pricing reflects published GPS rates and is illustrative for the estimator; a real quote is tailored to your environment. The contact form is not wired to a mailbox. Photography is representational.</p>
</div>
</footer>
<script src="js/app.js"></script>
</body>
</html>

View File

@@ -0,0 +1,11 @@
# Serve the ACG website showcase locally, then open it.
# Usage: powershell -ExecutionPolicy Bypass -File serve.ps1 [port]
param([int]$Port = 4327)
$ErrorActionPreference = "Stop"
Set-Location -Path $PSScriptRoot
Write-Host "Arizona Computer Guru - showcase" -ForegroundColor Yellow
Write-Host "Serving $PSScriptRoot at http://localhost:$Port/ (Ctrl+C to stop)"
Start-Process "http://localhost:$Port/"
# Prefer the launcher 'py'; fall back to python.
$pyCmd = (Get-Command py -ErrorAction SilentlyContinue)
if ($pyCmd) { py -m http.server $Port } else { python -m http.server $Port }

View File

@@ -0,0 +1,175 @@
## User
- **User:** Mike Swanson (mike)
- **Machine:** GURU-5070
- **Role:** admin
## Session Summary
Built a new local showcase website for Arizona Computer Guru (ACG) from scratch as a
"what we can do" test piece, driven by a multi-AI design pipeline (Grok for imagery and
design origination, Gemini for design-weight decisions and review, the Grok+Gemini
"quorum" for code/design review, the `impeccable` skill for craft + critique). All work
lives in `projects/acg-website-showcase/`. Nothing was deployed; everything is served
locally via `py -m http.server`.
Phase 1 produced a single-page flagship in the "Sonoran Ledger" art direction (warm cream
paper, bookkeeper's-ledger rhythm, mono numerals, burnt-amber accent). The direction was
chosen by a Grok+Gemini panel (both ranked it #1 over "Desert Threshold" and "The
Workbench") and confirmed by Mike. Grok generated four photographic assets (hero desk +
ledger, stucco windowsill, trust still-life, paper texture). The site (self-contained
HTML/CSS/vanilla-JS) shipped a light+dark theme, an interactive IT-cost calculator
(defaults compute to $952/mo = 22 endpoints x $26 + $380 Standard support), services,
published GPS pricing, FAQ, and contact. Grok+Gemini reviewed it; all real defects were
fixed (mobile nav hamburger, calculator clamp/handoff, AA contrast on small text, table
overflow, 44px touch targets, FAQ ARIA, theme FOUC). Gemini's vision model gave a final
polish pass. This single-page version was then PARKED at Mike's request (kept as a reusable
pattern for other Guru-related sites).
Phase 2 built the multipage version (Home, Services, Pricing, Calculator, About, Contact)
using `impeccable` craft (brand register, authored PRODUCT.md + DESIGN.md, enforced house
laws the single-page broke: no em dashes, no side-stripe accent borders, semantic
headings, imagery on every page). Three more Grok images were generated (patch panel,
handshake, Tucson storefront). The Grok+Gemini quorum code-reviewed all 8 files; fixes
applied (real semantic h1/h2/h3, AA contrast both themes, no-JS `.reveal` fallback, mobile
nav overflow, cross-page calculator->contact estimate handoff via sessionStorage incl.
middle-click via pointerdown, narrow-phone calc stacking, footer unification). Mike
declared multipage the winner for ACG, single-page kept for other sites.
Phase 3 explored alternate themes because Mike disliked the warm "paper" aesthetic. The
token-based design system was extended into a live skin switcher. "Midnight Concierge"
(cool dark-premium slate) was added as skin #2. Mike still felt it was "the paper
aesthetic" (the shared calm/editorial STRUCTURE, not just color). Grok was then asked to
originate a fresh design: it produced "Verdigris Gate" (oxidized-copper green on plaster,
Fraunces serif + Source Sans 3 + IBM Plex Mono, enamel double-rule frames, gate-rail
header), built as skin #3 with its own cool documentary photography and per-skin image
swap. Run through impeccable + the quorum; fixes applied (font weights 600/700, ink-3 AA on
surface-2, white-on-teal button, image de-dup, stale label).
Phase 4: Mike said "be radical." Built a standalone poster-brutalist concept ("Blowout",
`radical.html` + `radical.css`): Anton 13rem type, signal-orange drench, scrolling marquee,
blowout pricing numerals, full-bleed orange CTA. Ran it through the quorum + impeccable;
consensus was it overshoots trust for a B2B MSP. Mike said "dial it back a little, stick
with multipage." The dial-back was implemented as a fourth skin "Bold" on the existing
multipage (reuses all accessible markup/calculator/nav): Anton uppercase headlines capped
at ~5.5rem, orange restricted to accents + buttons + one highlight, no marquee (uses the
multipage's real trust strip), moderate pricing numerals, grayscale documentary photos,
thick 2px brutalist borders, light+dark. The standalone full-radical page is retained for
comparison.
End state: the multipage site has a 4-skin switcher (Paper / Midnight / Verdigris / Bold),
each in light+dark, served at http://localhost:4328/. The single-page Sonoran Ledger lives
at the project root (port 4327). All skins are scoped by `html[data-skin=...]`; none break
the others.
## Key Decisions
- **Self-contained static (HTML/CSS/vanilla-JS), no framework.** Chosen for trivial local
serving and zero deploy friction for a showcase. Calculator + theme + skins all vanilla.
- **Token-based design system from the start.** Made the later skin system (4 themes x
light/dark) a CSS-variable swap rather than a rebuild. Skins scoped to `html[data-skin]`
so they never collide (verified by the quorum).
- **Skin switcher over separate builds.** When Mike wanted alternate looks, added a header
swatch button cycling skins (persisted in localStorage, set before first paint to avoid
FOUC) instead of forking the site per theme.
- **"Dial back" implemented as a skin on the multipage, not a 6-page rebuild.** "Stick with
multipage" + "dial back" = make the tempered radical a 4th skin; the calm structure
inherently tempers the poster radicalism and reuses all accessible markup.
- **No fabricated testimonials.** The prior azcomputerguru.com prototype was flagged for
placeholder named testimonials; this build deliberately omits them and builds proof from
verifiable facts. Contact form is a demo (not wired to a mailbox); disclaimers say so.
- **impeccable house laws enforced on the multipage** (no em dashes, no side-stripe accent
borders, semantic headings) to make it a genuinely distinct variant from the single-page.
- **Pricing/content sourced from `projects/msp-pricing` + the MSP Buyers Guide**, not
invented (GPS $19/$26/$39, support plans, block time, hosting/365/VoIP, brand voice).
## Problems Encountered
- **Grok image text rendering:** the first Verdigris "about" image rendered a legible
invented business name ("ACE Refrigeration") on an enamel sign, unusable on an ACG page.
Regenerated as a text-free vintage service counter (parts drawers + brass bell + logbook).
- **Headless screenshot hangs:** an early multi-shot Edge batch stalled (~profile lock);
resolved by `taskkill //F //IM msedge.exe` between shots and a per-shot `timeout 50`.
Edge `--screenshot` needs an ABSOLUTE Windows path (via `cygpath -w`) or it silently fails.
- **Dark-theme screenshots:** headless has no stored localStorage; forced dark via
`--blink-settings=preferredColorScheme=0`, and forced a skin via a tiny `_seed.html` that
sets `localStorage` then `location.replace`s to the target page.
- **Gemini vision (`image-analyze`) defaulted to OCR** on the rendered pages twice instead
of giving a design verdict; relied on own design-director read + the two code reviewers.
- **impeccable deterministic detector unavailable** on this machine (`detect.mjs` bundle not
present); used the Grok+Gemini quorum as the two independent assessments instead.
- **perl em-dash strip needed `-CSD`** (UTF-8) flags; first pass missed the multi-byte char.
- **Quorum-found defects (all fixed):** faux-bold from loading Fraunces/IBM Plex Mono at
400;500 but using 600/700; `--ink-3` failing AA on the surface-2 header band; light
primary-button contrast (~4.4:1) fixed with white on-accent; `.reveal` hiding content if
JS fails (gated on a `.js` class).
## Configuration Changes
New project tree `projects/acg-website-showcase/`:
Single-page (parked, port 4327):
- `index.html`, `css/styles.css`, `js/app.js`
- `assets/images/{hero,story,trust,paper}.png` (Grok-generated)
- `DESIGN.md`, `README.md`, `serve.ps1`
- `design/` — pipeline artifacts (Grok/Gemini prompts+responses, reviews, screenshots)
Multipage (winner, port 4328) `multipage/`:
- `index.html`, `services.html`, `pricing.html`, `calculator.html`, `about.html`, `contact.html`
- `css/styles.css` (Sonoran Ledger system + Midnight + Verdigris + Bold skins),
`css/radical.css` (standalone Blowout concept)
- `js/app.js` (theme, 4-skin switcher + per-skin image swap, calculator, FAQ, mobile nav, reveal)
- `radical.html` (standalone full-radical poster concept)
- `assets/images/` (Sonoran/warm set) + `assets/images/verdigris/` (cool documentary set)
- `PRODUCT.md`, `DESIGN.md`, `serve.ps1`, `design/` (prompts, quorum reviews, screenshots)
No changes outside the project dir. (Pre-existing unrelated working-tree edits to
`.claude/memory/MEMORY.md`, `errorlog.md`, `wiki/clients/peaceful-spirit.md`,
`.claude/skills/remediation-tool/scripts/get-token.sh` were present at session start and
left untouched.)
## Credentials & Secrets
None created, discovered, or used. The contact forms are non-functional demos. No vault
operations.
## Infrastructure & Servers
- Local static preview only: `py -m http.server 4327` (single-page root),
`py -m http.server 4328` (multipage). Launch helpers: `serve.ps1` in each dir.
- Grok CLI (`grok` skill, GURU-5070) used for image generation + design origination.
- Gemini CLI (`agy` skill, GURU-5070) used for design-weight + reviews.
- No production hosting touched. The real ACG site (Astro) remains at
`clients/azcomputerguru.com/` and on IX Web Hosting — NOT modified.
## Commands & Outputs
- Image gen: `bash .claude/skills/grok/scripts/ask-grok.sh image "<prompt>" <out.png>`
- Text/design: `bash .claude/skills/grok/scripts/ask-grok.sh text --prompt-file <f>`
- Gemini review: `bash .claude/skills/agy/scripts/ask-gemini.sh review-files -i "<instr>" <files...>`
- Headless screenshot (works): Edge `--headless=new --screenshot="$(cygpath -w OUT)"
--window-size=W,H --virtual-time-budget=6000`; dark via `--blink-settings=preferredColorScheme=0`.
- Calculator math verified: default 22 endpoints x $26 (GPS-Pro) + $380 (Standard) = **$952/mo**.
- `node --check js/app.js` clean after every JS change. All 6 multipage pages return HTTP 200.
## Pending / Incomplete Tasks
- **Decision pending:** Mike to react to the "Bold" skin (right amount of dial-back, or
nudge orange/type). Open option to run the quorum + impeccable on the Bold skin itself
(prior reviews were on the un-tamed radical).
- Lock a final direction, then this becomes the candidate for the real azcomputerguru.com
redesign (would need: real testimonials/proof, a working contact backend, light variant
polish, and migration off the demo disclaimers).
- Not started: deploy/build pipeline (intentionally local-only for now).
- Single-page Sonoran Ledger retained as a reusable pattern for other Guru-related sites.
## Reference Information
- Project root: `D:/ClaudeTools/projects/acg-website-showcase/`
- Single-page: `index.html` (Sonoran Ledger, parked) — serve on :4327
- Multipage: `multipage/` (4-skin: Paper/Midnight/Verdigris/Bold) — serve on :4328
- Full-radical concept: `multipage/radical.html`
- Design pipeline artifacts + quorum reviews: `*/design/*.md`
- Pricing source: `projects/msp-pricing/` + `projects/msp-tools/quote-wizard/frontend/src/lib/pricing-data.ts`
- Brand voice source: `projects/msp-pricing/marketing/MSP-Buyers-Guide-Content.md`
- Prior/real ACG site article: `wiki/clients/azcomputerguru.com.md` (Astro, separate)
- ACG contact (from materials): 520.304.8300, info@azcomputerguru.com, 7437 E. 22nd St, Tucson AZ 85710