# Solution Design Document

## Validation Checklist

- [x] All required sections are complete
- [x] No [NEEDS CLARIFICATION] markers remain
- [x] All context sources are listed with relevance ratings
- [x] Project commands are discovered from actual project files
- [x] Constraints → Strategy → Design → Implementation path is logical
- [x] Architecture pattern is clearly stated with rationale
- [x] Every component in diagram has directory mapping
- [x] Every interface has specification
- [x] Error handling covers all error types
- [x] Quality requirements are specific and measurable
- [x] Every quality requirement has test coverage
- [x] **All architecture decisions confirmed by user** _(ADRs 1-5 confirmed 2026-01-14)_
- [x] Component names consistent across diagrams
- [x] A developer could implement from this design
- [x] PRD requirements mapped to design elements (Codex blocker resolved)
- [x] Team+ landing page fully specified (Codex blocker resolved)
- [x] Stats pipeline queries and schedule defined (Codex blocker resolved)
- [x] Demo form API aligned with PRD required fields (Codex blocker resolved)

---

## Constraints

CON-1 **Platform/Framework**
- PHP 8.x with Slim 2.6.2 framework
- Twig 1.44.8 templating engine
- MySQL databases (multi-store architecture)
- Bootstrap 5.3.3 CSS framework
- No React/Vue/modern SPA frameworks (server-rendered pages)

CON-2 **Browser Support**
- Chrome, Safari, Firefox, Edge (latest 2 versions)
- NO Internet Explorer 11 support
- Mobile-responsive required (375px - 1440px breakpoints)

CON-3 **Performance Targets**
- LCP (Largest Contentful Paint) < 2.5 seconds
- CLS (Cumulative Layout Shift) < 0.1
- FID (First Input Delay) < 100ms
- All images must lazy-load (except hero)

CON-4 **Accessibility**
- WCAG 2.1 Level AA compliance
- Proper semantic HTML (nav, section, main, footer)
- Keyboard navigation support
- Color contrast ratios must pass

CON-5 **Timeline**
- ASAP delivery (2-3 weeks)
- Focus on essentials, defer nice-to-haves
- Use existing design system components where possible

CON-6 **Existing Patterns**
- Must use existing CSS design system (tokens.css, admin-theme.css patterns)
- Must follow existing BEM naming: `modern-[component]__[element]--[modifier]`
- Must use existing form validation patterns (NoCSRF, filter_var)
- Must use existing demo lead capture system (demo_leads table)

---

## Implementation Context

### Required Context Sources

```yaml
# Internal Documentation & Patterns
- doc: CLAUDE.md
  relevance: CRITICAL
  why: "Project conventions, commands, and patterns"

- doc: docs/specs/029-homepage-redesign-feature-showcase/product-requirements.md
  relevance: CRITICAL
  why: "Complete feature requirements, acceptance criteria, and tracking events"

# Existing Codebase Patterns
- file: userfrosting/templates/common/home.html
  relevance: CRITICAL
  why: "Current homepage template - 1073 lines, 13 sections to redesign"

- file: public_html/css/front/modern-home.css
  relevance: HIGH
  why: "Current homepage CSS patterns and design tokens (~44KB)"

- file: public_html/css/admin/tokens.css
  relevance: HIGH
  why: "Design system tokens (colors, spacing, typography) - 9.5KB"

- file: userfrosting/src/BuyerKiosk/Core/Controllers/AccountController.php
  relevance: HIGH
  sections: [lines 51-63]
  why: "Homepage controller - pageHome() method"

- file: userfrosting/src/BuyerKiosk/TaskEngine/Jobs/StoreMetricsAggregatorJob.php
  relevance: HIGH
  why: "Best template for HomepageStatsJob - global aggregation pattern"

- file: userfrosting/routes/demo/api.php
  relevance: HIGH
  sections: [lines 229-286]
  why: "Demo form submission API pattern"

- file: userfrosting/src/BuyerKiosk/Demo/Repositories/LeadRepository.php
  relevance: MEDIUM
  why: "Lead data persistence pattern"

# External Documentation
- url: https://posthog.com/docs/libraries/js
  relevance: HIGH
  why: "PostHog JavaScript SDK integration for analytics"

- url: https://posthog.com/docs/product-analytics/autocapture
  relevance: MEDIUM
  why: "PostHog automatic event capture configuration"
```

### Implementation Boundaries

**Must Preserve:**
- Existing URL structure (homepage at `/`)
- Login flow and authenticated areas (unchanged)
- Demo request CRM/follow-up process
- Existing GA tracking (UA-80211901-1) during transition
- Mobile hamburger menu behavior

**Can Modify:**
- All homepage template content and structure
- Homepage CSS (modern-home.css)
- Homepage JavaScript (currently inline)
- Demo form fields and validation
- Stats display (currently hardcoded)

**Must Not Touch:**
- Admin panel templates/routes
- Store-specific routes (`/:typeNum/`)
- Mobile API endpoints
- Authentication system
- Core database schema (except new cache table)

### External Interfaces

#### System Context Diagram

```mermaid
graph TB
    subgraph "Visitors"
        Prospect[Prospective Store Owner]
        Customer[Existing Customer]
    end

    subgraph "BuyerKiosk Web"
        Homepage[Homepage / Team+ Page]
        DemoForm[Demo Request Form]
        StatsCache[Stats Cache]
    end

    subgraph "External Services"
        PostHog[PostHog Analytics]
        YCBM[YouCanBookMe]
        Email[SMTP/Email]
    end

    subgraph "Internal Systems"
        CentralDB[(kiosk_buykiosk DB)]
        StoreDBs[(Store Databases)]
        TaskEngine[TaskEngine Scheduler]
    end

    Prospect --> Homepage
    Customer --> Homepage
    Homepage --> PostHog
    DemoForm --> CentralDB
    DemoForm --> Email
    Homepage --> YCBM
    Homepage --> StatsCache
    TaskEngine --> StoreDBs
    TaskEngine --> StatsCache
    StatsCache --> CentralDB
```

#### Interface Specifications

