﻿# speeddates.com — UI Plan

Single source of truth for the visual design. Mirrors and extends [docs/03_speeddates_brand_design.docx](03_speeddates_brand_design.docx) with token names, component specs, and page-level layout. Kept in sync with [PLAN.md](PLAN.md). Update both whenever design decisions change.

## Status

- **Live demo pages:** [`demo/index.html`](../demo/index.html) — open in a browser to compare directions.
- **Three homepage directions to choose between:** [A · Editorial](../demo/homepage-a-editorial.html) · [B · Marketplace](../demo/homepage-b-marketplace.html) · [C · Magazine](../demo/homepage-c-magazine.html)
- **Supporting pages built:** [Events listing](../demo/listings.html) · [Event detail](../demo/event-detail.html)
- **Tokens live as CSS variables in** [`demo/styles/tokens.css`](../demo/styles/tokens.css) — colors strictly per doc 03; same names will map 1:1 to Tailwind config.
- **Polish pass complete:** fluid typography (`clamp()`), mobile nav drawer with hamburger, horizontally scrolling filter bar, fixed mobile CTA bar on event detail, `:focus-visible` rings, `prefers-reduced-motion`, lazy images, Lucide SVG icons, full aria/label coverage.
- **Footer:** gold rule inside `<footer>` is the sole separator across all pages — no `border-top` on `.site-footer`. **Cities grid (homepage B):** uniform `padding: var(--spacing-md)` on all sides; hover changes background only. **Hero photo (event detail):** `<figure>` margin is `var(--spacing-md) 0 0` to prevent overflow.
- **Direction choice:** still open — pick A / B / C before Batch 1 ports the chosen layout to Next.js.
- **Browser-readable docs:** [`docs/viewer.html`](viewer.html) renders the .md plans with marked.js (serve via `python -m http.server` from project root, then visit `/docs/viewer.html`).

---

## Brand Principles (from doc 03)

- **Restraint.** Boxes/cards/borders are earned, not decorative.
- **White space is a design element.** Minimum 64px between major sections on desktop.
- **Warm, confident, focused** — like a beautifully set table.
- **Voice:** approachable without saccharine, romantic without melodrama.

---

## Design Tokens

### Primitives Collection (mode: "Value")

| Token | Value | Notes |
|---|---|---|
| `color/burgundy/500` | `#8B1A4A` | Primary brand |
| `color/champagne/500` | `#D4A96A` | Gold accent |
| `color/cream/500` | `#F5ECD7` | Warm fill |
| `color/plum/900` | `#2D1B2E` | Headings, deep accents |
| `color/offwhite/50` | `#FDFAF5` | Page background |
| `color/gray/600` | `#5C5058` | Body text |
| `color/gray/300` | `#E5D9C8` | Borders, dividers |
| `color/gray/400` | `#B8A89A` | Muted / disabled |
| `color/white` | `#FFFFFF` | Card surface |
| `spacing/xs..3xl` | 4 / 8 / 16 / 24 / 32 / 48 / 64 | 8pt scale |
| `radius/sm..full` | 4 / 6 / 8 / 20 / 9999 | `md` for buttons, `lg` for cards, `pill` for tags |

All primitives scoped to `[]` so they don't pollute pickers. Code syntax: `var(--color-burgundy-500)` etc.

### Semantic Collection (mode: "Light")

Aliases to primitives. Components ONLY consume semantic tokens.

| Token | Alias | Scope |
|---|---|---|
| `color/bg/page` | `offwhite/50` | FRAME_FILL, SHAPE_FILL |
| `color/bg/card` | `white` | FRAME_FILL, SHAPE_FILL |
| `color/bg/tag` | `cream/500` | FRAME_FILL, SHAPE_FILL |
| `color/bg/cta` | `burgundy/500` | FRAME_FILL, SHAPE_FILL |
| `color/bg/section` | `cream/500` | FRAME_FILL, SHAPE_FILL |
| `color/text/primary` | `plum/900` | TEXT_FILL |
| `color/text/body` | `gray/600` | TEXT_FILL |
| `color/text/muted` | `gray/400` | TEXT_FILL |
| `color/text/cta` | `offwhite/50` | TEXT_FILL |
| `color/text/accent` | `burgundy/500` | TEXT_FILL |
| `color/text/tag` | `burgundy/500` | TEXT_FILL |
| `color/border/default` | `gray/300` | STROKE_COLOR |
| `color/border/focus` | `burgundy/500` | STROKE_COLOR |
| `color/accent/gold` | `champagne/500` | FRAME_FILL, SHAPE_FILL, STROKE_COLOR |

### Type Ramp (7 text styles, Inter)

