Solanasis Website — Claude Build Plan
What this is: The step-by-step execution plan for Claude Code to follow when building the Solanasis website. This is designed to be handed directly to Claude Code after the human completes
01-human-setup-guide.md.Prerequisites: All items in
01-human-setup-guide.mdmust be completed. The.envfile must exist in the project root with valid credentials.Single source of truth: The
.envfile in the project root contains ALL project configuration and credentials. Never hardcode values that exist in.env— always read them from there.Reference docs (all in the workspace directory
C:\Users\zasya\Documents\_solanasis\):
website-config/ai-native-website-building-guide-v2.md— the full strategy guidesolanasis-credentials-security-playbook.md— credential architecture and security detailssolanasis-website-execution-plan.md— execution plan with clarifying questions and additional suggestionswebsite-config/03-content-creation-strategy.md— blog content strategy, post structure, tone, and first blog post briefwebsite-config/decisions-log.md— all build decisions and user answers (created during preflight review)brand-style/Matchkeyz_Brand_Style_Guide.md— comprehensive design system with CSS custom properties, button styles, form styles, accessibility ratiosDirectory paths:
- Project build directory:
C:\Users\zasya\Documents\solanasis-site— this is where the Astro project lives and all code is written- Workspace directory (brand assets):
C:\Users\zasya\Documents\_solanasis— contains logos, copy, playbooks, brand style, and these config docsImportant: When the plan says “project root” it means the build directory (
solanasis-site). When it says “workspace directory” it means the_solanasisfolder. Claude Code mustcdto the project build directory before starting Phase 1.
Build Checklist
Phase 0: Environment Verification (5 min)
Goal: Confirm ALL tools, credentials, and project configuration are working before writing any code. DO NOT SKIP ANY STEP. If any check fails, STOP and troubleshoot before proceeding. Do not attempt to work around a failure.
Platform note: This plan is designed to work on Windows (Git Bash), macOS, and Linux. Where commands differ by platform, alternatives are noted.
0.1 — Load and Validate the .env File
-
0.1.1 Confirm
.envfile exists in the project root- If missing → STOP. Tell the user: “No .env file found. Please follow 01-human-setup-guide.md Phase 6 to create it from sample.env.”
-
0.1.2 Source the
.envfile so variables are available to subsequent commands:# Method: while-read loop (works on Windows Git Bash, macOS, Linux) # Process substitution (source <(...)) does NOT work reliably on Windows Git Bash while IFS='=' read -r key value; do [[ "$key" =~ ^[[:space:]]*# ]] && continue [[ -z "$key" ]] && continue key=$(echo "$key" | tr -d '\r' | xargs) value=$(echo "$value" | tr -d '\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') export "$key=$value" done < .envWhy: The
.envfile is not automatically loaded into the shell. Allcurland validation commands in Phase 0 reference these variables (e.g.,$BREVO_API_KEY), so they must be sourced first. Thetr -d '\r'handles Windows line endings (CRLF → LF). Note:source <(...)process substitution fails on Windows Git Bash — always use the while-read approach. -
0.1.3 Verify ALL of the following variables are present and non-empty:
Project Configuration (non-secret):
GITHUB_USERNAME— a GitHub username string (no spaces, no@)GITHUB_REPO_NAME— a repo name string (lowercase, hyphens OK, no spaces)SITE_URL— a full URL starting withhttps://(e.g.,https://solanasis.com)SITE_DOMAIN— a domain name without protocol (e.g.,solanasis.com)SITE_NAME— a human-readable name string (e.g.,Solanasis)CLOUDFLARE_PAGES_PROJECT— a project name string (lowercase, hyphens OK)
Credentials:
CLOUDFLARE_ACCOUNT_ID— a hex string (32 characters)BREVO_API_KEY— MUST start withxkeysib-BREVO_LIST_ID— MUST be a numberBREVO_SENDER_EMAIL— MUST be a valid email addressBREVO_SENDER_NAME— a stringTURNSTILE_SITE_KEY— MUST start with0x4TURNSTILE_SECRET_KEY— MUST start with0x4
Optional (needed for CI/CD in Phase 8, not required now):
CLOUDFLARE_API_TOKEN— if present, MUST be a non-empty string (used as a GitHub Secret for automated deployments)
-
0.1.4 If ANY required variable is missing, has a placeholder value (contains
PASTE_YOUR), or fails format validation → STOP. Tell the user exactly which variable(s) need to be filled in and reference the relevant phase in01-human-setup-guide.md.
0.2 — Verify CLI Tools Are Installed
- 0.2.1 Run
node --version→ must be v18+- If missing or too old → install Node.js LTS: download from nodejs.org or use
nvm install --lts
- If missing or too old → install Node.js LTS: download from nodejs.org or use
- 0.2.2 Run
npm --version→ must be v8+- Should come with Node.js. If missing → something went wrong with Node install.
- 0.2.3 Run
git --version→ must be available- If missing →
sudo apt install git(Linux),brew install git(macOS), or download from git-scm.com
- If missing →
- 0.2.4 Run
gh --version→ must be available- If missing → install:
brew install gh(macOS),sudo apt install gh(Linux),winget install GitHub.cli(Windows)
- If missing → install:
- 0.2.5 Run
npx wrangler --version→ must be available- If missing →
npm install -g wrangler
- If missing →
0.3 — Verify Authentication
- 0.3.1 Run
gh auth status→ must show “Logged in to github.com as [username]”- If not logged in → STOP. Tell the user: “Please run
gh auth loginand follow the browser prompts. See 01-human-setup-guide.md Phase 5.1.”
- If not logged in → STOP. Tell the user: “Please run
- 0.3.2 Verify the GitHub username from
gh auth statusmatchesGITHUB_USERNAMEin.env- If they don’t match → STOP. Ask the user which is correct and update
.envaccordingly.
- If they don’t match → STOP. Ask the user which is correct and update
- 0.3.3 Run
npx wrangler whoami→ must show an account name and account ID- Wrangler can authenticate via browser login (
wrangler login) OR via theCLOUDFLARE_API_TOKENenvironment variable. Either is fine. - If using API token: the
.envsourcing in 0.1.2 will have setCLOUDFLARE_API_TOKENin the shell, so wrangler will use that automatically. - If not logged in by either method → STOP. Tell the user: “Please run
wrangler loginand follow the browser prompts, or ensure CLOUDFLARE_API_TOKEN is set in .env. See 01-human-setup-guide.md Phase 5.2.”
- Wrangler can authenticate via browser login (
- 0.3.4 Verify the Account ID from
wrangler whoamimatchesCLOUDFLARE_ACCOUNT_IDin.env- If they don’t match → STOP. Ask the user which is correct and update
.envaccordingly.
- If they don’t match → STOP. Ask the user which is correct and update
0.4 — Verify External Services
-
0.4.1 Test Brevo API key — run:
curl -s -X GET "https://api.brevo.com/v3/account" \ -H "accept: application/json" \ -H "api-key: $BREVO_API_KEY"- Must return JSON with
"email"field (confirms the key is valid) - If 401 error → STOP. Tell the user: “Your Brevo API key is invalid. Please generate a new one: app.brevo.com → SMTP & API → API Keys.”
- Must return JSON with
-
0.4.2 Test Brevo List ID — run:
curl -s -X GET "https://api.brevo.com/v3/contacts/lists/$BREVO_LIST_ID" \ -H "accept: application/json" \ -H "api-key: $BREVO_API_KEY"- Must return JSON with
"name"field (confirms the list exists) - If 404 error → STOP. Tell the user: “Brevo list ID $BREVO_LIST_ID not found. Check the list ID at app.brevo.com → Contacts → Lists.”
- Must return JSON with
-
0.4.3 Test Brevo email sending — send a test email:
curl -s -X POST "https://api.brevo.com/v3/smtp/email" \ -H "accept: application/json" \ -H "api-key: $BREVO_API_KEY" \ -H "content-type: application/json" \ -d '{ "sender": {"name": "'$BREVO_SENDER_NAME'", "email": "'$BREVO_SENDER_EMAIL'"}, "to": [{"email": "'$BREVO_SENDER_EMAIL'", "name": "Test"}], "subject": "Solanasis Website Build — Preflight Test", "htmlContent": "<h2>Preflight Test Passed</h2><p>Brevo email delivery is working. This was sent during the website build preflight check.</p>" }'- Must return JSON with
"messageId"field - If error → STOP. Likely cause: sender email not verified in Brevo. Tell the user: “Brevo cannot send from $BREVO_SENDER_EMAIL. Verify this sender at app.brevo.com → Senders, Domains & Dedicated IPs → Senders.”
- Must return JSON with
-
0.4.4 Test DNS — run:
# Use nslookup (works on Windows, macOS, Linux) — dig is not available on Windows nslookup -type=NS $SITE_DOMAIN- Must show Cloudflare nameservers (*.ns.cloudflare.com) in the output
- If non-Cloudflare nameservers → STOP. Tell the user: “Domain $SITE_DOMAIN is not pointing to Cloudflare yet. See 01-human-setup-guide.md Phase 2.”
0.5 — Preflight Summary
- 0.5.1 Print a summary report to the user:
✅ PREFLIGHT CHECK COMPLETE ───────────────────────────── Project: $SITE_NAME Domain: $SITE_DOMAIN Site URL: $SITE_URL GitHub: $GITHUB_USERNAME/$GITHUB_REPO_NAME Cloudflare: $CLOUDFLARE_PAGES_PROJECT (Account: $CLOUDFLARE_ACCOUNT_ID) Brevo: API key valid, List #$BREVO_LIST_ID exists, sending from $BREVO_SENDER_EMAIL Turnstile: Site key and secret key loaded DNS: $SITE_DOMAIN → Cloudflare nameservers ✓ Test email: Sent to $BREVO_SENDER_EMAIL ✓ Ready to proceed with Phase 1: Project Scaffolding - 0.5.2 Wait for user confirmation before proceeding (give them a chance to check the test email arrived)
If all checks pass and user confirms → proceed to Phase 1.
Phase 1: Project Scaffolding (10 min)
Goal: Clone the theme, install dependencies, confirm it builds and runs locally.
Theme choice: This plan uses Astroplate (
https://github.com/zeon-studio/astroplate) — a free, MIT-licensed Astro + Tailwind CSS + TypeScript starter with pre-built pages (hero, features, blog, contact). If the user has purchased the Looka premium theme instead, substitute the Looka download path for the clone URL below and skip step 1.2 (copy Looka’s extracted files into the project directory instead).
-
1.1 Check if the project directory already has files:
- If it contains only
.env(and maybesample.env) → directory is ready, proceed with cloning - If it already contains an Astro project (has
package.jsonwith astro dependency) → skip cloning, go to 1.3 - If it contains other files → STOP. Ask the user if it’s safe to proceed.
- If it contains only
-
1.2 Clone Astroplate into the project directory:
# The directory already has .env in it, so clone to a temp location first git clone https://github.com/zeon-studio/astroplate "$TEMP/astro-theme" # Move everything (including dotfiles) into the project dir, preserving .env cp -r "$TEMP/astro-theme"/* "$TEMP/astro-theme"/.* . 2>/dev/null rm -rf "$TEMP/astro-theme"Note:
$TEMPworks on Windows (Git Bash) and can be set on macOS/Linux (export TEMP=/tmpif not already set). Thecp -r+rm -rfapproach avoids issues withmvacross filesystems. -
1.3 Remove the theme’s
.gitdirectory and initialize a fresh repo:rm -rf .git git init -
1.4 Run
npm install— install all dependencies -
1.5 Run
npm run build— confirm the theme builds without errors -
1.6 Run
npm run previewin the background — confirm the theme serves locally (typicallyhttp://localhost:4321):npm run preview & # Wait a few seconds, then test that it responds: curl -s -o /dev/null -w "%{http_code}" http://localhost:4321 # Should return 200 -
1.7 Stop the preview server:
kill %1 2>/dev/null || true -
1.8 Create
.gitignore(merge with existing if theme provides one):node_modules/ dist/ .env .env.* .dev.vars .wrangler/ .astro/ .DS_Store sample.envCritical:
.envand.dev.varsMUST be in.gitignore. Verify this. If the theme’s.gitignoredoesn’t include them, add them. This is a security requirement from the credentials playbook. -
1.9 Initial commit:
git add . git commit -m "Initial scaffold from Astroplate theme"
Phase 2: Foundation Files + Brand Assets (20 min)
Goal: Set up the design system from existing brand assets, copy logos into the project, and create the project conventions that ensure consistent output.
Brand assets source directory:
C:\Users\zasya\Documents\_solanasis(referred to as “workspace directory” throughout this plan). Key paths:
logo/Solanasis - Logo - Squared.png— full logo with triangle icon + wordmark + tagline (1000x1000px, transparent background)logo/Solanasis - Logo Horizontal.png— wordmark only with rules above/below (for nav bars)logo/Solanasis - Logo Icon.png— triangle icon only (for favicons, app icons)marketing_assets/banners/solanasis LinkedIn Banner.png— dark navy banner with wordmarkbrand-style/Matchkeyz_Brand_Style_Guide.md— comprehensive design system (CSS vars, buttons, forms, typography, accessibility)website-content/solanasis_site_copy_v3_smartcuts.md— approved website copy (Version 3: Smartcuts/High-Leverage) with FAQ sectionAbout Me for AI.md— founder bio (use “About me for LinkedIn Bio” section for the About page), image preferences, social linksAbout Solanasis.md— company info, taglines, legal details, phone numberwebsite-config/decisions-log.md— all build decisions (email address, services architecture, stock photos, etc.)
2.1 — Copy Brand Assets into the Project
-
2.1.1 Create the assets directory structure:
public/ ├── images/ │ ├── logo/ │ │ ├── solanasis-logo-squared.png │ │ ├── solanasis-logo-horizontal.png │ │ └── solanasis-logo-icon.png │ ├── blog/ (for blog post images) │ └── stock/ (for stock photos) └── favicon/ (generated in Phase 3.12) -
2.1.2 Copy the logo files from the workspace directory into
public/images/logo/:- Rename to web-friendly filenames (lowercase, hyphens, no spaces)
Solanasis - Logo - Squared.png→solanasis-logo-squared.pngSolanasis - Logo Horizontal.png→solanasis-logo-horizontal.pngSolanasis - Logo Icon.png→solanasis-logo-icon.png
-
2.1.3 Generate optimized versions if possible:
- Install
sharpif not already a dependency:npm install sharp(Astro uses sharp internally for image optimization, so it may already be installed) - Create WebP versions of each logo: use
sharpin a one-off Node.js script or use Astro’s built-in<Image>component (which auto-generates WebP at build time) - Resize the squared logo to standard OG image size (1200x630) for social sharing — pad with parchment (#FEF9F1) background
- If
sharpis problematic on the platform, skip this step — Astro’s<Image>component handles WebP conversion at build time automatically
- Install
2.2 — Create design-tokens.md
-
Create
design-tokens.mdin the project root using Palette P01: Navy + Parchment + Copper (the selected palette):Color System:
Primary: #020532 (deep navy — headers, nav, hero backgrounds) Secondary: #091652 (medium navy — subheadings, accents, links) Accent: #C47A3D (copper — CTAs, buttons, highlights, hover states) Background: #FEF9F1 (warm parchment — page background) Text: #111827 (near-black — body text) Text Muted: #6B7280 (gray — captions, meta text, secondary content) Link: #091652 (secondary navy) Link Hover: #C47A3D (copper) Border: #E5E0D8 (warm gray — cards, dividers) Card BG: #FFFFFF (white — card backgrounds for contrast against parchment)Gradient (for hero sections):
Hero gradient: linear-gradient(135deg, #020532 0%, #091652 65%, #C47A3D 130%)CTA Button:
Background: #C47A3D (copper) Text: #0B1220 (dark — high contrast on copper) Hover: darken 10% → #A8652FTypography:
- Font family:
Inter(load from Google Fonts — already used in the palette chooser and matches logo aesthetic) - Heading weights: 600 (semibold) for H2/H3, 700 (bold) for H1
- Body weight: 400 (regular)
- Body size: 16px / 1.6 line height
- Font scale: 1rem, 1.125rem, 1.25rem, 1.5rem, 1.875rem, 2.25rem, 3rem
Spacing scale: 4px, 8px, 12px, 16px, 24px, 32px, 48px, 64px, 96px
Border radius: 4px (inputs), 8px (cards), 12px (larger cards), 9999px (pills/badges)
Logo usage:
- Nav bar:
solanasis-logo-horizontal.png(wordmark, max height 40px) - Footer:
solanasis-logo-squared.png(full logo with icon, max height 80px) - Favicon:
solanasis-logo-icon.png(triangle icon only) - Social/OG:
solanasis-logo-squared.png(resized to 1200x630 with parchment background)
Brand taglines:
-
Primary: “Operational Resilience, Proven”
-
Alt (used in logo): “Security · Resilience · Operations”
Important: The warm parchment background (#FEF9F1) with copper accents matches the existing logo’s cream/warm aesthetic. Do NOT use pure white (#FFFFFF) as the page background — use the parchment for the overall page and white for card/content areas to create depth.
Comprehensive reference: The workspace directory contains
brand-style/Matchkeyz_Brand_Style_Guide.mdwhich has the FULL design system — CSS custom properties, button hierarchy (primary/secondary/tertiary/destructive), form field styles, card variants, navigation specs, accessibility contrast ratios, and spacing scale. Thedesign-tokens.mdfile in the project root should reference these and adapt them for the Astro/Tailwind implementation. Where there’s a conflict between the summary above and the full brand guide, the full guide wins. - Font family:
2.3 — Stock Photos
Stock photo approach (decided): Claude Code will download stock photos from Unsplash using
curlduring the build. If any download fails, use CSS gradient placeholders matching the design tokens with a TODO comment for later replacement.Photo style (from
About Me for AI.md): Documentary photo, natural window light, 35mm lens, shallow depth of field, subtle film grain, slight imperfections, muted colors, high contrast but not HDR, realistic, not glossy, not CGI. No logos, no brand names, no readable text, no futuristic neon, no hoodies, no masks, no “hacker” tropes.
-
Download stock photos to
public/images/stock/usingcurlfrom Unsplash/Pexels direct URLs (1600px wide). If a download fails, create a CSS gradient placeholder.Images needed (aligned to Copy V3 services):
Image Description Unsplash search terms Filename Hero Small team collaborating at a table, warm natural light, modern office ”team meeting natural light” hero-team.jpgResilience Checkup Professional reviewing a report or dashboard, focused, clean desk ”business analyst report laptop” offer-checkup.jpgRemediation Sprint Pair of people working on screens together, active collaboration ”coworkers working together laptop” service-remediation.jpgFractional Partner Professional in an advisory meeting, confident, boardroom feel ”business consultant meeting” service-partner.jpgMigrations Clean organized server room or structured data visualization ”server room modern clean” service-migrations.jpgCRM Setup Person working on a dashboard/CRM, organized workspace ”crm dashboard professional” service-crm.jpgIntegrations Connected systems or clean network infrastructure ”network infrastructure modern” service-integrations.jpgResponsible AI Professional workspace with clean tech setup, thoughtful mood ”technology workspace professional” service-ai.jpgAbout / Boulder Boulder CO Flatirons or downtown Pearl Street, warm tones ”boulder colorado flatirons” about-boulder.jpgBlog default Clean workspace with laptop and notebook, warm natural light ”workspace laptop notebook natural light” blog-default.jpg-
Download at 1600px wide (Astro’s image optimization handles the rest)
-
Source from Unsplash or Pexels — both free for commercial use
-
Create
public/images/stock/CREDITS.mdwith photographer name + source URL for each image
Pro Tip: These stock photos are placeholders. Replace with custom photography as Solanasis grows. The
CREDITS.mdfile tracks attribution and which images to eventually replace. -
2.4 — Create CLAUDE.md
- Create
CLAUDE.mdin the project root with:- Reference to design-tokens.md: “Always follow the design tokens. Do not introduce new colors, fonts, or spacing values.”
- Reference to
.envas the single source of truth for all project configuration - Code conventions: TypeScript, Tailwind CSS utility classes, Astro components
- Standard requirements for every page: SEO metadata, Open Graph tags, responsive design
- Contact form requirements: honeypot field, Turnstile, auto-reply, thank-you page
- Newsletter requirements: Turnstile, double opt-in flow, footer + dedicated page placement
- Performance target: “All pages must score 90+ on Google Lighthouse for Performance, Accessibility, Best Practices, and SEO.”
- Git conventions: conventional commits, descriptive branch names
- Variable naming: always use
import.meta.env.VARIABLE_NAMEto read.envvalues in Astro — never hardcode site URL, domain, project names, etc. - Image handling: always use Astro’s
<Image>component for automatic optimization, lazy loading, and WebP conversion. Stock photos live inpublic/images/stock/, logos inpublic/images/logo/.
2.5 — Create content-style-guide.md
- Create
content-style-guide.mdwith:- Reference to
03-content-creation-strategy.mdfor blog content guidelines - Reference to
website-content/solanasis_site_copy_v3_smartcuts.md(in workspace directory) as the approved website copy — includes FAQ section - Tone: professional but approachable, confident but not arrogant
- Terminology: “operational resilience” (not “IT support”), “growing organizations” (not “SMBs”), “security assessment” (not “pen test”)
- Writing style: clear, jargon-free, actionable
- Service descriptions: consistent format across all services
- Tagline usage: “Operational Resilience, Proven” (primary), “Security · Resilience · Operations” (alt/logo)
- Reference to
2.6 — Commit foundation files:
git add design-tokens.md CLAUDE.md content-style-guide.md public/images/
git commit -m "Add design system, brand assets, stock photos, and project conventions"Phase 3: Brand Customization (30-60 min)
Goal: Transform the generic theme into the Solanasis website with proper branding, colors, and content.
Important: Use
$SITE_NAME,$SITE_URL,$SITE_DOMAIN, and$BREVO_SENDER_EMAILfrom.env— never hardcode these values in templates or config files. Useimport.meta.env.*in Astro files.Approved website copy: Use
website-content/solanasis_site_copy_v3_smartcuts.mdfrom the workspace directory as the primary source for all page content. This is the approved “Smartcuts / High-Leverage” version.
-
3.1 Update
astro.config.mjs:- Set
siteto the value ofSITE_URLfrom.env - Use
process.env.SITE_URLinastro.config.mjs(config files run in Node.js context, not Vite) - Astro automatically loads
.envfiles via Vite, soprocess.envworks in config files without extra setup - In
.astrocomponent files (templates), useimport.meta.env.SITE_URLinstead - This is used by the sitemap, RSS feed, canonical URLs, and Open Graph tags
Rule of thumb:
process.envin config files (astro.config.mjs,tailwind.config.mjs).import.meta.envin Astro components and pages. Never mix them up. - Set
-
3.2 Update Tailwind config (
tailwind.config.mjsor similar):- Map the P01: Navy + Parchment + Copper palette from
design-tokens.mdto Tailwind:primary: '#020532'(deep navy)secondary: '#091652'(medium navy)accent: '#C47A3D'(copper)background: '#FEF9F1'(warm parchment)text: '#111827'(near-black)
- Set font family:
Inter(from Google Fonts —@fontsource/interor Google Fonts import) - Configure border-radius and spacing per design tokens
- Map the P01: Navy + Parchment + Copper palette from
-
3.3 Update site-wide metadata:
- Site title: “$SITE_NAME | Operational Resilience for Growing Organizations”
- Site description: SEO-optimized description mentioning cybersecurity, disaster recovery, data migrations, Boulder CO
- Favicon: generate from
public/images/logo/solanasis-logo-icon.png(the triangle icon) - Open Graph default image: create from
public/images/logo/solanasis-logo-squared.pngresized/padded to 1200x630 on parchment (#FEF9F1) background
-
3.4 Update navigation:
- Logo: use
solanasis-logo-horizontal.pngin the nav bar (max height 40px, links to home) - Navigation links (from approved copy v3):
Nav Label Type Target Why Anchor link /#whysection on home pageResilience Checkup Anchor link /#resilience-checkupsection on home pageServices Anchor link OR separate page /#servicessection on home page (or/servicesif building a separate services page)How We Work Anchor link /#how-we-worksection on home pageFAQ Anchor link /#faqsection on home pageBlog Separate page /blogContact Separate page /contact - CTA button in nav: Book a 30-min intro call (copper accent color, links to
https://go.solanasis.com/meet— if this booking URL is not yet live, fall back to/contact) - Mobile: hamburger menu that expands to full-screen nav
- Smooth scroll for anchor links (add
scroll-behavior: smoothto<html>)
- Logo: use
-
3.5 Customize the Home page (use approved copy from
solanasis_site_copy_v3_smartcuts.md):- Hero section:
- Headline: “High-leverage resilience for teams with no time.”
- Subhead: “Most orgs don’t need more tools. They need fewer unknowns.”
- Three-point value prop (from copy v3)
- Primary CTA: “Book a 30-min intro call” (copper button)
- Secondary: “Email us (hi@solanasis.com)” (text link, use
$BREVO_SENDER_EMAILfrom.env) - Microcopy: “Tight scope. Real artifacts. Minimal disruption.”
- Background: hero gradient from design tokens (navy → copper)
- Image:
stock/hero-team.jpg(if using photos) or gradient-only
- “Why Solanasis Exists” section (from copy v3): the punchy “Because…” statements
- Primary Offer: Resilience Checkup — the 10-day baseline offer with “What we do / What you get / What happens next”
- Services grid: cards for each service with icons and the descriptions from copy v3
- How We Work section: the principles + 5-step process from copy v3
- FAQ section (
#faq): 10 Q&As from copy v3’s FAQ section (timeline, audience, MSP coordination, standalone option, disruption level, NDAs, differentiation from IT audits, pen-test clarification, pricing, what-if-no-issues). Use an accordion/expandable format. - Newsletter signup CTA: inline form with value proposition
- Footer CTA: contact section from copy v3
- Hero section:
-
3.6 Create/customize the About page (
/about):Content sources:
About Me for AI.md(founder bio — “About me for LinkedIn Bio” section, lines 92-124),About Solanasis.md(legal details, taglines),misc-notes.md(mission, differentiators)- Founder section: Adapt Dmitri Sunshine’s LinkedIn bio into a website-appropriate format (third person). Include:
- 23+ years experience as Enterprise Architect
- Founded Solanasis to address the operational resilience gap he saw across organizations
- Based in Boulder, CO
- The “highly-selective about who we work with” positioning
- Company story: Why Solanasis exists — drawn from the “Why” section of copy v3 and the mission in misc-notes.md (“save companies from the pain of security incidents, failed disaster recovery and operational headaches”)
- Our difference: Hands-on (roll up sleeves), real restore tests not checkbox audits, practical 90-day plans, AI-native approach for efficiency
- Boulder, CO location with
stock/about-boulder.jpgimage - Do NOT include: Pricing, payment terms, LLC details, bank info, long-term vision (startup studio), or personal details (co-living, biohacking) — those are internal
- Social links: LinkedIn (https://www.linkedin.com/in/dmitri-sunshine/)
- Founder section: Adapt Dmitri Sunshine’s LinkedIn bio into a website-appropriate format (third person). Include:
-
3.7 Services — follow copy v3’s structure (NO separate services page at launch):
Architecture decision: The home page IS the services page. Copy v3 is a single-page design. Services are anchor-linked sections on the home page, not standalone pages. Individual service pages can be added later for SEO.
The services in copy v3 (NOT the same as the internal service offering names):
The home page
#servicessection shows these as cards/grid items:-
Remediation Sprint (2-4 weeks) — fix the top issues quickly → use
stock/service-remediation.jpg -
Fractional Resilience Partner (ongoing) — ongoing cadence + ownership → use
stock/service-partner.jpg -
Migrations — controlled moves with validation → use
stock/service-migrations.jpg -
CRM Setup — a CRM that matches reality → use
stock/service-crm.jpg -
Integrations — secure workflows that don’t silently break → use
stock/service-integrations.jpg -
Responsible AI — useful AI with guardrails → use
stock/service-ai.jpg
- These are listed BELOW the Resilience Checkup section as “when you’re ready” follow-on services
- Each card: short description from copy v3, icon, and CTA linking to
/contact - Use Astro’s
<Image>component for all stock photos (auto-optimizes to WebP, lazy loads, generates srcset) - The Resilience Checkup (primary offer) is its own prominent section above these services — see Phase 3.5
-
-
3.8 Set up the Blog/Insights section:
Reference: Follow the structure and guidelines in
03-content-creation-strategy.md3.8.1 — Content Collection Schema:
- Create
src/content/config.tsdefining the blog collection schema:title(string, required)description(string, required — used for SEO meta description)date(date, required — used for ordering AND scheduled publishing)author(string, default: “Solanasis Team”)tags(array of strings)pillar(enum: “cybersecurity”, “disaster-recovery”, “data-systems”, “crm-operations”, “ai-automation”, “operational-resilience”)image(string, optional — hero image path)imageAlt(string, required if image is set)readingTime(number — estimated minutes)draft(boolean, default: false)
3.8.2 — Date-Based Scheduled Publishing:
- Configure the blog listing and RSS feed to filter out posts where
date > today - This allows batch-committing posts with future dates — they appear automatically when the date arrives
- No cron jobs, no CMS, no publishing infrastructure needed
3.8.3 — Blog Listing Page (
/blog):- Grid of post cards showing: title, description excerpt, date, reading time, pillar tag
- Sorted by date (newest first), filtered to exclude future-dated and draft posts
- Pagination if more than 10 posts (won’t be needed at launch, but build it in)
3.8.4 — Individual Blog Post Template:
- Hero section with title, date, author, reading time, pillar tag
- Formatted markdown body with proper heading hierarchy (H2/H3)
- “Key Takeaways” section styling
- Contextual CTA block at the bottom (links to relevant service page)
- Newsletter signup component (reusable from Phase 5)
- “Related Posts” section (2-3 posts from the same pillar, if available)
- Social share links (LinkedIn, Twitter/X, copy link)
- JSON-LD
Articleschema for SEO
3.8.5 — RSS Feed:
- Install
@astrojs/rss - Create
/rss.xmlendpoint with full post content - Include in
<head>as<link rel="alternate" type="application/rss+xml">
3.8.6 — Write the First Blog Post:
- Follow the detailed brief in
03-content-creation-strategy.md→ “First Blog Post Brief” - File:
src/content/blog/password-manager-for-growing-organizations.md - Title: “Why Every Growing Organization Needs a Password Manager (And How to Pick One)”
- Pillar: cybersecurity
- Estimated length: 1,200-1,500 words (~6-7 min read)
- Must include: internal link to Resilience Checkup section on home page (
/#resilience-checkup), internal link to Contact page, newsletter CTA at bottom - Set
dateto the site launch date (or first Tuesday after launch)
- Create
-
3.9 Create/customize the Contact page (UI only — backend in Phase 4):
- Contact form component (will be wired up in Phase 4)
- Booking link: Prominent “Book a 30-min intro call” button linking to
https://go.solanasis.com/meet(if not yet live, link to a placeholder or omit until confirmed) - Office location: Boulder, CO (no street address needed — use city only)
- Email: use
$BREVO_SENDER_EMAILfrom.env(hi@solanasis.com) - Phone: 303-900-8969 — render using JavaScript to prevent scraping (e.g., store as data attributes, assemble on click/hover). This is a Google Voice number so spam risk is manageable.
- Social links: LinkedIn (https://www.linkedin.com/in/dmitri-sunshine/)
- Map embed (optional — Boulder, CO area)
-
3.10 Create additional pages:
- Thank-you page (
/thank-you) — confirmation after form submission - Newsletter page (
/newsletter) — dedicated signup with value proposition - Privacy Policy page (
/privacy) — standard privacy policy - Terms of Service page (
/terms) — standard terms - Custom 404 page — branded, with helpful links
- Custom 500 page — branded error page
- Thank-you page (
-
3.11 Update the footer:
- Logo:
solanasis-logo-squared.png(full logo with icon, max height 80px) - Tagline: “practical security + operational resilience + systems that work” (from copy v3)
- Navigation links
- Social media icons: LinkedIn (https://www.linkedin.com/in/dmitri-sunshine/), X/Twitter (https://x.com/ZaMrSunshine)
- Newsletter signup form (reusable component)
- Copyright: ”© 2026 Solanasis. All rights reserved.”
- Background: dark navy (#020532) with light text for contrast
- Logo:
-
3.12 Generate favicons from
public/images/logo/solanasis-logo-icon.png(triangle icon):- Use
sharporfaviconsnpm package to generate all sizes from the source icon - Output sizes: 16x16, 32x32, apple-touch-icon (180x180), android-chrome (192x192, 512x512)
- Save to
public/favicon/ - Create
site.webmanifestwith basic PWA (Progressive Web App) metadata referencing the generated icons
- Use
-
3.13 Build and visual review:
npm run build npm run preview- Check every page at desktop (1440px), tablet (768px), and mobile (375px) widths
- Verify all links work, images load, colors match design tokens
-
3.14 Commit brand customization:
git add . git commit -m "Customize theme with Solanasis branding, content, and pages"
Phase 4: Contact Form Backend (20-30 min)
Goal: Build the contact form API endpoint with Brevo email delivery, Turnstile spam protection, and auto-reply.
Important: All API keys and secrets in this phase come from
.env(local dev) and Cloudflare Worker Secrets (production). Never hardcode credentials in source files. Useenv.VARIABLE_NAMEin Worker/endpoint code to access runtime secrets.
-
4.1 Install the Cloudflare adapter:
npm install @astrojs/cloudflare -
4.2 Update
astro.config.mjsto use the Cloudflare adapter:import cloudflare from '@astrojs/cloudflare'; export default defineConfig({ site: process.env.SITE_URL, // from .env output: 'hybrid', // hybrid lets most pages stay static, only API routes are server-rendered adapter: cloudflare(), });Astro version note: If using Astro 5+,
output: 'hybrid'is the default andadaptermay needmode: 'directory'depending on the version. Checkpackage.jsonfor the installed Astro version and consult the Astro Cloudflare adapter docs for the correct config syntax. -
4.3 Create the contact form API endpoint (
src/pages/api/send-email.ts):- Accept POST with JSON body: name, email, company, phone, service, message, turnstileToken
- Validate required fields (name, email, message)
- Validate Turnstile token via
https://challenges.cloudflare.com/turnstile/v0/siteverifyusingenv.TURNSTILE_SECRET_KEY - Check honeypot field — if filled, silently return success (don’t alert the bot)
- Send notification email to
env.BREVO_SENDER_EMAILvia Brevo API (https://api.brevo.com/v3/smtp/email) usingenv.BREVO_API_KEY:- Include all form fields in a nicely formatted HTML template
- Subject: “New Contact Form Submission from [Name] at [Company]”
- Send auto-reply to the submitter via Brevo API:
- Professional branded template
- Subject: “Thanks for reaching out to $SITE_NAME”
- Body: “We received your message and will be in touch within one business day.”
- (Optional) Create a contact in Brevo via
https://api.brevo.com/v3/contacts:- Add to a “Website Leads” list
- Attributes: NAME, COMPANY, SERVICE_INTEREST, SOURCE=website
- Return JSON response:
{ success: true }or{ error: "message" } - Set
export const prerender = falseat the top of the file
-
4.4 Create the Turnstile client-side component:
- Load the Turnstile script:
https://challenges.cloudflare.com/turnstile/v0/api.js - Important — Astro
PUBLIC_prefix requirement: Astro only exposes env vars prefixed withPUBLIC_to client-side code. To make the Turnstile site key available in the browser:- Option A (recommended): Add
PUBLIC_TURNSTILE_SITE_KEYto.env(duplicating the value fromTURNSTILE_SITE_KEY), then useimport.meta.env.PUBLIC_TURNSTILE_SITE_KEYin client-side code - Option B: Read
TURNSTILE_SITE_KEYon the server side in the Astro component’s frontmatter and pass it as a prop to the client-side<script>via adata-attribute on a DOM element
- Option A (recommended): Add
- Render the Turnstile widget in the form using the site key
- Handle success/error callbacks
- Load the Turnstile script:
-
4.5 Wire up the ContactForm component:
- Form fields: name (required), email (required), company (optional), phone (optional), service dropdown (required), message (required)
- Hidden honeypot field (name it something innocent like
websiteorurl) - Turnstile widget
- Submit handler: fetch POST to
/api/send-email, handle loading state, show success/error messages - Client-side validation (HTML5 required attributes + JavaScript validation)
-
4.6 Create
wrangler.tomlfor Workers configuration:name = "$CLOUDFLARE_PAGES_PROJECT" # Read from .env compatibility_date = "2024-01-01" [vars] TURNSTILE_SITE_KEY = "$TURNSTILE_SITE_KEY" # Public key — OK in config BREVO_SENDER_EMAIL = "$BREVO_SENDER_EMAIL" BREVO_SENDER_NAME = "$BREVO_SENDER_NAME" # Secrets are set via 'wrangler secret put' — NEVER put them here # BREVO_API_KEY → set in Phase 7 via wrangler pages secret put # TURNSTILE_SECRET_KEY → set in Phase 7 via wrangler pages secret put # BREVO_LIST_ID → set in Phase 7 via wrangler pages secret putImportant: Replace the
$VARIABLEplaceholders above with actual values from.envwhen writing the file. These are NOT shell variables —wrangler.tomldoesn’t support variable interpolation. But the values come FROM.env. -
4.7 Create
.dev.varsfor local Wrangler development (mirrors production secrets):- Copy these values from
.env:BREVO_API_KEY,BREVO_LIST_ID,TURNSTILE_SECRET_KEY - Format:
BREVO_API_KEY=xkeysib-actual-key-here BREVO_LIST_ID=2 TURNSTILE_SECRET_KEY=0x4actual-key-here - Verify
.dev.varsis in.gitignore(should already be there from Phase 1)
- Copy these values from
-
4.8 Test the contact form locally:
npm run dev- Fill out the form on localhost
- Confirm notification email arrives at the address in
$BREVO_SENDER_EMAIL - Confirm auto-reply arrives at the submitter’s email
- Confirm Turnstile widget renders and validates
- Test with honeypot filled → should silently succeed (no email sent)
- Test with missing required fields → should show validation errors
-
4.9 Commit contact form:
git add . git commit -m "Add contact form with Brevo email delivery and Turnstile spam protection"
Phase 5: Newsletter Signup (15-20 min)
Goal: Build the newsletter subscription flow with Brevo integration and Turnstile protection.
-
5.1 Create the newsletter API endpoint (
src/pages/api/newsletter-subscribe.ts):- Accept POST with JSON body: email, turnstileToken
- Validate email format
- Validate Turnstile token using
env.TURNSTILE_SECRET_KEY - Add contact to Brevo via
https://api.brevo.com/v3/contactsusingenv.BREVO_API_KEY:{ "email": "subscriber@example.com", "listIds": [env.BREVO_LIST_ID], "attributes": { "SOURCE": "website", "SIGNUP_DATE": "2026-03-04T..." }, "updateEnabled": true } - Handle “already exists” response gracefully (return friendly message, not error)
- Return JSON:
{ success: true, message: "..." } - Set
export const prerender = false
-
5.2 Create the
NewsletterSignup.astrocomponent (reusable):- Email input field with placeholder: “Enter your email for weekly insights”
- Turnstile widget
- Submit button: “Subscribe”
- Success state: “Check your inbox — welcome aboard!”
- Already-subscribed state: “You’re already on the list!”
- Error state: user-friendly error message
- Styled to match design tokens
-
5.3 Place the newsletter component:
- Site footer (every page)
- End of every blog post
- Dedicated
/newsletterpage with a value proposition section:- Headline: “Operational Resilience Insights, Weekly”
- Description: what subscribers get, how often, sample topics
- The signup form
- Social proof: “Join X+ professionals…” (placeholder)
-
5.4 Test newsletter signup locally:
- Submit an email → confirm contact appears in Brevo under the newsletter list
- Submit the same email again → confirm “already subscribed” message
- Submit without Turnstile → confirm rejection
- Check Brevo dashboard → confirm contact has SOURCE and SIGNUP_DATE attributes
-
5.5 Commit newsletter:
git add . git commit -m "Add newsletter signup with Brevo subscriber management"
Phase 6: SEO & Performance (15 min)
Goal: Add SEO foundations, structured data, sitemap, and verify performance targets.
Important: Use
$SITE_URLand$SITE_DOMAINfrom.envfor all URLs. Never hardcode the domain.
-
6.1 Install Astro sitemap integration:
npx astro add sitemap- Configure in
astro.config.mjswithsite: process.env.SITE_URL
- Configure in
-
6.2 Create/verify
robots.txt(use$SITE_URLfrom.env):User-agent: * Allow: / Sitemap: $SITE_URL/sitemap-index.xml -
6.3 Add structured data (JSON-LD) to the home page:
LocalBusinessschema with:- Name:
$SITE_NAME - Description
- Address: Boulder, CO
- Services offered
- Contact info
- Name:
-
6.4 Verify every page has:
- Unique
<title>tag - Unique
<meta name="description"> - Open Graph tags:
og:title,og:description,og:image,og:url - Twitter card meta tags
- Canonical URL (using
$SITE_URL)
- Unique
-
6.5 Run a Lighthouse audit on the local build:
npm run build && npm run preview- Open Chrome DevTools → Lighthouse → run audit on each key page
- Target: 90+ on Performance, Accessibility, Best Practices, SEO
- Fix any issues flagged
-
6.6 Commit SEO additions:
git add . git commit -m "Add SEO foundations: sitemap, structured data, meta tags"
Phase 7: Deployment (10-15 min)
Goal: Push to GitHub, deploy to Cloudflare, set production secrets, verify live site.
Important: Use
$GITHUB_USERNAME,$GITHUB_REPO_NAME, and$CLOUDFLARE_PAGES_PROJECTfrom.env. Never hardcode project names in commands.
7.1 — GitHub Repository
-
7.1.1 Check if a GitHub repo already exists:
gh repo view $GITHUB_USERNAME/$GITHUB_REPO_NAME 2>&1- If repo exists → check if local repo is already linked:
git remote -v- If remote
originis set to the correct repo → skip to 7.1.3 - If remote
originis set to a DIFFERENT repo → STOP. Ask the user which repo to use. - If no remote → add it:
git remote add origin https://github.com/$GITHUB_USERNAME/$GITHUB_REPO_NAME.git
- If remote
- If repo does NOT exist → create it:
gh repo create $GITHUB_REPO_NAME --private --source=. --push
- If repo exists → check if local repo is already linked:
-
7.1.2 If repo was pre-existing, push the current code:
git push -u origin main- If the branch is named
masterinstead ofmain, rename it first:git branch -M main git push -u origin main
- If the branch is named
-
7.1.3 Verify the repo is accessible:
gh repo view $GITHUB_USERNAME/$GITHUB_REPO_NAME --json url -q .url- Should return a GitHub URL. Save this for the handoff summary.
7.2 — Cloudflare Pages Project
-
7.2.1 Check if a Cloudflare Pages project already exists:
npx wrangler pages project list 2>&1 | grep $CLOUDFLARE_PAGES_PROJECT- If project exists → skip to 7.3
- If project does NOT exist → create it:
npx wrangler pages project create $CLOUDFLARE_PAGES_PROJECT --production-branch=main
-
7.3 Deploy the site:
npm run build npx wrangler pages deploy dist/ --project-name=$CLOUDFLARE_PAGES_PROJECT -
7.4 Set production secrets on Cloudflare:
⚠️ These commands are INTERACTIVE —
wrangler pages secret putprompts for the secret value on stdin. Claude Code can initiate each command, but the user must paste the value and press Enter when prompted. Claude Code cannot automate this step.Tell the user: “I need to set 3 production secrets on Cloudflare. For each command, I’ll tell you which value to paste from your .env file. You’ll see a
? Enter a secret value:prompt — paste the value and press Enter.”Run these one at a time (each requires user interaction):
# Secret 1: Tell user to paste their BREVO_API_KEY (starts with xkeysib-) npx wrangler pages secret put BREVO_API_KEY --project-name=$CLOUDFLARE_PAGES_PROJECT # Secret 2: Tell user to paste their BREVO_LIST_ID (a number, e.g., 2) npx wrangler pages secret put BREVO_LIST_ID --project-name=$CLOUDFLARE_PAGES_PROJECT # Secret 3: Tell user to paste their TURNSTILE_SECRET_KEY (starts with 0x4) npx wrangler pages secret put TURNSTILE_SECRET_KEY --project-name=$CLOUDFLARE_PAGES_PROJECT -
7.5 Verify the deployment:
- Note the Cloudflare Pages URL (e.g.,
$CLOUDFLARE_PAGES_PROJECT.pages.dev) - Visit the URL → confirm the site loads correctly
- Check all pages render properly
- Test the contact form → confirm email delivery works in production
- Test newsletter signup → confirm subscriber appears in Brevo
- Note the Cloudflare Pages URL (e.g.,
-
7.6 Report deployment URL to user
Phase 8: CI/CD Pipeline (10 min)
Goal: Set up GitHub Actions to auto-deploy on every push to main.
-
8.1 Create
.github/workflows/deploy.yml:name: Deploy to Cloudflare Pages on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest permissions: contents: read deployments: write steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - name: Install dependencies run: npm ci - name: Build Astro site run: npm run build - name: Deploy to Cloudflare Pages uses: cloudflare/wrangler-action@v3 with: apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} command: pages deploy dist/ --project-name=$CLOUDFLARE_PAGES_PROJECTNote: Replace
$CLOUDFLARE_PAGES_PROJECTin the YAML above with the actual value from.env. GitHub Actions YAML does not read from.env— this value must be written literally into the workflow file. -
8.2 Inform the user they need to add these GitHub Secrets manually:
- Go to:
https://github.com/$GITHUB_USERNAME/$GITHUB_REPO_NAME/settings/secrets/actions - Or: Repository → Settings → Secrets and variables → Actions → New repository secret
- Add these two secrets:
CLOUDFLARE_API_TOKEN— if the.envfile already has aCLOUDFLARE_API_TOKENvalue, use that. Otherwise, the user needs to create one (see security playbook, Section 7, Step 2 “Alternative: Create an API Token manually”). The token needs “Cloudflare Pages:Edit” permission.CLOUDFLARE_ACCOUNT_ID— the value from.env($CLOUDFLARE_ACCOUNT_ID)
- Go to:
-
8.3 Commit the workflow:
git add .github/workflows/deploy.yml git commit -m "Add CI/CD: auto-deploy to Cloudflare on push to main" git push -
8.4 Verify CI/CD works:
- Check GitHub → Actions tab → confirm the workflow runs
- If it fails because GitHub Secrets aren’t set yet → inform the user: “The auto-deploy workflow will start working once you add the two GitHub Secrets from step 8.2. The manual deploy from Phase 7 is still working in the meantime.”
Phase 9: Custom Domain (Manual — User Instruction)
Goal: Connect the custom domain to the Cloudflare Pages deployment.
-
9.1 Inform the user to complete these steps in the Cloudflare dashboard:
- Go to Cloudflare dashboard → Workers & Pages →
$CLOUDFLARE_PAGES_PROJECT→ Custom domains - Click Set up a custom domain
- Enter:
$SITE_DOMAIN - Cloudflare automatically configures the DNS records
- Wait for SSL certificate to be issued (usually 1-5 minutes)
- Also add
www.$SITE_DOMAIN→ configure redirect to the apex domain
- Go to Cloudflare dashboard → Workers & Pages →
-
9.2 After user completes domain setup, verify:
- Visit
$SITE_URL→ site loads with SSL - Visit
https://www.$SITE_DOMAIN→ redirects to$SITE_URL - Test contact form on the live domain
- Test newsletter signup on the live domain
- Visit
Phase 10: Final Testing & QA (15 min)
Goal: Comprehensive end-to-end testing of everything.
-
10.1 Page-by-page visual review:
- Home page — hero, “Why Solanasis Exists”, Resilience Checkup offer, services grid (6 follow-on services), How We Work, FAQ (10 Q&As), newsletter CTA, footer contact
- About page — founder bio (Dmitri Sunshine), company story, differentiators, Boulder CO image
- Home page services section — all 6 follow-on services present (Remediation Sprint, Fractional Partner, Migrations, CRM, Integrations, Responsible AI)
- Blog listing (
/blog) — shows the first post, no future-dated posts visible - Blog post — password manager post renders correctly, reading time shows, pillar tag shows
- Blog post — newsletter signup component renders at bottom of post
- Blog post — CTA links to Resilience Checkup section on home page
- Blog post — JSON-LD Article schema in page source
- RSS feed (
/rss.xml) — renders with the first post - Contact page — form renders, Turnstile widget loads
- Newsletter page — signup form, value proposition
- Thank-you page — displays after form submission
- Privacy Policy — content present
- Terms of Service — content present
- 404 page — navigate to
/this-page-does-not-exist→ custom 404 renders
-
10.2 Responsive testing (check each page at these widths):
- Desktop: 1440px
- Tablet: 768px
- Mobile: 375px
- All navigation should collapse to mobile menu at small widths
- All text should be readable, no horizontal scrolling
-
10.3 Contact form end-to-end test on PRODUCTION:
- Fill out all fields → submit → notification email arrives at
$BREVO_SENDER_EMAIL - Auto-reply email arrives at the submitter’s inbox
- Thank-you page renders after submission
- Spam test: fill honeypot field → submit → no email sent, still shows success
- Validation test: submit with empty required fields → client-side validation prevents submission
- Fill out all fields → submit → notification email arrives at
-
10.4 Newsletter signup end-to-end test on PRODUCTION:
- Enter email in footer signup → subscriber appears in Brevo list #
$BREVO_LIST_ID - Enter same email again → “already subscribed” message shown
- Enter email on
/newsletterpage → subscriber appears in Brevo list - Enter email on blog post signup → subscriber appears in Brevo list
- Enter email in footer signup → subscriber appears in Brevo list #
-
10.5 SEO checks:
- Visit
$SITE_URL/sitemap-index.xml→ sitemap renders with all pages - Visit
$SITE_URL/robots.txt→ correct content, references correct sitemap URL - View page source on home page → confirm JSON-LD structured data
- View page source → confirm OG tags are present on each page
- Visit
-
10.6 Performance audit:
- Run Lighthouse on the live production URL (not localhost)
- Performance: 90+
- Accessibility: 90+
- Best Practices: 90+
- SEO: 90+
- If any score is below 90, fix the issues and redeploy
-
10.7 SSL/Security:
-
https://works (no mixed content warnings) - HTTP redirects to HTTPS
- No exposed API keys in page source or network requests
-
.envis NOT in the GitHub repo (check withgh api repos/$GITHUB_USERNAME/$GITHUB_REPO_NAME/contents/.env— should return 404)
-
Phase 11: Reusable Assets (15 min)
Goal: Package what we built into reusable templates for future client sites.
-
11.1 Create a Claude Code Skill file (
solanasis-new-site.md):- Document the complete workflow: clone theme → apply design tokens → customize content → set up contact form → set up newsletter → deploy
- Include template prompts for each step
- Reference the design-tokens.md and CLAUDE.md patterns
-
11.2 Create a client site template checklist (
client-site-checklist.md):- Fill-in-the-blank version of this build plan
- Client info fields: business name, domain, services, brand colors, contact email
- Stripped-down version without Solanasis-specific content
- Include a blank
sample.envtemplate with the same variable structure
-
11.3 Commit reusable assets:
git add . git commit -m "Add reusable skill file and client site template" git push
Phase 12: Handoff Summary
Goal: Report everything back to the user with all relevant URLs and next steps.
-
12.1 Provide the user with:
- Live site URL:
$SITE_URL - Cloudflare Pages URL:
https://$CLOUDFLARE_PAGES_PROJECT.pages.dev - GitHub repo URL:
https://github.com/$GITHUB_USERNAME/$GITHUB_REPO_NAME - Remaining manual steps (custom domain setup, GitHub Secrets for CI/CD)
- How to make future edits (open Claude Code → describe changes → push → auto-deploy)
- Live site URL:
-
12.2 Recommend immediate next steps:
- Complete custom domain setup (Phase 9)
- Add GitHub Secrets for CI/CD (Phase 8.2)
- Replace any placeholder content with real content
- Replace placeholder team photos with real headshots
- Set up Plausible or Umami analytics
- Share the site URL for feedback
- Send 2-3 test emails from the contact form to warm up the Brevo sender reputation
-
12.3 Blog & content next steps:
- First blog post (password managers) is live — share on LinkedIn
- Draft the next 3 posts from the content calendar in
03-content-creation-strategy.md - Commit all 3 with future Tuesday dates → they auto-publish weekly
- Flesh out
03-content-creation-strategy.mdwith keyword research and expanded calendar - Ongoing: Claude drafts 4 posts/month → Dmitri reviews batch → Claude commits with scheduled dates
Quick Command Reference
All project-specific values come from
.env. Replace$VARIABLEwith actual values.
# Local development
npm run dev # Start dev server (localhost:4321)
npm run build # Build for production
npm run preview # Preview production build locally
# Git
git add . # Stage all changes
git commit -m "message" # Commit
git push # Push to GitHub → triggers auto-deploy
# Deployment (manual)
npx wrangler pages deploy dist/ --project-name=$CLOUDFLARE_PAGES_PROJECT
# Production secrets (interactive — user pastes values)
npx wrangler pages secret put BREVO_API_KEY --project-name=$CLOUDFLARE_PAGES_PROJECT
npx wrangler pages secret put BREVO_LIST_ID --project-name=$CLOUDFLARE_PAGES_PROJECT
npx wrangler pages secret put TURNSTILE_SECRET_KEY --project-name=$CLOUDFLARE_PAGES_PROJECT
# Verification
gh auth status # GitHub auth
npx wrangler whoami # Cloudflare auth
npm run build # Build check
gh repo view $GITHUB_USERNAME/$GITHUB_REPO_NAME # Repo checkThis plan is designed to be followed sequentially. Each phase builds on the previous one. If any step fails, STOP and troubleshoot before proceeding — do not skip steps or work around failures.