```yaml
# Inbound Interfaces
inbound:
  - name: "Homepage Web Interface"
    type: HTTP/HTTPS
    format: HTML (Twig rendered)
    authentication: None (public page)
    routes: ["/", "/team-plus"]
    data_flow: "Static content + cached stats"

  - name: "Demo Form API"
    type: HTTPS
    format: JSON
    authentication: CSRF token
    route: POST /api/demo/contact
    data_flow: "Lead capture and storage"

# Outbound Interfaces
outbound:
  - name: "PostHog Analytics"
    type: HTTPS
    format: JavaScript SDK
    authentication: Project API Key (public)
    data_flow: "Event tracking, pageviews, user properties"
    criticality: MEDIUM

  - name: "YouCanBookMe"
    type: External Link
    format: URL redirect
    authentication: None
    data_flow: "Demo scheduling (external)"
    criticality: LOW

# Data Interfaces
data:
  - name: "Central Database (kiosk_buykiosk)"
    type: MySQL
    connection: PDO via dbConnectByName()
    tables: [demo_leads, homepage_stats, stores]
    data_flow: "Lead storage, stats cache, store list"

  - name: "Store Databases (kiosk_{typeNum})"
    type: MySQL
    connection: PDO via dbConnectByName()
    tables: [buyQueue, dailySalesData]
    data_flow: "Aggregate statistics (read-only)"
```

### Project Commands

```bash
# Development Environment
Install Dependencies: cd userfrosting && composer install
Start Server: php -S localhost:8080 -t public_html/

# CSS Build
Development Build: php userfrosting/conductor build-css
Production Build: php userfrosting/conductor build-css --minify
Watch Mode: php userfrosting/conductor build-css --watch

# Testing
All Tests: ./test.sh
Unit Tests: ./test.sh --testsuite unit
Integration Tests: ./test.sh --testsuite integration
With Coverage: ./test.sh --coverage
PHPStan: ./test.sh --stan

# Database Migrations
Run Migrations: php userfrosting/conductor run

# TaskEngine (for HomepageStatsJob)
Start Worker: php userfrosting/bin/task worker:start
Worker Status: php userfrosting/bin/task worker:manager --status
Queue Status: php userfrosting/bin/task queue:status
List Jobs: php userfrosting/bin/task job:list
Dispatch Job: php userfrosting/bin/task job:dispatch homepage-stats

# Deployment
Full Deploy: ./deploy.sh
```

---

## PRD Requirements Mapping

This section maps every PRD acceptance criterion to its design/implementation element.

### Feature 1: Integrated Platform Hero Section (PRD lines 113-123)

| PRD Acceptance Criterion | Design Element | Implementation |
|-------------------------|----------------|----------------|
| Headline max 10 words | `partials/homepage/hero.html` | Twig variable `{{ hero.headline }}` with copy review |
| Subhead max 25 words | `partials/homepage/hero.html` | Twig variable `{{ hero.subhead }}` |
| Primary CTA "Schedule Demo" above fold at 1024x768/375x812 | `hero.html` + CSS | `.modern-hero__cta--primary` positioned in hero grid |
| Secondary CTA below primary | `hero.html` | `.modern-hero__cta--secondary` below primary |
| At least 3 franchise logos in hero | `hero.html` | `{% for logo in hero.logos %}` with logos array (min 3) |
| Breakpoints: 375px, 768px, 1024px, 1440px | `modern-home.css` | Media queries for all breakpoints |
| PostHog `hero_cta_clicked` with `cta_type` | `homepage.js` | `posthog.capture('hero_cta_clicked', { cta_type, position })` |
| LCP < 2.5s on 4G | Performance strategy | Hero image preload, no lazy-load on hero |

### Feature 2: Feature Showcase Sections (PRD lines 125-133)

| PRD Acceptance Criterion | Design Element | Implementation |
|-------------------------|----------------|----------------|
| 11 feature sections | `partials/homepage/features/` | 11 partial files (kiosk, signage, daybook, etc.) |
| Consistent structure per section | Template pattern | Each partial: h3 (max 6 words), p (max 50 words), 3-4 li bullets, img |
| `data-posthog-section` attribute | Each section partial | `<section data-posthog-section="kiosk">` etc. |
| PostHog `section_viewed` via Intersection Observer | `homepage.js` | IntersectionObserver at 50% visibility threshold |
| Lazy-load section visuals | Each partial | `<img loading="lazy" src="...">` |
| Single column mobile, 2-column desktop | `modern-home.css` | CSS Grid with `@media (min-width: 768px)` |

### Feature 3: Team+ Premium Section (PRD lines 135-144)

| PRD Acceptance Criterion | Design Element | Implementation |
|-------------------------|----------------|----------------|
| Visually distinct (different background) | `team-plus-section.html` | `.modern-team-plus--highlight` with `var(--primary-50)` |
| Price "$30/month per store" >= 1.25rem | CSS | `.modern-team-plus__price { font-size: 1.5rem; }` |
| "Premium Add-On" badge visible | `team-plus-section.html` | `.modern-badge--premium` |
| 5 feature bullets | `team-plus-section.html` | ul with: Scheduling, AI Scheduling, Staff Chat, Team App, Live App |
| "Learn More" CTA to `/team-plus` | `team-plus-section.html` | `<a href="/team-plus">` |
| PostHog `team_plus_cta_clicked` | `homepage.js` | `posthog.capture('team_plus_cta_clicked', { source: 'homepage' })` |
| At least one visual | `team-plus-section.html` | Screenshot of scheduling calendar |

### Feature 4: Social Proof & Stats Section (PRD lines 146-161)

| PRD Acceptance Criterion | Design Element | Implementation |
|-------------------------|----------------|----------------|
| 6 dynamic stats in grid (2x3 mobile, 6x1 desktop) | `stats.html` + CSS | CSS Grid with responsive columns |
| Stores Served from DB | HomepageStatsJob | `SELECT COUNT(*) FROM kiosk_buykiosk.stores WHERE active = 1` |
| Years in Business static | homepage_stats seed | `{ statKey: 'years_in_business', displayValue: '10+ Years' }` |
| Franchise Brands from DB | HomepageStatsJob | `SELECT COUNT(DISTINCT concept) FROM kiosk_buykiosk.stores WHERE active = 1` |
| Dollar Value from buyQueue | HomepageStatsJob | Loop all stores: `SUM(buyTotal) FROM buyQueue` |
| Buys Processed from stores | HomepageStatsJob | `SELECT SUM(buyCount) FROM kiosk_buykiosk.stores` |
| Uptime static | homepage_stats seed | `{ statKey: 'uptime_percent', displayValue: '99.9%' }` |
| TaskEngine job at 2am daily | Job schedule | `schedule: '0 2 * * *'` in task_job_definitions |
| Fallback if stale >48hrs | HomepageStatsService | Check `MAX(updatedAt)` vs NOW(), use last values if stale |
| At least 6 franchise logos | `stats.html` | Logo grid with PIAS, PC, CM, OUAC, SE, SW |
| 2-3 testimonials | `testimonials.html` | Quote, name, store name, store type |
| Count-up animation on scroll | `homepage.js` | countUp.js library or vanilla JS animate |
| PostHog `stats_section_viewed` | `homepage.js` | IntersectionObserver trigger |

### Feature 5: Pricing Section (PRD lines 163-171)