| Style | Size | Weight | Line-height | Letter-spacing | Use |
|---|---|---|---|---|---|
| Hero/Headline | 56 | Bold | 110% | -2% | Homepage hero only |
| Page Title | 32 | Bold | 120% | -1% | H1 |
| Section Title | 24 | Semi Bold | 130% | 0 | H2 |
| Card Title | 18 | Semi Bold | 135% | 0 | Event card name |
| Body | 16 | Regular | 155% | 0 | Descriptions, paragraphs |
| Secondary | 14 | Regular | 150% | 0 | Metadata, dates, captions |
| Label | 14 | Semi Bold | 140% | 1% | CTAs, form labels |

Font stack: `Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif`.

### Effects

- `Card / Subtle` — drop shadow `0 2px 12px rgba(45,27,46,0.08)`. Only on event cards.

### Layout

- Max content width: **1200px**, centered.
- Desktop grid: 12 columns, **24px gutters**.
- Card grid: 3 col (desktop) / 2 col (tablet ≥768px) / 1 col (mobile ≤375px).
- Section vertical rhythm: **64px** minimum between major sections.
- Mobile tap targets: **44px** minimum height.

---

## Components

Every component binds visual properties to semantic variables — no hardcoded fills/strokes/spacing.

### Button — Primary
- Background: `color/bg/cta`
- Text: `color/text/cta`, style `Label`
- Padding: 12px / 28px (vertical / horizontal)
- Radius: `radius/md` (6px)
- No drop shadow
- Hover: lighten slightly or add gold undertone
- Variants: Default / Hover / Disabled

### Button — Secondary (Ghost)
- Background: transparent
- Border: 1.5px `color/border/focus` (burgundy)
- Text: `color/text/accent`, style `Label`
- Same padding/radius as primary
- Use for "Learn More", "View Organizer Profile"

### Tag / Badge
- Background: `color/bg/tag` (cream)
- Text: `color/text/tag` (burgundy), 12px Semi Bold
- Padding: 4px / 10px
- Radius: `radius/pill` (20px)
- Use cases: event type (LGBTQ+ Friendly, All Ages), age range tag, "Sold Out"

### Input / Filter
- Background: `color/offwhite/50`
- Border: 1px `color/border/default`
- Focus border: 1px `color/border/focus`
- Radius: `radius/md`
- No heavy shadows
- Height: 44px (mobile-safe tap)

### Event Card
- Background: `color/bg/card` (white)
- Border: 0.5px `color/border/default`
- Radius: `radius/lg` (8px)
- Effect: `Card / Subtle`
- Inner structure (vertical auto-layout):
  - Hero image (16:9 ratio, fills width, 8px radius top)
  - Content area (16px padding):
    - Row 1: organizer name (Secondary, muted) + city (Secondary, muted)
    - Row 2: event title (Card Title)
    - Row 3: date/time (Secondary)
    - Row 4: tag row (event type, age range)
- "Sold Out" overlay badge (Tag in burgundy bg) when status = SOLD_OUT

### Filter Bar
- Horizontal auto-layout, gap 16px
- Background: cream `color/bg/section` OR borderless on page background
- Each filter is an Input/Select component
- Mobile: scrollable horizontal pill row

### Organizer Mini-Card
- Avatar (40px circle, 1px `color/border/default` ring)
- Display name (Card Title)
- "N events listed" (Secondary, muted)
- Link to full profile

### Section Divider (Gold Rule)
- 1px line, `color/accent/gold`, 120px wide
- Used between major homepage sections

---

## Three Homepage Directions

Pick one before Batch 1. The supporting pages will be conformed to whatever you choose. The HTML demos are the source of truth — these summaries are reference.

### A — Editorial Restaurant · [open demo](../demo/homepage-a-editorial.html)

Full-bleed candlelit hero photo with a 96pt headline laid over it. Search card floats on the photo as a translucent off-white surface. Below the fold: numbered how-it-works (champagne numerals), featured events card grid, cream organizer-CTA panel with a pull quote. Mood: Kinfolk meets The Wing.

### B — Confident Marketplace · [open demo](../demo/homepage-b-marketplace.html)

Centered hero with a 60pt headline on off-white. Single pill-shaped search bar with three labelled fields (Where / When / Vibe). Featured cards visible above the fold. "Browse by city" rendered as a borderless row of links (restraint principle — no boxy containers). Mood: Airbnb home meets Resy.

### C — Curated Magazine · [open demo](../demo/homepage-c-magazine.html)

Asymmetric two-column hero: 96pt three-line headline on the left, single featured event card on the right with icons + tags. Issue-bar nav at top. "Browse by city" rendered as a large-type list with gold left-rule hover. Mood: editorial, opinionated, brand-distinctive.

**Shared across all three:** same tokens (Champagne & Burgundy, Inter, 8pt spacing), same nav drawer on mobile, same Lucide icon set, same event-card and tag components.

---

## Pages — Layout Spec

The demos render the live spec. Skim these only when you need a written reference.

### Page 1 — Homepage (`/`)

Three demo variants — see "Three Homepage Directions" above. Each implements:

