<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know

This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` before writing any code. Heed deprecation notices.
<!-- END:nextjs-agent-rules -->

# Project: VibePass Tickets

A Next.js + Supabase event ticketing storefront/marketplace for the Kenyan market
(prices in KES, M-Pesa payouts). The public brand name is **VibePass Tickets**
(short form "VibePass") — keep copy consistent (`src/lib/platform-content.ts`,
`src/lib/settings.ts`). General platform copy (nav, footer legal line, About page
mission/origin story, contact email/website, homepage, calendar, floating WhatsApp
widget, placeholder art) reads as VibePass, not Shamz Media. Vulai Festival keeps
its own producer credit — "Shamz Media" still appears as the named producer inside
Vulai-specific content only: `src/lib/vulai-content.ts` (`PRODUCER`, `PARTNERS`,
hero slide eyebrows), `src/lib/sponsors.ts`, `src/lib/blog.ts` post authors, and
the gallery/Vulai-event-page copy that credits Vulai as a Shamz Media production.
"Bash by Shamz" is a distinct product name and is unaffected either way.

## Settlement identity (money in)

`src/lib/merchant.ts` (`MERCHANT`) is the single source of truth for the accounts
buyers pay **into** — business name VIBE PASS TICKETS, KCB Bank a/c 1355227259,
M-Pesa PayBill **522533** with account number **8127015**, business contact
vibepasstickets@gmail.com / 0143003000. Don't duplicate these literals elsewhere.

522533 is KCB's *shared bank* paybill, so the payer's Account Number is what
routes funds to our KCB account — `initiateStkPush` therefore sends `8127015` as
the STK `AccountReference` (overridable per-deployment via the admin secret
`MPESA_ACCOUNT_REFERENCE`; `MPESA_SHORTCODE` likewise defaults to 522533).
Orders are reconciled by `CheckoutRequestID`, never by the reference. STK against
a bank paybill needs Daraja credentials issued for that shortcode (KCB Buni or an
aggregator) — the consumer key/secret/passkey still come from the admin
Secrets page, `MERCHANT` supplies only the routing numbers.

Do not confuse this with `CONTACT` (`src/lib/vulai-content.ts`, public marketing
contact) or organizer `payout_accounts` (money **out**).

## Event data flow

The app renders events from **Supabase**, not from any file — see
`src/lib/events.ts` (`getAllEvents` / `getUpcomingEvents`). The JSON dataset is a
**seed source only**.

- `src/data/events.json` — the hustle.events discovery dataset. The seed reads it.
- `scripts/seed-hustle.mjs` — inserts each event + its ticket tiers as a published
  event under one organizer account. **Idempotent**: skips events whose `title`
  already exists, so it is safe to re-run. Auto-creates any new `category`.
  Run: `node --env-file=.env.local scripts/seed-hustle.mjs`
- `scripts/sync-event-images.mjs` — pushes `image` URLs from `events.json` onto
  already-seeded rows (`cover_image_url`), matched by title. Idempotent; run after
  adding/replacing images. Run: `node --env-file=.env.local scripts/sync-event-images.mjs`

### events.json schema (per event)

`id`, `slug`, `title`, `organizer`, `category`, `description`, `venue`, `city`,
`startDate`/`endDate` (ISO 8601, **`+03:00`** EAT — Kenya has no DST),
optional `ageRestriction`, `priceFrom` (lowest tier price), `soldOut`, and
`ticketTiers[]` with `name`, `price`, optional `originalPrice`, and `status`
(**`on_sale`** or **`sold_out`**). For images use either `image` (a remote URL)
or `imageMissing: true` (falls back to `/placeholder-event.svg`).

### Adding events

1. Append objects to `events.json` (raw image URLs; do not pre-encode Firebase URLs —
   the scripts normalize `firebasestorage.googleapis.com` links with `?alt=media`).
2. Run `seed-hustle.mjs`, then `sync-event-images.mjs` if you set images separately.
3. Remote image hosts must be allowlisted in `next.config.ts` `images.remotePatterns`
   (currently `firebasestorage.googleapis.com` and `hustle.imgix.net`). Local fallback
   images live in `public/events/`.

Secrets (`NEXT_PUBLIC_SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`) come from
`.env.local`, which is gitignored — never commit it.