| PRD Acceptance Criterion | Design Element | Implementation |
|-------------------------|----------------|----------------|
| 3 tiers: Phone, Apparel, Sports | `pricing.html` | 3 pricing cards in grid |
| Team+ addon "$30/month" as optional | `pricing.html` | Add-on callout below main tiers |
| Each tier: price, 3-5 bullets, CTA | `pricing.html` | Consistent card structure |
| Enterprise "Contact for pricing" | `pricing.html` | Footer text with contact link |
| PostHog `pricing_section_viewed` | `homepage.js` | IntersectionObserver trigger |
| PostHog `pricing_cta_clicked` with tier | `homepage.js` | `posthog.capture('pricing_cta_clicked', { tier })` |

### Feature 6: Primary CTA / Demo Form (PRD lines 173-183)

| PRD Acceptance Criterion | Design Element | Implementation |
|-------------------------|----------------|----------------|
| CTA in hero, mid-page, footer | `hero.html`, `features/`, `footer.html` | Button triggers modal |
| Button text "Schedule Demo" | All CTA buttons | Consistent copy |
| Required fields: name, email, phone, store name, store type | `demo-form.html` + API | All fields required, validated |
| Store type dropdown | `demo-form.html` | Options: Resale/Consignment, Sporting Goods, Phone Check-In, Other |
| Email/phone validation | Client + server | filter_var for email, regex for phone (10+ digits) |
| POST to demo endpoint | `demo-form.html` JS | fetch POST to `/api/demo/contact` |
| PostHog `demo_form_opened` | `homepage.js` | On modal open |
| PostHog `demo_form_submitted` with store_type | `homepage.js` | On successful submit |
| Inline validation, no page reload | `demo-form.html` JS | Client-side validation, fetch API |

### Feature 7: Team+ Landing Page (PRD lines 187-204)

| PRD Acceptance Criterion | Design Element | Implementation |
|-------------------------|----------------|----------------|
| Route `/team-plus` | `routes/index.php` | New GET route |
| Nav link + homepage CTA link | `nav.html`, `team-plus-section.html` | `href="/team-plus"` |
| Hero max 8 words, "replace WhenIWork/Homebase" | `team-plus.html` hero | Twig variable with copy constraint |
| 5 feature sections with screenshots | `team-plus.html` | Scheduling, AI, Chat, Team App, Live App |
| Pricing "$30/month" with breakdown | `team-plus.html` pricing | Feature list under price |
| Competitor comparison table | `team-plus.html` | Table: Feature / Team+ / WhenIWork / Homebase |
| CTA uses main demo form with `team_plus_interest=true` | `team-plus.html` + `demo-form.html` | Hidden field or JS flag |
| Mobile screenshots only (no badges) | `team-plus.html` | Device mockups, no app store links |
| PostHog `team_plus_page_view` with source | `team-plus.html` JS | `posthog.capture('team_plus_page_view', { source })` |
| PostHog `team_plus_demo_requested` | `homepage.js` | Fire on demo submit from Team+ page |
| LCP < 2.5s | Performance strategy | Lazy-load non-hero images |

### Feature 10: Updated Navigation (PRD lines 222-227)

| PRD Acceptance Criterion | Design Element | Implementation |
|-------------------------|----------------|----------------|
| Nav items: Features, Team+, Pricing, Contact | `nav.html` | `<nav>` with anchor links + Team+ page link |
| Mobile hamburger menu | `nav.html` + CSS + JS | `.modern-nav__toggle` with `aria-expanded` |
| Login link | `nav.html` | Link to existing login route |

### Should Have Features Scope Decision

| Feature | Status | Rationale |
|---------|--------|-----------|
| Feature 8: Demo Video | **INCLUDED** | Existing videos available per PRD |
| Feature 9: Mobile App Showcase | **INCLUDED** | Screenshots only (apps not in stores) |
| Feature 11: Case Studies | **DEFERRED** | Content not available, nice-to-have |
| Feature 12: Resource Center | **DEFERRED** | Out of scope for 2-3 week timeline |

---

## Solution Strategy

### Architecture Pattern: **Server-Rendered Modular Template Architecture**

The homepage redesign follows a **server-rendered modular architecture** where:
- Twig templates are organized into modular partials (one per section)
- CSS follows BEM methodology with design tokens
- JavaScript is minimal (vanilla JS for interactivity)
- Dynamic data is cached via TaskEngine scheduled job
- Analytics are event-driven via PostHog SDK

**Why this approach:**
1. **Matches existing patterns** - Stays consistent with BuyerKiosk's Slim/Twig architecture
2. **Fast time-to-market** - No new framework learning curve
3. **Performance-first** - Server-rendered pages are fast, no hydration delays
4. **SEO-friendly** - Full content available on initial load
5. **Simple caching** - Stats cached at DB level, no complex CDN setup needed

### Integration Approach

| Component | Integration Point | Method |
|-----------|-------------------|--------|
| Homepage Template | `templates/common/home.html` | **Replace** existing template |
| Team+ Landing | `templates/common/team-plus.html` | **New** template |
| Homepage Controller | `AccountController::pageHome()` | **Modify** to load cached stats |
| Team+ Controller | `AccountController::pageTeamPlus()` | **New** method |
| Stats Job | `TaskEngine/Jobs/HomepageStatsJob.php` | **New** job class |
| Stats Cache Table | `kiosk_buykiosk.homepage_stats` | **New** table via migration |
| Demo Form | Existing `demo_leads` system | **Enhance** with store_type field |
| PostHog | JavaScript SDK in template head | **New** integration |

### Key Decisions

1. **Modular Partials** - Break monolithic 1073-line template into section partials for maintainability
2. **Cached Stats** - Daily job caches aggregate stats to avoid expensive cross-DB queries on page load
3. **PostHog over GA** - Modern analytics with heatmaps and session recording (keep GA during transition)
4. **Same Demo Form** - Enhance existing demo lead system rather than building new

---

## Building Block View

### Components