- Brand-warm hero with city + date search → submits to `/events?city=...&date=...`
- "How it works" section (3 steps: Search / Discover / Attend)
- Featured events strip (3 event cards) → links to listings
- "For organizers" CTA section
- Footer with gold rule and links

**Acceptance (US-008):**

- Single-scroll on desktop ≥1280px to "Featured this week"
- Stacks cleanly at 768px (cards 2-col) and 375px (1-col, search inputs stack)
- Search form submit → `/events?city=...&date=...`

### Page 2 — Events Listing (`/events`)

```
┌──────────────────────────────────────────────────────────┐
│  NAV                                                     │
├──────────────────────────────────────────────────────────┤
│  Speed dating events in Chicago               (H1 32pt) │
│                                                          │
│  [📍 Chicago] [📅 Next 2 wks] [👤 All ages] [🏳️] [📍5mi] │ ← Filter bar
│                                                          │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐                  │
│  │ [photo] │  │ [photo] │  │ [photo] │   ← Event cards   │
│  │ Title   │  │ Title   │  │ Title   │     (3-col grid)  │
│  │ Date    │  │ Date    │  │ Date    │                  │
│  │ [tag]   │  │ [tag]   │  │ [tag]   │                  │
│  └─────────┘  └─────────┘  └─────────┘                  │
│  ┌─────────┐  ┌─────────┐  ┌─────────┐                  │
│  │  ...    │  │  ...    │  │  ...    │                  │
│  └─────────┘  └─────────┘  └─────────┘                  │
│                                                          │
│  [Load more]                                             │
└──────────────────────────────────────────────────────────┘
```

**Acceptance (US-009, US-010):**
- 25 events per page (paginated)
- Filter changes update URL params, results refresh without full reload
- Empty state: friendly message when no results
- Sold-out cards show overlay badge

### Page 3 — Event Detail (`/events/{country}/{city}/{slug}`)

```
┌──────────────────────────────────────────────────────────┐
│  NAV                                                     │
├──────────────────────────────────────────────────────────┤
│  [          full-width hero photo (16:9)         ]       │
│                                                          │
│  The Lounge Speed Date — River North        (H1 32pt)   │
│  by [Spark Events]                          (link)       │
│                                                          │
│  📅 Thu, Aug 15, 2026 · 7:30 PM                          │
│  📍 The Lounge, 123 N State St, Chicago                  │
│  👥 30 spots · 🎂 25-38 · [LGBTQ+ Friendly]              │
│                                                          │
│  ┌─────────────────────────┐  ┌────────────────────────┐│
│  │  About this event       │  │  ┌──────────────────┐  ││ ← Sidebar
│  │  Lorem ipsum dolor...   │  │  │  Get Tickets →   │  ││   (sticky)
│  │  (Body)                 │  │  └──────────────────┘  ││
│  │                         │  │                        ││
│  │  [photo gallery 6 max]  │  │  Hosted by             ││
│  │                         │  │  ◯ Spark Events        ││
│  │                         │  │  24 events listed      ││
│  └─────────────────────────┘  └────────────────────────┘│
└──────────────────────────────────────────────────────────┘
```

**Acceptance (US-011):**
- Get Tickets button visible to everyone but inactive for logged-out users (inline prompt: "Create a free account…")
- Logged-in: button opens organizer's `ticket_url` in new tab
- Lightbox on photo click
- View count increments on each page load
- Server-rendered for SEO with schema.org `Event` JSON-LD

---

## Out of Scope for First Design Pass

Pushed to a later round once the three flagship pages are validated:
- Organizer public profile page (`/organizers/[slug]`)
- Attendee dashboards (`/account/*`)
- Organizer dashboards (`/dashboard/*`)
- Admin panel (`/admin/*`)
- Application form (`/organizers/apply`)
- Email templates
- 404 / 500 / expired-token states

---

## Build Order

1. ~~Demo gallery + 3 homepage directions (HTML)~~ ✅ — [`demo/`](../demo/)
2. ~~Events listing demo~~ ✅ — [`demo/listings.html`](../demo/listings.html)
3. ~~Event detail demo~~ ✅ — [`demo/event-detail.html`](../demo/event-detail.html)
4. **You pick a homepage direction** (A / B / C) from the demo gallery
5. Conform listings + event detail to the chosen direction
6. Port the chosen direction's tokens & components into the Next.js app (Batch 1+ from [PLAN.md](PLAN.md))
7. Build remaining pages once core flows are live: organizer profile, attendee dashboard, organizer dashboard, admin panel, application form, transactional emails, 404/500 states

---

## When This Doc Changes — Sync Checklist

- [ ] Update token tables if any variable name/value changes in [`demo/styles/tokens.css`](../demo/styles/tokens.css)
- [ ] Update component specs if a base component gets a new variant or prop
- [ ] Update page layout sketches if information architecture shifts
- [ ] Cross-link any matching change in [PLAN.md](PLAN.md) (especially batch deliverables that depend on visual decisions)
