App Pages Overhaul + Growth Intelligence Analytics — Implementation Plan
For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
Goal: Redesign the account page, add growth intelligence analytics, and bring all pages to a consistent design standard using shared components.
Architecture: Design-system-first approach. Define new shared CSS components (stat-card-v2, section-header-v2, status-badge, heatmap), then apply them to the account page (full redesign), analytics page (growth intelligence sections), and a consistency pass across dashboard/templates/domains/team.
Tech Stack: Static Jekyll frontend in docs/, vanilla JS, CSS custom properties, SVG sparklines, CSS Grid heatmaps. Backend: Node.js Lambda in terraform/lambda_src/.
Phase 1: Design System Components
Task 1: Add shared CSS component classes
Files:
- Modify:
docs/assets/css/style.css(append after existing analytics CSS, around line 7600+)
Step 1: Add stat-card-v2 CSS
Add after the last rule in style.css:
/* ── Stat Card v2 ────────────────────────────────────────────────────────── */
.stat-card-v2-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 1rem;
margin-bottom: 1.5rem;
}
.stat-card-v2 {
background: var(--glass-bg);
border: 1px solid var(--glass-border);
border-radius: var(--radius-lg);
padding: 1.25rem;
backdrop-filter: blur(var(--glass-blur));
transition: transform 0.2s, box-shadow 0.2s;
}
.stat-card-v2:hover {
transform: translateY(-2px);
box-shadow: var(--glow-sm);
}
.stat-card-v2-label {
font-size: 0.75rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 0.25rem;
}
.stat-card-v2-value {
font-size: 1.75rem;
font-weight: 700;
color: var(--text);
line-height: 1.2;
}
.stat-card-v2-delta {
font-size: 0.75rem;
margin-top: 0.25rem;
}
.stat-card-v2-delta.positive { color: var(--success); }
.stat-card-v2-delta.negative { color: var(--danger); }
.stat-card-v2-delta.neutral { color: var(--text-muted); }
.stat-card-v2-sparkline {
margin-top: 0.5rem;
height: 24px;
}
.stat-card-v2-sparkline svg {
width: 100%;
height: 24px;
}
.stat-card-v2-sparkline polyline {
fill: none;
stroke: var(--accent);
stroke-width: 1.5;
stroke-linecap: round;
stroke-linejoin: round;
}
.stat-card-v2-sparkline .sparkline-area {
fill: var(--accent);
opacity: 0.1;
}
@media (max-width: 640px) {
.stat-card-v2-grid { grid-template-columns: repeat(2, 1fr); }
.stat-card-v2-value { font-size: 1.35rem; }
}
Step 2: Add section-header-v2 CSS
/* ── Section Header v2 ──────────────────────────────────────────────────── */
.section-header-v2 {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 1rem;
padding-bottom: 0.75rem;
border-bottom: 1px solid var(--border);
}
.section-header-v2-text h3 {
font-size: 1.1rem;
font-weight: 600;
color: var(--text);
margin: 0;
}
.section-header-v2-text p {
font-size: 0.8rem;
color: var(--text-muted);
margin-top: 0.25rem;
}
Step 3: Add status-badge-v2 CSS
These expand on the existing .status-badge (line 2988) but add the dot indicator pattern:
/* ── Status Badge v2 ────────────────────────────────────────────────────── */
.status-dot {
display: inline-flex;
align-items: center;
gap: 0.375rem;
font-size: 0.8rem;
font-weight: 500;
}
.status-dot::before {
content: "";
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.status-dot-active::before { background: var(--success); }
.status-dot-trialing::before { background: var(--accent); }
.status-dot-past-due::before { background: #f59e0b; }
.status-dot-canceled::before { background: var(--danger); }
.status-dot-verified::before { background: var(--success); }
.status-dot-pending::before { background: #f59e0b; }
.status-dot-inactive::before { background: var(--text-muted); }
Step 4: Add heatmap CSS
/* ── Signup Heatmap ──────────────────────────────────────────────────────── */
.analytics-heatmap-grid {
display: grid;
grid-template-columns: auto repeat(24, 1fr);
gap: 2px;
margin-top: 1rem;
}
.analytics-heatmap-label {
font-size: 0.65rem;
color: var(--text-muted);
display: flex;
align-items: center;
padding-right: 0.5rem;
white-space: nowrap;
}
.analytics-heatmap-cell {
aspect-ratio: 1;
border-radius: 2px;
min-width: 0;
position: relative;
}
.analytics-heatmap-cell[data-tooltip]:hover::after {
content: attr(data-tooltip);
position: absolute;
bottom: calc(100% + 4px);
left: 50%;
transform: translateX(-50%);
background: var(--bg-card);
color: var(--text);
border: 1px solid var(--border);
border-radius: var(--radius);
padding: 0.25rem 0.5rem;
font-size: 0.65rem;
white-space: nowrap;
pointer-events: none;
z-index: 10;
}
.analytics-heatmap-legend {
display: flex;
align-items: center;
gap: 0.5rem;
margin-top: 0.5rem;
font-size: 0.65rem;
color: var(--text-muted);
}
.analytics-heatmap-legend-bar {
display: flex;
gap: 2px;
}
.analytics-heatmap-legend-bar span {
width: 12px;
height: 12px;
border-radius: 2px;
}
@media (max-width: 640px) {
.analytics-heatmap-grid { overflow-x: auto; min-width: 500px; }
}
Step 5: Add velocity panel and insights CSS
/* ── Velocity Panel ──────────────────────────────────────────────────────── */
.analytics-velocity-stats {
display: flex;
gap: 2rem;
flex-wrap: wrap;
margin-top: 1rem;
}
.analytics-velocity-stat {
display: flex;
flex-direction: column;
}
.analytics-velocity-stat-value {
font-size: 1.25rem;
font-weight: 700;
color: var(--text);
}
.analytics-velocity-stat-label {
font-size: 0.7rem;
color: var(--text-muted);
margin-top: 0.125rem;
}
/* ── Insights Card ───────────────────────────────────────────────────────── */
.analytics-insight-list {
display: flex;
flex-direction: column;
gap: 0.75rem;
margin-top: 1rem;
}
.analytics-insight-item {
display: flex;
gap: 0.75rem;
align-items: flex-start;
padding: 0.75rem 1rem;
border-radius: var(--radius);
background: rgba(99, 102, 241, 0.04);
border: 1px solid rgba(99, 102, 241, 0.08);
}
.analytics-insight-item.positive { background: rgba(34, 197, 94, 0.04); border-color: rgba(34, 197, 94, 0.08); }
.analytics-insight-item.negative { background: rgba(239, 68, 68, 0.04); border-color: rgba(239, 68, 68, 0.08); }
.analytics-insight-icon { font-size: 1rem; flex-shrink: 0; line-height: 1.4; }
.analytics-insight-text { font-size: 0.85rem; color: var(--text); line-height: 1.4; }
.analytics-insight-text a { color: var(--accent); }
/* ── Account Page v2 ─────────────────────────────────────────────────────── */
.account-section {
background: var(--glass-bg);
border: 1px solid var(--glass-border);
border-radius: var(--radius-lg);
padding: 1.5rem;
backdrop-filter: blur(var(--glass-blur));
margin-bottom: 1rem;
}
.account-section-danger {
border-color: rgba(239, 68, 68, 0.2);
background: rgba(239, 68, 68, 0.03);
}
.account-field-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem 0;
}
.account-field-row + .account-field-row {
border-top: 1px solid var(--border);
}
.account-field-label {
font-size: 0.8rem;
color: var(--text-muted);
min-width: 120px;
}
.account-field-value {
font-size: 0.9rem;
color: var(--text);
font-weight: 500;
}
.account-field-actions {
display: flex;
gap: 0.5rem;
align-items: center;
}
.account-plan-features {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
margin-top: 0.75rem;
}
.account-plan-feature {
display: inline-flex;
align-items: center;
gap: 0.25rem;
font-size: 0.8rem;
color: var(--text-muted);
padding: 0.25rem 0.5rem;
background: rgba(99, 102, 241, 0.06);
border-radius: 9999px;
}
.account-plan-feature::before {
content: "\2713";
color: var(--success);
font-weight: 700;
font-size: 0.7rem;
}
.account-invoice-table {
width: 100%;
border-collapse: collapse;
font-size: 0.85rem;
margin-top: 0.75rem;
}
.account-invoice-table th {
text-align: left;
padding: 0.5rem 0.75rem;
color: var(--text-muted);
font-weight: 600;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.03em;
border-bottom: 1px solid var(--border);
}
.account-invoice-table td {
padding: 0.5rem 0.75rem;
border-bottom: 1px solid var(--border);
}
.account-invoice-table tr:last-child td { border-bottom: none; }
.account-card-icon {
display: inline-flex;
align-items: center;
gap: 0.5rem;
font-size: 0.9rem;
}
.account-masked-key {
font-family: monospace;
font-size: 0.85rem;
background: var(--bg-input);
padding: 0.5rem 0.75rem;
border-radius: var(--radius);
border: 1px solid var(--border);
display: flex;
justify-content: space-between;
align-items: center;
gap: 0.5rem;
margin-top: 0.5rem;
}
@media (max-width: 640px) {
.account-field-row { flex-direction: column; align-items: flex-start; gap: 0.25rem; }
.account-section { padding: 1rem; }
}
Step 6: Commit
git add docs/assets/css/style.css
git commit -m "Design system: stat-card-v2, section-header-v2, status-dot, heatmap, velocity, account-v2 CSS"
Phase 2: Shared JS Helpers
Task 2: Add stat-card-v2 and SVG sparkline builder to app.js
Files:
- Modify:
docs/assets/js/app.js(append before the closing navigation/auth code, around line 445)
Step 1: Add helpers
Add before the showEl function (line 450):
// ── Stat Card v2 Builder ────────────────────────────────────────────────
function buildStatCardV2(label, value, delta, sparkData) {
var card = document.createElement("div");
card.className = "stat-card-v2";
var labelEl = document.createElement("div");
labelEl.className = "stat-card-v2-label";
labelEl.textContent = label;
card.appendChild(labelEl);
var valueEl = document.createElement("div");
valueEl.className = "stat-card-v2-value";
valueEl.textContent = value;
card.appendChild(valueEl);
if (delta) {
var deltaEl = document.createElement("div");
var cls = delta.value > 0 ? "positive" : delta.value < 0 ? "negative" : "neutral";
deltaEl.className = "stat-card-v2-delta " + cls;
var arrow = delta.value > 0 ? "\u25B2 " : delta.value < 0 ? "\u25BC " : "";
deltaEl.textContent = arrow + delta.label;
card.appendChild(deltaEl);
}
if (sparkData && sparkData.length > 1) {
var sparkContainer = document.createElement("div");
sparkContainer.className = "stat-card-v2-sparkline";
sparkContainer.appendChild(buildSvgSparkline(sparkData));
card.appendChild(sparkContainer);
}
return card;
}
function buildSvgSparkline(data) {
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
svg.setAttribute("viewBox", "0 0 100 24");
svg.setAttribute("preserveAspectRatio", "none");
var max = Math.max.apply(null, data.concat([1]));
var points = data.map(function (v, i) {
var x = data.length > 1 ? (i / (data.length - 1)) * 100 : 50;
var y = 24 - (max > 0 ? (v / max) * 22 : 0) - 1;
return x.toFixed(1) + "," + y.toFixed(1);
}).join(" ");
// Area fill
var area = document.createElementNS("http://www.w3.org/2000/svg", "polygon");
var areaPoints = "0,24 " + points + " 100,24";
area.setAttribute("points", areaPoints);
area.setAttribute("class", "sparkline-area");
svg.appendChild(area);
// Line
var line = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
line.setAttribute("points", points);
svg.appendChild(line);
return svg;
}
function buildStatusDot(status, label) {
var span = document.createElement("span");
span.className = "status-dot status-dot-" + status;
span.textContent = label;
return span;
}
function buildSectionHeader(title, description, actionBtn) {
var header = document.createElement("div");
header.className = "section-header-v2";
var textDiv = document.createElement("div");
textDiv.className = "section-header-v2-text";
var h3 = document.createElement("h3");
h3.textContent = title;
textDiv.appendChild(h3);
if (description) {
var p = document.createElement("p");
p.textContent = description;
textDiv.appendChild(p);
}
header.appendChild(textDiv);
if (actionBtn) header.appendChild(actionBtn);
return header;
}
Step 2: Commit
git add docs/assets/js/app.js
git commit -m "Shared helpers: buildStatCardV2, buildSvgSparkline, buildStatusDot, buildSectionHeader"
Phase 3: Account Page Redesign
Task 3: Rewrite account.html structure
Files:
- Modify:
docs/account.html— replace entire.account-gridcontent (lines 35-181)
Step 1: Replace the account grid HTML
Replace the current account-grid div and its contents with the new stacked-sections layout. Keep the same element IDs for JS binding but restructure into vertical account-section blocks:
- Subscription section: Plan name, status-dot, billing date, amount, feature pills, action buttons
- Payment method section: hidden-until-js, loads from billing API
- Billing history section: hidden-until-js, invoice table with status dots
- Account balance section: hidden-until-js, balance amount + transactions
- Profile section: Email + verification status-dot + resend button
- API Key section: Masked key + copy + regenerate with warning + docs link
- Security section: Password change form
- Danger zone section: Red-tinted, logout + delete account
Each section uses:
.account-section(or.account-section.account-section-danger)buildSectionHeader()pattern (heading + description + optional action).account-field-rowfor label-value pairs.status-dotfor all status indicators
Step 2: Commit HTML
git add docs/account.html
git commit -m "Account page: restructure to stacked sections layout"
Task 4: Rewrite account.js to render new layout
Files:
- Modify:
docs/assets/js/account.js— updaterenderAccount()and related functions
Step 1: Update renderAccount() to use new element IDs and patterns
Key changes:
- Replace
.account-value.status-*class toggling withbuildStatusDot()calls - Replace inline
el.className = ...status styling with status-dot badges - Use
.account-field-rowlayout instead of old.account-field+.account-label+.account-value - Use
.account-plan-featurepills instead of.account-feature-tag - Use
.account-invoice-tablewith status dots for billing history - Use
.account-masked-keylayout for API key display - Use
.account-card-iconfor payment card display
Step 2: Update renderPaymentMethod() to use status dots and new layout
Step 3: Update renderBillingHistory() to use invoice table with status dots
Step 4: Update renderPlanFeatures() to use feature pills
Step 5: Commit JS
git add docs/assets/js/account.js
git commit -m "Account page: rewrite JS for stacked sections + status dots + feature pills"
Phase 4: Analytics Growth Intelligence — Backend
Task 5: Expand breakdown endpoint with day×hour heatmap data
Files:
- Modify:
terraform/lambda_src/analytics.mjs— updatehandleGetBreakdown
Step 1: Add day-of-week tracking to the pagination loop
In the existing handleGetBreakdown function, add these accumulators before the do loop:
const signupByDayOfWeek = new Array(7).fill(0);
const signupByDayHour = Array.from({ length: 7 }, () => new Array(24).fill(0));
Inside the for (const item of result.items) loop, after the existing signupByHour[hour]++, add:
if (item.created_at) {
const date = new Date(item.created_at);
const dayOfWeek = date.getUTCDay(); // 0=Sun, 1=Mon, ..., 6=Sat
signupByDayOfWeek[dayOfWeek]++;
signupByDayHour[dayOfWeek][date.getUTCHours()]++;
}
Note: The created_at parsing and hour variable already exist, so just add the day-of-week lines after them.
Step 2: Add velocity stats computation after the pagination loop
After the } while (lastEvaluatedKey); line, add:
// Compute velocity stats
const totalSignups = Object.values(deviceBreakdown).reduce((a, b) => a + b, 0);
const dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
let bestDayIdx = 0;
let worstDayIdx = 0;
for (let i = 1; i < 7; i++) {
if (signupByDayOfWeek[i] > signupByDayOfWeek[bestDayIdx]) bestDayIdx = i;
if (signupByDayOfWeek[i] < signupByDayOfWeek[worstDayIdx]) worstDayIdx = i;
}
let peakHour = 0;
for (let i = 1; i < 24; i++) {
if (signupByHour[i] > signupByHour[peakHour]) peakHour = i;
}
Step 3: Add new fields to the response
In the return jsonResponse(200, { ... }), add:
signup_by_day_of_week: signupByDayOfWeek,
signup_by_day_hour: signupByDayHour,
velocity: {
total_signups: totalSignups,
best_day: dayNames[bestDayIdx],
best_day_count: signupByDayOfWeek[bestDayIdx],
worst_day: dayNames[worstDayIdx],
worst_day_count: signupByDayOfWeek[worstDayIdx],
peak_hour: peakHour,
peak_hour_count: signupByHour[peakHour],
},
Step 4: Validate Terraform
Run: cd terraform && terraform validate
Expected: Success
Step 5: Commit
git add terraform/lambda_src/analytics.mjs
git commit -m "Breakdown endpoint: add day-of-week heatmap + velocity stats"
Phase 5: Analytics Growth Intelligence — Frontend
Task 6: Add HTML sections for new analytics panels
Files:
- Modify:
docs/analytics.html— add new section placeholders
Step 1: Add sections between the KPI grid and the main chart
After the analytics-kpi-grid div (line 73) and before the analytics-main-chart div (line 76), insert:
<!-- Signup Velocity -->
<div class="glass-card analytics-panel-card" id="analytics-velocity">
<p class="loading">Loading velocity...</p>
</div>
After the Growth Insights section (around line 110, before the comparison table), insert:
<!-- Signup Activity Heatmap -->
<div class="glass-card analytics-panel-card" id="analytics-heatmap">
<p class="loading">Loading heatmap...</p>
</div>
<!-- Geographic + Leaderboard -->
<div class="analytics-two-grid">
<div class="glass-card analytics-panel-card" id="analytics-timezones">
<p class="loading">Loading...</p>
</div>
<div class="glass-card analytics-panel-card" id="analytics-leaderboard">
<p class="loading">Loading...</p>
</div>
</div>
<!-- Actionable Insights -->
<div class="glass-card analytics-panel-card" id="analytics-insights">
<p class="loading">Loading insights...</p>
</div>
Step 2: Commit
git add docs/analytics.html
git commit -m "Analytics HTML: add velocity, heatmap, leaderboard, insights placeholders"
Task 7: Add aggregateBreakdown expansion + 4 new render functions
Files:
- Modify:
docs/assets/js/analytics.js
Step 1: Expand aggregateBreakdown() to include new fields
In the existing aggregateBreakdown() function, add accumulators for:
signupByDayOfWeek— 7-element arraysignupByDayHour— 7×24 nested arrayvelocity— merged best/worst day and peak hour from per-waitlist data
Merge logic: sum the arrays across selected waitlists.
Step 2: Update renderDashboard() to call new functions
After the existing renderGrowthInsights(breakdown) call, add:
renderVelocity(breakdown, viewsByDay, signupsByDay, currentDays, previousDays);
renderHeatmap(breakdown);
renderTimezones(breakdown);
renderLeaderboard(currentDays);
renderInsights(breakdown, viewsByDay, signupsByDay, currentDays, previousDays);
Step 3: Add renderVelocity()
Renders into #analytics-velocity:
- Header: “Signup Velocity”
- Stats row: daily average, best day + count, worst day + count, peak hour
- Rolling average bar chart (reuse overlay-bar pattern from existing main chart)
- Actionable text: “Your signups peak on {bestDay} at {peakHour}:00 UTC.”
Step 4: Add renderHeatmap()
Renders into #analytics-heatmap:
- Header: “Signup Activity”
- CSS Grid: 7 rows × 24 columns
- Row labels: Mon, Tue, Wed, Thu, Fri, Sat, Sun
- Cell background: accent color with opacity from 0.05 (zero) to 1.0 (max)
- Tooltip on each cell: “{Day} {Hour}:00 — {count} signups”
- Legend bar: Low → High with 4 sample swatches
Step 5: Add renderTimezones()
Renders into #analytics-timezones:
- Header: “Top Timezones”
- Reuse
buildHorizontalBarsElement()with timezone data from breakdown
Step 6: Add renderLeaderboard()
Renders into #analytics-leaderboard:
- Header: “Waitlist Leaderboard”
- Ranked list of all waitlists with: rank number, name, total signups in period, delta arrow + percentage vs previous period
- Highlight best performer (green background tint) and worst (red background tint)
Step 7: Add renderInsights()
Renders into #analytics-insights:
- Header: “Growth Insights” with lightbulb icon
- Rule-based insights list, each as
.analytics-insight-item:- Viral rate: if > 20% → positive (“Above 20% benchmark”), if < 10% → negative (“Below 10%, consider incentivizing referrals”)
- Mobile share: if device_breakdown.mobile > 60% of total → neutral (“72% of signups from mobile — optimize embed for mobile”)
- Declining waitlists: any waitlist with negative delta → negative (“{name} declined {pct}%. Send a blast email → [link]”)
- Best timing: positive (“Peak day: {day}, peak hour: {hour}:00 UTC. Schedule blasts before peak.”)
- Unverified rate: if unverified / total > 30% → negative (“31% unverified. Enable verification reminders.”)
- Empty state: “Not enough data for insights yet.”
Step 8: Commit
git add docs/assets/js/analytics.js
git commit -m "Analytics JS: velocity, heatmap, leaderboard, and rule-based insights"
Phase 6: Dashboard Consistency
Task 8: Replace dashboard summary cards with stat-card-v2
Files:
- Modify:
docs/dashboard.html— replace summary grid HTML (lines 71-114) - Modify:
docs/assets/js/dashboard.js— rewriteshowDashboardSummary()(lines 1181-1230)
Step 1: Replace HTML summary grid
Replace the 4 .summary-card divs with a single container:
<div id="dashboard-summary" class="stat-card-v2-grid">
<div class="stat-card-v2"><div class="analytics-skeleton analytics-skeleton-value"></div><div class="analytics-skeleton analytics-skeleton-label"></div></div>
<div class="stat-card-v2"><div class="analytics-skeleton analytics-skeleton-value"></div><div class="analytics-skeleton analytics-skeleton-label"></div></div>
<div class="stat-card-v2"><div class="analytics-skeleton analytics-skeleton-value"></div><div class="analytics-skeleton analytics-skeleton-label"></div></div>
<div class="stat-card-v2"><div class="analytics-skeleton analytics-skeleton-value"></div><div class="analytics-skeleton analytics-skeleton-label"></div></div>
</div>
Step 2: Rewrite showDashboardSummary()
Use buildStatCardV2() to create 4 cards:
- Total Signups — sum of all waitlist signup_counts, 7-day sparkline from stats, delta vs previous period
- Today’s Signups — from stats if available (or 0), delta vs yesterday
- Viral Rate — referral_signups / total_signups × 100, delta as pp change
- Active Waitlists — count of waitlists with signups, plan badge as subtitle text
For sparkline data: If per-waitlist stats endpoint provides daily data, use it. Otherwise, show sparkline only when analytics data is loaded (Pro users).
Step 3: Add mini sparklines to waitlist cards
In the existing waitlist card rendering function, add a small SVG sparkline showing the 7-day signup trend if data is available. This is a bonus enhancement — uses the same buildSvgSparkline() helper.
Step 4: Commit
git add docs/dashboard.html docs/assets/js/dashboard.js
git commit -m "Dashboard: stat-card-v2 summary cards with sparklines + deltas"
Phase 7: Templates, Domains, Team Polish
Task 9: Templates — action empty state + type icons
Files:
- Modify:
docs/assets/js/templates.js— update empty state rendering (~line 23)
Step 1: Replace empty state with action empty state
Replace the current innerHTML empty state with DOM-built action empty state that includes:
- Icon
- “No email templates yet”
- Description with list of what templates are for
- Two CTAs: “Create Template” (primary) + “Browse Starters” (outline)
Step 2: Add type icons to template cards
In the template card rendering, prepend a small SVG icon based on template type:
signup_confirmation→ envelope iconreferral_milestone→ gift iconoffboarding→ wave iconblast→ megaphone icon
Step 3: Commit
git add docs/assets/js/templates.js
git commit -m "Templates: action empty state + type icons on cards"
Task 10: Domains — status dots + copy buttons
Files:
- Modify:
docs/assets/js/domains.js— update domain card and DNS record rendering
Step 1: Replace status text with status-dot badges
In domain card rendering, replace the plain status-badge span with a buildStatusDot() call:
verified→buildStatusDot("verified", "Verified")pending→buildStatusDot("pending", "Pending")
Step 2: Add copy buttons to DNS record values
In the DNS record display, add a small “Copy” button next to each record value that copies the value to clipboard using navigator.clipboard.writeText().
Step 3: Add progress indicator
Above the domains grid, add a “2 of 3 domains verified” progress text.
Step 4: Commit
git add docs/assets/js/domains.js
git commit -m "Domains: status-dot badges, copy buttons on DNS records, progress indicator"
Task 11: Team — role badges + (You) label + pending status
Files:
- Modify:
docs/assets/js/team.js— update member card rendering
Step 1: Add role color coding
Update role badge classes:
- Owner → accent background (indigo)
- Admin → blue background
- Viewer → muted background
Step 2: Add (You) label
Compare member email to current user email (from getUser()). If match, append “(You)” span after the email.
Step 3: Add pending invite badge
If member has status === "pending", show a yellow buildStatusDot("pending", "Pending Invite") badge.
Step 4: Commit
git add docs/assets/js/team.js
git commit -m "Team: role color badges, (You) label, pending invite status"
Phase 8: Verification
Task 12: Final verification pass
Step 1: Terraform validate
Run: cd terraform && terraform validate
Expected: Success
Step 2: Jekyll build
Run: cd docs && bundle exec jekyll build
Expected: Success (ignore pre-existing Liquid warnings in integration-react.md)
Step 3: Grep for remaining inline display:none
Run grep on all modified files to verify no regressions:
docs/account.html— should have zerostyle="display:none;"docs/dashboard.html— should have zero new inline styles
Step 4: Visual consistency check
Verify all pages use consistent patterns:
- Account: stacked glass sections with section-header-v2
- Dashboard: stat-card-v2 grid with sparklines
- Analytics: velocity + heatmap + leaderboard + insights
- Templates/Domains/Team: status dots, action empty states
Step 5: Final commit + push
git push origin main