```mermaid
graph TB
    subgraph "Templates"
        Home[home.html]
        TeamPlus[team-plus.html]

        subgraph "Partials"
            Nav[nav.html]
            Hero[hero.html]
            Features[features/]
            TeamSection[team-plus-section.html]
            Stats[stats.html]
            Pricing[pricing.html]
            Testimonials[testimonials.html]
            DemoForm[demo-form.html]
            Footer[footer.html]
        end
    end

    subgraph "Controllers"
        AccountCtrl[AccountController]
    end

    subgraph "Services"
        StatsService[HomepageStatsService]
    end

    subgraph "TaskEngine"
        StatsJob[HomepageStatsJob]
    end

    subgraph "Database"
        StatsCache[(homepage_stats)]
        DemoLeads[(demo_leads)]
    end

    Home --> Nav
    Home --> Hero
    Home --> Features
    Home --> TeamSection
    Home --> Stats
    Home --> Pricing
    Home --> Testimonials
    Home --> DemoForm
    Home --> Footer

    TeamPlus --> Nav
    TeamPlus --> TeamPlusHero[team-plus-hero.html]
    TeamPlus --> TeamPlusFeatures[team-plus-features/]
    TeamPlus --> TeamPlusComparison[comparison-table.html]
    TeamPlus --> TeamPlusPricing[team-plus-pricing.html]
    TeamPlus --> DemoForm
    TeamPlus --> Footer

    AccountCtrl --> Home
    AccountCtrl --> TeamPlus
    AccountCtrl --> StatsService

    StatsService --> StatsCache
    StatsJob --> StatsCache
    DemoForm --> DemoLeads
```

### Directory Map

```
userfrosting/
├── templates/
│   └── common/
│       ├── home.html                              # MODIFY: Main homepage template
│       ├── team-plus.html                         # NEW: Team+ landing page
│       └── partials/
│           └── team-plus/                         # NEW: Team+ page partials
│               ├── hero.html                      # NEW: Team+ hero section
│               ├── features/                      # NEW: 5 feature sections
│               │   ├── scheduling.html
│               │   ├── ai-scheduling.html
│               │   ├── staff-chat.html
│               │   ├── team-app.html
│               │   └── live-app.html
│               ├── comparison-table.html          # NEW: Competitor comparison
│               └── pricing.html                   # NEW: Team+ pricing breakdown
│       └── partials/
│           └── homepage/                          # NEW: Homepage partials directory
│               ├── nav.html                       # NEW: Navigation partial
│               ├── hero.html                      # NEW: Hero section
│               ├── features/                      # NEW: Feature section partials
│               │   ├── kiosk.html
│               │   ├── signage.html
│               │   ├── daybook.html
│               │   ├── backstock.html
│               │   ├── floor-plan.html
│               │   ├── customer-chat.html
│               │   ├── reporting.html
│               │   ├── events.html
│               │   ├── comeback-cash.html
│               │   ├── quickbooks.html
│               │   └── fivestars.html
│               ├── team-plus-section.html         # NEW: Team+ homepage section
│               ├── stats.html                     # NEW: Dynamic stats section
│               ├── pricing.html                   # NEW: Pricing section
│               ├── testimonials.html              # NEW: Testimonials
│               ├── demo-form.html                 # NEW: Demo request form modal
│               └── footer.html                    # NEW: Footer partial
│
├── src/BuyerKiosk/
│   ├── Core/
│   │   ├── Controllers/
│   │   │   └── AccountController.php              # MODIFY: Add pageTeamPlus(), modify pageHome()
│   │   └── Services/
│   │       └── HomepageStatsService.php           # NEW: Stats cache service
│   │
│   └── TaskEngine/
│       └── Jobs/
│           └── HomepageStatsJob.php               # NEW: Daily stats aggregation job
│
├── routes/
│   └── api.php                                    # MODIFY: Add store_type to demo form
│
└── migrations/
    └── input/
        ├── 20260113_001_homepage_stats_table.json # NEW: Stats cache table
        └── 20260113_002_homepage_stats_job.json   # NEW: Job registration

public_html/
├── css/
│   └── front/
│       └── modern-home.css                        # MODIFY: Update styles for new sections
│
├── js/
│   └── front/
│       └── homepage.js                            # NEW: Extracted JS (analytics, interactions)
│
└── images/
    └── homepage/                                  # NEW: Screenshot/visual assets
        ├── features/                              # Feature screenshots
        ├── apps/                                  # Mobile app mockups
        └── logos/                                 # Franchise logos
```

### Interface Specifications

#### Data Storage Changes

```yaml
# NEW TABLE: homepage_stats (cache for aggregated statistics)
Table: homepage_stats
  Location: kiosk_buykiosk database

  Columns:
    id: INT PRIMARY KEY AUTO_INCREMENT
    statKey: VARCHAR(50) NOT NULL UNIQUE
    statValue: VARCHAR(100) NOT NULL
    displayValue: VARCHAR(100) NOT NULL  # Pre-formatted for display
    updatedAt: DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP

  Indexes:
    - PRIMARY (id)
    - UNIQUE (statKey)

  Staleness Rule:
    - Query: SELECT MAX(updatedAt) FROM homepage_stats
    - If MAX(updatedAt) < NOW() - INTERVAL 48 HOUR: return last known values, log warning
    - If table empty: return hardcoded fallback values, log error

  Initial Seed Data (fallback values):
    - statKey: 'stores_served', statValue: '127', displayValue: '127+'
    - statKey: 'years_in_business', statValue: '10', displayValue: '10+ Years'
    - statKey: 'franchise_brands', statValue: '8', displayValue: '8'
    - statKey: 'dollar_value_processed', statValue: '847000000', displayValue: '$847M+'
    - statKey: 'buys_processed', statValue: '25600000', displayValue: '25.6M+'
    - statKey: 'uptime_percent', statValue: '99.9', displayValue: '99.9%'
```

#### HomepageStatsJob Aggregation Queries

```sql
-- Job Schedule: '0 2 * * *' (2:00 AM daily)
-- Timeout: 600 seconds
-- Queue: low
-- Scope: global

-- 1. Stores Served (dynamic)
SELECT COUNT(*) AS stores_served
FROM kiosk_buykiosk.stores
WHERE active = 1;

-- 2. Franchise Brands (dynamic)
SELECT COUNT(DISTINCT concept) AS franchise_brands
FROM kiosk_buykiosk.stores
WHERE active = 1 AND concept IS NOT NULL;

-- 3. Buys Processed (from central stores table)
SELECT SUM(buyCount) AS buys_processed
FROM kiosk_buykiosk.stores;

-- 4. Dollar Value Processed (loop all store DBs)
-- For each active store:
SELECT SUM(buyTotal) AS store_total
FROM kiosk_{typeNum}.buyQueue;
-- Aggregate all store totals

-- 5. Years in Business: Static '10' (founded 2015)
-- 6. Uptime: Static '99.9'

-- Update cache with REPLACE
REPLACE INTO homepage_stats (statKey, statValue, displayValue, updatedAt)
VALUES
  ('stores_served', ?, ?, NOW()),
  ('franchise_brands', ?, ?, NOW()),
  ('buys_processed', ?, ?, NOW()),
  ('dollar_value_processed', ?, ?, NOW()),
  ('years_in_business', '10', '10+ Years', NOW()),
  ('uptime_percent', '99.9', '99.9%', NOW());
```

#### Internal API Changes

