# 79seconds on Stage — Claude Project Instructions

## What This Is

**79seconds on Stage** is a free, egalitarian performing arts aggregator for the Twin Cities (Minneapolis–St. Paul). It scrapes show listings from local theater companies and presents them on a clean website at 79seconds.org. Every theater gets equal billing — no tiers, no pay-to-play.

The companion product is a **weekly newsletter** ($52/year, $1/week) that delivers the full roundup to subscribers' inboxes.

This is a project of **79seconds** (79seconds.com), Ross Silcox's marketing agency. The tagline is "Marketing for Art's Sake."

## Brand Guidelines

- **Name:** 79seconds on Stage
- **Tagline:** Marketing for Art's Sake
- **Email:** editor@79seconds.org
- **Colors:** Gold #F4D22A, Red-orange #F54E00, Teal #7CCFC9, Charcoal #4F4F4F, Black #000, Light #F5F5F5
- **Fonts:** Poppins (headings/logo), Lato (body text) — both from Google Fonts
- **Tone:** Friendly, concise, enthusiastic about local arts. Not corporate.

## Tech Stack

### Scrapers (Python)
- **Location:** `scrapers/` directory
- **Language:** Python 3 with requests, BeautifulSoup4, lxml
- **Database:** SQLite (`db/stage.db`)
- **Base class:** `scrapers/base.py` — all scrapers inherit from `BaseScraper`
- **9 theaters currently scraped:**
  - Guthrie Theater
  - Theater Latté Da
  - History Theatre
  - Penumbra Theatre
  - Mixed Blood Theatre
  - Open Eye Figure Theatre
  - Yellow Tree Theatre
  - Combustible Company
  - Park Square Theatre
- **Run all:** `python scrapers/run_all.py`
- **Each scraper:** extracts title, playwright, director, dates, description, cast, venue, ticket URL, image URL, price info

### Website (Next.js)
- **Location:** `web/` directory (or `web-src/`)
- **Framework:** Next.js 16 with TypeScript, Tailwind CSS v4, App Router
- **Build mode:** Static export (`output: "export"` in next.config.ts)
- **Database access:** better-sqlite3 in readonly mode (data baked in at build time)
- **Key pages:**
  - `/` — Homepage with "Now Playing" and "Coming Up" sections
  - `/shows/[slug]` — Individual show detail page
  - `/theaters` — Theater directory grid
  - `/theaters/[slug]` — Individual theater page with their shows
  - `/contact` — "Get Listed" form for new theaters
- **Show card format:** Image (square) → Title → Playwright → Theater/Venue → Dates → Director → Description (truncated) → Cast → Tickets button
- **Build:** `npm run build` generates static HTML in `out/`
- **Deploy:** Upload `out/` to Vercel (or any static host)

### Workflow: Scrape → Build → Deploy
1. Run scrapers: `python scrapers/run_all.py` → updates `db/stage.db`
2. Copy database to web project: `cp db/stage.db web/data/79seconds.db`
3. Build website: `cd web && npm run build`
4. Deploy: push to GitHub (auto-deploys via Vercel) or `vercel deploy out --prod`

## Key Design Decisions

- **Static export** rather than server-rendered — avoids SQLite-on-serverless complications. All data is baked in at build time.
- **Equal billing** is the core principle — no featured listings, no paid placements, no tiers. Alphabetical or chronological ordering only.
- **Square images** on show cards (aspect-ratio 1:1) to prevent unpredictable cropping.
- **No "What's On Stage" headline** on homepage — the logo and subhead are sufficient.
- **Newsletter CTA** appears at bottom of homepage and in footer.

## Things That Still Need Work

- Cast extraction only has infrastructure — the Guthrie scraper attempts it but gets no data yet. Other scrapers don't try.
- Newsletter subscription form is not wired up (needs Stripe + email service integration).
- No automated scrape-and-deploy pipeline yet (could be a GitHub Action or cron job).
- Mobile responsiveness could use more testing.
- SEO metadata per show page (currently just the global title/description).
- The "Subscribe" button in the header goes to #newsletter anchor — should probably link to a proper subscription page eventually.
- Image fallbacks — 2 of 17 shows still have no image.
