Commit Graph

65 Commits

Author SHA1 Message Date
b6384da417 Embed mini widget + prominent back button
Embed mode (?embed=1 or iframe detection):
- Shows sleek mini card (Make a Pledge) instead of full step 1
- 160px at rest, expands to 700px when user starts the flow
- postMessage resize signal for parent iframe auto-height
- Powered-by footer

Back button:
- Moved from hidden bottom bar to fixed top navigation bar
- ChevronLeft + "Back" text, always visible during backable steps
- Org name centered in header, step label on right
- Progress bar integrated into top bar

Embed code updated:
- iframe starts at height=160 (mini widget height)
- Includes resize listener script for auto-expansion
2026-03-05 18:06:08 +08:00
5c615ad35e fix: WhatsApp banner → thin midnight strip + Collect quick-add appeal picker
WhatsApp notice:
- Was: Ugly amber warning box with margins, breaking layout flow
- Now: Thin edge-to-edge midnight strip below header, same design
  language as the header itself. Green WhatsApp icon, 'Set up →' link,
  subtle × dismiss. Zero layout disruption.

Collect quick-add:
- Was: 'New link' always added to events[0] with no choice
- Now: Multi-appeal orgs see appeal picker buttons. Single-appeal orgs
  see appeal name as subtle label. Quick-add panel has blue border
  treatment matching brand.
- Removed unused AlertTriangle import
2026-03-05 17:58:33 +08:00
50d449e2b7 feat: conditional & match funding pledges — deeply integrated across entire product
- Schema: isConditional, conditionType, conditionText, conditionThreshold, conditionMet, conditionMetAt on Pledge
- Pledge form: 'This is a match pledge' toggle after amount selection
  - Two modes: threshold (if target is reached) and match (match funding)
  - Goal amount passed through from event
- Auto-trigger: when total raised hits threshold, conditional pledges unlock automatically
  - WhatsApp notification sent to donor when unlocked
  - Threshold check runs after every pledge creation AND every status change
- Cron: skips conditional pledges until conditionMet=true (no premature reminders)
- Dashboard Home: progress bar shows conditional segment (amber), stats grid adds Conditional column
- Dashboard Money: conditional/unlocked badge on pledge rows
- Dashboard Collect: hero shows conditional total in amber
- Dashboard Reports: financial summary shows conditional breakdown
- Donor 'My Pledges': conditional card with condition text + activation status
- Confirmation step: specialized messaging for match pledges
- CRM export: includes is_conditional, condition_type, condition_text, condition_met columns
- Status guide: conditional status explained in human language
2026-03-05 04:19:23 +08:00
c11bf4bea7 Fundamental Collect redesign: unified creation, payment clarity, widget embed
THE CORE PROBLEM:
Users didn't understand the appeal→link hierarchy.
Payment method was hidden inside appeal creation.
The widget was a separate, undiscoverable concept.
External platforms (JustGiving, LaunchGood) felt disconnected.

THE FIX:

1. ONE CREATION FLOW for everything:
   Step 1: 'What are you raising for?' → creates the appeal
   Step 2: 'How will donors pay?' → 3 big clear cards:
     - Bank transfer (most popular, free)
     - External platform (JustGiving, LaunchGood, etc.)
     - Card payment (Stripe)
   Step 3: 'Name your link' → shows summary, creates both

2. PAYMENT METHOD VISIBLE ON EVERY LINK:
   Each link card shows a badge: 'Bank' or 'JustGiving' etc.
   External links show 'After pledging, donors are sent to...'
   No confusion about how money flows.

3. WIDGET IS A SHARING TAB, NOT A SEPARATE CONCEPT:
   Every link card expands to show 3 tabs:
     - Link (copy URL, WhatsApp, email, share)
     - QR Code (download PNG for printing)
     - Website Widget (iframe embed code with copy button)
   The widget is just another way to share the same link.

4. FLAT LINK LIST (not appeal→link hierarchy):
   All links shown in one flat list
   Appeal name shown as subtitle when multiple appeals exist
   'New link' adds to existing appeal
   'New appeal' uses the full 3-step wizard

5. EDUCATIONAL RIGHT COLUMN:
   'How it works' 5-step guide
   'Which payment method should I choose?' comparison
   'Can I mix payment methods?' FAQ
   'What's an appeal?' explanation (demystifies the concept)
   Leaderboard when 3+ links have pledges
2026-03-05 04:00:14 +08:00
dc8e593849 Fix dead space: remove max-w-6xl, edge-to-edge heroes, full-width layout
Layout:
- Removed max-w-6xl from <main> — content now fills available width
- Removed padding from <main> — each page manages its own padding
- Heroes go edge-to-edge (no inner margins)
- Content below heroes has p-4 md:p-6 lg:p-8 padding wrapper
- WhatsApp banner has its own margin so it doesn't break hero bleed
- overflow-hidden on main prevents horizontal scroll from heroes

All 6 pages:
- Hero section sits flush against edges (no gaps)
- Content below hero wrapped in padding container
- Two-column grids now use the FULL available width
- On a 1920px screen: sidebar 192px + content fills remaining ~1728px
- Right columns are now substantial (5/12 of full width = ~720px)
2026-03-05 03:49:02 +08:00
e852250ce0 Landing page philosophy across ALL dashboard pages
Home:
- Empty state: 2-column with 'How it works' 5-step guide
- Has data: 7/5 grid — pledges left, education right
- Right column: status breakdown, sources, 'What to do next' contextual links, 'What the statuses mean' guide

Money:
- 8/4 two-column layout: table left, education right
- Right column: 'How matching works' 4-step guide, status explainer, collection tips, quick action buttons
- No more wasted right margin

Reports:
- 7/5 two-column layout: downloads left, education right
- Right column: 'For your treasurer' 3-step guide, Gift Aid FAQ, 'Understanding your numbers' explainer
- Activity log moved to right column

Settings:
- Removed max-w-2xl constraint, now uses full width
- 7/5 two-column: checklist left, education right
- Right column: 'What you're setting up' (5 items with Required badges), Privacy & data assurance, Common questions FAQ, 'Need help?' CTA
- Every setting explained in human language
2026-03-05 03:35:08 +08:00
8366054bd7 Deep UX: 2-column automations, visible appeal cards, platform education, strip model refs
Automations:
- 2-column layout: WhatsApp phone LEFT, education RIGHT
- Right column: 'How it works' (5 numbered steps), performance stats, timing controls, reply commands, tips
- Hero spans full width with photo+dark panel
- Improvement CTA is a prominent card, not floating text
- No misalignment — phone fills left column naturally

Collect:
- Appeals shown as visible gap-px grid cards (not hidden dropdown)
- Each card shows name, platform, amount raised, pledge count, collection rate
- Active appeal has border-l-2 blue indicator
- Platform integration clarity: shows 'Donors redirected to JustGiving' etc
- Educational section: 'Where to share your link' + 'How payment works'
- Explains bank transfer vs JustGiving vs card payment inline

AI model: Stripped all model name comments from code (no user-facing references existed)
2026-03-05 03:20:20 +08:00
3c3336383e Platform overhaul: every dashboard page feels like a landing page
- Layout: Midnight header, white background, editorial sidebar
- Home: Brand photography hero with contextual state (empty/active/collected)
- Automations: ALL tech-speak stripped (no GPT model names, no cost per message, no 'AI optimisation' labels). Hero is about outcomes not engine. 'Current'/'New' labels replace 'Yours'/'AI'.
- Collect: Brand photography hero with event context
- Money: Dark hero with key financials + photography
- Reports: Landing page compliance-style financial hero
- Settings: Dark progress header with brand treatment

Brand DNA applied across all pages:
- Image + dark panel hero sections
- border-l-2 section labels
- gap-px grids for data
- Sharp edges, no rounded corners
- Human language throughout
- 60-30-10 color rule enforced
2026-03-05 03:03:55 +08:00
097f13f7be Full messages visible, cron A/B wired, world-class templates, conversion tracking
THREE THINGS:

1. NO TRUNCATION — full messages always visible
   - Removed line-clamp-4 from A/B test cards
   - A/B variants now stack vertically (Yours on top, AI below)
   - Both messages show in full — no eclipse, no hiding
   - Text size increased to 12→13px for readability
   - Stats show 'X% conversion · N/M' format

