Removes RESEND_AUDIENCE_WAITLIST env var. capture_waitlist_email() now
derives audience name from request.blueprints[0] (e.g. waitlist-auth,
waitlist-suppliers), lazily creates it via Resend API on first signup,
and caches the ID in a new resend_audiences table. Zero config beyond
RESEND_API_KEY — adding @waitlist_gate to any new blueprint auto-creates
its audience on first use.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Extract duplicated waitlist logic into two reusable abstractions:
1. @waitlist_gate(template, **context) decorator
- Intercepts GET requests when WAITLIST_MODE=true
- Passes through POST requests for form handling
- Evaluates callable context at request time
2. capture_waitlist_email(email, intent, plan, email_intent) helper
- Idempotent DB insertion (INSERT OR IGNORE)
- Enqueues confirmation email only for new signups
- Adds to Resend audience with silent error handling
- Supports separate email_intent for supplier messaging
Applied to auth, suppliers, and planner routes, reducing ~80+ lines
of duplicated code. Added comprehensive tests (55 total, all passing)
and documentation (25KB guide in docs/WAITLIST.md).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Replace globe lines with 3x2 dot grid (characteristic padel racket holes)
- Makes racket immediately recognizable vs globe/frying pan shape
- Add ?v=3 to all favicon links to force browser cache refresh
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace circle with rounded rectangle (rx=6, ~30% corner radius)
matching the typical padel racket head proportions
- Handle now connects from bottom edge of rect
- New favicon.svg plus regenerated ico/png/apple-touch-icon from same design
- Add SVG favicon link (modern browsers) with ico/png fallbacks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add /imprint route and template stub (fill in company details)
- Move About out of Legal into a new Company column
- Add /imprint to sitemap and RESERVED_PREFIXES
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Assigns visitors to experiment variants via cookie (30-day), sets g.ab_variant
and g.ab_tag, and conditionally adds data-tag to the Umami script tag so all
pageviews and events are automatically tagged in the Umami dashboard.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix CSS-only billing toggle: radio inputs were nested inside
.s-billing-toggle, so ~ siblings couldn't reach plan card price
elements inside <form>. Moved radios to be direct siblings of both
.s-billing-toggle and <form>. Updated CSS selectors accordingly.
Removed .price-yearly{display:block} override that broke monthly view.
- Set Most Popular badge on Growth (not Pro) in signup wizard — Growth
is the natural entry point for lead-receiving suppliers
- Update public pricing page: Growth €149→€199, Pro €399→€499
- Refresh Pro feature list: logo+cover photo, full stats, verified badge
(no longer described as add-ons since they're built into Pro)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eliminated dual-maintenance of schema.sql + versioned migrations.
All databases (fresh and existing) now replay migrations in order
starting from 0000_initial_schema.py. Removed _is_fresh_db() and
the fresh-DB fast-path that skipped migration execution.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Paddle sandbox sends lifecycle events (subscription.updated, etc.) with
"custom_data": null. The .get("custom_data", {}) default only applies
when the key is missing, not when the value is explicitly null, causing
AttributeError on the next .get() call. Also guarded subscription.activated
to skip when user_id is absent (was inserting user_id=0 → FK violation).
Replaced manual HMAC verification with paddle_billing.Notifications.Verifier
via a lightweight _WebhookRequest wrapper satisfying the SDK's Request Protocol.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
24 tests for credits.py (balance, spend, unlock, refill, ledger) and
10 integration tests for supplier webhook handlers (credit packs, sticky
boosts, subscription activation, business plan purchase). Removed
test_mobile_nav_no_overflow which never asserted its JS result.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Supplier dashboard: impersonate redirects to supplier dashboard when user
owns a supplier, sidenav active tab updates on click, listing preview
capped at 420px, insufficient-credits error shows balance and buy CTA,
boost buy buttons resolve Paddle price IDs server-side.
Dev tooling: dev_run.sh kills child processes on Ctrl-C (process
substitution fix), syncs Paddle products to DB on each run,
setup_paddle.py gains --sync mode and fixes Duration/Interval SDK change.
Seed data: each claimed supplier gets its own owner user and subscription.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
run_with_label piped output through a while loop, so $! tracked the
formatter PID instead of the actual command. Ctrl-C killed the formatters
but left app/worker/css-watch running as orphans. Switch to process
substitution so $! is the real command PID, and pkill -P children before
killing tracked PIDs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Webhook handler called Verifier().verify() with raw bytes instead of a
request object, so signature verification always failed. Replaced with
manual HMAC check matching Paddle's ts=...;h1=... format. Updated tests
to produce correct signature format, mock the SDK instead of httpx for
manage/cancel routes, and expect JSON for overlay checkout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a complete content generation system for producing SEO articles
at scale with embedded financial scenario widgets. Includes DB schema
(published_scenarios, article_templates, template_data, articles with
FTS5), bulk generation pipeline with staggered publish dates, admin CRUD
for templates/scenarios/articles, public markets hub with HTMX filtering,
catch-all article serving from pre-rendered static HTML, sitemap
integration, and 94 pytest tests covering the full stack.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
setup_paddle.py creates a notification destination in Paddle and writes
the webhook secret + setting ID to .env. dev_run.sh resets the DB, seeds
data, and starts an ngrok tunnel to update the webhook URL automatically
for end-to-end checkout testing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Quote wizard _accumulated hidden inputs used double-quote attribute delimiters
which broke on tojson output containing literal " characters — all step data was
lost by step 9. Admin dashboard crashed on credit_ledger queries referencing
wrong column names (amount/entry_type vs delta/event_type). Also: opaque nav bar,
pricing card button alignment, newsletter boost replaced with card color boost,
admin CRUD for suppliers/leads, dev seed data script, and playwright quote wizard
tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Migrate all checkouts to Paddle.js overlay (no redirect), move Paddle price
IDs from env vars to DB table, add 4-tab supplier dashboard (overview, leads,
listing, boosts), business plan PDF export with WeasyPrint, enhanced supplier
landing page with live stats, admin supplier management + feedback widget.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Guest quote submissions now require email verification before the lead
goes live. The verification click also creates a user account and logs
them in. Logged-in users submitting with their own email skip verification.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Nav: Zillow-style centered logo, solid blue Sign In button
- Planner: center app at 72rem, center wizard steps/header/preview
- Country pills: UK/USA labels, remove Other, show permits slider
inline under country so the effect is transparent and adjustable
- Reset button: inline confirm (red "Sure? Reset") instead of alert
- Worker: print magic link to console when DEBUG=true for local dev
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Parameter bindings for ORDER BY (now, country) were placed before WHERE
params, causing mismatched bindings when any filter was active. Also
switched hx-include from .dir-search class (shared by form + inputs)
to #dir-search-form ID to prevent duplicate parameters.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Toggle buttons (Indoor/Outdoor, Rent/Buy) now visually update
their active state on click
- Space requirement sliders start from minimum court size
(200m² double, 120m² single) instead of 0
- Defaults updated to court + 2m buffer (336/240/312/216 m²)
- Reference dimensions panel shows standard court sizes
- Chart.js font updated from JetBrains Mono to Commit Mono
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wrap magic link and welcome emails in a proper HTML email template
with navy header, white card body, CTA button, and muted footer.
Fallback plain-text URL included below the button.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace Pico CSS CDN with Tailwind v4 standalone CLI (no Node.js).
Brand theme with navy/electric/accent palette, component classes,
self-hosted Commit Mono font. Docker multi-stage CSS build.
Logo links to dashboard when logged in.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Namespace admin templates under admin/ subdirectory to prevent
Quart's template loader from resolving auth's login.html and
dashboard's index.html instead of admin's own templates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Expand migrate.py docstring with algorithm, protocol, and design decisions
- Add 20-test suite for migration framework (test_migrations.py)
- Fix: empty env vars (SECRET_KEY=) now fall back to defaults via _env() helper
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Production DB had already been migrated by the manual script, so
the old lemonsqueezy columns no longer exist. Check column names
before attempting the rename.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the simple schema.sql runner with a proper sequential
migration system that tracks applied versions in a _migrations table.
Absorb scripts/migrate_to_paddle.py as versions/0001.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Run copier update with payment_provider=paddle to switch all billing
integration: routes, config, schema columns, webhook handling, tests,
and CI deploy secrets. Add one-time migration script for renaming
lemonsqueezy_* columns to paddle_* in production DB.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace toy-like sliders (rent/m², exit multiple) with more intuitive
inputs (build cost per court, equity %). Replace outputs (annual revenue,
equity IRR) with payback period and cash-on-cash return. Show model
assumptions inline. Remove "free" messaging from all CTAs and meta tags,
focus on the plan→suppliers→financing value chain instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The variable-based overrides (--pico-h1-color, --pico-color) weren't
reliably winning against Pico's multi-layer cascade of theme selectors,
prefers-color-scheme media queries, and variable indirection. Headings
were rendering near-white (#f0f1f3) on dark-OS systems despite
data-theme="light". Using !important on color is the pragmatic fix.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The heading color override (bare h1 selector, specificity 0,0,1) was
losing to Pico's theme-scoped heading rules ([data-theme=light] h1,
specificity 0,1,1). On systems with prefers-color-scheme: dark, Pico's
dark heading colors (#f0f1f3 — near white) leaked through, making
headings invisible on the light background.
Fix: scope heading overrides under [data-theme="light"] to match Pico's
specificity. Also bump nav logo from 24px to 32px, footer to 24px.
Visual tests now run with color_scheme="dark" to catch this class of bug.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Playwright visual tests need browser binaries (playwright install
chromium) which CI doesn't have. Mark them with pytest.mark.visual
and add addopts = "-m 'not visual'" so they're skipped by default.
Run locally with: uv run pytest -m visual tests/test_visual.py -v
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>