```yaml
# EXISTING ENDPOINT - Enhanced for Homepage Demo Form
Endpoint: Create Demo Lead (Homepage)
  Method: POST
  Path: /api/homepage/demo-request  # NEW dedicated endpoint
  Note: Separate from existing /api/demo/contact to handle different field requirements

  Request (all REQUIRED per PRD):
    name: string (required)           # Contact name
    email: string (required)          # Valid email format (filter_var)
    phone: string (required)          # 10+ digits
    store_name: string (required)     # Business/store name
    store_type: string (required)     # Enum: 'resale', 'sporting', 'phone', 'other'
    team_plus_interest: boolean (optional, default: false)  # Set true from Team+ page
    csrf_token: string (required)     # NoCSRF protection

  Validation Rules:
    - email: filter_var($email, FILTER_VALIDATE_EMAIL)
    - phone: preg_match('/^\d{10,}$/', preg_replace('/\D/', '', $phone))
    - store_type: in_array($type, ['resale', 'sporting', 'phone', 'other'])
    - All required fields non-empty after trim()

  Response:
    success (200):
      success: true
      message: "Thanks! Our team will reach out soon."
      lead_id: integer
    validation_error (400):
      success: false
      error: "VALIDATION_ERROR"
      message: "Please check your input"
      fields: { field_name: "Error message" }
    server_error (500):
      success: false
      error: "CREATE_FAILED"
      message: "Something went wrong. Please try again."

  Database:
    - Inserts into demo_leads table with new columns:
      - store_name: VARCHAR(255)
      - store_type: VARCHAR(50)
      - team_plus_interest: TINYINT(1) DEFAULT 0

# NEW INTERNAL SERVICE
Service: HomepageStatsService
  Location: userfrosting/src/BuyerKiosk/Core/Services/HomepageStatsService.php

  Methods:
    getStats(): array
      - Returns all stats from homepage_stats cache
      - Checks staleness (>48hr) and logs warning
      - Falls back to hardcoded values if empty/error

    getStat(string $key): ?array
      - Returns single stat by key
      - Returns null if not found

    isStale(): bool
      - Returns true if MAX(updatedAt) > 48 hours ago

  Response Format:
    {
      'stores_served': { 'value': '127', 'display': '127+' },
      'years_in_business': { 'value': '10', 'display': '10+ Years' },
      'franchise_brands': { 'value': '8', 'display': '8' },
      'dollar_value_processed': { 'value': '847000000', 'display': '$847M+' },
      'buys_processed': { 'value': '25600000', 'display': '25.6M+' },
      'uptime_percent': { 'value': '99.9', 'display': '99.9%' }
    }
```

#### Team+ Competitor Comparison Table (PRD Feature 7)

```yaml
# comparison-table.html data structure
Table: Team+ vs Competitors
  Columns: Feature, Team+, WhenIWork, Homebase

  Rows:
    - feature: "Monthly Price (per store)"
      team_plus: "$30"
      when_i_work: "$4-8/user"
      homebase: "$0-80/location"

    - feature: "Staff Scheduling"
      team_plus: "✓"
      when_i_work: "✓"
      homebase: "✓"

    - feature: "AI-Powered Schedule Generation"
      team_plus: "✓"
      when_i_work: "✗"
      homebase: "✗"

    - feature: "Time Clock / Clock In-Out"
      team_plus: "✓"
      when_i_work: "✓"
      homebase: "✓"

    - feature: "Staff Chat / Messaging"
      team_plus: "✓"
      when_i_work: "✓"
      homebase: "✓ (paid)"

    - feature: "Mobile Employee App"
      team_plus: "✓"
      when_i_work: "✓"
      homebase: "✓"

    - feature: "Mobile Manager App"
      team_plus: "✓"
      when_i_work: "✓"
      homebase: "✓"

    - feature: "Integrates with POS System"
      team_plus: "✓ (BuyerKiosk)"
      when_i_work: "✗"
      homebase: "Limited"

    - feature: "Labor Cost Tracking"
      team_plus: "✓"
      when_i_work: "✓ (paid)"
      homebase: "✓ (paid)"

    - feature: "Geofenced Clock-In"
      team_plus: "✓"
      when_i_work: "✓"
      homebase: "✓"

  Styling:
    - Team+ column highlighted (primary color background)
    - Checkmarks use primary-600 color
    - X marks use neutral-400 color
    - Responsive: horizontal scroll on mobile
```

#### PostHog Integration

```yaml
# PostHog JavaScript SDK Configuration
Integration: PostHog Analytics
  Type: JavaScript SDK (client-side)
  Installation: CDN script tag in template head

  Configuration:
    api_host: 'https://app.posthog.com' # or self-hosted URL
    api_key: 'phc_XXXXXXXXXXXX' # Environment variable
    autocapture: true
    capture_pageview: true
    capture_pageleave: true
    persistence: 'localStorage'

  Custom Events (per PRD):
    - $pageview (auto-captured)
    - section_viewed: { section_name: string, time_visible: number }
    - hero_cta_clicked: { cta_type: 'primary'|'secondary', position: string }
    - team_plus_cta_clicked: { source: 'homepage'|'nav' }
    - pricing_section_viewed: {}
    - pricing_cta_clicked: { tier: string }
    - demo_form_opened: { source: string, page: 'homepage'|'team-plus' }
    - demo_form_submitted: { store_type: string, source: string, team_plus_interest: boolean }
    - demo_form_error: { field: string, error_type: string }
    - team_plus_page_view: { source: 'homepage'|'direct'|'nav' }
    - team_plus_demo_requested: {}  # Fires on demo submit from Team+ page (PRD requirement)
    - video_played: { video_name: string, watch_percent: number }
    - stats_section_viewed: {}

  Section Tracking:
    - Each section has data-posthog-section="[name]" attribute
    - Intersection Observer fires section_viewed when 50% visible
    - time_visible calculated from entry to exit
```

---

## Runtime View

### Primary Flow: Homepage Visit

```mermaid
sequenceDiagram
    actor Visitor
    participant Browser
    participant Slim as Slim Router
    participant Controller as AccountController
    participant StatsService as HomepageStatsService
    participant Cache as homepage_stats
    participant Twig
    participant PostHog

    Visitor->>Browser: Navigate to buyerkiosk.com
    Browser->>Slim: GET /
    Slim->>Controller: pageHome()
    Controller->>StatsService: getStats()
    StatsService->>Cache: SELECT * FROM homepage_stats
    Cache-->>StatsService: Stats array
    StatsService-->>Controller: Formatted stats
    Controller->>Twig: render('home.html', { stats })
    Twig-->>Controller: Rendered HTML
    Controller-->>Browser: HTTP 200 + HTML
    Browser->>PostHog: posthog.capture('$pageview')

    Note over Visitor,PostHog: User scrolls and interacts

    Visitor->>Browser: Scrolls to Stats section
    Browser->>PostHog: posthog.capture('stats_section_viewed')

    Visitor->>Browser: Clicks "Schedule Demo"
    Browser->>PostHog: posthog.capture('hero_cta_clicked', { cta_type: 'primary' })
    Browser->>Browser: Open demo form modal
    Browser->>PostHog: posthog.capture('demo_form_opened')
```