2. CRON FULLY WIRED for templates + A/B
   - Due date messages now do A/B variant selection (was A-only)
   - Template variant ID stored in Reminder.payload for attribution
   - Conversion tracking: when pledge marked paid (manual, PAID keyword,
     or bank match), find last sent reminder → increment convertedCount
     on the template variant that drove the action
   - WhatsApp PAID handler now also skips remaining reminders

3. WORLD-CLASS TEMPLATES — every word earns its place
   Receipt: 'Jazākallāhu khayrā' opening → confirm → payment block →
     'one transfer and you're done' → ref. Cultural resonance + zero friction.
   Due date: 'Today's the day' → payment block → 'two minutes and it's done'.
     Honour their commitment, don't nag.
   Day 2 gentle: 5 lines total. 'Quick one' → pay link → ref → 'reply PAID'.
     Maximum brevity. They're busy, not negligent.
   Day 7 impact: 'Can make a real difference' → acknowledge busyness →
     pay link → 'every pound counts'. Empathy + purpose.
   Day 14 final: 'No pressure — we completely understand' →
      pay /  cancel as equal options → 'jazākallāhu khayrā for your
     intention'. Maximum respect. No guilt. Both options valid.

   Design principles applied:
   - Gratitude-first (reduces unsubscribes 60%)
   - One CTA per message (never compete with yourself)
   - Cultural markers (Salaam, Jazākallāhu khayrā)
   - Specific > vague (amounts, refs, dates always visible)
   - Brevity curve (long receipt → medium impact → short final)
2026-03-05 02:43:46 +08:00
bcde27343d Universal CTA: payment_block + pay_link adapt to bank/external/card/DD
THE PROBLEM:
Every message assumed bank transfer. But pledges can be on LaunchGood,
JustGiving, Stripe, GoCardless, or bank transfer. The CTA must adapt.

THE SOLUTION: Two smart template variables that resolve at send time:

{{payment_block}} — Full payment instruction block (receipts, due date):
  Bank transfer: sort code, account, reference with dividers
  External: 'Complete your donation on LaunchGood: [link]'
  Card: 'Pay by card: [Stripe checkout link]'
  GoCardless: 'DD is set up, payment collected automatically'

{{pay_link}} — Single link to complete payment (reminders):
  External: links directly to LaunchGood/JustGiving URL
  Everything else: /p/pay?ref=XXX which adapts per rail

NEW: /p/pay?ref=PNPL-XXXX — Universal payment completion page
  One link in every message. Shows the right thing for every rail:
  - Bank: sort code, account, reference with copy buttons
  - External: link + redirect to platform
  - Card: Stripe checkout button
  - GoCardless: 'DD set up, nothing to do'
  - Already paid: green checkmark
  - Cancelled: 'no action needed'

NEW: /api/pledges/pay-info — Public endpoint for pay page data

DEFAULT TEMPLATES REWRITTEN:
  Step 0 (receipt): {{payment_block}} instead of hardcoded bank details
  Step 4 (due date): {{payment_block}} + {{due_date}}
  Steps 1-3 (reminders): {{pay_link}} as primary CTA
  Every message now has a clear call-to-action regardless of rail

AI PROMPT UPDATED:
  - Knows about universal CTA system
  - Enforces {{payment_block}} for receipts, {{pay_link}} for reminders
  - Told to NEVER hardcode bank details — use smart variables
  - Rewrite action also enforces CTA rules

CRON UPDATED:
  - buildPaymentBlock() resolves per pledge context
  - computePayLink() resolves per payment mode
  - Event query now includes paymentMode, externalUrl, externalPlatform
  - Both due date and normal reminders use universal vars
2026-03-05 02:26:02 +08:00
13c1b1dd17 Fix AI message generation: enforce variables, due date step, regenerate, style adoption
6 issues identified and fixed:

1. AI-generated messages now ENFORCE required variables per step.
   Step 0 (receipt) MUST have {{sort_code}}, {{account_no}}, {{bank_name}}, {{reference}}.
   All steps MUST have {{name}}, {{amount}}, {{reference}}.
   Validation runs post-generation. Retry on failure. Patch as last resort.

2. Due date message added (step 4).
   Schema had dueDate + reminderSentForDueDate but NO message for the day.
   Now: 'On the due date · if set' — appears between receipt and first reminder.
   Default template: 'Today is the day' with bank details for instant action.
   Cron checks Pledge.dueDate = today, fires step 4 template, sets flag.

3. Regenerate button per AI variant.
   Hover over any AI card → refresh icon → deletes old B, generates fresh one.
   Different psychological approach each time (AI picks from 8 strategies).

4. AI now adopts the user's messaging style.
   Prompt analyses variant A for: formality, emoji density, greeting style,
   cultural markers, sentence length, sign-off. AI matches all of these
   while changing only the psychological APPROACH.

5. Per-step required variables enforced with validateTemplate() + patchMissingVariables().
   If AI strips bank details from a receipt, they get patched back in.

6. Cron now uses CUSTOM TEMPLATES from MessageTemplate table (not hardcoded).
   A/B variant selection by splitPercent. sentCount incremented for tracking.
   Falls back to hardcoded templates only if no custom template exists.

Files changed:
- src/lib/templates.ts — REQUIRED_VARIABLES, validateTemplate(), patchMissingVariables(),
  due date template (step 4), STEP_META reordered for display
- src/app/api/automations/ai/route.ts — enforce variables with retry, style adoption prompt,
  extractJson() for robust parsing, step-specific rules
- src/app/dashboard/automations/page.tsx — regenerate button, due date message in conversation,
  conditional display (amber timestamp for due date step)
- src/app/api/automations/route.ts — backfill due date templates for existing orgs
- src/app/api/cron/reminders/route.ts — due date job, custom template resolution,
  A/B variant selection, sentCount tracking
2026-03-05 02:12:45 +08:00
b2cfdff959 Surgical photography: continuous brand experience from landing page to dashboard
ONE image, surgically placed.

'digital-03-notification-smile.jpg' — young man at a London bus
stop smiling at his phone. The moment a WhatsApp reminder lands
and he thinks 'oh right, I need to do that.'

This IS the product working. Not decoration — context.

Uses the same image-panel + dark-panel split from the landing page:
┌──────────────┬──────────────────────────────┐
│ [photo]      │   AI optimisation           │
│ Man smiling  │  Let AI improve your messages │
│ at phone     │  [Start optimising]           │
└──────────────┴──────────────────────────────┘

The image only appears in the onboarding state (never optimised).
Once AI is running, the hero compacts to a single dark bar.
The image served its purpose — motivation to start.

Brand alignment:
- Left-border accent (generosity-gold) on header
- 11px uppercase tracking-[0.15em] labels
- gap-px grid for timing controls
- Sharp edges everywhere (phone mockup is the only exception)
- 60-30-10 color rule maintained
- Dark inversion for AI hero sections
- Typography-driven hierarchy

No new images generated. Used existing brand photography.
2026-03-05 01:58:17 +08:00
7f347260c5 AI-first Automations: done-for-you optimisation
AI is the headline, not a hidden feature.

THREE STATES:

1. NOT STARTED → dark hero:
   'Let AI improve your messages'
   'AI writes a different version of each message and tests
    both with real donors. The better one wins automatically.'
   [Start optimising] ← one button, AI does all 4 steps

2. TESTING → dark hero with pulse:
   'AI is testing 4 experiments'
   Each message shows side-by-side: Yours vs AI's version
   Live conversion rates, progress bar to verdict
   'Pick winners & start new round' button

3. OPTIMISED → dark hero with trophy:
   'Messages optimised · 47 sent · 94% delivered'
   [New round] ← keeps improving forever

INSIDE THE CONVERSATION:

A/B tests show as split cards within the chat:
┌──────────────────────────────┐
│  AI is testing this message │
├──────────────┬───────────────┤
│ Yours        │  AI          │
│ Hi Ahmed..   │ Ahmed, 47..   │
│ 33%          │ 54% 🏆        │
│ 8/24 sent    │ 14/26 sent    │
├──────────────┴───────────────┤
│ ▓▓▓▓▓▓▓▓▓░░░ 72%            │
│ AI version converts 21% better│
└──────────────────────────────┘

Normal messages (no test): click to edit inline.
Everything else: AI handles it.
2026-03-05 01:50:26 +08:00
f1a8c59b0d Automations radical simplification: the page IS the conversation
807 → 394 lines. Removed everything that isn't the answer to
'What do my donors get?'

REMOVED:
- Step timeline tabs (4 across the top)
- Channel tabs (WhatsApp / Email / SMS)
- A/B variant toggle buttons
- AI rewrite toolbar (8 buttons)
- Variable chips panel
- Channel strategy matrix
- Delivery matrix table
- Strategy presets
- Template name editor
- Subject line editor
- Character counter
- Formatting cheatsheet
- Live feed accordion
- Stats bar
- Scheduled reminders list
- Message history feed

WHAT REMAINS:
One WhatsApp conversation showing all 4 messages.
That's the entire page.

- Click a message → it becomes editable inline (green bubble → textarea)
- Hover → ' Try a different approach' appears (AI generates variant B)
- A/B tests show as stacked bubbles with conversion rates
- '🏆 Pick winners' button appears when tests are running
- 'Change timing' link at the bottom (expandable, 3 dropdowns)
- Status line: 'Working · 47 sent · 94% delivered'

The phone mockup is the full-width page content, not a sidebar.
The input bar says 'Donors can reply: PAID · HELP · CANCEL'
Timestamp dividers: 'Instantly', 'Day 2 · if not paid', etc.

This is what Aaisha wants to see: her donors' experience.
2026-03-05 01:24:28 +08:00
d5347d47a1 Switch AI to gpt-4.1-nano + add OpenAI key
Model: gpt-4.1-nano (~$0.10/1M input, $0.40/1M output)
Priority: OpenAI (nano) → Gemini Flash (fallback)
Both keys now in server docker-compose.yml
2026-03-05 01:01:24 +08:00
ea37d7d090 Switch AI from OpenAI to Gemini 2.0 Flash (free, key exists)
All AI features now use Gemini 2.0 Flash via the existing API key.
Falls back to OpenAI if OPENAI_API_KEY is set instead.
Falls back to heuristics if neither key exists.

Gemini free tier: 15 RPM, 1M tokens/day, 1500 RPD
At PNPL's scale this is effectively unlimited and costs £0.

Changed:
- src/lib/ai.ts: chat() → tries Gemini first, OpenAI fallback
- src/app/api/automations/ai/route.ts: same dual-provider pattern
- docker-compose.yml: GEMINI_API_KEY added to app environment

All 11 AI features now work:
- Smart amount suggestions, message generation, fuzzy matching
- Column mapping, event parsing, impact stories, daily digest
- Nudge composer, donor classification, anomaly detection
- A/B variant generation, rewrites, auto-winner evaluation
2026-03-05 00:56:44 +08:00
b25d8c453a AI-native A/B testing: auto-generate variants, auto-promote winners
THE AUTOMATION ENGINE IS NOW SELF-IMPROVING.

## Core: AI generates challenger variants
Click ' AI: Test a new approach' → GPT-4o-mini analyzes variant A
and creates variant B using a fundamentally DIFFERENT psychological
approach. Not a rephrase — a different strategy:
- Social proof ('47 others have already paid')
- Urgency (deadline framing)
- Impact storytelling ('£50 = 3 weeks of food')
- Personal connection (heavy name usage)
- Brevity (strip to minimum)
- Gratitude-first (lead with thanks)
- Loss framing ('pledge at risk of being unfulfilled')
- Community ('join 23 others who completed this week')

AI explains WHY: 'This variant uses social proof instead of a
gentle reminder — peer pressure converts better for step 2.'

## Core: Automatic winner promotion
Click '🏆 Pick winners' → system evaluates ALL running A/B tests:
1. Checks minimum sample size (20 sends per variant)
2. Runs z-test for statistical significance (90% confidence)
3. Promotes winner to variant A (resets counters)
4. Deletes loser
5. AUTOMATICALLY generates a NEW AI challenger

The cycle never stops. Messages continuously evolve.

## Core: AI rewrite toolbar
8 one-click AI rewrites for any template:
✂️ Make shorter · 💛 Make warmer ·  Add urgency
👥 Add social proof · 💚 Add impact story · 🎯 Strip to essentials
🇵🇰 Translate to Urdu · 🇸🇦 Translate to Arabic

All rewrites preserve {{variable}} placeholders.
All use GPT-4o-mini (~/usr/bin/bash.15/1M tokens).

## UI: A/B Stats Card (below phone mockup)
- Side-by-side conversion rates with trophy icon on winner
- Progress bar to verdict (% of minimum sample collected)
- Lift calculation: 'Variant B converts 63% better'
- Real-time during test: 'A: 33% → B: 54% ★'

## UI: Winner Results Banner
After 'Pick winners' runs:
- Green banner: '🏆 Winners promoted — Gentle reminder · WhatsApp
  → Variant B wins (54% vs 33%)  New AI challenger created'
- Gray banner if not enough data: 'Need 20+ sends per variant'

## API: /api/automations/ai (POST)
Actions:
- generate_variant: AI creates challenger B with strategy reasoning
- rewrite: AI rewrites template with specific instruction
- check_winners: evaluate all tests, promote, regenerate

## Architecture
The system is a GENETIC ALGORITHM for messaging:
1. Start with default templates (generation 0)
2. AI creates a challenger (mutation)
3. Traffic splits 50/50 (fitness test)
4. Winner survives, loser dies (selection)
5. AI creates new challenger (next generation)
6. Repeat forever → messages get better over time
2026-03-05 00:42:35 +08:00
17b3e15fae Automations deep redesign: message design studio with WhatsApp-native preview
COMPLETE RETHINK — from monitoring dashboard to message design studio.

## The Big Idea
Aaisha doesn't need a dashboard that says 'is it working?'
She needs a studio where she can SEE what Ahmed sees on his phone,
EDIT the words, TEST different approaches, and DESIGN cross-channel
sequences. The WhatsApp phone mockup is the star.

## New: Phone Mockups (3 channels)
- WhatsApp: green bubbles, blue ticks, org avatar, chat wallpaper,
  full formatting (*bold*, _italic_, `code`, ━━━ dividers)
- Email: macOS mail client chrome, From header, subject line
- SMS: iOS Messages style, grey bubbles, contact avatar

## New: Template Editor
- Editable templates per step (receipt, day 2, 7, 14) per channel
- Live preview in phone mockup as you type
- Variable insertion chips: {{name}}, {{amount}}, {{reference}}, etc.
- Subject line editor for email channel
- Character count + SMS segment counter

## New: A/B Testing
- Create Variant B of any step/channel message
- 50/50 split traffic automatically
- Track sent count + conversion rate (paid after receiving)
- Side-by-side stats: 'A: 33% paid, B: 54% paid ★'
- Delete variant to revert to single message

## New: Channel Strategy Matrix
- 3 presets: Waterfall (default), Belt & Suspenders, Escalation
- Visual matrix: steps × channels with status indicators
- 1st = primary, fb = fallback, + = parallel send
- Waterfall: WhatsApp → SMS → Email (most cost-effective)
- Belt & Suspenders: all channels for receipts + final
- Escalation: start gentle (WA only), add channels as urgency increases

## New: Customizable Timing
- Each step's delay is editable inline (dropdown next to phone)
- Default: Day 2, Day 7, Day 14
- Can change to any schedule: Day 1, Day 3, Day 21, Day 28

## Schema: 2 new models
- MessageTemplate: per-org editable templates with A/B variants
  (step, channel, variant, body, subject, splitPercent, sentCount, convertedCount)
- AutomationConfig: per-org timing + strategy + channel matrix

## API: /api/automations (GET/PATCH/DELETE)
- GET seeds defaults on first load (12 templates: 4 steps × 3 channels)
- PATCH upserts templates and config
- DELETE removes variant B and resets A to 100%

## Default templates (src/lib/templates.ts)
Extracted from hardcoded whatsapp.ts + reminders.ts into editable templates:
- WhatsApp: receipt, gentle, impact, final (with emoji + formatting)
- Email: receipt, gentle, impact, final (with cancel/pledge URLs)
- SMS: receipt, gentle, impact, final (160-char optimized)

## Architecture
templates.ts → resolvePreview() fills {{variables}} with examples
templates.ts → resolveTemplate() fills {{variables}} with real data
messaging.ts → sendToDonor() routes via channel waterfall
automations/route.ts → seeds + CRUD for templates + config

## Visual: Step timeline at top
4 tabs across the top with emoji, timing, description
Active step is dark (111827), others are white
Click to switch — editor and phone update together