### Primary Flow: Demo Form Submission

```mermaid
sequenceDiagram
    actor Visitor
    participant Browser
    participant API as POST /api/demo/contact
    participant Validator
    participant LeadRepo as LeadRepository
    participant DB as demo_leads
    participant PostHog

    Visitor->>Browser: Fills demo form
    Browser->>Browser: Client-side validation
    Browser->>API: POST { name, email, phone, store_type }
    API->>Validator: Validate CSRF, email format

    alt Validation Fails
        Validator-->>API: Error
        API-->>Browser: 400 { error, message }
        Browser->>PostHog: posthog.capture('demo_form_error')
        Browser->>Browser: Show error message
    else Validation Passes
        Validator-->>API: OK
        API->>LeadRepo: createFromSession(...)
        LeadRepo->>DB: INSERT INTO demo_leads
        DB-->>LeadRepo: lead_id
        LeadRepo-->>API: Lead created
        API-->>Browser: 200 { success, lead_id }
        Browser->>PostHog: posthog.capture('demo_form_submitted')
        Browser->>Browser: Show success message
    end
```

### Background Flow: Stats Cache Update

```mermaid
sequenceDiagram
    participant Cron
    participant Scheduler
    participant Worker
    participant StatsJob as HomepageStatsJob
    participant CentralDB as kiosk_buykiosk
    participant StoreDBs as Store Databases
    participant Cache as homepage_stats

    Cron->>Scheduler: Every minute check
    Note over Scheduler: Is it 2:00 AM?
    Scheduler->>Worker: Dispatch HomepageStatsJob
    Worker->>StatsJob: handle()

    StatsJob->>CentralDB: SELECT typeNum, dbName FROM stores WHERE active=1
    CentralDB-->>StatsJob: Store list (127 stores)

    loop For each store
        StatsJob->>StoreDBs: SELECT COUNT(*), SUM(buyTotal) FROM buyQueue
        StoreDBs-->>StatsJob: Store metrics
    end

    StatsJob->>StatsJob: Aggregate totals
    StatsJob->>Cache: REPLACE INTO homepage_stats
    Cache-->>StatsJob: Updated
    StatsJob-->>Worker: JobResult::success()
```

### Error Handling

| Error Type | Handler | User Feedback | Logging |
|------------|---------|---------------|---------|
| **Form validation error** | Client-side + API | Inline error messages per field, form data retained, focus on first error | PostHog `demo_form_error` event |
| **Network failure (form)** | JavaScript catch | "Something went wrong. Please try again." with form data retained | Browser console + PostHog |
| **Stats cache stale (>48hr)** | StatsService | Display last known values (no error shown) | PHP error_log warning |
| **Stats cache empty** | StatsService | Display hardcoded fallback values | PHP error_log error |
| **Stats table missing** | StatsService | Display hardcoded fallback values | PHP error_log critical |
| **Video load failure** | JavaScript onerror | Show poster image, hide play button, no broken UI | PostHog custom event |
| **PostHog unavailable** | Graceful degradation | Site works normally, no tracking | Browser console only |
| **TaskEngine job failure** | BaseJob retry (3 attempts) | N/A (background) | Job logs + optional email notification |

### PRD Edge Cases (per PRD lines 277-286)

| Edge Case | Expected Behavior | Implementation |
|-----------|-------------------|----------------|
| **Demo form submission fails (network)** | Show "Something went wrong, please try again", retain form data | `catch` block in fetch, form.reset() NOT called on error |
| **Demo form validation fails** | Inline error messages per field, no form clear, focus on first error field | Client-side validation before submit, focus() on first invalid |
| **Stats cache stale (>48hrs)** | Display last cached values, no error shown to user | StatsService checks MAX(updatedAt), returns cached |
| **Stats cache table missing** | Display hardcoded fallback values (current homepage values) | try-catch in StatsService, fallback array |
| **User on slow connection (3G)** | Images lazy-load, page remains usable, skeleton loaders for dynamic content | `loading="lazy"`, CSS skeleton animations |
| **User has JavaScript disabled** | Static content visible, forms work via standard POST, no animations | `<noscript>` fallback, form `action` attribute |
| **Video fails to load** | Show poster image, hide play button, no broken player UI | `onerror` handler, CSS `.video--error` state |
| **Mobile user in landscape mode** | Layout adapts, no horizontal scroll, CTAs remain accessible | CSS media queries, `max-width: 100vw` |

---

## Deployment View

### Environment Configuration

```yaml
# Required Environment Variables
POSTHOG_API_KEY: string  # PostHog project API key (phc_XXXX...)
POSTHOG_HOST: string     # PostHog instance URL (default: https://app.posthog.com)

# Existing Variables (unchanged)
SMTP_HOST: string
SMTP_USER: string
SMTP_PASS: string
SMTP_SECURE: string
SMTP_PORT: integer
```

### Deployment Sequence

1. **Database Migration** (run first)
   - Create `homepage_stats` table
   - Register `HomepageStatsJob` in `task_job_definitions`
   - Seed initial fallback stats values

2. **Code Deployment** (standard deploy.sh)
   - New templates and partials
   - Updated AccountController
   - New HomepageStatsService
   - New HomepageStatsJob
   - Updated CSS/JS

3. **Manual One-Time Steps**
   - Dispatch initial stats job: `php userfrosting/bin/task job:dispatch homepage-stats`
   - Verify PostHog receives events
   - Update PostHog project settings (if needed)

4. **PostHog Setup** (external)
   - Create project or use existing
   - Enable heatmaps and session recording
   - Set up dashboard for KPIs

### Rollback Strategy

| Issue | Rollback Action |
|-------|-----------------|
| Stats cache broken | Hardcoded fallback values display automatically |
| Template rendering error | Revert to previous home.html via git |
| PostHog blocking page load | Remove/comment PostHog script (graceful degradation) |
| Demo form broken | Revert API changes, keep YCBM fallback link |

---

## Cross-Cutting Concepts

### Pattern Documentation