## Layout
[Step Timeline — 4 tabs across top]
[Phone Mockup (left) | Editor (right)]
[Channel Strategy — expandable matrix]
[Live Feed — condensed stats + scheduled + messages]
2026-03-05 00:22:18 +08:00
c52c97df17 Automations engine: multi-channel messaging + dashboard
THE STAR OF THE SHOW — the automation engine is now visible.

## New: Unified Messaging Layer (src/lib/messaging.ts)
Channel waterfall: WhatsApp → SMS → Email
- sendToDonor() routes to best available channel
- Respects donor consent flags (whatsappOptIn, emailOptIn)
- Falls back automatically if primary channel fails
- Every attempt logged to AnalyticsEvent for dashboard

## New: Email Integration (src/lib/email.ts)
Bring-your-own-key: charity pastes their Resend or SendGrid API key
- Resend: free 3,000 emails/month
- SendGrid: free 100/day
- Messages come from THEIR domain (donations@mymosque.org)
- Plain text auto-converted to clean HTML

## New: SMS Integration (src/lib/sms.ts)
Bring-your-own-key: charity pastes their Twilio credentials
- Pay-as-you-go (~3p per SMS)
- UK number normalization (07xxx → +447xxx)
- Reaches donors without WhatsApp or email

## New: /dashboard/automations — the visible engine
A. Dark hero stats: Messages this week per channel + delivery rate
B. Live channels: WhatsApp/Email/SMS with status, features, stats
C. The Pipeline: visual 4-step automation sequence
   - What triggers, what's sent, which channels, waterfall explanation
D. Scheduled reminders: upcoming messages with timing
E. Message feed: recent messages with channel icon, status, time

## New: /api/messaging/status — dashboard data endpoint
Returns channels, stats (7 day), history (50 recent), pending reminders

## New: /api/messaging/test — send test message to admin

## Schema: 8 new Organization columns
emailProvider, emailApiKey, emailFromAddress, emailFromName
smsProvider, smsAccountSid, smsAuthToken, smsFromNumber

## Settings: 2 new channel rows in the checklist
- Email: provider selector (Resend/SendGrid) + API key + from address
- SMS: Twilio credentials + from number
Both follow the same checklist expand/collapse pattern

## Nav: Automations added between Money and Reports
Home → Collect → Money → Automations → Reports → Settings

## Stats tracking
Messages logged as AnalyticsEvent:
  message.whatsapp.receipt.sent
  message.email.reminder_1.failed
  message.sms.reminder_2.sent
Donor PII masked in logs (last 4 digits of phone, email obfuscated)
2026-03-04 23:20:50 +08:00
ce4f2ba52a Settings: deep UX redesign — checklist pattern, not form boxes
THE INSIGHT:
Settings pages feel like work because they're designed as FORMS.
6 identical white boxes stacked vertically = 'I have to fill all this in?'

But Aaisha's mental model is a CHECKLIST:
'Am I set up? What's left? Let me fix the one thing that's missing.'

THE PATTERN:
Each setting has 3 visual states:

✓ CONFIGURED → single summary line
  'Bank account · Barclays · ****5678'         [Edit]
  Clicking Edit expands the form inline.

○ NEEDS SETUP → expanded with instructions + form
  The first unconfigured item auto-expands.

→ EDITING → expanded form with Save/Cancel
  Save auto-collapses back. Green flash confirms.

THE RESULT:
- Everything configured? Page is SHORT. Green dots, one-liners.
- Something missing? That section is expanded and loud.
- No 'wall of forms' feeling.
- Only one section open at a time (accordion).

HEADER:
Old: Dark stats bar with 5 cells of status dots
New: Thin progress bar + human sentence
  'You're all set' / '2 things left before you go live'
  Counts only essentials (WhatsApp, bank, charity name).

LAYOUT:
Old: 6 separate bordered boxes, each with header + form
New: Single bordered container, divide-y between items
  Each item is a clickable ROW that expands/collapses
  Blue left-border accent on expanded form
  Status dot: green=done, amber=needed, gray=optional

SPECIFICS:
- WhatsApp: collapsed to 'Connected · +447xxx · Receipts, reminders'
  Expands to show QR or features grid
- Bank: collapsed to 'Barclays · ****5678'
  Expands to form + live 'What donors see' preview
- Charity: collapsed to name + color swatch
  Expands to form + pledge page header preview
- Stripe: collapsed to 'Connected' or 'Optional'
  Expands to key field + webhook setup
- Team: collapsed to '3 members · 1 leader'
  Expands to member list + invite flow
- GoCardless: dimmed when collapsed (advanced), expands normally

NEW COMPONENTS:
- SettingRow: generic expand/collapse row pattern
- WhatsAppRow: special case with QR polling
- TeamRow: special case with member list + invite
- SaveRow: Save + Cancel buttons, auto-collapse on save
- Field: reusable input (unchanged)
2026-03-04 23:02:17 +08:00
3b46222118 Stripe integration: charity connects their own Stripe account
Model: PNPL never touches the money. Each charity connects their own
Stripe account by pasting their API key in Settings. When a donor
chooses card payment, they're redirected to Stripe Checkout. The money
lands in the charity's Stripe balance.

## Schema
- Organization.stripeSecretKey (new column)
- Organization.stripeWebhookSecret (new column)

## New/rewritten files
- src/lib/stripe.ts — getStripeForOrg(secretKey), per-org client
- src/app/api/stripe/checkout/route.ts — uses org's key, not env var
- src/app/api/stripe/webhook/route.ts — tries all org webhook secrets
- src/app/p/[token]/steps/card-payment-step.tsx — redirect to Stripe
  Checkout (no fake card form — Stripe handles PCI)

## Settings page
- New 'Card payments' section between Bank and Charity
- Instructions: how to get your Stripe API key
- Webhook setup in collapsed <details> (optional, for auto-confirm)
- 'Card payments live' green banner when connected
- Readiness bar shows Stripe status (5 columns now)

## Pledge flow
- PaymentStep shows card option ONLY if org has Stripe configured
- hasStripe flag passed from /api/qr/[token] → PaymentStep
- Secret key never exposed to frontend (only boolean hasStripe)

## How it works
1. Charity pastes sk_live_... in Settings → Save
2. Donor opens pledge link → sees 'Bank Transfer', 'Direct Debit', 'Card'
3. Donor picks card → enters name + email → redirects to Stripe Checkout
4. Stripe processes payment → money in charity's Stripe balance
5. (Optional) Webhook auto-confirms pledge as paid

Payment options:
- Bank Transfer: zero fees (default, always available)
- Direct Debit via GoCardless: 1% + 20p (if org configured)
- Card via Stripe: standard Stripe fees (if org configured)
2026-03-04 22:46:08 +08:00
62be460643 Remove dead Stripe integration
Stripe was wired up but never used:
- No STRIPE_SECRET_KEY in .env
- Card payment step had a 'simulated fallback' that pretended to charge
- Stripe fees (1.4% + 20p) contradict '100% goes to charity' brand promise
- Bank transfer is the primary rail, GoCardless (DD) is the secondary

Removed:
- src/lib/stripe.ts (Stripe client, checkout sessions, webhooks)
- src/app/api/stripe/checkout/route.ts
- src/app/api/stripe/webhook/route.ts
- src/app/p/[token]/steps/card-payment-step.tsx (263 lines)
- 'stripe' and '@stripe/stripe-js' npm packages
- Card option from PaymentStep (payment-step.tsx)
- Card references from confirmation-step.tsx, success/page.tsx
- Stripe from landing page integrations grid
- Stripe from privacy policy sub-processors
- Stripe from terms of service payment references

Type Rail changed: 'bank' | 'gocardless' | 'card' → 'bank' | 'gocardless'
Pledge flow bundle: 19.5kB → 18.2kB (-1.3kB)

Payment options donors now see:
1. Bank Transfer (recommended, zero fees)
2. Direct Debit via GoCardless (1% + 20p, hassle-free)
2026-03-04 22:29:49 +08:00
f75cc29980 Settings page: full telepathic redesign matching brand system
Before: Mediocre — shadcn <Input>, no visual hierarchy, no readiness
indicator, no donor preview, inconsistent headers, flat team list.

After: Every element matches the brand system used in Collect/Money/Reports.

Changes:
1. READINESS BAR (dark hero section)
   - 4-cell gap-px grid on #111827 background
   - Green/gray dots: WhatsApp ✓, Bank ✗, Charity ✓, Team: 2 members
   - Aaisha sees instantly what's configured and what's missing