```yaml
# Existing patterns used
- pattern: BEM CSS naming (modern-[component]__[element]--[modifier])
  relevance: CRITICAL
  why: "Consistent with existing frontend codebase"

- pattern: Twig partial includes
  relevance: HIGH
  why: "Standard BuyerKiosk template organization"

- pattern: TaskEngine BaseJob
  relevance: HIGH
  why: "Required pattern for scheduled jobs"

- pattern: Repository pattern for data access
  relevance: HIGH
  why: "Consistent with demo_leads and other data access"

# New patterns introduced
- pattern: PostHog event tracking via JavaScript SDK
  relevance: HIGH
  why: "New analytics platform integration"

- pattern: Cached stats service
  relevance: MEDIUM
  why: "Avoid expensive cross-DB queries on page load"
```

### Security Considerations

| Concern | Implementation |
|---------|----------------|
| **CSRF Protection** | NoCSRF token on all form submissions (existing pattern) |
| **Input Validation** | filter_var for email, maxlength on inputs, server-side validation |
| **XSS Prevention** | Twig auto-escaping enabled, no raw HTML from user input |
| **Rate Limiting** | Existing DemoRateLimiter for form submissions |
| **PostHog Privacy** | No PII in custom events, use anonymous IDs |

### Performance Strategy

| Optimization | Implementation |
|--------------|----------------|
| **Lazy Loading** | All images except hero use `loading="lazy"` |
| **Image Optimization** | WebP format with JPEG fallback, responsive srcset |
| **CSS Loading** | Single CSS file, critical styles inline in head |
| **JavaScript** | Defer non-critical scripts, minimal vanilla JS |
| **Stats Caching** | Pre-computed stats in database, no runtime aggregation |
| **Font Loading** | `font-display: swap` for Google Fonts |
| **Video Handling** | Lazy-load videos, poster images as placeholders, load on interaction |
| **Count-Up Animations** | Trigger only on IntersectionObserver visibility |
| **Reduced Motion** | `@media (prefers-reduced-motion: reduce)` disables animations |
| **Skeleton Loaders** | CSS-only skeleton animations for slow connection states |

### Accessibility Implementation

| Requirement | Implementation |
|-------------|----------------|
| **Semantic HTML** | nav, main, section, article, footer elements |
| **Keyboard Navigation** | All interactive elements focusable, skip links |
| **Screen Readers** | ARIA labels on icons, alt text on images |
| **Color Contrast** | Minimum 4.5:1 ratio (verified against tokens.css) |
| **Focus Indicators** | Visible focus states on all interactive elements |
| **Reduced Motion** | Respect `prefers-reduced-motion` media query |

---

## Architecture Decisions

### ADR-1: Modular Template Partials ✅

- [x] **Decision**: Break 1073-line home.html into ~15 partial files
- **Rationale**: Improves maintainability, allows parallel development, easier to test individual sections
- **Trade-offs**: Slightly more file I/O, more complex include structure
- **Alternatives Considered**:
  - Keep monolithic template (rejected: too hard to maintain)
  - Full React SPA (rejected: timeline, complexity, SEO concerns)
- **User confirmed**: ✅ 2026-01-14

### ADR-2: PostHog for Analytics ✅

- [x] **Decision**: Use PostHog JavaScript SDK for event tracking, heatmaps, and session recording
- **Rationale**: Open source, includes heatmaps/recordings (no additional tools), feature flags for future A/B testing
- **Trade-offs**: New dependency, learning curve, PostHog instance cost (or self-host)
- **Alternatives Considered**:
  - Google Analytics 4 (rejected: no heatmaps, complex setup)
  - Mixpanel (rejected: expensive for needed features)
  - Hotjar + GA4 (rejected: two tools vs one)
- **User confirmed**: ✅ 2026-01-14

### ADR-3: TaskEngine Cached Stats ✅

- [x] **Decision**: Daily TaskEngine job aggregates stats to `homepage_stats` cache table
- **Rationale**: Avoids expensive cross-DB queries on every page load, stats don't need real-time accuracy
- **Trade-offs**: Stats up to 24 hours stale, additional table and job to maintain
- **Alternatives Considered**:
  - Real-time queries (rejected: performance impact at scale)
  - Static hardcoded values (rejected: require manual updates, less compelling)
  - Redis cache (rejected: overkill for single-value lookups)
- **User confirmed**: ✅ 2026-01-14

### ADR-4: New Custom Demo Form System ✅

- [x] **Decision**: Build new in-house demo form system to replace FormSite dependency
- **Rationale**: User wants to move away from FormSite; in-house system gives full control over data, validation, and UX
- **Trade-offs**: More development work, must handle validation/spam protection ourselves
- **Alternatives Considered**:
  - Keep FormSite (rejected by user: external dependency)
  - Enhance existing demo_leads only (rejected by user: wants fresh approach)
- **User confirmed**: ✅ 2026-01-14

### ADR-5: Single Form for Homepage and Team+ Page ✅

- [x] **Decision**: Both pages use the same demo form modal, with `team_plus_interest` flag passed from Team+ page
- **Rationale**: Simpler implementation, consistent UX, single lead capture flow for sales
- **Trade-offs**: Can't have Team+ specific form fields without conditional logic
- **Alternatives Considered**:
  - Separate Team+ form (rejected: duplicates effort, confuses sales process)
  - Multi-step wizard form (rejected: timeline, complexity)
- **User confirmed**: ✅ 2026-01-14

---

## Quality Requirements

| Category | Requirement | Metric | Test Method |
|----------|-------------|--------|-------------|
| **Performance** | Page load time | LCP < 2.5s on 4G | Lighthouse audit |
| **Performance** | Layout stability | CLS < 0.1 | Lighthouse audit |
| **Performance** | Interactivity | FID < 100ms | Lighthouse audit |
| **Accessibility** | WCAG compliance | Level AA | axe-core automated scan |
| **Accessibility** | Keyboard navigation | 100% interactive elements | Manual testing |
| **Browser Support** | Cross-browser | Chrome, Safari, Firefox, Edge | BrowserStack testing |
| **Mobile** | Responsive breakpoints | 375px, 768px, 1024px, 1440px | Visual regression testing |
| **Analytics** | Event tracking | All PRD events fire | PostHog debugger verification |
| **Reliability** | Stats fallback | Works with empty/stale cache | Unit test StatsService |
| **SEO** | Meta tags | Title, description, OG tags present | Lighthouse SEO audit |

---

## Risks and Technical Debt

### Known Technical Issues

| Issue | Impact | Mitigation |
|-------|--------|------------|
| Legacy GA tracking (UA) | May be deprecated by Google | Keep GA during transition, PostHog is primary |
| Inline JavaScript in current template | Hard to maintain/test | Extract to separate file in redesign |
| Hardcoded stats in current template | Outdated, manual updates needed | Replace with cached dynamic values |

### Technical Debt

| Item | Description | Recommendation |
|------|-------------|----------------|
| Monolithic template | 1073 lines in single file | Refactor to partials (in scope) |
| Mixed CSS patterns | Some Bootstrap, some custom | Standardize on BEM + tokens (in scope) |
| GA vs PostHog overlap | Two analytics systems briefly | Remove GA after 30-day validation period |

### Implementation Gotchas

| Gotcha | Details | Prevention |
|--------|---------|------------|
| Twig + Handlebars conflict | If any JS frameworks use `{{`, wrap in `{% raw %}{% endraw %}` | Document in code comments |
| PostHog adblock | Some users may block PostHog | Graceful degradation (site works without) |
| Stats job timing | If job fails, stats could be stale | Fallback values in database, monitoring alerts |
| Image sizes | Large images will tank LCP | Require compressed/optimized images before merge |
| CSS cache busting | Old CSS may persist | Use conductor build-css --minify for version hash |

---

## Test Specifications

### Critical Test Scenarios

**Scenario 1: Homepage Happy Path**
```gherkin
Given: User navigates to buyerkiosk.com
When: Page loads completely
Then: All 11 feature sections are visible
And: Stats section displays 6 metrics
And: Pricing section shows 3 tiers + Team+ addon
And: PostHog $pageview event is captured
And: LCP is under 2.5 seconds
```

**Scenario 2: Demo Form Submission**
```gherkin
Given: User is on homepage
When: User clicks "Schedule Demo" CTA
Then: Demo form modal opens
And: PostHog demo_form_opened event fires
When: User fills valid data and submits
Then: Success message displays
And: Lead is created in demo_leads table
And: PostHog demo_form_submitted event fires
```

**Scenario 3: Stats Cache Stale**
```gherkin
Given: homepage_stats.updatedAt is more than 48 hours ago
When: User loads homepage
Then: Last known stat values are displayed
And: No error is shown to user
And: Warning is logged to error_log
```

**Scenario 4: Stats Cache Empty**
```gherkin
Given: homepage_stats table is empty
When: User loads homepage
Then: Hardcoded fallback values are displayed
And: No error is shown to user
And: Error is logged to error_log
```

**Scenario 5: Team+ Page Journey**
```gherkin
Given: User is on homepage
When: User clicks "Team+" in navigation
Then: User is navigated to /team-plus
And: PostHog team_plus_page_view event fires with source='nav'
When: User clicks "Schedule Demo" on Team+ page
Then: Demo form opens with team_plus_interest=true
```

**Scenario 6: Mobile Responsive**
```gherkin
Given: User is on mobile device (375px width)
When: Page loads
Then: Navigation shows hamburger menu
And: Feature sections display in single column
And: Stats grid displays 2x3 layout
And: All CTAs are full-width
And: No horizontal scroll exists
```

**Scenario 7: Team+ Comparison Table**
```gherkin
Given: User is on /team-plus page
When: User scrolls to comparison section
Then: Table displays Team+ vs WhenIWork vs Homebase
And: Team+ column is visually highlighted
And: All 10 feature rows are visible
And: Table is scrollable on mobile
```

**Scenario 8: Navigation Content**
```gherkin
Given: User is on homepage
When: Page loads
Then: Navigation displays: Features, Team+, Pricing, Contact, Login
And: Features links to #features anchor
And: Team+ links to /team-plus
And: Pricing links to #pricing anchor
And: Contact links to #contact anchor
And: Login links to existing login route
```

**Scenario 9: Video Playback**
```gherkin
Given: User is on homepage with videos
When: Video section is visible
Then: Video shows poster image initially
And: Play button is visible
When: User clicks play
Then: Video starts playing
And: PostHog video_played event fires with video_name
When: Video fails to load
Then: Poster image remains visible
And: Play button is hidden
```

**Scenario 10: Team+ Demo Request**
```gherkin
Given: User is on /team-plus page
When: User clicks "Schedule Demo" CTA
Then: Demo form modal opens with team_plus_interest=true
When: User submits valid form data
Then: PostHog team_plus_demo_requested event fires
And: Lead is created with team_plus_interest=1
```

### Test Coverage Requirements

| Area | Coverage | Test Type |
|------|----------|-----------|
| **HomepageStatsService** | getStats(), getStat(), fallback logic | Unit tests |
| **HomepageStatsJob** | Aggregation logic, error handling | Unit + Integration tests |
| **Demo Form API** | Validation, store_type field | Integration tests |
| **Template Rendering** | All partials render without error | Smoke tests |
| **PostHog Events** | All custom events fire correctly | Manual QA + PostHog debugger |
| **Mobile Responsive** | All breakpoints | Visual regression tests |
| **Accessibility** | WCAG AA compliance | axe-core automated scan |
| **Performance** | Core Web Vitals | Lighthouse CI |

---

## Glossary

### Domain Terms

| Term | Definition | Context |
|------|------------|---------|
| **Demo Lead** | A prospect who has submitted the demo request form | Stored in demo_leads table, followed up by sales |
| **Team+** | Premium add-on module ($30/month) with scheduling, AI, chat, mobile apps | Separate homepage section + landing page |
| **TypeNum** | Store identifier pattern (e.g., ou00, pa00) | Used to connect to store-specific databases |
| **Comeback Cash** | Promotional coupon system ("Kohl's Cash" style) | One of the 11 feature sections |
| **Daybook** | Centralized operations hub (unified workspace) | One of the 11 feature sections |

### Technical Terms

| Term | Definition | Context |
|------|------------|---------|
| **BEM** | Block Element Modifier CSS naming convention | Class format: `modern-[block]__[element]--[modifier]` |
| **LCP** | Largest Contentful Paint - time to render largest element | Core Web Vital, target < 2.5s |
| **CLS** | Cumulative Layout Shift - visual stability metric | Core Web Vital, target < 0.1 |
| **FID** | First Input Delay - time to first interaction response | Core Web Vital, target < 100ms |
| **TaskEngine** | BuyerKiosk's background job processing system | Used for HomepageStatsJob |
| **PostHog** | Open-source product analytics platform | Replaces GA for event tracking |

### API Terms

| Term | Definition | Context |
|------|------------|---------|
| **CSRF** | Cross-Site Request Forgery protection token | Required on all POST forms (NoCSRF library) |
| **demo_leads** | Database table storing demo request submissions | Central database (kiosk_buykiosk) |
| **homepage_stats** | Cache table for aggregated statistics | Central database (kiosk_buykiosk) |

---

*SDD Status: ✅ COMPLETE - All ADRs confirmed*
*Codex Review: PASSED - All blockers resolved 2026-01-14*
*ADR Confirmations: 2026-01-14 (5/5 confirmed)*
*Last Updated: 2026-01-14*
*Ready for: Implementation Plan (PLAN)*