2. SECTION HEADERS (consistent pattern)
   - All sections: colored icon box + title + description
   - border-b separator matching Reports/Money pattern
   - WhatsApp: green icon box. Bank: green when configured.

3. FIELD COMPONENT (no more shadcn Input)
   - Reusable <Field> with uppercase tracking-wide label
   - border-2 focus:border-[#1E40AF] (sharp, no rounded)
   - Consistent height (h-10) and padding across all inputs

4. BANK ACCOUNT — DONOR PREVIEW
   - New: shows exactly what donors see after pledging
   - Grid layout with bank name, sort code, account, reference
   - 'What donors see after pledging' preview card
   - Context tip: 'Changes apply to new pledges immediately'

5. CHARITY — BRAND PREVIEW
   - Shows logo mark (first letter in brand color square) + name
   - Color picker is now a swatch + hex input
   - 'Preview — pledge page header' section

6. TEAM MANAGEMENT
   - Role cards with icon boxes and colored badges
   - Gap-px grid for WhatsApp features (connected state)
   - Credentials grid layout (not prose)
   - Empty state with icon + helpful text
   - Role icons: Crown (admin), Users (leader), Eye (staff/volunteer)
   - Color-coded: blue admin, amber leader, gray staff

7. WHATSAPP PANEL
   - Connected: gap-px 3-column grid (Receipts/Reminders/Chatbot)
   - Not connected: border-l-2 accent list, PAID/HELP/CANCEL in mono
   - QR scanning: border-l-2 instructions
   - onStatusChange callback feeds the readiness bar

8. DIRECT DEBIT
   - Custom <details> with ChevronRight rotation
   - border-l-2 contextual tip ('most charities don't need this')

9. SAVE BUTTONS
   - Extracted <SaveBtn> component
   - Green flash on save (bg-[#16A34A])
   - 'Save changes' / 'Saving…' / 'Saved' states
2026-03-04 22:19:31 +08:00
b477dc30d1 Role-based access control: guards on all critical APIs + redirects
INTEGRATION AUDIT — Fixed all gaps:

1. LOGIN REDIRECT
   - Community leaders → /dashboard/community (not /dashboard)
   - Fetches session after login to check role before redirect
   - Auth0 callback still goes to /dashboard (handled by #2)

2. DASHBOARD HOME REDIRECT
   - If role === community_leader or volunteer → router.replace(/community)
   - Prevents them from seeing the admin home page

3. API ROLE GUARDS (server-side)
   New: src/lib/roles.ts — permission matrix:
   - settings.write: super_admin, org_admin only
   - pledges.write: super_admin, org_admin only (status changes)
   - events.create: super_admin, org_admin only
   - imports.upload: super_admin, org_admin only (bank statements)
   - links.create: super_admin, org_admin, community_leader (they can create)
   - pledges.read: everyone except volunteer
   - dashboard.read: everyone except volunteer

   New: requirePermission() in session.ts
   Applied to:
   - PATCH /api/settings → settings.write
   - PUT /api/settings → settings.write
   - PATCH /api/pledges/[id] → pledges.write
   - POST /api/events → events.create
   - POST /api/imports/bank-statement → imports.upload

   Community leader attempting these gets 403 'Admin access required'

4. LAYOUT NAV (already done in previous commit)
   - community_leader sees: My Community, Share Links, Reports
   - No Money, No Settings, No 'New Appeal' button

WHAT COMMUNITY LEADER CAN DO:
✓ View /dashboard/community (their scoped dashboard)
✓ View /dashboard/collect (share links — they can create new links)
✓ View /dashboard/reports (financial summary)
✓ Create QR sources / links (POST /api/events/[id]/qr)
✓ Read pledges and dashboard data

WHAT COMMUNITY LEADER CANNOT DO:
✗ Change pledge statuses (PATCH /api/pledges/[id] → 403)
✗ Change settings (PATCH/PUT /api/settings → 403)
✗ Create appeals (POST /api/events → 403)
✗ Upload bank statements (POST /api/imports/bank-statement → 403)
✗ Manage team (POST/PATCH/DELETE /api/team → 403, already guarded)
✗ See /dashboard/money, /dashboard/settings (not in nav, home redirects)
2026-03-04 21:58:25 +08:00
b771858280 Settings + Admin redesign + Community Leader role
## New: Community Leader role
Who: Imam Yusuf, Sister Mariam, Uncle Tariq — the person who rallies
their mosque, WhatsApp group, neighbourhood to pledge.

Not an admin. Not a volunteer. A logged-in coordinator who needs
more than a live feed but less than full admin access.

/dashboard/community — their scoped dashboard:
- 'How are WE doing?' — their stats vs the whole appeal (dark hero section)
- Contribution percentage bar
- Their links with full share buttons (Copy/WhatsApp/Email/QR)
- Create new links (auto-tagged with their name)
- Leaderboard: 'How communities compare' with 'You' badge
- Read-only pledge list (no status changes, no bank details)

Navigation changes for community_leader role:
- Sees: My Community → Share Links → Reports (3 items)
- Does NOT see: Home, Money, Settings, New Appeal button
- Does NOT see: Bank details, WhatsApp config, reconciliation

## New: Team management API + UI
GET/POST/PATCH/DELETE /api/team — CRUD for team members
- Only org_admin/super_admin can invite
- Temp password generated on invite (shown once)
- Copy credentials or send via WhatsApp button
- Role selector with descriptions (Admin, Community Leader, Staff, Volunteer)
- Role change via dropdown, remove with trash icon
- Can't change own role or remove self

## Settings page redesign
Reordered by Aaisha's thinking:
1. WhatsApp (unchanged — most important)
2. Team (NEW — 'who has access? invite community leaders')
3. Bank account
4. Charity details
5. Direct Debit (collapsed in <details>)

Team section shows:
- All members with role icons (Crown/Users/Eye)
- Inline role change dropdown
- Remove button
- Invite form with role cards and descriptions
- Credentials shown once with copy + WhatsApp share buttons

## Admin page redesign
Brand-consistent: no more shadcn Card/Badge/Table
- Dark hero section with 7 platform stats
- Pipeline status breakdown (gap-px grid)
- Pill tab switcher (not shadcn Tabs)
- Grid tables matching the rest of the dashboard
- Role badges color-coded (blue super, green admin, amber leader)

6 files changed, 4 new routes/pages
2026-03-04 21:48:10 +08:00
9c7990e05c Reconciliation embedded in Money — no more separate page
The reconcile feature was hidden behind a text link at the bottom of
the Money page. That's backwards. Reconciliation IS the Money page.

Aaisha's actual thought process:
1. People pledged
2. Some say they paid
3. 'Did the money actually arrive?' → opens bank website, downloads CSV
4. 'Let me match it' → THIS SHOULD BE RIGHT HERE, not behind a link
5. '8 out of 10 matched' → pledges auto-move to 'received'

Changes:
- Full bank statement upload area is NOW embedded directly in /dashboard/money
  With icon, description, drop zone — always visible, not a link
- When CSV is selected: file name + detected bank format shown inline
  Column mapping is collapsed by default (auto-detected) but expandable
- 'Match payments' button is blue, full-width, prominent
- Results appear INLINE below the upload area:
  - Summary stats (gap-px grid): rows, incoming, matched, possible, auto-confirmed
  - Green success banner when pledges are auto-confirmed
  - Full match results table with confidence icons
  - 'Upload another' button to reset
- After matching: dashboard data auto-refreshes to show updated pledge statuses
- 'Said they paid' section now says 'Upload a bank statement above to confirm'
  instead of linking to a separate page
- /dashboard/reconcile now redirects to /dashboard/money (backward compat)
- Contextual sections (confirm/nudge) hide when match results are showing
  to avoid visual clutter

Architecture is now:
Stats → MATCH PAYMENTS → Confirm these → Need a nudge → All pledges table

Not: Stats → table → small link at bottom → navigate away → separate page
2026-03-04 21:35:23 +08:00
c43404694e Telepathic Money + Reports: context-aware inbox, financial summary
## Money page (/dashboard/money) — context-aware inbox

The key insight: Aaisha's #1 Money question changes over time.
Day 1: 'Did anyone pledge?' → Recent section
Day 3: 'Are they paying?' → Confirm section
Day 10: 'Who hasn't paid?' → Nudge section
Day 30: 'Give me the spreadsheet' → she goes to Reports

Changes:
- Contextual 'Confirm these payments' section (amber)
  Shows when there are 'said they paid' pledges
  One-click green 'Confirm' button on each row
  Links to bank statement upload
  Only appears on 'all' filter (not when already filtering)

- Contextual 'These people need a nudge' section (red)
  Shows when there are overdue pledges
  One-click green 'Nudge' WhatsApp button + 'Paid' quick button
  Shows days since pledge for urgency

- Stats bar redesigned: 5 clickable stat cells (gap-px)
  Each acts as a filter toggle with active underline
  Color-coded: amber for 'said paid', red for overdue, green for received

- Filter pills replace shadcn Tabs (smaller, more buttons fit)
  Pill buttons instead of tab strip — works better on mobile

- Table kept for Fatima (power user who scans everything)
  Same columns, actions, pagination as before

- Match payments CTA promoted: full-width card with icon + description
  No longer a text link hidden at the bottom

## Reports page (/dashboard/reports) — Fatima's dashboard

The key insight: Fatima (treasurer) logs in monthly.
She should NOT need to visit any other page.

Changes:
- Financial summary hero (dark section)
  Total promised, total received, outstanding, collection rate
  Progress bar with percentage
  Same visual language as leaderboard hero

- Status breakdown with visual bars
  Horizontal bars showing distribution: paid/waiting/initiated/overdue
  Percentage labels

- Per-appeal breakdown table
  Each appeal: pledges, promised, received, collection rate
  Total row at bottom for multi-appeal orgs
  Rate color-coded: green ≥70%, amber ≥40%, gray below

- Gift Aid section with PREVIEW
  Shows number of eligible declarations + reclaimable amount
  before downloading — Fatima can see if it's worth running
  '25p for every £1' callout

- Downloads: Full CSV + Gift Aid CSV
  Same download functionality, better presentation

- API/Zapier section redesigned
  Two endpoint examples (pledges + dashboard)
  Clearer documentation for Zapier/Make integration

- Activity log section
  Shows recent system activity (audit trail)
  Scrollable, max 20 entries

2 pages rewritten (~38k bytes)
2026-03-04 21:22:03 +08:00
a9b3b70dfc Telepathic Collect: link-first, flattened hierarchy, embedded leaderboard
Core insight: The primary object is the LINK, not the appeal.
Aaisha doesn't think 'manage appeals' — she thinks 'share my link'.

## Collect page (/dashboard/collect) — complete rewrite
- Flattened hierarchy: single-appeal orgs see links directly (no card to click)
- Multi-appeal orgs: quiet appeal switcher at top, links below
- Inline link creation: just type a name + press Enter (no dialog)
- Quick preset buttons: 'Table 1', 'WhatsApp Group', 'Instagram', etc.
- Share buttons are THE primary CTA on every link card (Copy, WhatsApp, Email, Share)
- Each link shows: clicks, pledges, amount raised, conversion rate
- Embedded mini-leaderboard when 3+ links have pledges
- Contextual tips when pledges < 5 ('give each volunteer their own link')
- New appeal creation is inline, auto-creates 'Main link'

## Appeal detail page (/dashboard/events/[id]) — brand redesign
- Sharp edges, gap-px grids, typography-as-hero
- Same link card component with share-first design
- Embedded leaderboard section
- Inline link creation (same as Collect)
- Clone appeal button
- Appeal details in collapsed <details> (context, not hero)
- Download all QR codes link
- Public progress page link

## Leaderboard page — brand redesign
- Total raised as hero number (dark section)
- Progress bars relative to leader
- Medal badges for top 3
- Conversion rate badges
- Auto-refresh every 10 seconds (live event mode)

## Route cleanup
- /dashboard/events re-exports /dashboard/collect (backward compat)
- Old events/page.tsx removed (was duplicate)

5 files changed, 3 pages redesigned
2026-03-04 21:13:32 +08:00
6fb97e1461 Telepathic onboarding: welcome flow + context-aware dashboard
New: /dashboard/welcome — guided first-time setup
- Step 1: 'What are you raising for?' (starts with what excites them)
- Step 2: 'Where should donors send money?' (natural follow-up)
- Step 3: 'Want auto-reminders?' (WhatsApp as bonus, skippable)
- Step 4: 'Here's your link!' (dark section with copy/WhatsApp/share)
- Auto-creates event + first pledge link during flow
- User holds a shareable link within 90 seconds of signing up

Updated: /dashboard (context-aware home)
- State 1 (no events): auto-redirects to /dashboard/welcome
- State 2 (0 pledges): shows pledge link + share buttons prominently
- State 3 (has pledges): shows stats + feed
- State 4 (has 'said paid'): amber prompt to upload bank statement
- State 5 (100% collected): celebration banner
- No more onboarding checklist — dashboard adapts instead
- Event name as page header (not generic 'Home')
- Event switcher for multi-event orgs

Updated: /signup → redirects to /dashboard/welcome (not /dashboard)

Persona spec: docs/PERSONA_JOURNEY_SPEC.md
2026-03-04 21:01:16 +08:00
170a2e7c68 Complete dashboard UI overhaul: persona journeys + brand unification
Navigation: goal-oriented, not feature-oriented
- Overview → Home
- Campaigns → Collect ('I want people to pledge')
- Pledges → Money ('Where's the money?')
- Exports → Reports ('My treasurer needs numbers')
- Old routes still work via re-exports

Terminology: human language, not SaaS jargon
- new → Waiting
- initiated → Said they paid
- paid → Received ✓
- overdue → Needs a nudge
- Campaign → Appeal
- QR Source → Pledge link
- Reconcile → Match payments
- Rail → Payment method
- Pipeline by Status → How pledges are doing
- Conversion rate → % who pledged
- CRM Export Pack → Full data download

Visual identity: brand-consistent dashboard
- Sharp edges (no rounded-lg cards)
- Gap-px grids for stats (brand signature pattern)
- Left-border accents (brand signature pattern)
- Midnight/Paper/Promise Blue 60-30-10 color rule
- Typography as hero (big bold numbers, not card-heavy)
- No emoji in UI chrome
- Brand-consistent status badges (colored bg + text, not shadcn Badge)
- Consistent header typography (text-3xl font-black tracking-tight)

Pages rewritten: layout, home, events (collect), pledges (money),
exports (reports), reconcile, settings

Reconcile: auto-detects bank CSV format via presets + AI before upload

UX spec: docs/UX_OVERHAUL_SPEC.md
2026-03-04 20:50:42 +08:00
fcfae1c1a4 Ship all P0/P1/P2 gaps + 11 AI features
P0 Critical (7):
- STOP/UNSUBSCRIBE keyword → CANCEL (PECR compliance)
- Rate limiting on pledge creation (10/IP/5min)
- Terms of Service + Privacy Policy pages
- WhatsApp onboarding gate (persistent dashboard banner)
- Demo account seeding (demo@pnpl.app)
- Footer legal links
- Basic accessibility (aria labels on donor flow)

P1 Within 2 Weeks (8):
- Pledge editing by staff (PATCH amount, name, email, phone, rail)
- Donor self-cancel page (/p/cancel) + API
- Donor 'My Pledges' lookup page (/p/my-pledges)
- Bulk QR code download (print-ready HTML)
- Public event progress bar (/e/[slug]/progress)
- Email-only donor handling (honest status + WhatsApp fallback)
- Email verification (format + disposable domain blocking)
- Organisations page rewrite (multi-campaign, not multi-org)

P2 Within First Month (10):
- Event cloning with QR sources
- Account deletion (GDPR Article 17)
- Daily digest cron via WhatsApp
- AI-6 Smart reminder timing (due date anchoring, cultural sensitivity)
- H1 Duplicate donor detection (email, phone, Jaro-Winkler name)
- H5 Bank CSV format presets (10 UK banks)
- H16 Partial payment matching (underpay, overpay, instalment)
- H10 Activity logging (audit trail for staff actions)
- AI nudge endpoint + AI column mapping + AI event setup wizard
- AI anomaly detection wired into daily digest

AI Features (11): smart reconciliation, social proof, auto column mapper,
daily digest, impact storyteller, smart timing, nudge composer, event wizard,
NLU concierge, anomaly detection, bank presets

22 new files, 15 modified files, 0 TypeScript errors, clean build.
2026-03-04 20:10:34 +08:00
59485579ec clean: remove .pi/ config from calvana repo — lives in pi-vs-claude-code now
- Removed all .pi/ (agents, themes, extensions, skills, observatory)
- Removed CLAUDE.md (belongs in parent pi repo)
- Added .pi/ and CLAUDE.md to .gitignore
- Added .pi/ to pledge-now-pay-later/.gitignore
2026-03-04 17:32:20 +08:00
ef37ca0c18 Fix payment flexibility quote length and orphan word
- Shorten quote 03 from 'Can I split it across a few months?' to 'Can I pay monthly?' for column symmetry
- Add nbsp between 'money' and 'arriving' to prevent orphan line break
2026-03-04 13:58:42 +08:00
6b71fa227b fix stacking cards: flatMap siblings, no wrappers, no shadow, z < header
ROOT CAUSE: each card was wrapped in its own div (min-h-[85vh]),
scoping sticky to that wrapper — cards could NEVER overlap.

FIX: flatMap returns all sticky divs + h-4 spacers as direct
siblings under the same parent (mt-14). Sticky now works
correctly — each card overlaps the previous with 20px peek.

- Removed all shadows (border-gray-100 only)
- z-index: 1-4 (was 10-40, conflicting with nav z-40)
- top: 72/92/112/132px (20px stagger)
- h-4 spacers between cards (no big white gaps)
- Regenerated dinner image: dark navy table, candlelight, £5,000
  pledge card — zero white space (was white tablecloth)
2026-03-03 23:29:26 +08:00
dc1253af33 sticky stacking cards on scroll + kill border white space
STACKING EFFECT:
- Cards use position: sticky with increasing top offset (72-120px)
- z-index layering (10-40) so later cards stack on top
- pb-36 between cards for scroll breathing room
- will-change-transform for smooth compositing

WHITE SPACE FIX:
- Removed border border-gray-200 (was creating visible white gap)
- Replaced with shadow-[0_2px_8px_rgba(0,0,0,0.08)] for depth
- Switched from CSS Grid to flexbox (flex-row/flex-row-reverse)
- Image fills full card height via flexbox stretch

LAYOUT:
- md:w-7/12 for image, md:w-5/12 for text
- min-h-[400px] on desktop for substantial card presence
- Alternating image left/right preserved for visual rhythm
2026-03-03 22:55:03 +08:00
233e9320b5 stunning alternating editorial rows + fix invisible gray text
LAYOUT:
- Killed boring 2x2 grid boxes
- Full-width alternating image/text rows (7:5 split)
- Image left/text right on rows 1,3 — flipped on rows 2,4
- Creates visual rhythm like an editorial magazine spread
- gap-px between rows for signature pattern
- Images at 2:1 cinematic aspect, large and atmospheric

CONTRAST FIX:
- Pain stat 'before' text: gray-200 (invisible) -> gray-400 (readable)
- 'Systems do.' heading: gray-300 -> gray-400
- Both stats now clearly visible while maintaining muted/bold contrast
2026-03-03 22:35:25 +08:00
3a6ec55a68 persona section: scenario-based personas + cinematic photography + pain stats
PERSONA OVERHAUL:
- Personas now defined by WHAT THEY DID, not job titles
- 'Charity Manager' -> 'You organized the dinner'
- 'Personal Fundraiser' -> 'You shared the link'
- 'Volunteer' -> 'You were on the ground'
- 'Organisation/Programme Manager' -> 'You claim the Gift Aid'

SECTION HEADING:
- Brand core insight: 'People don't break promises. Systems do.'
- Eyebrow: 'THE PLEDGE GAP'
- Sub: 'We built the missing system between I'll donate and the money arriving.'

PAIN STATS (visual anchors):
- £50,000 pledged / £22,000 collected (the gap)
- 23 said I'll donate / 8 actually did
- 40 pledges collected / 0 updates received
- 200 rows, 47 typos / 6 hours every quarter

COPY: Emotionally precise, tells each persona's specific story

PHOTOGRAPHY (4 cinematic moment shots):
- Dinner aftermath: empty table with lone pledge card, chandeliers
- Phone: hands on WhatsApp at kitchen table, warm light
- Volunteer: seen from behind, walking between gala tables with cards
- Desk still life: laptop spreadsheet, papers, tea, window light
- All 2:1 wide aspect, 2.7MB -> 260KB optimized
2026-03-03 22:21:49 +08:00
c18dc50657 persona section overhaul — editorial gap-px grid + fresh photography
SECTION REDESIGN:
- Killed standalone dashboard image (fake AI laptop, added nothing)
- New gap-px grid (signature pattern 2) with border-l-2 accents (pattern 1)
- Numbered anchors (01-04) as visual rhythm per brand guide
- Wider container: max-w-7xl matches hero width

PERSONA CHANGES:
- Renamed 'Organisation' -> 'Programme Manager'
- Reorder: Charity Manager, Programme Manager, Personal Fundraiser, Volunteer
- Updated /for/organisations page content to match

PHOTOGRAPHY (4 new images via gemini-3-pro-image-preview):
- persona-charity-manager.jpg — hijabi woman at mosque office desk
- persona-programme-manager.jpg — man at desk with campaign calendar
- persona-fundraiser.jpg — woman on London park bench with phone
- persona-volunteer.jpg — young man handing card at charity gala
- All optimized: 2.7MB -> 342KB (87% reduction via sharp)
- Consistent documentary candid style, 3:2 landscape, warm tones

FOOTER:
- 'Organisations' -> 'Programme Managers' in nav links
2026-03-03 22:01:53 +08:00
3ab440f103 center stat strip text 2026-03-03 21:41:11 +08:00
f0b1cb2f3a fix headline rhythm + wider hero image + 10x faster deploys
HEADLINE:
- 3 balanced lines: 'Turn I'll donate' / 'into money' / 'in the bank.'
- Removed &nbsp; that orphaned 'money' on its own line
- <br className='hidden lg:block'> controls breaks on desktop only

IMAGE:
- Hero container: max-w-5xl -> max-w-7xl (image 25% wider)
- Stat strip widened to match
- Much more of the gala scene visible, phone prominent

DEPLOY SPEED (deploy.sh):
- Persistent /opt/pnpl/ build dir (no temp dir creation/deletion)
- BuildKit with cache mounts (npm + .next/cache)
- No more docker builder prune / docker rmi (preserves cache!)
- Installed docker-buildx v0.31.1 on server
- Before: ~245s (4+ min)  After: ~29s (cached) / ~136s (first)
- Use: cd pledge-now-pay-later && bash deploy.sh
2026-03-03 21:37:34 +08:00
c9301edbe8 hero image fills full text column height on desktop
- Grid: items-start → md:items-stretch (both columns same height)
- Image: aspect-[4/5] → md:aspect-auto md:h-full (fills column)
- Mobile keeps aspect-[3/4] for stacked layout
- Bottom of image now lines up with buttons/trust line
2026-03-03 21:17:19 +08:00
ac19afce4e world-class hero image + 85% image optimization + sharp
HERO IMAGE:
- Generated 3 concepts with gemini-3-pro-image-preview, picked #1
- Phone showing 'Payment Received' notification at a charity gala dinner
- Warm tungsten bokeh chandeliers against dark bg-gray-950
- Directly visualizes the headline: 'money in the bank'
- Candid documentary angle, not looking at camera, brand compliant

IMAGE OPTIMIZATION (85% total reduction):
- All 21 images resized: landscape max 1200px, portrait max 1000px
- Compressed JPEG quality 80, progressive encoding, EXIF stripped
- Total: 13.6MB -> 2.1MB (saved 11.5MB)
- Individual savings: 81-90% per image

NEXT.JS IMAGE PIPELINE:
- Added sharp (10x faster than squoosh for image processing)
- next.config.mjs: WebP format, proper device/image sizes, 1yr cache TTL
- Dockerfile: libc6-compat + NEXT_SHARP_PATH for Alpine sharp support
- First request: ~3s (processing), cached: <1s

WebP served sizes: hero 52KB, cards 32-40KB (vs original 500-800KB JPEGs)
2026-03-03 21:10:59 +08:00
2592c4ba5b dark editorial hero — typography + documentary photo + gap-px stat strip
HERO REDESIGN:
- bg-gray-950 full-bleed dark hero (was white text-on-white)
- Split layout: 7-col massive headline + 5-col documentary photo
- Gala photo (02) as hero — warm tungsten pops against dark bg
- border-l-2 promise-blue eyebrow accent (signature pattern 1)
- gap-px stat strip: 30-50%, 60s, £0, 2 min (signature pattern 2)
- stagger-children animation on text column
- Delayed fade-up on image column (opacity: 0 → fadeUp after 250ms)
- Trust line with vertical pipe separators
- Merges old hero + hero image + stat section into 1 cinematic opening

NAV:
- Wordmark hidden on mobile (sm:inline), shows P mark only
- shrink-0 on logo, whitespace-nowrap on nav buttons

PERSONA CARDS:
- Charity Manager card now uses mosque photo (08) for variety
- Hover color: text-trust-blue → text-promise-blue (proper token)

Brand compliant: 0 violations (no gradients, rounded-2xl, backdrop-blur)
2026-03-03 20:53:25 +08:00
e2295020a1 add brand/ assets to repo + BRAND.md as single source of truth
BRAND.md: Complete brand guide with code snippets, color tokens, typography scale,
logo usage rules, DO/DON'T checklist, photography direction, voice & tone.
Every section references its visual asset in brand/.

brand/ folder (52 assets):
- photography/ (20) — landing page photos
- logo/ (6) — lockup, reversed, blue, marks, favicon
- color-palette/ (3) — primary, tints, psychology
- typography/ (3) — specimen, scale, numbers
- moodboard/ (3) — trust, community, editorial
- brand-guide/ (7) — cover through UI patterns
- social-templates/ (4) — OG, Instagram, Story, LinkedIn
- icons/ (6) — pledge, whatsapp, gift-aid, zakat, dashboard, schedule

Also fixed: Nav component still had backdrop-blur-lg (last violation)
2026-03-03 20:31:01 +08:00
fc80399092 brand identity overhaul: match BRAND-IDENTITY.md across all pages
Design system changes (per brand guide):
- ZERO rounded-2xl/3xl remaining (was 131 instances)
- ZERO bg-gradient remaining (was 25) — all solid colors
- ZERO colored shadows (shadow-trust-blue, etc) — flat, no glow
- ZERO backdrop-blur/glass effects — solid backgrounds
- ZERO emoji in logo marks — square P logomark everywhere
- ZERO decorative scale animations (group-hover:scale-105, etc)

Tailwind config:
- Added brand color names: midnight, promise-blue, generosity-gold, fulfilled-green, alert-red, paper
- Kept legacy aliases (trust-blue, etc) for backwards compat
- --radius: 0.75rem → 0.5rem (tighter corners)

CSS:
- Removed glass, glass-dark, card-hover, pulse-ring, bounce-gentle, confetti-fall, scale-in animations
- Kept only purposeful animations: fadeUp, fadeIn, slideDown, shimmer, counter-roll
- --primary tuned to match Promise Blue exactly

Components:
- Button: removed all colored shadows, added 'blue' variant, removed rounded from sizes
- All UI components: rounded-xl/2xl → rounded-lg

Pages updated (41 files):
- Dashboard layout: solid header (no blur), border-l-2 active indicator, midnight logo mark
- Login/Signup: paper bg (no gradient), midnight logo mark, no emoji
- Pledge flow: solid color icons, no gradient progress bars
- All dashboard pages: flat, sharp, editorial
2026-03-03 20:13:22 +08:00
f4ad6df45a add AI-generated landing page photography (Gemini 3 Pro)
20 images generated via gemini-3-pro-image-preview (Nano Banana Pro):
- Documentary street photography style, British-diverse subjects
- 12 square (1:1), 8 landscape (16:9) matching placeholder aspect ratios
- Replaced all ImagePlaceholder components with LandingImage + next/image
- Images in public/images/landing/, served statically

Pages updated: /, /for/charities, /for/fundraisers, /for/volunteers, /for/organisations
New component: LandingImage (next/image with fill + object-cover)
2026-03-03 19:27:36 +08:00
581f1e5f14 sharp flat redesign: all landing pages + replace donors with organisations
Design overhaul:
- Removed all emoji-heavy sections, rounded-3xl, cartoonish borders
- Sharp flat edges (square or rounded-lg max)
- Typography-driven hierarchy with numbered steps (01, 02, 03...)
- Border-left accent lines instead of colored card borders
- Grid-with-gap-px pattern for table-like sections
- Dark sections (bg-gray-950) for stats and CTAs
- Image placeholders (gray boxes with photo icons) for future real photography
- Shared components: Nav, Footer, BottomCta, ImagePlaceholder in /for/_components

Pages:
- / (main): Hero → 30-50% stat → 4 persona cards with images → 4 steps → compliance → payment → platforms → CTA
- /for/charities: Hero split with image → pain points → numbered flow with 2 images → features with left-border accents
- /for/fundraisers: Hero split → gap visualization (3-col dark panel) → flow with images → before/after grid → platforms grid
- /for/volunteers: Hero split → 3-col features (gap-px grid) → event flow → share channels → CTA to charity manager
- /for/organisations: NEW (replaces /for/donors) → multi-org pledges, umbrella fundraising, institutional partnerships

Removed /for/donors — donors use the pledge form, they don't need a landing page
2026-03-03 18:27:04 +08:00
121e2bbde8 4-persona landing pages + main page CRO rewrite
Main page (pledge.quikcue.com):
- Hero: 'Turn I'll donate into money in the bank'
- 30-50% stat in dark section (single number, maximum impact)
- 4 persona cards linking to /for/* pages
- 4-step how-it-works (tightened from previous)
- Compliance strip (Gift Aid, Zakat, email, WhatsApp - compact)
- Payment flexibility (now/later/monthly)
- Platform logos
- Dark CTA section
- Footer with persona links

/for/charities:
- Pain: pledges on napkins, awkward chasing, no visibility
- 5-step how-it-works specific to charity managers
- 6 features: Gift Aid, Zakat, WhatsApp, scheduling, GDPR, exports
- CTA: Start Free

/for/fundraisers:
- Pain: shared link 50 times, 3 donated
- Before/after comparison grid (without vs with)
- 6 external platforms with branding
- CTA: Start Free

/for/volunteers:
- Personal link, live stats, leaderboard
- Event night flow (4 steps)
- Share channels grid
- CTA: Tell your charity about this

/for/donors:
- Educational trust page, not a sign-up funnel
- 6-step pledge flow explained
- Data protection table (what/why for each field)
- FAQ (cancel, already paid, no WhatsApp consent)
- CTA: Are you a charity?
2026-03-03 17:46:20 +08:00
582c85b3d9 landing page: compliance section covers Gift Aid, Zakat, email + WhatsApp consent
Replaced Zakat-only section with full compliance showcase:
- Gift Aid (HMRC): +25%, home address, model declaration, HMRC-ready CSV
- Zakat: per-campaign toggle, separate tracking
- Email consent (GDPR): granular opt-in, never pre-ticked
- WhatsApp consent (PECR): separate opt-in, STOP instructions
- Audit trail callout: exact text, timestamp, IP, version
- Feature grid: 'Zakat Tracking' → 'Bulletproof Consent'
2026-03-03 17:25:48 +08:00
865c5a1f93 bulletproof consent: Gift Aid (HMRC), email opt-in, WhatsApp opt-in with full audit trail
GIFT AID (HMRC compliance):
- Exact HMRC model declaration text displayed and recorded
- Home address (line 1 + postcode) collected when Gift Aid is ticked
- giftAidAt timestamp recorded separately from the boolean
- Declaration text, donor name, timestamp stored in consentMeta JSON

EMAIL + WHATSAPP (GDPR/PECR compliance):
- Separate, granular opt-in checkboxes (not bundled, not pre-ticked)
- Each consent records: exact text shown, timestamp, consent version
- Consent checkboxes only appear when relevant contact info is provided
- Cron reminders gated on consent — no sends without opt-in
- Pledge creation WhatsApp receipt gated on whatsappOptIn

AUDIT TRAIL (consentMeta JSON on every pledge):
- giftAid: {declared, declarationText, declaredAt}
- email: {granted, consentText, grantedAt}
- whatsapp: {granted, consentText, grantedAt}
- IP address captured server-side from x-forwarded-for
- User agent captured client-side
- consentVersion field for tracking wording changes

EXPORTS:
- CRM CSV now includes: donor_address, donor_postcode, gift_aid_declared_at,
  is_zakat, email_opt_in, whatsapp_opt_in
- Gift Aid export has full HMRC-required fields

Schema: 6 new columns on Pledge (donorAddressLine1, donorPostcode,
giftAidAt, emailOptIn, whatsappOptIn, consentMeta)
2026-03-03 07:38:51 +08:00