merge: pricing-overhaul — Basic free, card color €59, BP PDF €149, supplier page CRO, lead-back guarantee
This commit is contained in:
24
CHANGELOG.md
24
CHANGELOG.md
@@ -151,6 +151,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||||||
2. Worker count auto-detected from proxy count (drops `EXTRACT_WORKERS`).
|
2. Worker count auto-detected from proxy count (drops `EXTRACT_WORKERS`).
|
||||||
3. True crash resumption via `.partial.jsonl` sidecar: progress flushed every 50 venues,
|
3. True crash resumption via `.partial.jsonl` sidecar: progress flushed every 50 venues,
|
||||||
resume skips already-fetched venues and merges prior results into the final file.
|
resume skips already-fetched venues and merges prior results into the final file.
|
||||||
|
- **Lead-Back Guarantee** — suppliers can claim credits back for non-responding leads
|
||||||
|
with one click after 3 business days. Route `POST /suppliers/leads/<id>/guarantee-claim`,
|
||||||
|
`refund_lead_guarantee()` in credits.py, "Lead didn't respond" button on unlocked
|
||||||
|
lead cards (visible 3–30 days after unlock). Migration 0020 adds `guarantee_claimed_at`
|
||||||
|
and `guarantee_contact_method` columns to `lead_forwards`.
|
||||||
|
- **Supplier page CRO restructure** — `/suppliers` page reordered to lead with value
|
||||||
|
before pricing (Why Padelnomics → Lead-Back Guarantee → lead preview → social proof
|
||||||
|
→ pricing). All CTAs changed from "See Plans & Pricing" to "Get Started Free".
|
||||||
|
- **Static ROI line** — one-sentence ROI callout near pricing grounded in
|
||||||
|
`research/padel-hall-economics.md` data (4-court project = €30K+ contractor profit).
|
||||||
|
- **Credits-only callout** — below pricing grid: "Not ready for a subscription? Buy
|
||||||
|
a credit pack and unlock leads one at a time."
|
||||||
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- **`datetime.utcnow()` deprecation warnings** — replaced all 94 occurrences
|
- **`datetime.utcnow()` deprecation warnings** — replaced all 94 occurrences
|
||||||
@@ -1197,6 +1210,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||||||
- Pico CSS CDN dependency
|
- Pico CSS CDN dependency
|
||||||
- `custom.css` (replaced by Tailwind `input.css` with `@layer components`)
|
- `custom.css` (replaced by Tailwind `input.css` with `@layer components`)
|
||||||
- JetBrains Mono font (replaced by self-hosted Commit Mono)
|
- JetBrains Mono font (replaced by self-hosted Commit Mono)
|
||||||
|
- **Basic tier is now free** — `supplier_basic` monthly/yearly price → €0. No Paddle
|
||||||
|
subscription required for Basic. Signup wizard shows "Free forever" instead of €39.
|
||||||
|
- **Card color boost** — price corrected €19/mo → €59/mo (aligns with MARKETING.md).
|
||||||
|
- **Business plan PDF** — price raised €99 → €149 (KfW-ready document supporting
|
||||||
|
€200K+ investment decision).
|
||||||
|
- **Hero and final CTA** — links changed from `#pricing` anchor to direct signup URL.
|
||||||
|
- **Comparison table** — €1,799/yr annotated with "(yearly plan)" for clarity.
|
||||||
|
- **EN+DE translations** — `sup_meta_desc` updated (removed "from €39/mo"); all
|
||||||
|
Basic-tier strings updated to reflect free tier; FAQ updated with guarantee mention.
|
||||||
|
- **`setup_paddle.py`** — Basic subscription products commented out (no longer needed);
|
||||||
|
`boost_card_color` 1900 → 5900 cents; `business_plan` 9900 → 14900 cents.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Empty env vars (e.g. `SECRET_KEY=`) now fall back to defaults instead of silently using `""` — fixes 500 on every request when `.env` has blank values
|
- Empty env vars (e.g. `SECRET_KEY=`) now fall back to defaults instead of silently using `""` — fixes 500 on every request when `.env` has blank values
|
||||||
|
|||||||
@@ -59,8 +59,10 @@
|
|||||||
- [x] Business Plan PDF purchase flow (Paddle one-time → webhook → async generation)
|
- [x] Business Plan PDF purchase flow (Paddle one-time → webhook → async generation)
|
||||||
- [x] Boost purchases (logo, highlight, verified, card color, sticky week/month)
|
- [x] Boost purchases (logo, highlight, verified, card color, sticky week/month)
|
||||||
- [x] Credit pack purchases (25/50/100/250)
|
- [x] Credit pack purchases (25/50/100/250)
|
||||||
- [x] Supplier subscription tiers (Basic free / Growth €149 / Pro €399, monthly + annual)
|
- [x] Supplier subscription tiers (Basic free / Growth €199 / Pro €499, monthly + annual)
|
||||||
- [x] **Feature flags** (DB-backed, migration 0019) — `is_flag_enabled()` + `feature_gate()` decorator replace `WAITLIST_MODE`; 5 flags (markets, payments, planner_export, supplier_signup, lead_unlock); admin UI at `/admin/flags` with toggle
|
- [x] **Feature flags** (DB-backed, migration 0019) — `is_flag_enabled()` + `feature_gate()` decorator replace `WAITLIST_MODE`; 5 flags (markets, payments, planner_export, supplier_signup, lead_unlock); admin UI at `/admin/flags` with toggle
|
||||||
|
- [x] **Pricing overhaul** — Basic free (no Paddle sub), card color €59, BP PDF €149; supplier page restructured value-first (why → guarantee → leads → social proof → pricing); all CTAs "Get Started Free"; static ROI line; credits-only callout
|
||||||
|
- [x] **Lead-Back Guarantee** (migration 0020) — 1-click credit refund for non-responding leads (3–30 day window); `refund_lead_guarantee()` in credits.py; "Lead didn't respond" button on unlocked lead cards
|
||||||
- [x] **Python supervisor** (`src/padelnomics/supervisor.py`) + `workflows.toml` — replaces `supervisor.sh`; topological wave scheduling; croniter-based `is_due()`; systemd service updated
|
- [x] **Python supervisor** (`src/padelnomics/supervisor.py`) + `workflows.toml` — replaces `supervisor.sh`; topological wave scheduling; croniter-based `is_due()`; systemd service updated
|
||||||
- [x] **Proxy rotation** (`extract/padelnomics_extract/proxy.py`) — round-robin + sticky hash-based selector via `PROXY_URLS` env var
|
- [x] **Proxy rotation** (`extract/padelnomics_extract/proxy.py`) — round-robin + sticky hash-based selector via `PROXY_URLS` env var
|
||||||
- [x] Resend email integration (transactional: magic link, welcome, quote verify, lead forward, enquiry)
|
- [x] Resend email integration (transactional: magic link, welcome, quote verify, lead forward, enquiry)
|
||||||
@@ -266,3 +268,6 @@
|
|||||||
| 2026-02-22 | No soft email gate on planner | Planner already captures emails at natural points (scenario save → login, quote wizard step 9). Gate would add friction without meaningful list value. Revisit if data shows a gap. |
|
| 2026-02-22 | No soft email gate on planner | Planner already captures emails at natural points (scenario save → login, quote wizard step 9). Gate would add friction without meaningful list value. Revisit if data shows a gap. |
|
||||||
| 2026-02-22 | Wipe test suppliers before launch | 5 `example.com` entries from seed_dev_data.py — empty directory with "Be the first" CTA is better than obviously fake data |
|
| 2026-02-22 | Wipe test suppliers before launch | 5 `example.com` entries from seed_dev_data.py — empty directory with "Be the first" CTA is better than obviously fake data |
|
||||||
| 2026-02-24 | Split market score into two branded scores | Marktreife-Score (existing market maturity, cities with ≥1 venue) vs Marktpotenzial-Score (greenfield opportunity, all GeoNames locations globally). SERP analysis confirmed zero competition for hyperlocal Gemeinde-level market intelligence pages. |
|
| 2026-02-24 | Split market score into two branded scores | Marktreife-Score (existing market maturity, cities with ≥1 venue) vs Marktpotenzial-Score (greenfield opportunity, all GeoNames locations globally). SERP analysis confirmed zero competition for hyperlocal Gemeinde-level market intelligence pages. |
|
||||||
|
| 2026-02-26 | Basic tier free, no Paddle subscription | Simplest onboarding — signup without payment flow; MARKETING.md already said free, code said €39 |
|
||||||
|
| 2026-02-26 | Lead-Back Guarantee: supplier-initiated, credits back (not cash) | Risk reduction beats ROI projection (competitor research: #1 complaint is paying for silent leads). Credits-only keeps cash while removing the psychological barrier. |
|
||||||
|
| 2026-02-26 | Static ROI line, not interactive calculator | No lead marketplace uses inline ROI calcs; B2B SaaS trend away from them (Zendesk, Intercom, Gong all removed theirs). One bold number grounded in research beats a widget. |
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ All balance mutations go through this module to keep credit_ledger (source of tr
|
|||||||
and suppliers.credit_balance (denormalized cache) in sync within a single transaction.
|
and suppliers.credit_balance (denormalized cache) in sync within a single transaction.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from datetime import UTC, datetime
|
||||||
|
|
||||||
from .core import execute, fetch_all, fetch_one, transaction, utcnow_iso
|
from .core import execute, fetch_all, fetch_one, transaction, utcnow_iso
|
||||||
|
|
||||||
# Credit cost per heat tier
|
# Credit cost per heat tier
|
||||||
@@ -202,3 +204,92 @@ async def get_ledger(supplier_id: int, limit: int = 50) -> list[dict]:
|
|||||||
ORDER BY cl.created_at DESC, cl.id DESC LIMIT ?""",
|
ORDER BY cl.created_at DESC, cl.id DESC LIMIT ?""",
|
||||||
(supplier_id, limit),
|
(supplier_id, limit),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class GuaranteeAlreadyClaimed(Exception):
|
||||||
|
"""Raised when supplier tries to claim guarantee twice for the same lead."""
|
||||||
|
|
||||||
|
|
||||||
|
class GuaranteeWindowClosed(Exception):
|
||||||
|
"""Raised when the 3-30 day guarantee window has passed."""
|
||||||
|
|
||||||
|
|
||||||
|
async def refund_lead_guarantee(
|
||||||
|
supplier_id: int,
|
||||||
|
forward_id: int,
|
||||||
|
contact_method: str,
|
||||||
|
) -> int:
|
||||||
|
"""Refund credits for a non-responding lead. Returns new balance.
|
||||||
|
|
||||||
|
Preconditions:
|
||||||
|
- forward must exist, belong to supplier_id, status='sent'
|
||||||
|
- guarantee_claimed_at must be NULL (not already claimed)
|
||||||
|
- created_at must be 3–30 days ago (calendar days)
|
||||||
|
- contact_method must be one of: 'email', 'phone', 'both'
|
||||||
|
|
||||||
|
The refund is a ledger credit entry (event_type='guarantee_refund') referencing
|
||||||
|
the forward_id. lead_forwards.status is updated to 'no_response'.
|
||||||
|
We keep the cash — only credits are returned to the supplier balance.
|
||||||
|
"""
|
||||||
|
assert contact_method in ("email", "phone", "both"), (
|
||||||
|
f"Invalid contact_method: {contact_method!r}"
|
||||||
|
)
|
||||||
|
|
||||||
|
forward = await fetch_one(
|
||||||
|
"SELECT * FROM lead_forwards WHERE id = ? AND supplier_id = ?",
|
||||||
|
(forward_id, supplier_id),
|
||||||
|
)
|
||||||
|
assert forward is not None, f"Forward {forward_id} not found for supplier {supplier_id}"
|
||||||
|
|
||||||
|
if forward["guarantee_claimed_at"] is not None:
|
||||||
|
raise GuaranteeAlreadyClaimed(
|
||||||
|
f"Guarantee already claimed for forward {forward_id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check 3–30 day window (calendar days)
|
||||||
|
created = datetime.fromisoformat(forward["created_at"].replace("Z", "+00:00"))
|
||||||
|
now = datetime.now(UTC)
|
||||||
|
age_days = (now - created).days
|
||||||
|
|
||||||
|
if age_days < 3 or age_days > 30:
|
||||||
|
raise GuaranteeWindowClosed(
|
||||||
|
f"Guarantee window closed: forward is {age_days} days old (must be 3–30)"
|
||||||
|
)
|
||||||
|
|
||||||
|
refund_amount = forward["credit_cost"]
|
||||||
|
now_iso = datetime.utcnow().isoformat()
|
||||||
|
|
||||||
|
async with transaction() as db:
|
||||||
|
row = await db.execute_fetchall(
|
||||||
|
"SELECT credit_balance FROM suppliers WHERE id = ?", (supplier_id,)
|
||||||
|
)
|
||||||
|
current = row[0][0] if row else 0
|
||||||
|
new_balance = current + refund_amount
|
||||||
|
|
||||||
|
await db.execute(
|
||||||
|
"""INSERT INTO credit_ledger
|
||||||
|
(supplier_id, delta, balance_after, event_type, reference_id, note, created_at)
|
||||||
|
VALUES (?, ?, ?, 'guarantee_refund', ?, ?, ?)""",
|
||||||
|
(
|
||||||
|
supplier_id,
|
||||||
|
refund_amount,
|
||||||
|
new_balance,
|
||||||
|
forward_id,
|
||||||
|
f"Lead-back guarantee refund for forward #{forward_id}",
|
||||||
|
now_iso,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"UPDATE suppliers SET credit_balance = ? WHERE id = ?",
|
||||||
|
(new_balance, supplier_id),
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"""UPDATE lead_forwards
|
||||||
|
SET status = 'no_response',
|
||||||
|
guarantee_claimed_at = ?,
|
||||||
|
guarantee_contact_method = ?
|
||||||
|
WHERE id = ?""",
|
||||||
|
(now_iso, contact_method, forward_id),
|
||||||
|
)
|
||||||
|
|
||||||
|
return new_balance
|
||||||
|
|||||||
@@ -888,11 +888,11 @@
|
|||||||
"month_nov": "Nov",
|
"month_nov": "Nov",
|
||||||
"month_dec": "Dez",
|
"month_dec": "Dez",
|
||||||
"sup_meta_title": "Für Anbieter – Erreiche Padel-Unternehmer",
|
"sup_meta_title": "Für Anbieter – Erreiche Padel-Unternehmer",
|
||||||
"sup_meta_desc": "Werde auf Padelnomics gelistet. Erreiche Unternehmer, die bereits einen Finanzplan für ihr Padel-Projekt erstellt haben. Basic, Growth und Pro ab €39/Monat.",
|
"sup_meta_desc": "Kostenloser Verzeichniseintrag auf Padelnomics. Qualifizierte Leads von Interessenten mit fertigem Businessplan. Growth- und Pro-Pläne ab €199/Monat.",
|
||||||
"sup_hero_h1a": "Kein Kaltakquise mehr.",
|
"sup_hero_h1a": "Kein Kaltakquise mehr.",
|
||||||
"sup_hero_h1b": "Triff Käufer, die bereits einen Businessplan haben.",
|
"sup_hero_h1b": "Triff Käufer, die bereits einen Businessplan haben.",
|
||||||
"sup_hero_sub": "Jeder Lead auf Padelnomics hat CAPEX, Umsatz und ROI bereits modelliert – bevor er dich kontaktiert. Keine Zeitverschwender. Kein „ich schau mich nur um.“",
|
"sup_hero_sub": "Jeder Lead auf Padelnomics hat CAPEX, Umsatz und ROI bereits modelliert – bevor er dich kontaktiert. Keine Zeitverschwender. Kein „ich schau mich nur um.“",
|
||||||
"sup_hero_cta": "Pläne & Preise ansehen",
|
"sup_hero_cta": "Kostenlos starten",
|
||||||
"sup_hero_trust_pre": "Vertrauen von Anbietern in",
|
"sup_hero_trust_pre": "Vertrauen von Anbietern in",
|
||||||
"sup_hero_trust_post": "Ländern",
|
"sup_hero_trust_post": "Ländern",
|
||||||
"sup_stat_plans": "erstellte Businesspläne",
|
"sup_stat_plans": "erstellte Businesspläne",
|
||||||
@@ -932,7 +932,7 @@
|
|||||||
"sup_lead_timeline": "Zeitplan",
|
"sup_lead_timeline": "Zeitplan",
|
||||||
"sup_lead_contact": "Kontakt",
|
"sup_lead_contact": "Kontakt",
|
||||||
"sup_leads_unlock_pre": "Vollständige Kontaktdaten und Projektspezifikationen mit Credits freischalten.",
|
"sup_leads_unlock_pre": "Vollständige Kontaktdaten und Projektspezifikationen mit Credits freischalten.",
|
||||||
"sup_leads_unlock_cta": "Jetzt starten →",
|
"sup_leads_unlock_cta": "Leads freischalten →",
|
||||||
"sup_leads_example": "Dies sind Beispiel-Leads. Echte Leads erscheinen, sobald Unternehmer Angebotsanfragen einreichen.",
|
"sup_leads_example": "Dies sind Beispiel-Leads. Echte Leads erscheinen, sobald Unternehmer Angebotsanfragen einreichen.",
|
||||||
"sup_why_h2": "Warum Padelnomics-Leads anders sind",
|
"sup_why_h2": "Warum Padelnomics-Leads anders sind",
|
||||||
"sup_why_sub": "Jeder Lead hat bereits ein Finanzmodell für sein Projekt erstellt.",
|
"sup_why_sub": "Jeder Lead hat bereits ein Finanzmodell für sein Projekt erstellt.",
|
||||||
@@ -948,14 +948,14 @@
|
|||||||
"sup_billing_yearly": "Jährlich",
|
"sup_billing_yearly": "Jährlich",
|
||||||
"sup_billing_save": "Bis zu 26% sparen",
|
"sup_billing_save": "Bis zu 26% sparen",
|
||||||
"sup_basic_name": "Basic",
|
"sup_basic_name": "Basic",
|
||||||
"sup_basic_dir": "Verzeichniseintrag",
|
"sup_basic_dir": "Dauerhaft kostenlos",
|
||||||
"sup_basic_f1": "Verifiziert ✓ Badge",
|
"sup_basic_f1": "Verifiziert ✓ Badge",
|
||||||
"sup_basic_f2": "Firmenlogo",
|
"sup_basic_f2": "Firmenlogo",
|
||||||
"sup_basic_f3": "Vollständige Beschreibung & Slogan",
|
"sup_basic_f3": "Vollständige Beschreibung & Slogan",
|
||||||
"sup_basic_f4": "Website & Kontaktdaten",
|
"sup_basic_f4": "Website & Kontaktdaten",
|
||||||
"sup_basic_f5": "Checkliste der angebotenen Leistungen",
|
"sup_basic_f5": "Checkliste der angebotenen Leistungen",
|
||||||
"sup_basic_f6": "Kontaktformular auf der Listing-Seite",
|
"sup_basic_f6": "Kontaktformular auf der Listing-Seite",
|
||||||
"sup_basic_cta": "Jetzt listen",
|
"sup_basic_cta": "Unternehmen kostenlos eintragen",
|
||||||
"sup_growth_name": "Growth",
|
"sup_growth_name": "Growth",
|
||||||
"sup_growth_popular": "Beliebtester Plan",
|
"sup_growth_popular": "Beliebtester Plan",
|
||||||
"sup_growth_credits": "30 Credits/Monat inklusive",
|
"sup_growth_credits": "30 Credits/Monat inklusive",
|
||||||
@@ -975,11 +975,11 @@
|
|||||||
"sup_pro_f5": "Bevorzugte Platzierung im Verzeichnis",
|
"sup_pro_f5": "Bevorzugte Platzierung im Verzeichnis",
|
||||||
"sup_pro_f6": "100 Lead-Credits pro Monat",
|
"sup_pro_f6": "100 Lead-Credits pro Monat",
|
||||||
"sup_pro_cta": "Jetzt starten",
|
"sup_pro_cta": "Jetzt starten",
|
||||||
"sup_yearly_note_basic": "€349 jährlich",
|
"sup_yearly_note_basic": "Dauerhaft kostenlos",
|
||||||
"sup_yearly_note_growth": "€1.799 jährlich",
|
"sup_yearly_note_growth": "€1.799 jährlich",
|
||||||
"sup_yearly_note_pro": "€4.499 jährlich",
|
"sup_yearly_note_pro": "€4.499 jährlich",
|
||||||
"sup_boosts_h3": "Boost Add-Ons",
|
"sup_boosts_h3": "Boost Add-Ons",
|
||||||
"sup_boosts_sub": "Mit jedem kostenpflichtigen Plan verfügbar. Im Dashboard verwalten.",
|
"sup_boosts_sub": "Für jeden Plan verfügbar. Im Dashboard verwalten. Kartenfarbe ab €59/Monat.",
|
||||||
"sup_boost_logo": "Logo",
|
"sup_boost_logo": "Logo",
|
||||||
"sup_boost_highlight": "Hervorhebung",
|
"sup_boost_highlight": "Hervorhebung",
|
||||||
"sup_boost_verified": "Verifiziert-Badge",
|
"sup_boost_verified": "Verifiziert-Badge",
|
||||||
@@ -1026,13 +1026,13 @@
|
|||||||
"sup_faq_a1_post": "und klicke auf „Ist das Dein Unternehmen?“ Wir prüfen Deine Identität und geben Dir Zugang, um einen Plan auszuwählen und Dein Profil zu aktualisieren.",
|
"sup_faq_a1_post": "und klicke auf „Ist das Dein Unternehmen?“ Wir prüfen Deine Identität und geben Dir Zugang, um einen Plan auszuwählen und Dein Profil zu aktualisieren.",
|
||||||
"sup_faq_dir_link": "Verzeichnis",
|
"sup_faq_dir_link": "Verzeichnis",
|
||||||
"sup_faq_q2": "Wie viel kostet es?",
|
"sup_faq_q2": "Wie viel kostet es?",
|
||||||
"sup_faq_a2": "Wir bieten drei Pläne an: Basic (€39/Monat) für einen verifizierten Verzeichniseintrag mit Kontaktformular; Growth (€199/Monat, 30 Credits) mit vollem Lead-Zugang und Prioritätsplatzierung; und Pro (€499/Monat, 100 Credits) für maximale Sichtbarkeit und Lead-Volumen. Jährliche Abrechnung spart bis zu 26 % – Basic bei €349/Jahr, Growth bei €1.799/Jahr, Pro bei €4.499/Jahr. Optionale Boost-Add-Ons sind zusätzlich erhältlich.",
|
"sup_faq_a2": "Basic ist kostenlos – verifizierter Verzeichniseintrag mit Kontaktformular, kein Abo nötig. Growth (€199/Monat, 30 Credits) bietet vollen Lead-Zugang und Prioritätsplatzierung. Pro (€499/Monat, 100 Credits) maximiert Sichtbarkeit und Lead-Volumen. Jährliche Abrechnung spart bis zu 25 %: Growth bei €1.799/Jahr, Pro bei €4.499/Jahr. Optionale Boost-Add-Ons sind für jeden Plan verfügbar.",
|
||||||
"sup_faq_q3": "Was macht Padelnomics-Leads anders als andere Plattformen?",
|
"sup_faq_q3": "Was macht Padelnomics-Leads anders als andere Plattformen?",
|
||||||
"sup_faq_a3": "Jeder Lead auf Padelnomics hat unser Finanzplanungstool genutzt, um sein Projekt zu modellieren – CAPEX, Umsatzprognosen, ROI und Schuldendienstdeckung – bevor er sich meldet. Das bedeutet: sie sind ernst, haben ein realistisches Budget und sind bereit, mit Anbietern zu sprechen. Du bekommst keine Kaltanfragen, sondern vorqualifizierte Projektbriefings.",
|
"sup_faq_a3": "Jeder Lead auf Padelnomics hat unser Finanzplanungstool genutzt, um sein Projekt zu modellieren – CAPEX, Umsatzprognosen, ROI und Schuldendienstdeckung – bevor er sich meldet. Das bedeutet: sie sind ernst, haben ein realistisches Budget und sind bereit, mit Anbietern zu sprechen. Du bekommst keine Kaltanfragen, sondern vorqualifizierte Projektbriefings.",
|
||||||
"sup_faq_q4": "Wie sieht der Preisvergleich mit Alternativen aus?",
|
"sup_faq_q4": "Wie sieht der Preisvergleich mit Alternativen aus?",
|
||||||
"sup_faq_a4": "Eine Messepräsenz kostet €10.000+ pro Veranstaltung und liefert meist nur Browsing-Kontakte. Google Ads für Padel-Baukeywords kosten €20–80 pro Klick – das sind €5.000+/Jahr, bevor du mit einem einzigen Interessenten sprichst. Ein typischer Kaltverzeichniseintrag kostet ~€600/Jahr ohne einen einzigen Lead. Padelnomics Growth bei €1.799/Jahr beinhaltet 30 Lead-Credits pro Monat mit vollständigen Projektbriefings.",
|
"sup_faq_a4": "Eine Messepräsenz kostet €10.000+ pro Veranstaltung und liefert meist nur Browsing-Kontakte. Google Ads für Padel-Baukeywords kosten €20–80 pro Klick – das sind €5.000+/Jahr, bevor du mit einem einzigen Interessenten sprichst. Ein typischer Kaltverzeichniseintrag kostet ~€600/Jahr ohne einen einzigen Lead. Padelnomics Growth bei €1.799/Jahr beinhaltet 30 Lead-Credits pro Monat mit vollständigen Projektbriefings.",
|
||||||
"sup_faq_q5": "Wie funktionieren Credits?",
|
"sup_faq_q5": "Wie funktionieren Credits – und was ist die Lead-Back-Garantie?",
|
||||||
"sup_faq_a5": "Mit Credits schaltest du die Kontaktdaten von Leads frei. Jeder Plan beinhaltet monatliche Credits (Growth: 30, Pro: 100). Heiße Leads kosten 35 Credits, warme 20 und coole 8. Du kannst jederzeit zusätzliche Credit-Pakete über dein Dashboard kaufen. Ungenutzte Credits werden auf den nächsten Monat übertragen.",
|
"sup_faq_a5": "Credits schalten die Kontaktdaten von Leads frei. Growth enthält 30 Credits/Monat, Pro 100. Heiße Leads kosten 35 Credits, warme 20, coole 8. Credit-Pakete sind auch ohne Abo erhältlich. Ungenutzte Credits werden übertragen. Wenn ein Lead nicht antwortet, klickst du nach 3 Werktagen auf „Lead hat nicht geantwortet“ – die Credits werden sofort zurückgebucht, ohne Support-Ticket.",
|
||||||
"sup_faq_q6": "Welche Informationen enthalten Leads?",
|
"sup_faq_q6": "Welche Informationen enthalten Leads?",
|
||||||
"sup_faq_a6": "Jeder Lead enthält: Anlagentyp (innen/außen), Court-Anzahl, Glas- und Beleuchtungsvorlieben, Land und Stadt, Budgetschätzung, Projektphase, Zeitplan, Finanzierungsstatus, Stakeholder-Typ, benötigte Leistungen und vollständige Kontaktdaten.",
|
"sup_faq_a6": "Jeder Lead enthält: Anlagentyp (innen/außen), Court-Anzahl, Glas- und Beleuchtungsvorlieben, Land und Stadt, Budgetschätzung, Projektphase, Zeitplan, Finanzierungsstatus, Stakeholder-Typ, benötigte Leistungen und vollständige Kontaktdaten.",
|
||||||
"sup_faq_q7": "Wie werden Leads Anbietern zugeordnet?",
|
"sup_faq_q7": "Wie werden Leads Anbietern zugeordnet?",
|
||||||
@@ -1560,23 +1560,22 @@
|
|||||||
"bp_lbl_confidential": "Vertraulich",
|
"bp_lbl_confidential": "Vertraulich",
|
||||||
"bp_lbl_table_of_contents": "Inhaltsverzeichnis",
|
"bp_lbl_table_of_contents": "Inhaltsverzeichnis",
|
||||||
|
|
||||||
|
|
||||||
"email_magic_link_heading": "Bei {app_name} anmelden",
|
"email_magic_link_heading": "Bei {app_name} anmelden",
|
||||||
"email_magic_link_body": "Hier ist dein Anmeldelink. Er läuft in {expiry_minutes} Minuten ab.",
|
"email_magic_link_body": "Hier ist dein Anmeldelink. Er läuft in {expiry_minutes} Minuten ab.",
|
||||||
"email_magic_link_btn": "Anmelden →",
|
"email_magic_link_btn": "Anmelden →",
|
||||||
"email_magic_link_fallback": "Wenn der Button nicht funktioniert, kopiere diese URL in deinen Browser:",
|
"email_magic_link_fallback": "Wenn der Button nicht funktioniert, kopiere diese URL in deinen Browser:",
|
||||||
"email_magic_link_ignore": "Falls Du das nicht angefordert hast, kannst Du diese E-Mail ignorieren.",
|
"email_magic_link_ignore": "Wenn du das nicht angefordert hast, kannst du diese E-Mail ignorieren.",
|
||||||
"email_magic_link_subject": "Dein Anmeldelink für {app_name}",
|
"email_magic_link_subject": "Dein Anmeldelink für {app_name}",
|
||||||
"email_magic_link_preheader": "Dieser Link läuft in {expiry_minutes} Minuten ab",
|
"email_magic_link_preheader": "Dieser Link läuft in {expiry_minutes} Minuten ab",
|
||||||
"email_quote_verify_heading": "Bestätige deine E-Mail für Angebote",
|
"email_quote_verify_heading": "Bestätige deine E-Mail für Angebote",
|
||||||
"email_quote_verify_greeting": "Hallo {first_name},",
|
"email_quote_verify_greeting": "Hallo {first_name},",
|
||||||
"email_quote_verify_body": "Danke für deine Angebotsanfrage. Bestätige deine E-Mail, um deine Anfrage zu aktivieren und dein {app_name}-Konto zu erstellen.",
|
"email_quote_verify_body": "Danke für deine Angebotsanfrage. Bestätige deine E-Mail, um deine Anfrage zu aktivieren und dein {app_name}-Konto zu erstellen.",
|
||||||
"email_quote_verify_project_label": "Dein Projekt:",
|
"email_quote_verify_project_label": "Dein Projekt:",
|
||||||
"email_quote_verify_urgency": "Verifizierte Anfragen werden von unserem Anbieternetzwerk bevorzugt bearbeitet.",
|
"email_quote_verify_urgency": "Verifizierte Anfragen werden von unserem Anbieternetzwerk bevorzugt behandelt.",
|
||||||
"email_quote_verify_btn": "Bestätigen & Aktivieren →",
|
"email_quote_verify_btn": "Bestätigen & Aktivieren →",
|
||||||
"email_quote_verify_expires": "Dieser Link läuft in 60 Minuten ab.",
|
"email_quote_verify_expires": "Dieser Link läuft in 60 Minuten ab.",
|
||||||
"email_quote_verify_fallback": "Wenn der Button nicht funktioniert, kopiere diese URL in deinen Browser:",
|
"email_quote_verify_fallback": "Wenn der Button nicht funktioniert, kopiere diese URL in deinen Browser:",
|
||||||
"email_quote_verify_ignore": "Falls Du das nicht angefordert hast, kannst Du diese E-Mail ignorieren.",
|
"email_quote_verify_ignore": "Wenn du das nicht angefordert hast, kannst du diese E-Mail ignorieren.",
|
||||||
"email_quote_verify_subject": "Bestätige deine E-Mail — Anbieter sind bereit für Angebote",
|
"email_quote_verify_subject": "Bestätige deine E-Mail — Anbieter sind bereit für Angebote",
|
||||||
"email_quote_verify_preheader": "Ein Klick, um deine Angebotsanfrage zu aktivieren",
|
"email_quote_verify_preheader": "Ein Klick, um deine Angebotsanfrage zu aktivieren",
|
||||||
"email_quote_verify_preheader_courts": "Ein Klick, um dein {court_count}-Court-Projekt zu aktivieren",
|
"email_quote_verify_preheader_courts": "Ein Klick, um dein {court_count}-Court-Projekt zu aktivieren",
|
||||||
@@ -1588,7 +1587,7 @@
|
|||||||
"email_welcome_link_markets": "Marktdaten — erkunde die Padel-Nachfrage nach Stadt",
|
"email_welcome_link_markets": "Marktdaten — erkunde die Padel-Nachfrage nach Stadt",
|
||||||
"email_welcome_link_quotes": "Angebote einholen — verbinde dich mit verifizierten Anbietern",
|
"email_welcome_link_quotes": "Angebote einholen — verbinde dich mit verifizierten Anbietern",
|
||||||
"email_welcome_btn": "Jetzt planen →",
|
"email_welcome_btn": "Jetzt planen →",
|
||||||
"email_welcome_subject": "Du bist dabei — so fängst Du an",
|
"email_welcome_subject": "Du bist dabei — so fängst du an",
|
||||||
"email_welcome_preheader": "Dein Padel-Planungstoolkit ist bereit",
|
"email_welcome_preheader": "Dein Padel-Planungstoolkit ist bereit",
|
||||||
"email_waitlist_supplier_heading": "Du stehst auf der Anbieter-Warteliste",
|
"email_waitlist_supplier_heading": "Du stehst auf der Anbieter-Warteliste",
|
||||||
"email_waitlist_supplier_body": "Danke für dein Interesse am <strong>{plan_name}</strong>-Plan. Wir bauen eine Plattform, die dich mit qualifizierten Leads von Padel-Unternehmern verbindet, die aktiv Projekte planen.",
|
"email_waitlist_supplier_body": "Danke für dein Interesse am <strong>{plan_name}</strong>-Plan. Wir bauen eine Plattform, die dich mit qualifizierten Leads von Padel-Unternehmern verbindet, die aktiv Projekte planen.",
|
||||||
@@ -1607,12 +1606,11 @@
|
|||||||
"email_waitlist_general_perk_1": "Frühen Zugang vor dem öffentlichen Launch",
|
"email_waitlist_general_perk_1": "Frühen Zugang vor dem öffentlichen Launch",
|
||||||
"email_waitlist_general_perk_2": "Exklusive Launch-Preise",
|
"email_waitlist_general_perk_2": "Exklusive Launch-Preise",
|
||||||
"email_waitlist_general_perk_3": "Prioritäts-Onboarding und Support",
|
"email_waitlist_general_perk_3": "Prioritäts-Onboarding und Support",
|
||||||
"email_waitlist_general_outro": "Wir melden uns in Kürze.",
|
"email_waitlist_general_outro": "Wir melden uns bald.",
|
||||||
"email_waitlist_general_subject": "Du stehst auf der Liste — wir benachrichtigen dich zum Launch",
|
"email_waitlist_general_subject": "Du stehst auf der Liste — wir benachrichtigen dich zum Launch",
|
||||||
"email_waitlist_general_preheader": "Früher Zugang + exklusive Launch-Preise",
|
"email_waitlist_general_preheader": "Früher Zugang + exklusive Launch-Preise",
|
||||||
"email_lead_forward_heading": "Neues Projekt-Lead",
|
"email_lead_forward_heading": "Neues Projekt-Lead",
|
||||||
"email_lead_forward_urgency": "Dieses Lead wurde gerade freigeschaltet. Anbieter, die innerhalb von 24 Stunden antworten, gewinnen das Projekt 3× häufiger.",
|
"email_lead_forward_urgency": "Dieses Lead wurde gerade freigeschaltet. Anbieter, die innerhalb von 24 Stunden antworten, gewinnen das Projekt 3× häufiger.", "email_lead_forward_section_brief": "Projektbeschreibung",
|
||||||
"email_lead_forward_section_brief": "Projektbeschreibung",
|
|
||||||
"email_lead_forward_section_contact": "Kontakt",
|
"email_lead_forward_section_contact": "Kontakt",
|
||||||
"email_lead_forward_lbl_facility": "Anlage",
|
"email_lead_forward_lbl_facility": "Anlage",
|
||||||
"email_lead_forward_lbl_courts": "Plätze",
|
"email_lead_forward_lbl_courts": "Plätze",
|
||||||
@@ -1631,15 +1629,15 @@
|
|||||||
"email_lead_forward_preheader_suffix": "Kontaktdaten enthalten",
|
"email_lead_forward_preheader_suffix": "Kontaktdaten enthalten",
|
||||||
"email_lead_matched_heading": "Ein Anbieter möchte dein Projekt besprechen",
|
"email_lead_matched_heading": "Ein Anbieter möchte dein Projekt besprechen",
|
||||||
"email_lead_matched_greeting": "Hallo {first_name},",
|
"email_lead_matched_greeting": "Hallo {first_name},",
|
||||||
"email_lead_matched_body": "Gute Neuigkeit — ein verifizierter Anbieter wurde mit Deinem Padel-Projekt abgeglichen. Er hat Dein Projektbriefing und Deine Kontaktdaten.",
|
"email_lead_matched_body": "Gute Nachrichten — ein verifizierter Anbieter wurde mit deinem Padel-Projekt abgeglichen. Er hat deine Projektbeschreibung und Kontaktdaten.",
|
||||||
"email_lead_matched_context": "Du hast eine Angebotsanfrage für eine {facility_type}-Anlage mit {court_count} Plätzen in {country} eingereicht.",
|
"email_lead_matched_context": "Du hast eine Angebotsanfrage für eine {facility_type}-Anlage mit {court_count} Plätzen in {country} eingereicht.",
|
||||||
"email_lead_matched_next_heading": "Was passiert als Nächstes",
|
"email_lead_matched_next_heading": "Was passiert als Nächstes",
|
||||||
"email_lead_matched_next_body": "Der Anbieter hat Dein Projektbriefing und Deine Kontaktdaten erhalten. Die meisten Anbieter melden sich innerhalb von 24–48 Stunden per E-Mail oder Telefon.",
|
"email_lead_matched_next_body": "Der Anbieter hat deine Projektbeschreibung und Kontaktdaten erhalten. Die meisten Anbieter melden sich innerhalb von 24–48 Stunden per E-Mail oder Telefon.",
|
||||||
"email_lead_matched_tip": "Tipp: Wer schnell auf Anbieter-Kontaktaufnahmen reagiert, erhöht seine Chancen auf wettbewerbsfähige Angebote.",
|
"email_lead_matched_tip": "Tipp: Schnelles Reagieren auf Anbieter-Kontaktaufnahmen erhöht deine Chance auf wettbewerbsfähige Angebote.",
|
||||||
"email_lead_matched_btn": "Zum Dashboard →",
|
"email_lead_matched_btn": "Zum Dashboard →",
|
||||||
"email_lead_matched_note": "Du erhältst diese Benachrichtigung jedes Mal, wenn ein neuer Anbieter Deine Projektdetails freischaltet.",
|
"email_lead_matched_note": "Du erhältst diese Benachrichtigung jedes Mal, wenn ein neuer Anbieter deine Projektdetails freischaltet.",
|
||||||
"email_lead_matched_subject": "{first_name}, ein Anbieter möchte dein Projekt besprechen",
|
"email_lead_matched_subject": "{first_name}, ein Anbieter möchte dein Projekt besprechen",
|
||||||
"email_lead_matched_preheader": "Der Anbieter meldet sich direkt bei Dir — das erwartet Dich",
|
"email_lead_matched_preheader": "Der Anbieter wird sich direkt bei dir melden — das erwartet dich",
|
||||||
"email_enquiry_heading": "Neue Anfrage von {contact_name}",
|
"email_enquiry_heading": "Neue Anfrage von {contact_name}",
|
||||||
"email_enquiry_body": "Du hast eine neue Anfrage über deinen <strong>{supplier_name}</strong>-Verzeichniseintrag.",
|
"email_enquiry_body": "Du hast eine neue Anfrage über deinen <strong>{supplier_name}</strong>-Verzeichniseintrag.",
|
||||||
"email_enquiry_lbl_from": "Von",
|
"email_enquiry_lbl_from": "Von",
|
||||||
@@ -1647,8 +1645,7 @@
|
|||||||
"email_enquiry_respond_fast": "Antworte innerhalb von 24 Stunden für den besten ersten Eindruck.",
|
"email_enquiry_respond_fast": "Antworte innerhalb von 24 Stunden für den besten ersten Eindruck.",
|
||||||
"email_enquiry_reply": "Antworte direkt an <a href=\"mailto:{contact_email}\" style=\"color:#1D4ED8;\">{contact_email}</a>.",
|
"email_enquiry_reply": "Antworte direkt an <a href=\"mailto:{contact_email}\" style=\"color:#1D4ED8;\">{contact_email}</a>.",
|
||||||
"email_enquiry_subject": "Neue Anfrage von {contact_name} über deinen Verzeichniseintrag",
|
"email_enquiry_subject": "Neue Anfrage von {contact_name} über deinen Verzeichniseintrag",
|
||||||
"email_enquiry_preheader": "Antworte, um mit diesem potenziellen Kunden in Kontakt zu kommen",
|
"email_enquiry_preheader": "Antworte, um mit diesem potenziellen Kunden in Kontakt zu treten", "email_business_plan_heading": "Dein Businessplan ist fertig",
|
||||||
"email_business_plan_heading": "Dein Businessplan ist fertig",
|
|
||||||
"email_business_plan_body": "Dein Padel-Businessplan wurde als PDF erstellt und steht zum Download bereit.",
|
"email_business_plan_body": "Dein Padel-Businessplan wurde als PDF erstellt und steht zum Download bereit.",
|
||||||
"email_business_plan_includes": "Dein Plan enthält Investitionsübersicht, Umsatzprognosen und Break-Even-Analyse.",
|
"email_business_plan_includes": "Dein Plan enthält Investitionsübersicht, Umsatzprognosen und Break-Even-Analyse.",
|
||||||
"email_business_plan_btn": "PDF herunterladen →",
|
"email_business_plan_btn": "PDF herunterladen →",
|
||||||
@@ -1730,5 +1727,26 @@
|
|||||||
"mscore_faq_q6": "Was ist der Unterschied zwischen dem padelnomics Marktreife-Score und dem padelnomics Marktpotenzial-Score?",
|
"mscore_faq_q6": "Was ist der Unterschied zwischen dem padelnomics Marktreife-Score und dem padelnomics Marktpotenzial-Score?",
|
||||||
"mscore_faq_a6": "Der padelnomics Marktreife-Score misst, wie etabliert und ausgereift ein bestehender Padel-Markt ist — er gilt nur für Städte mit mindestens einer Anlage. Der padelnomics Marktpotenzial-Score bewertet Investitionschancen in noch unbestellten Märkten und erfasst alle Standorte weltweit. Angebotslücken und unterversorgte Einzugsgebiete fließen positiv ein — auch dort, wo es noch gar keine Anlagen gibt.",
|
"mscore_faq_a6": "Der padelnomics Marktreife-Score misst, wie etabliert und ausgereift ein bestehender Padel-Markt ist — er gilt nur für Städte mit mindestens einer Anlage. Der padelnomics Marktpotenzial-Score bewertet Investitionschancen in noch unbestellten Märkten und erfasst alle Standorte weltweit. Angebotslücken und unterversorgte Einzugsgebiete fließen positiv ein — auch dort, wo es noch gar keine Anlagen gibt.",
|
||||||
"mscore_faq_q7": "Warum hat mein Ort einen hohen padelnomics Marktpotenzial-Score, aber keine Padelanlagen?",
|
"mscore_faq_q7": "Warum hat mein Ort einen hohen padelnomics Marktpotenzial-Score, aber keine Padelanlagen?",
|
||||||
"mscore_faq_a7": "Genau darum geht es. Ein hoher padelnomics Marktpotenzial-Score signalisiert einen unterversorgten Standort: solide Bevölkerungsbasis, wirtschaftliche Kaufkraft, kein bestehendes Angebot und weite Entfernung zur nächsten Anlage. Das sind genau die Signale, die auf eine Pionierchance hinweisen — kein Zeichen für einen schwachen Markt."
|
"mscore_faq_a7": "Genau darum geht es. Ein hoher padelnomics Marktpotenzial-Score signalisiert einen unterversorgten Standort: solide Bevölkerungsbasis, wirtschaftliche Kaufkraft, kein bestehendes Angebot und weite Entfernung zur nächsten Anlage. Das sind genau die Signale, die auf eine Pionierchance hinweisen — kein Zeichen für einen schwachen Markt.",
|
||||||
}
|
"sup_cta_btn": "Kostenlos starten",
|
||||||
|
"sup_basic_free_label": "Kostenlos",
|
||||||
|
"sup_pricing_eur_note": "Alle Preise in EUR",
|
||||||
|
"sup_guarantee_h2": "Lead-Back-Garantie",
|
||||||
|
"sup_guarantee_p": "Wenn ein Lead nicht antwortet, Credits per Klick zurückbuchen. Kein Support-Ticket, kein Warten – nach 3 Werktagen einfach im Dashboard auf „Lead hat nicht geantwortet“ klicken.",
|
||||||
|
"sup_guarantee_badge": "Garantie ohne Risiko",
|
||||||
|
"sup_leads_section_h2": "So sehen deine Interessenten aus",
|
||||||
|
"sup_leads_section_sub": "Jeder Lead hat unseren Finanzplaner genutzt. Kontaktdaten werden nach dem Freischalten sichtbar.",
|
||||||
|
"sup_roi_line": "Ein einziges 4-Court-Projekt = <strong>€30.000+ Gewinn</strong>. Growth-Plan: €2.388/Jahr. Die Rechnung ist einfach.",
|
||||||
|
"sup_credits_only_pre": "Noch nicht bereit für ein Abo? Kaufe ein Credit-Paket und schalte Leads einzeln frei. Keine Bindung, keine Monatsgebühr.",
|
||||||
|
"sup_credits_only_cta": "Credits kaufen →",
|
||||||
|
"sup_step1_free_forever": "Dauerhaft kostenlos",
|
||||||
|
"sd_guarantee_btn": "Lead hat nicht geantwortet",
|
||||||
|
"sd_guarantee_contact_label": "Wie hast du versucht, den Lead zu erreichen?",
|
||||||
|
"sd_guarantee_contact_email": "E-Mail",
|
||||||
|
"sd_guarantee_contact_phone": "Telefon",
|
||||||
|
"sd_guarantee_contact_both": "Beides – E-Mail und Telefon",
|
||||||
|
"sd_guarantee_submit": "Credits zurückbuchen",
|
||||||
|
"sd_guarantee_success": "Credits wurden deinem Guthaben gutgeschrieben.",
|
||||||
|
"sd_guarantee_window_error": "Garantiezeitraum abgelaufen (nur 3–30 Tage nach dem Freischalten verfügbar).",
|
||||||
|
"sd_guarantee_already_claimed": "Du hast für diesen Lead bereits eine Rückerstattung beantragt."
|
||||||
|
}
|
||||||
@@ -888,11 +888,11 @@
|
|||||||
"month_nov": "Nov",
|
"month_nov": "Nov",
|
||||||
"month_dec": "Dec",
|
"month_dec": "Dec",
|
||||||
"sup_meta_title": "For Suppliers - Reach Padel Entrepreneurs",
|
"sup_meta_title": "For Suppliers - Reach Padel Entrepreneurs",
|
||||||
"sup_meta_desc": "Get listed on Padelnomics. Reach entrepreneurs who've already built a financial model for their padel project. Basic, Growth and Pro plans from €39/mo.",
|
"sup_meta_desc": "Free directory listing on Padelnomics. Qualified leads from buyers with business plans. Growth and Pro plans from €199/mo.",
|
||||||
"sup_hero_h1a": "Stop Chasing Cold Leads.",
|
"sup_hero_h1a": "Stop Chasing Cold Leads.",
|
||||||
"sup_hero_h1b": "Meet Buyers Who Already Have a Business Plan.",
|
"sup_hero_h1b": "Meet Buyers Who Already Have a Business Plan.",
|
||||||
"sup_hero_sub": "Every lead on Padelnomics has modeled their CAPEX, projected revenue, and calculated ROI — before they contact you. No tire-kickers. No “just browsing.”",
|
"sup_hero_sub": "Every lead on Padelnomics has modeled their CAPEX, projected revenue, and calculated ROI — before they contact you. No tire-kickers. No “just browsing.”",
|
||||||
"sup_hero_cta": "See Plans & Pricing",
|
"sup_hero_cta": "Get Started Free",
|
||||||
"sup_hero_trust_pre": "Trusted by suppliers in",
|
"sup_hero_trust_pre": "Trusted by suppliers in",
|
||||||
"sup_hero_trust_post": "countries",
|
"sup_hero_trust_post": "countries",
|
||||||
"sup_stat_plans": "Business plans created",
|
"sup_stat_plans": "Business plans created",
|
||||||
@@ -932,7 +932,7 @@
|
|||||||
"sup_lead_timeline": "Timeline",
|
"sup_lead_timeline": "Timeline",
|
||||||
"sup_lead_contact": "Contact",
|
"sup_lead_contact": "Contact",
|
||||||
"sup_leads_unlock_pre": "Unlock full contact details and project specs with credits.",
|
"sup_leads_unlock_pre": "Unlock full contact details and project specs with credits.",
|
||||||
"sup_leads_unlock_cta": "Get started →",
|
"sup_leads_unlock_cta": "Start Getting Leads",
|
||||||
"sup_leads_example": "These are example leads. Real leads appear as entrepreneurs submit quote requests.",
|
"sup_leads_example": "These are example leads. Real leads appear as entrepreneurs submit quote requests.",
|
||||||
"sup_why_h2": "Why Padelnomics Leads Are Different",
|
"sup_why_h2": "Why Padelnomics Leads Are Different",
|
||||||
"sup_why_sub": "Every lead has already built a financial model for their project.",
|
"sup_why_sub": "Every lead has already built a financial model for their project.",
|
||||||
@@ -948,14 +948,14 @@
|
|||||||
"sup_billing_yearly": "Yearly",
|
"sup_billing_yearly": "Yearly",
|
||||||
"sup_billing_save": "Save up to 26%",
|
"sup_billing_save": "Save up to 26%",
|
||||||
"sup_basic_name": "Basic",
|
"sup_basic_name": "Basic",
|
||||||
"sup_basic_dir": "Directory listing",
|
"sup_basic_dir": "Free forever",
|
||||||
"sup_basic_f1": "Verified ✓ badge",
|
"sup_basic_f1": "Verified ✓ badge",
|
||||||
"sup_basic_f2": "Company logo",
|
"sup_basic_f2": "Company logo",
|
||||||
"sup_basic_f3": "Full description & tagline",
|
"sup_basic_f3": "Full description & tagline",
|
||||||
"sup_basic_f4": "Website & contact details",
|
"sup_basic_f4": "Website & contact details",
|
||||||
"sup_basic_f5": "Services offered checklist",
|
"sup_basic_f5": "Services offered checklist",
|
||||||
"sup_basic_f6": "Enquiry form on listing page",
|
"sup_basic_f6": "Enquiry form on listing page",
|
||||||
"sup_basic_cta": "Get Listed",
|
"sup_basic_cta": "List Your Company Free",
|
||||||
"sup_growth_name": "Growth",
|
"sup_growth_name": "Growth",
|
||||||
"sup_growth_popular": "Most Popular",
|
"sup_growth_popular": "Most Popular",
|
||||||
"sup_growth_credits": "30 credits/mo included",
|
"sup_growth_credits": "30 credits/mo included",
|
||||||
@@ -975,11 +975,11 @@
|
|||||||
"sup_pro_f5": "Priority placement in directory",
|
"sup_pro_f5": "Priority placement in directory",
|
||||||
"sup_pro_f6": "100 lead credits per month",
|
"sup_pro_f6": "100 lead credits per month",
|
||||||
"sup_pro_cta": "Get Started",
|
"sup_pro_cta": "Get Started",
|
||||||
"sup_yearly_note_basic": "€349 billed yearly",
|
"sup_yearly_note_basic": "Free forever",
|
||||||
"sup_yearly_note_growth": "€1,799 billed yearly",
|
"sup_yearly_note_growth": "€1,799 billed yearly",
|
||||||
"sup_yearly_note_pro": "€4,499 billed yearly",
|
"sup_yearly_note_pro": "€4,499 billed yearly",
|
||||||
"sup_boosts_h3": "Boost Add-Ons",
|
"sup_boosts_h3": "Boost Add-Ons",
|
||||||
"sup_boosts_sub": "Available with any paid plan. Manage from your dashboard.",
|
"sup_boosts_sub": "Available on any plan. Manage from your dashboard. Card color from €59/mo.",
|
||||||
"sup_boost_logo": "Logo",
|
"sup_boost_logo": "Logo",
|
||||||
"sup_boost_highlight": "Highlight",
|
"sup_boost_highlight": "Highlight",
|
||||||
"sup_boost_verified": "Verified Badge",
|
"sup_boost_verified": "Verified Badge",
|
||||||
@@ -1026,13 +1026,13 @@
|
|||||||
"sup_faq_a1_post": "and click “Is this your company?” We’ll verify your identity and give you access to choose a plan and upgrade your profile.",
|
"sup_faq_a1_post": "and click “Is this your company?” We’ll verify your identity and give you access to choose a plan and upgrade your profile.",
|
||||||
"sup_faq_dir_link": "directory",
|
"sup_faq_dir_link": "directory",
|
||||||
"sup_faq_q2": "How much does it cost?",
|
"sup_faq_q2": "How much does it cost?",
|
||||||
"sup_faq_a2": "We offer three plans: Basic (€39/mo) for a verified directory listing with enquiry form; Growth (€199/mo, 30 credits) with full lead access and priority placement; and Pro (€499/mo, 100 credits) for maximum visibility and lead volume. Yearly billing saves up to 26% — Basic at €349/yr, Growth at €1,799/yr, Pro at €4,499/yr. Optional boost add-ons are available on top.",
|
"sup_faq_a2": "Basic is free — a verified directory listing with an enquiry form, no subscription required. Growth (€199/mo, 30 credits) gives full lead access and priority placement. Pro (€499/mo, 100 credits) adds maximum visibility and lead volume. Yearly billing saves up to 25%: Growth at €1,799/yr, Pro at €4,499/yr. Optional boost add-ons are available on any plan.",
|
||||||
"sup_faq_q3": "What makes Padelnomics leads different from other platforms?",
|
"sup_faq_q3": "What makes Padelnomics leads different from other platforms?",
|
||||||
"sup_faq_a3": "Every lead on Padelnomics has used our financial planning tool to model their project — CAPEX, revenue projections, ROI, and debt service coverage — before reaching out. This means they’re serious, they have a realistic budget, and they’re ready to talk to suppliers. You’re not getting cold enquiries; you’re getting pre-qualified project briefs.",
|
"sup_faq_a3": "Every lead on Padelnomics has used our financial planning tool to model their project — CAPEX, revenue projections, ROI, and debt service coverage — before reaching out. This means they’re serious, they have a realistic budget, and they’re ready to talk to suppliers. You’re not getting cold enquiries; you’re getting pre-qualified project briefs.",
|
||||||
"sup_faq_q4": "How does pricing compare to alternatives?",
|
"sup_faq_q4": "How does pricing compare to alternatives?",
|
||||||
"sup_faq_a4": "A trade show booth costs €10,000+ per event and delivers mostly browsing contacts. Google Ads for padel construction keywords run €20–80 per click — that’s €5,000+/yr before you talk to a single prospect. A typical cold directory listing charges ~€600/yr with no leads at all. Padelnomics Growth at €1,799/yr includes 30 lead credits per month with full project briefs.",
|
"sup_faq_a4": "A trade show booth costs €10,000+ per event and delivers mostly browsing contacts. Google Ads for padel construction keywords run €20–80 per click — that’s €5,000+/yr before you talk to a single prospect. A typical cold directory listing charges ~€600/yr with no leads at all. Padelnomics Growth at €1,799/yr includes 30 lead credits per month with full project briefs.",
|
||||||
"sup_faq_q5": "How do credits work?",
|
"sup_faq_q5": "How do credits work — and what's the Lead-Back Guarantee?",
|
||||||
"sup_faq_a5": "Credits are how you unlock lead contact details. Each plan includes monthly credits (Growth: 30, Pro: 100). Hot leads cost 35 credits, warm leads 20, and cool leads 8. You can buy additional credit packs anytime from your dashboard. Unused credits roll over month to month.",
|
"sup_faq_a5": "Credits unlock lead contact details. Growth includes 30 credits/mo; Pro includes 100. Hot leads cost 35 credits, warm leads 20, cool leads 8. You can also buy credit packs without a subscription. Unused credits roll over. If a lead doesn't respond, click \"Lead didn't respond\" on the card after 3 business days and your credits are instantly returned to your balance — no support ticket, no waiting.",
|
||||||
"sup_faq_q6": "What information do leads include?",
|
"sup_faq_q6": "What information do leads include?",
|
||||||
"sup_faq_a6": "Every lead includes: facility type (indoor/outdoor), court count, glass and lighting preferences, country and city, budget estimate, project phase, timeline, financing status, stakeholder type, services needed, and full contact details.",
|
"sup_faq_a6": "Every lead includes: facility type (indoor/outdoor), court count, glass and lighting preferences, country and city, budget estimate, project phase, timeline, financing status, stakeholder type, services needed, and full contact details.",
|
||||||
"sup_faq_q7": "How are leads matched to suppliers?",
|
"sup_faq_q7": "How are leads matched to suppliers?",
|
||||||
@@ -1047,6 +1047,27 @@
|
|||||||
"sup_faq_a10_post": "with your company details and we’ll add you to the directory within 48 hours.",
|
"sup_faq_a10_post": "with your company details and we’ll add you to the directory within 48 hours.",
|
||||||
"sup_cta_h2": "Your Next Client Is Already Building a Business Plan",
|
"sup_cta_h2": "Your Next Client Is Already Building a Business Plan",
|
||||||
"sup_cta_p": "They’ve modeled the ROI. They know their budget. They’re looking for a supplier like you.",
|
"sup_cta_p": "They’ve modeled the ROI. They know their budget. They’re looking for a supplier like you.",
|
||||||
|
"sup_cta_btn": "Get Started Free",
|
||||||
|
"sup_basic_free_label": "Free",
|
||||||
|
"sup_pricing_eur_note": "All prices in EUR",
|
||||||
|
"sup_guarantee_h2": "Lead-Back Guarantee",
|
||||||
|
"sup_guarantee_p": "If a lead doesn’t respond, claim your credits back with one click. No support tickets, no waiting — just click \"Lead didn’t respond\" in your dashboard after 3 business days.",
|
||||||
|
"sup_guarantee_badge": "No-risk guarantee",
|
||||||
|
"sup_leads_section_h2": "See What Your Prospects Look Like",
|
||||||
|
"sup_leads_section_sub": "Every lead has used our financial planner. Contact details are blurred until you unlock.",
|
||||||
|
"sup_roi_line": "A single 4-court project = <strong>€30,000+ in profit</strong>. Growth plan costs €2,388/year. The math is simple.",
|
||||||
|
"sup_credits_only_pre": "Not ready for a subscription? Buy a credit pack and unlock leads one at a time. No commitment, no monthly fee.",
|
||||||
|
"sup_credits_only_cta": "Buy Credits →",
|
||||||
|
"sup_step1_free_forever": "Free forever",
|
||||||
|
"sd_guarantee_btn": "Lead didn’t respond",
|
||||||
|
"sd_guarantee_contact_label": "How did you try to reach them?",
|
||||||
|
"sd_guarantee_contact_email": "Email",
|
||||||
|
"sd_guarantee_contact_phone": "Phone",
|
||||||
|
"sd_guarantee_contact_both": "Both email and phone",
|
||||||
|
"sd_guarantee_submit": "Claim credits back",
|
||||||
|
"sd_guarantee_success": "Credits returned to your balance.",
|
||||||
|
"sd_guarantee_window_error": "Guarantee window has closed (only available 3–30 days after unlock).",
|
||||||
|
"sd_guarantee_already_claimed": "You’ve already claimed a refund for this lead.",
|
||||||
"scenario_cta_try_numbers": "Try with your own numbers →",
|
"scenario_cta_try_numbers": "Try with your own numbers →",
|
||||||
"scenario_payback_label": "Payback",
|
"scenario_payback_label": "Payback",
|
||||||
"scenario_months_unit": "months",
|
"scenario_months_unit": "months",
|
||||||
@@ -1731,4 +1752,4 @@
|
|||||||
"mscore_faq_a6": "The padelnomics Marktreife-Score measures how established and mature an existing padel market is — it only applies to cities with at least one venue. The padelnomics Marktpotenzial-Score measures greenfield investment opportunity and covers all locations globally, rewarding supply gaps and underserved catchment areas where no courts exist yet.",
|
"mscore_faq_a6": "The padelnomics Marktreife-Score measures how established and mature an existing padel market is — it only applies to cities with at least one venue. The padelnomics Marktpotenzial-Score measures greenfield investment opportunity and covers all locations globally, rewarding supply gaps and underserved catchment areas where no courts exist yet.",
|
||||||
"mscore_faq_q7": "Why does my town have a high padelnomics Marktpotenzial-Score but no padel courts?",
|
"mscore_faq_q7": "Why does my town have a high padelnomics Marktpotenzial-Score but no padel courts?",
|
||||||
"mscore_faq_a7": "That is exactly the point. A high padelnomics Marktpotenzial-Score indicates an underserved location: strong demographics, economic purchasing power, no existing supply, and distance from the nearest court. These are precisely the signals that suggest a greenfield opportunity — not a sign of a weak market."
|
"mscore_faq_a7": "That is exactly the point. A high padelnomics Marktpotenzial-Score indicates an underserved location: strong demographics, economic purchasing power, no existing supply, and distance from the nearest court. These are precisely the signals that suggest a greenfield opportunity — not a sign of a weak market."
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
"""Add lead-back guarantee columns to lead_forwards.
|
||||||
|
|
||||||
|
guarantee_claimed_at: ISO timestamp when supplier claimed the guarantee refund.
|
||||||
|
guarantee_contact_method: how the supplier tried to reach the lead (email/phone/both).
|
||||||
|
Status 'no_response' added to lead_forwards.status by convention (no CHECK constraint change
|
||||||
|
needed — existing status TEXT column accepts any value).
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def up(conn):
|
||||||
|
conn.execute(
|
||||||
|
"ALTER TABLE lead_forwards ADD COLUMN guarantee_claimed_at TEXT"
|
||||||
|
)
|
||||||
|
conn.execute(
|
||||||
|
"ALTER TABLE lead_forwards ADD COLUMN guarantee_contact_method TEXT"
|
||||||
|
)
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
<p>{{ t.export_subtitle }}</p>
|
<p>{{ t.export_subtitle }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="exp-price">€99 <span>one-time</span></div>
|
<div class="exp-price">€149 <span>one-time</span></div>
|
||||||
|
|
||||||
<ul class="exp-features">
|
<ul class="exp-features">
|
||||||
{% for key in ['planner_export_f1','planner_export_f2','planner_export_f3','planner_export_f4','planner_export_f5','planner_export_f6','planner_export_f7','planner_export_f8'] %}
|
{% for key in ['planner_export_f1','planner_export_f2','planner_export_f3','planner_export_f4','planner_export_f5','planner_export_f6','planner_export_f7','planner_export_f8'] %}
|
||||||
|
|||||||
@@ -44,38 +44,35 @@
|
|||||||
margin-top: 1.5rem; font-weight: 500;
|
margin-top: 1.5rem; font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* How it works */
|
/* Why section */
|
||||||
.sup-steps {
|
.sup-why { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
|
||||||
display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem;
|
.sup-why-card {
|
||||||
max-width: 800px; margin: 0 auto;
|
border: 1px solid #E2E8F0; border-radius: 14px; padding: 1.25rem;
|
||||||
|
text-align: center; box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
||||||
}
|
}
|
||||||
.sup-step { text-align: center; }
|
.sup-why-card h3 { font-size: 0.9375rem; margin-bottom: 0.25rem; }
|
||||||
.sup-step__num {
|
.sup-why-card p { font-size: 0.8125rem; color: #64748B; }
|
||||||
display: inline-flex; align-items: center; justify-content: center;
|
|
||||||
width: 48px; height: 48px; border-radius: 50%;
|
|
||||||
background: #EFF6FF; color: #1D4ED8; font-weight: 700; font-size: 1.25rem;
|
|
||||||
margin-bottom: 0.75rem;
|
|
||||||
}
|
|
||||||
.sup-step h3 { font-size: 1rem; margin-bottom: 0.25rem; }
|
|
||||||
.sup-step p { font-size: 0.8125rem; color: #64748B; }
|
|
||||||
|
|
||||||
/* Credit explainer */
|
/* Lead-back guarantee */
|
||||||
.credit-explainer {
|
.sup-guarantee {
|
||||||
background: #F8FAFC; border: 1px solid #E2E8F0; border-radius: 16px;
|
background: linear-gradient(135deg, #F0FDF4 0%, #ECFDF5 100%);
|
||||||
padding: 1.5rem; max-width: 600px; margin: 2rem auto 0;
|
border: 2px solid #16A34A; border-radius: 20px; padding: 2rem;
|
||||||
|
max-width: 680px; margin: 0 auto; text-align: center;
|
||||||
}
|
}
|
||||||
.credit-explainer h3 { font-size: 1rem; margin-bottom: 1rem; text-align: center; }
|
.sup-guarantee__icon {
|
||||||
.credit-tiers { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.75rem; }
|
font-size: 2.5rem; margin-bottom: 0.5rem; display: block; line-height: 1;
|
||||||
.credit-tier {
|
}
|
||||||
text-align: center; padding: 0.75rem; background: white;
|
.sup-guarantee h2 {
|
||||||
border: 1px solid #E2E8F0; border-radius: 10px;
|
font-size: 1.375rem; color: #14532D; margin-bottom: 0.5rem; text-align: center;
|
||||||
|
}
|
||||||
|
.sup-guarantee p {
|
||||||
|
color: #166534; font-size: 0.9375rem; line-height: 1.65; max-width: 520px; margin: 0 auto;
|
||||||
|
}
|
||||||
|
.sup-guarantee__badge {
|
||||||
|
display: inline-block; background: #16A34A; color: white; font-size: 0.6875rem;
|
||||||
|
font-weight: 700; padding: 4px 12px; border-radius: 999px; margin-top: 1rem;
|
||||||
|
text-transform: uppercase; letter-spacing: 0.04em;
|
||||||
}
|
}
|
||||||
.credit-tier .tier-heat { font-weight: 700; font-size: 0.875rem; margin-bottom: 4px; }
|
|
||||||
.credit-tier .tier-cost { font-size: 1.25rem; font-weight: 800; color: #1D4ED8; }
|
|
||||||
.credit-tier .tier-label { font-size: 0.6875rem; color: #64748B; }
|
|
||||||
.heat-hot { color: #DC2626; }
|
|
||||||
.heat-warm { color: #D97706; }
|
|
||||||
.heat-cool { color: #3B82F6; }
|
|
||||||
|
|
||||||
/* Lead preview */
|
/* Lead preview */
|
||||||
.sup-lead-preview {
|
.sup-lead-preview {
|
||||||
@@ -100,14 +97,39 @@
|
|||||||
.lead-preview-card dd { color: #1E293B; font-weight: 500; margin: 0; }
|
.lead-preview-card dd { color: #1E293B; font-weight: 500; margin: 0; }
|
||||||
.lead-preview-card .lp-blur { color: transparent; text-shadow: 0 0 8px rgba(30,41,59,0.5); user-select: none; }
|
.lead-preview-card .lp-blur { color: transparent; text-shadow: 0 0 8px rgba(30,41,59,0.5); user-select: none; }
|
||||||
|
|
||||||
/* Why section */
|
/* How it works */
|
||||||
.sup-why { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; }
|
.sup-steps {
|
||||||
.sup-why-card {
|
display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem;
|
||||||
border: 1px solid #E2E8F0; border-radius: 14px; padding: 1.25rem;
|
max-width: 800px; margin: 0 auto;
|
||||||
text-align: center; box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
|
||||||
}
|
}
|
||||||
.sup-why-card h3 { font-size: 0.9375rem; margin-bottom: 0.25rem; }
|
.sup-step { text-align: center; }
|
||||||
.sup-why-card p { font-size: 0.8125rem; color: #64748B; }
|
.sup-step__num {
|
||||||
|
display: inline-flex; align-items: center; justify-content: center;
|
||||||
|
width: 48px; height: 48px; border-radius: 50%;
|
||||||
|
background: #EFF6FF; color: #1D4ED8; font-weight: 700; font-size: 1.25rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
.sup-step h3 { font-size: 1rem; margin-bottom: 0.25rem; }
|
||||||
|
.sup-step p { font-size: 0.8125rem; color: #64748B; }
|
||||||
|
|
||||||
|
/* Social proof */
|
||||||
|
.sup-proof-grid {
|
||||||
|
display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem;
|
||||||
|
max-width: 720px; margin: 0 auto;
|
||||||
|
}
|
||||||
|
.sup-proof-card {
|
||||||
|
background: #F8FAFC; border: 1px solid #E2E8F0; border-radius: 16px;
|
||||||
|
padding: 1.75rem; text-align: left; position: relative;
|
||||||
|
}
|
||||||
|
.sup-proof-card::before {
|
||||||
|
content: "\201C"; font-size: 4rem; color: #DBEAFE; line-height: 1;
|
||||||
|
position: absolute; top: 1rem; left: 1.25rem; font-family: Georgia, serif;
|
||||||
|
}
|
||||||
|
.sup-proof-card blockquote {
|
||||||
|
font-size: 0.9375rem; color: #334155; font-style: italic;
|
||||||
|
line-height: 1.6; margin: 1.5rem 0 0.75rem; padding: 0;
|
||||||
|
}
|
||||||
|
.sup-proof-card cite { font-size: 0.8125rem; color: #94A3B8; font-style: normal; }
|
||||||
|
|
||||||
/* Billing toggle — CSS-only via radio sibling trick */
|
/* Billing toggle — CSS-only via radio sibling trick */
|
||||||
input[name="billing"] { position: absolute; opacity: 0; pointer-events: none; width: 1px; height: 1px; }
|
input[name="billing"] { position: absolute; opacity: 0; pointer-events: none; width: 1px; height: 1px; }
|
||||||
@@ -167,6 +189,23 @@
|
|||||||
}
|
}
|
||||||
.pricing-card li::before { content: "\2713"; color: #16A34A; font-weight: 700; flex-shrink: 0; }
|
.pricing-card li::before { content: "\2713"; color: #16A34A; font-weight: 700; flex-shrink: 0; }
|
||||||
.pricing-card .btn, .pricing-card .btn-outline { width: 100%; text-align: center; margin-top: auto; }
|
.pricing-card .btn, .pricing-card .btn-outline { width: 100%; text-align: center; margin-top: auto; }
|
||||||
|
.pricing-card__eur-note { font-size: 0.6875rem; color: #94A3B8; text-align: center; margin-top: 0.5rem; }
|
||||||
|
|
||||||
|
/* ROI callout */
|
||||||
|
.sup-roi {
|
||||||
|
background: #0F172A; border-radius: 14px; padding: 1.25rem 2rem;
|
||||||
|
text-align: center; max-width: 720px; margin: 1.75rem auto 0;
|
||||||
|
}
|
||||||
|
.sup-roi p { color: #E2E8F0; font-size: 1rem; font-weight: 600; margin: 0; line-height: 1.5; }
|
||||||
|
.sup-roi strong { color: #34D399; }
|
||||||
|
|
||||||
|
/* Credits-only callout */
|
||||||
|
.sup-credits-only {
|
||||||
|
background: #F8FAFC; border: 1px solid #E2E8F0; border-radius: 14px;
|
||||||
|
padding: 1.25rem 1.5rem; text-align: center; max-width: 640px; margin: 1.5rem auto 0;
|
||||||
|
}
|
||||||
|
.sup-credits-only p { font-size: 0.875rem; color: #475569; margin: 0; }
|
||||||
|
.sup-credits-only a { color: #1D4ED8; font-weight: 600; }
|
||||||
|
|
||||||
/* Boosts */
|
/* Boosts */
|
||||||
.boost-grid {
|
.boost-grid {
|
||||||
@@ -198,25 +237,6 @@
|
|||||||
margin: 0.75rem auto 0; max-width: 560px;
|
margin: 0.75rem auto 0; max-width: 560px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Social proof */
|
|
||||||
.sup-proof-grid {
|
|
||||||
display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem;
|
|
||||||
max-width: 720px; margin: 0 auto;
|
|
||||||
}
|
|
||||||
.sup-proof-card {
|
|
||||||
background: #F8FAFC; border: 1px solid #E2E8F0; border-radius: 16px;
|
|
||||||
padding: 1.75rem; text-align: left; position: relative;
|
|
||||||
}
|
|
||||||
.sup-proof-card::before {
|
|
||||||
content: "\201C"; font-size: 4rem; color: #DBEAFE; line-height: 1;
|
|
||||||
position: absolute; top: 1rem; left: 1.25rem; font-family: Georgia, serif;
|
|
||||||
}
|
|
||||||
.sup-proof-card blockquote {
|
|
||||||
font-size: 0.9375rem; color: #334155; font-style: italic;
|
|
||||||
line-height: 1.6; margin: 1.5rem 0 0.75rem; padding: 0;
|
|
||||||
}
|
|
||||||
.sup-proof-card cite { font-size: 0.8125rem; color: #94A3B8; font-style: normal; }
|
|
||||||
|
|
||||||
/* FAQ */
|
/* FAQ */
|
||||||
.sup-faq { max-width: 640px; margin: 0 auto; }
|
.sup-faq { max-width: 640px; margin: 0 auto; }
|
||||||
.sup-faq details { border-bottom: 1px solid #E2E8F0; padding: 1rem 0; }
|
.sup-faq details { border-bottom: 1px solid #E2E8F0; padding: 1rem 0; }
|
||||||
@@ -240,7 +260,6 @@
|
|||||||
.sup-stats { grid-template-columns: repeat(2, 1fr); }
|
.sup-stats { grid-template-columns: repeat(2, 1fr); }
|
||||||
.sup-steps, .sup-why, .sup-problem-grid, .sup-proof-grid { grid-template-columns: 1fr; }
|
.sup-steps, .sup-why, .sup-problem-grid, .sup-proof-grid { grid-template-columns: 1fr; }
|
||||||
.pricing-grid { grid-template-columns: 1fr; }
|
.pricing-grid { grid-template-columns: 1fr; }
|
||||||
.credit-tiers { grid-template-columns: 1fr; }
|
|
||||||
.lead-preview-grid { grid-template-columns: 1fr; }
|
.lead-preview-grid { grid-template-columns: 1fr; }
|
||||||
.sup-hero h1 { font-size: 1.75rem; }
|
.sup-hero h1 { font-size: 1.75rem; }
|
||||||
}
|
}
|
||||||
@@ -254,7 +273,7 @@
|
|||||||
<div class="sup-hero">
|
<div class="sup-hero">
|
||||||
<h1>{{ t.sup_hero_h1a }}<br>{{ t.sup_hero_h1b }}</h1>
|
<h1>{{ t.sup_hero_h1a }}<br>{{ t.sup_hero_h1b }}</h1>
|
||||||
<p>{{ t.sup_hero_sub }}</p>
|
<p>{{ t.sup_hero_sub }}</p>
|
||||||
<a href="#pricing" class="btn">{{ t.sup_hero_cta }}</a>
|
<a href="{{ url_for('suppliers.signup') }}" class="btn">{{ t.sup_hero_cta }}</a>
|
||||||
<p class="sup-hero__proof">{{ t.sup_hero_trust_pre }} {{ total_countries }} {{ t.sup_hero_trust_post }}</p>
|
<p class="sup-hero__proof">{{ t.sup_hero_trust_pre }} {{ total_countries }} {{ t.sup_hero_trust_post }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -304,54 +323,40 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- How it works -->
|
<!-- Why Padelnomics — moved above lead preview -->
|
||||||
<section class="sup-section">
|
<section class="sup-section">
|
||||||
<h2>{{ t.sup_how_h2 }}</h2>
|
<h2>{{ t.sup_why_h2 }}</h2>
|
||||||
<p class="sub">{{ t.sup_how_sub }}</p>
|
<p class="sub">{{ t.sup_why_sub }}</p>
|
||||||
<div class="sup-steps">
|
<div class="sup-why">
|
||||||
<div class="sup-step">
|
<div class="sup-why-card">
|
||||||
<div class="sup-step__num">1</div>
|
<h3>{{ t.sup_why_card1_h3 }}</h3>
|
||||||
<h3>{{ t.sup_how_step1_h3 }}</h3>
|
<p>{{ t.sup_why_card1_p }}</p>
|
||||||
<p>{{ t.sup_how_step1_p }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="sup-step">
|
<div class="sup-why-card">
|
||||||
<div class="sup-step__num">2</div>
|
<h3>{{ t.sup_why_card2_h3 }}</h3>
|
||||||
<h3>{{ t.sup_how_step2_h3 }}</h3>
|
<p>{{ t.sup_why_card2_p }}</p>
|
||||||
<p>{{ t.sup_how_step2_p }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="sup-step">
|
<div class="sup-why-card">
|
||||||
<div class="sup-step__num">3</div>
|
<h3>{{ t.sup_why_card3_h3 }}</h3>
|
||||||
<h3>{{ t.sup_how_step3_h3 }}</h3>
|
<p>{{ t.sup_why_card3_p }}</p>
|
||||||
<p>{{ t.sup_how_step3_p }}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- Credit explainer -->
|
<!-- Lead-Back Guarantee -->
|
||||||
<div class="credit-explainer">
|
<section class="sup-section">
|
||||||
<h3>{{ t.sup_credits_h3 }}</h3>
|
<div class="sup-guarantee">
|
||||||
<p style="text-align:center;font-size:0.8125rem;color:#64748B;margin-bottom:1rem">
|
<span class="sup-guarantee__icon">🛡️</span>
|
||||||
{{ t.sup_credits_sub }}
|
<h2>{{ t.sup_guarantee_h2 }}</h2>
|
||||||
</p>
|
<p>{{ t.sup_guarantee_p }}</p>
|
||||||
<div class="credit-tiers">
|
<span class="sup-guarantee__badge">{{ t.sup_guarantee_badge }}</span>
|
||||||
<div class="credit-tier">
|
|
||||||
<div class="tier-heat heat-hot">{{ t.sup_credits_hot }}</div>
|
|
||||||
<div class="tier-cost">35</div>
|
|
||||||
<div class="tier-label">{{ t.sup_credits_hot_label }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="credit-tier">
|
|
||||||
<div class="tier-heat heat-warm">{{ t.sup_credits_warm }}</div>
|
|
||||||
<div class="tier-cost">20</div>
|
|
||||||
<div class="tier-label">{{ t.sup_credits_warm_label }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="credit-tier">
|
|
||||||
<div class="tier-heat heat-cool">{{ t.sup_credits_cool }}</div>
|
|
||||||
<div class="tier-cost">8</div>
|
|
||||||
<div class="tier-label">{{ t.sup_credits_cool_label }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- Live lead preview -->
|
<!-- Live lead preview -->
|
||||||
|
<section class="sup-section">
|
||||||
|
<h2>{{ t.sup_leads_section_h2 }}</h2>
|
||||||
|
<p class="sub">{{ t.sup_leads_section_sub }}</p>
|
||||||
<div class="sup-lead-preview">
|
<div class="sup-lead-preview">
|
||||||
<h4>{{ t.sup_leads_heading }}</h4>
|
<h4>{{ t.sup_leads_heading }}</h4>
|
||||||
{% if preview_leads %}
|
{% if preview_leads %}
|
||||||
@@ -376,7 +381,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<p style="text-align:center;margin-top:1rem;font-size:0.8125rem;color:#64748B">
|
<p style="text-align:center;margin-top:1rem;font-size:0.8125rem;color:#64748B">
|
||||||
{{ t.sup_leads_unlock_pre }}
|
{{ t.sup_leads_unlock_pre }}
|
||||||
<a href="#pricing" style="color:#1D4ED8;font-weight:600">{{ t.sup_leads_unlock_cta }}</a>
|
<a href="{{ url_for('suppliers.signup') }}" style="color:#1D4ED8;font-weight:600">{{ t.sup_leads_unlock_cta }}</a>
|
||||||
</p>
|
</p>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="lead-preview-grid">
|
<div class="lead-preview-grid">
|
||||||
@@ -418,22 +423,41 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Why Padelnomics -->
|
<!-- How it works -->
|
||||||
<section class="sup-section">
|
<section class="sup-section">
|
||||||
<h2>{{ t.sup_why_h2 }}</h2>
|
<h2>{{ t.sup_how_h2 }}</h2>
|
||||||
<p class="sub">{{ t.sup_why_sub }}</p>
|
<p class="sub">{{ t.sup_how_sub }}</p>
|
||||||
<div class="sup-why">
|
<div class="sup-steps">
|
||||||
<div class="sup-why-card">
|
<div class="sup-step">
|
||||||
<h3>{{ t.sup_why_card1_h3 }}</h3>
|
<div class="sup-step__num">1</div>
|
||||||
<p>{{ t.sup_why_card1_p }}</p>
|
<h3>{{ t.sup_how_step1_h3 }}</h3>
|
||||||
|
<p>{{ t.sup_how_step1_p }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="sup-why-card">
|
<div class="sup-step">
|
||||||
<h3>{{ t.sup_why_card2_h3 }}</h3>
|
<div class="sup-step__num">2</div>
|
||||||
<p>{{ t.sup_why_card2_p }}</p>
|
<h3>{{ t.sup_how_step2_h3 }}</h3>
|
||||||
|
<p>{{ t.sup_how_step2_p }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="sup-why-card">
|
<div class="sup-step">
|
||||||
<h3>{{ t.sup_why_card3_h3 }}</h3>
|
<div class="sup-step__num">3</div>
|
||||||
<p>{{ t.sup_why_card3_p }}</p>
|
<h3>{{ t.sup_how_step3_h3 }}</h3>
|
||||||
|
<p>{{ t.sup_how_step3_p }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Social proof — moved before pricing -->
|
||||||
|
<section class="sup-section">
|
||||||
|
<h2>{{ t.sup_proof_h2 }}</h2>
|
||||||
|
<p class="sub">{{ calc_requests }}+ {{ t.sup_proof_stat1 }} · {{ total_suppliers }}+ {{ t.sup_proof_stat2 }} · {{ total_countries }} {{ t.sup_proof_stat3 }}</p>
|
||||||
|
<div class="sup-proof-grid">
|
||||||
|
<div class="sup-proof-card">
|
||||||
|
<blockquote>“{{ t.sup_proof_q1 }}”</blockquote>
|
||||||
|
<cite>{{ t.sup_proof_cite1 }}</cite>
|
||||||
|
</div>
|
||||||
|
<div class="sup-proof-card">
|
||||||
|
<blockquote>“{{ t.sup_proof_q2 }}”</blockquote>
|
||||||
|
<cite>{{ t.sup_proof_cite2 }}</cite>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -453,15 +477,14 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pricing-grid">
|
<div class="pricing-grid">
|
||||||
<!-- Basic -->
|
<!-- Basic — Free -->
|
||||||
<div class="pricing-card">
|
<div class="pricing-card">
|
||||||
<h3>{{ t.sup_basic_name }}</h3>
|
<h3>{{ t.sup_basic_name }}</h3>
|
||||||
<div class="price-monthly">
|
<div class="price-monthly">
|
||||||
<div class="price">€39 <span>/mo</span></div>
|
<div class="price">{{ t.sup_basic_free_label }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="price-yearly">
|
<div class="price-yearly">
|
||||||
<div class="price">€29 <span>/mo</span></div>
|
<div class="price">{{ t.sup_basic_free_label }}</div>
|
||||||
<span class="yearly-note">{{ t.sup_yearly_note_basic }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="credits-inc credits-inc--muted">{{ t.sup_basic_dir }}</div>
|
<div class="credits-inc credits-inc--muted">{{ t.sup_basic_dir }}</div>
|
||||||
<ul>
|
<ul>
|
||||||
@@ -473,6 +496,7 @@
|
|||||||
<li>{{ t.sup_basic_f6 }}</li>
|
<li>{{ t.sup_basic_f6 }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<a href="{{ url_for('suppliers.signup') }}?plan=supplier_basic" class="btn-outline" style="display:block;text-align:center">{{ t.sup_basic_cta }}</a>
|
<a href="{{ url_for('suppliers.signup') }}?plan=supplier_basic" class="btn-outline" style="display:block;text-align:center">{{ t.sup_basic_cta }}</a>
|
||||||
|
<p class="pricing-card__eur-note">{{ t.sup_pricing_eur_note }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Growth -->
|
<!-- Growth -->
|
||||||
@@ -496,6 +520,7 @@
|
|||||||
<li>{{ t.sup_growth_f6 }}</li>
|
<li>{{ t.sup_growth_f6 }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<a href="{{ url_for('suppliers.signup') }}?plan=supplier_growth" class="btn" style="display:block;text-align:center">{{ t.sup_growth_cta }}</a>
|
<a href="{{ url_for('suppliers.signup') }}?plan=supplier_growth" class="btn" style="display:block;text-align:center">{{ t.sup_growth_cta }}</a>
|
||||||
|
<p class="pricing-card__eur-note">{{ t.sup_pricing_eur_note }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Pro -->
|
<!-- Pro -->
|
||||||
@@ -518,9 +543,20 @@
|
|||||||
<li>{{ t.sup_pro_f6 }}</li>
|
<li>{{ t.sup_pro_f6 }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
<a href="{{ url_for('suppliers.signup') }}?plan=supplier_pro" class="btn-outline" style="display:block;text-align:center">{{ t.sup_pro_cta }}</a>
|
<a href="{{ url_for('suppliers.signup') }}?plan=supplier_pro" class="btn-outline" style="display:block;text-align:center">{{ t.sup_pro_cta }}</a>
|
||||||
|
<p class="pricing-card__eur-note">{{ t.sup_pricing_eur_note }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Static ROI line -->
|
||||||
|
<div class="sup-roi">
|
||||||
|
<p>{{ t.sup_roi_line }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Credits-only callout -->
|
||||||
|
<div class="sup-credits-only">
|
||||||
|
<p>{{ t.sup_credits_only_pre }} <a href="{{ url_for('suppliers.signup') }}?plan=supplier_basic#credits">{{ t.sup_credits_only_cta }}</a></p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Boost add-ons -->
|
<!-- Boost add-ons -->
|
||||||
<h3 style="text-align:center;font-size:1rem;margin-top:2rem;margin-bottom:0.25rem">{{ t.sup_boosts_h3 }}</h3>
|
<h3 style="text-align:center;font-size:1rem;margin-top:2rem;margin-bottom:0.25rem">{{ t.sup_boosts_h3 }}</h3>
|
||||||
<p style="text-align:center;color:#64748B;font-size:0.8125rem;margin-bottom:1rem">{{ t.sup_boosts_sub }}</p>
|
<p style="text-align:center;color:#64748B;font-size:0.8125rem;margin-bottom:1rem">{{ t.sup_boosts_sub }}</p>
|
||||||
@@ -543,7 +579,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="boost-card">
|
<div class="boost-card">
|
||||||
<strong>{{ t.sup_boost_color }}</strong>
|
<strong>{{ t.sup_boost_color }}</strong>
|
||||||
<span class="boost-price">€19/mo</span>
|
<span class="boost-price">€59/mo</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -566,7 +602,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ t.sup_cmp_row1 }}</td>
|
<td>{{ t.sup_cmp_row1 }}</td>
|
||||||
<td class="col-us">€1,799/yr</td>
|
<td class="col-us">€1,799/yr <small style="font-weight:400;font-size:0.6875rem">(yearly plan)</small></td>
|
||||||
<td>€10,000+/event</td>
|
<td>€10,000+/event</td>
|
||||||
<td>€5,000+/yr*</td>
|
<td>€5,000+/yr*</td>
|
||||||
<td>€600/yr</td>
|
<td>€600/yr</td>
|
||||||
@@ -612,22 +648,6 @@
|
|||||||
<p class="comparison-footnote">{{ t.sup_cmp_footnote }}</p>
|
<p class="comparison-footnote">{{ t.sup_cmp_footnote }}</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<!-- Social proof -->
|
|
||||||
<section class="sup-section">
|
|
||||||
<h2>{{ t.sup_proof_h2 }}</h2>
|
|
||||||
<p class="sub">{{ calc_requests }}+ {{ t.sup_proof_stat1 }} · {{ total_suppliers }}+ {{ t.sup_proof_stat2 }} · {{ total_countries }} {{ t.sup_proof_stat3 }}</p>
|
|
||||||
<div class="sup-proof-grid">
|
|
||||||
<div class="sup-proof-card">
|
|
||||||
<blockquote>“{{ t.sup_proof_q1 }}”</blockquote>
|
|
||||||
<cite>{{ t.sup_proof_cite1 }}</cite>
|
|
||||||
</div>
|
|
||||||
<div class="sup-proof-card">
|
|
||||||
<blockquote>“{{ t.sup_proof_q2 }}”</blockquote>
|
|
||||||
<cite>{{ t.sup_proof_cite2 }}</cite>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<!-- FAQ -->
|
<!-- FAQ -->
|
||||||
<section class="sup-section">
|
<section class="sup-section">
|
||||||
<h2>{{ t.sup_faq_h2 }}</h2>
|
<h2>{{ t.sup_faq_h2 }}</h2>
|
||||||
@@ -679,7 +699,7 @@
|
|||||||
<section class="sup-cta">
|
<section class="sup-cta">
|
||||||
<h2>{{ t.sup_cta_h2 }}</h2>
|
<h2>{{ t.sup_cta_h2 }}</h2>
|
||||||
<p>{{ t.sup_cta_p }}</p>
|
<p>{{ t.sup_cta_p }}</p>
|
||||||
<a href="#pricing" class="btn">{{ t.sup_hero_cta }}</a>
|
<a href="{{ url_for('suppliers.signup') }}" class="btn">{{ t.sup_cta_btn }}</a>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|||||||
@@ -44,23 +44,15 @@ if not PADDLE_API_KEY:
|
|||||||
# Maps our internal key -> product name in Paddle.
|
# Maps our internal key -> product name in Paddle.
|
||||||
# The name is used to match existing products on sync.
|
# The name is used to match existing products on sync.
|
||||||
PRODUCTS = [
|
PRODUCTS = [
|
||||||
# Subscriptions — Basic tier (new)
|
# NOTE: Basic tier is free — no Paddle subscription product needed.
|
||||||
{
|
# Suppliers select Basic during signup without a payment flow.
|
||||||
"key": "supplier_basic_monthly",
|
# These entries are kept as dead/legacy references only; do not create them.
|
||||||
"name": "Supplier Basic (Monthly)",
|
# {
|
||||||
"price": 3900,
|
# "key": "supplier_basic_monthly",
|
||||||
"currency": CurrencyCode.EUR,
|
# "name": "Supplier Basic (Monthly)",
|
||||||
"interval": "month",
|
# "price": 0,
|
||||||
"billing_type": "subscription",
|
# ...
|
||||||
},
|
# },
|
||||||
{
|
|
||||||
"key": "supplier_basic_yearly",
|
|
||||||
"name": "Supplier Basic (Yearly)",
|
|
||||||
"price": 34900,
|
|
||||||
"currency": CurrencyCode.EUR,
|
|
||||||
"interval": "year",
|
|
||||||
"billing_type": "subscription",
|
|
||||||
},
|
|
||||||
# Subscriptions — Growth tier (existing monthly + new yearly)
|
# Subscriptions — Growth tier (existing monthly + new yearly)
|
||||||
{
|
{
|
||||||
"key": "supplier_growth",
|
"key": "supplier_growth",
|
||||||
@@ -123,7 +115,7 @@ PRODUCTS = [
|
|||||||
{
|
{
|
||||||
"key": "boost_card_color",
|
"key": "boost_card_color",
|
||||||
"name": "Boost: Custom Card Color",
|
"name": "Boost: Custom Card Color",
|
||||||
"price": 1900,
|
"price": 5900,
|
||||||
"currency": CurrencyCode.EUR,
|
"currency": CurrencyCode.EUR,
|
||||||
"interval": "month",
|
"interval": "month",
|
||||||
"billing_type": "subscription",
|
"billing_type": "subscription",
|
||||||
@@ -176,7 +168,7 @@ PRODUCTS = [
|
|||||||
{
|
{
|
||||||
"key": "business_plan",
|
"key": "business_plan",
|
||||||
"name": "Padel Business Plan (PDF)",
|
"name": "Padel Business Plan (PDF)",
|
||||||
"price": 9900,
|
"price": 14900,
|
||||||
"currency": CurrencyCode.EUR,
|
"currency": CurrencyCode.EUR,
|
||||||
"billing_type": "one_time",
|
"billing_type": "one_time",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ bp = Blueprint(
|
|||||||
PLAN_FEATURES = {
|
PLAN_FEATURES = {
|
||||||
"supplier_basic": {
|
"supplier_basic": {
|
||||||
"name": "Basic",
|
"name": "Basic",
|
||||||
"monthly_price": 39,
|
"monthly_price": 0,
|
||||||
"yearly_price": 349,
|
"yearly_price": 0,
|
||||||
"yearly_monthly_equivalent": 29,
|
"yearly_monthly_equivalent": 0,
|
||||||
"monthly_credits": 0,
|
"monthly_credits": 0,
|
||||||
"paddle_key_monthly": "supplier_basic_monthly",
|
"paddle_key_monthly": "supplier_basic_monthly",
|
||||||
"paddle_key_yearly": "supplier_basic_yearly",
|
"paddle_key_yearly": "supplier_basic_yearly",
|
||||||
@@ -112,7 +112,7 @@ BOOST_OPTIONS = [
|
|||||||
"key": "boost_card_color",
|
"key": "boost_card_color",
|
||||||
"type": "card_color",
|
"type": "card_color",
|
||||||
"name_key": "sd_boost_card_color_name",
|
"name_key": "sd_boost_card_color_name",
|
||||||
"price": 19,
|
"price": 59,
|
||||||
"desc_key": "sd_boost_card_color_desc",
|
"desc_key": "sd_boost_card_color_desc",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@@ -535,12 +535,15 @@ async def _get_lead_feed_data(supplier, country="", heat="", timeline="", q="",
|
|||||||
|
|
||||||
leads = await fetch_all(
|
leads = await fetch_all(
|
||||||
f"""SELECT lr.*,
|
f"""SELECT lr.*,
|
||||||
EXISTS(SELECT 1 FROM lead_forwards lf WHERE lf.lead_id = lr.id AND lf.supplier_id = ?) as is_unlocked,
|
(SELECT lf.id FROM lead_forwards lf WHERE lf.lead_id = lr.id AND lf.supplier_id = ? LIMIT 1) as forward_id,
|
||||||
|
(SELECT lf.created_at FROM lead_forwards lf WHERE lf.lead_id = lr.id AND lf.supplier_id = ? LIMIT 1) as forward_created_at,
|
||||||
|
(SELECT lf.guarantee_claimed_at FROM lead_forwards lf WHERE lf.lead_id = lr.id AND lf.supplier_id = ? LIMIT 1) as guarantee_claimed_at,
|
||||||
|
(SELECT lf.id FROM lead_forwards lf WHERE lf.lead_id = lr.id AND lf.supplier_id = ? LIMIT 1) IS NOT NULL as is_unlocked,
|
||||||
(SELECT COUNT(*) FROM lead_forwards lf2 WHERE lf2.lead_id = lr.id) as bidder_count
|
(SELECT COUNT(*) FROM lead_forwards lf2 WHERE lf2.lead_id = lr.id) as bidder_count
|
||||||
FROM lead_requests lr
|
FROM lead_requests lr
|
||||||
WHERE {where}
|
WHERE {where}
|
||||||
ORDER BY lr.created_at DESC LIMIT ?""",
|
ORDER BY lr.created_at DESC LIMIT ?""",
|
||||||
(supplier["id"], *params),
|
(supplier["id"], supplier["id"], supplier["id"], supplier["id"], *params),
|
||||||
)
|
)
|
||||||
|
|
||||||
countries = await fetch_all(
|
countries = await fetch_all(
|
||||||
@@ -643,6 +646,57 @@ async def unlock_lead(token: str):
|
|||||||
supplier=updated_supplier,
|
supplier=updated_supplier,
|
||||||
credit_cost=result["credit_cost"],
|
credit_cost=result["credit_cost"],
|
||||||
scenario_id=scenario_id,
|
scenario_id=scenario_id,
|
||||||
|
forward_id=result["forward_id"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route("/leads/<int:forward_id>/guarantee-claim", methods=["POST"])
|
||||||
|
@_lead_tier_required
|
||||||
|
@csrf_protect
|
||||||
|
async def guarantee_claim(forward_id: int):
|
||||||
|
"""Claim lead-back guarantee: return credits for a non-responding lead.
|
||||||
|
|
||||||
|
Validates the 3–30 day window and contact method, then calls
|
||||||
|
credits.refund_lead_guarantee(). Returns an updated lead card partial.
|
||||||
|
"""
|
||||||
|
from ..credits import GuaranteeAlreadyClaimed, GuaranteeWindowClosed, refund_lead_guarantee
|
||||||
|
|
||||||
|
t = get_translations(g.get("lang") or "en")
|
||||||
|
supplier = g.supplier
|
||||||
|
|
||||||
|
form = await request.form
|
||||||
|
contact_method = form.get("contact_method", "")
|
||||||
|
if contact_method not in ("email", "phone", "both"):
|
||||||
|
return t["sd_guarantee_window_error"], 400
|
||||||
|
|
||||||
|
try:
|
||||||
|
await refund_lead_guarantee(
|
||||||
|
supplier["id"], forward_id, contact_method
|
||||||
|
)
|
||||||
|
except GuaranteeAlreadyClaimed:
|
||||||
|
return t["sd_guarantee_already_claimed"], 409
|
||||||
|
except GuaranteeWindowClosed:
|
||||||
|
return t["sd_guarantee_window_error"], 409
|
||||||
|
except AssertionError:
|
||||||
|
return "Lead not found.", 404
|
||||||
|
|
||||||
|
updated_supplier = await fetch_one(
|
||||||
|
"SELECT * FROM suppliers WHERE id = ?", (supplier["id"],)
|
||||||
|
)
|
||||||
|
forward = await fetch_one(
|
||||||
|
"""SELECT lf.*, lr.*
|
||||||
|
FROM lead_forwards lf
|
||||||
|
JOIN lead_requests lr ON lf.lead_id = lr.id
|
||||||
|
WHERE lf.id = ?""",
|
||||||
|
(forward_id,),
|
||||||
|
)
|
||||||
|
|
||||||
|
return await render_template(
|
||||||
|
"suppliers/partials/lead_card_unlocked.html",
|
||||||
|
lead=forward,
|
||||||
|
supplier=updated_supplier,
|
||||||
|
guarantee_claimed=True,
|
||||||
|
guarantee_success_msg=t["sd_guarantee_success"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -95,6 +95,52 @@
|
|||||||
{% if credit_cost is defined %}
|
{% if credit_cost is defined %}
|
||||||
<p style="font-size:0.6875rem;color:#94A3B8;margin-top:0.5rem;text-align:center">{{ credit_cost }} {{ t.sd_unlocked_credits_used }} · {{ supplier.credit_balance }} {{ t.sd_unlocked_remaining }}</p>
|
<p style="font-size:0.6875rem;color:#94A3B8;margin-top:0.5rem;text-align:center">{{ credit_cost }} {{ t.sd_unlocked_credits_used }} · {{ supplier.credit_balance }} {{ t.sd_unlocked_remaining }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{# --- Lead-Back Guarantee button ---
|
||||||
|
Show after 3 days, hide after 30 days, hide if already claimed.
|
||||||
|
forward_id comes from routes (unlock response) or from lead.forward_id (dashboard feed).
|
||||||
|
#}
|
||||||
|
{% set _fid = forward_id if forward_id is defined else lead.get('forward_id') %}
|
||||||
|
{% set _claimed_at = (guarantee_claimed_at if guarantee_claimed_at is defined else lead.get('guarantee_claimed_at')) %}
|
||||||
|
{% set _forward_created = lead.get('forward_created_at') or lead.get('created_at') %}
|
||||||
|
|
||||||
|
{% if guarantee_claimed %}
|
||||||
|
<p style="font-size:0.8125rem;color:#16A34A;font-weight:600;margin-top:0.75rem;text-align:center">
|
||||||
|
✓ {{ guarantee_success_msg or t.sd_guarantee_success }}
|
||||||
|
</p>
|
||||||
|
{% elif _fid and not _claimed_at %}
|
||||||
|
{# JS calculates age client-side to avoid server-side timezone math in template #}
|
||||||
|
<div id="guarantee-btn-{{ _fid }}" style="margin-top:0.75rem">
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
var fwd = "{{ _fid }}";
|
||||||
|
var createdAt = "{{ _forward_created }}";
|
||||||
|
if (!createdAt) return;
|
||||||
|
var ageDays = (Date.now() - new Date(createdAt).getTime()) / 86400000;
|
||||||
|
if (ageDays < 3 || ageDays > 30) return;
|
||||||
|
var container = document.getElementById("guarantee-btn-" + fwd);
|
||||||
|
if (!container) return;
|
||||||
|
container.innerHTML = '<details style="border:1px solid #D1FAE5;border-radius:10px;padding:0.75rem;">' +
|
||||||
|
'<summary style="cursor:pointer;font-size:0.8125rem;color:#16A34A;font-weight:600;list-style:none">' +
|
||||||
|
'🛡️ {{ t.sd_guarantee_btn | e }}</summary>' +
|
||||||
|
'<form hx-post="{{ url_for("suppliers.guarantee_claim", forward_id=0) }}'.replace("/0/", "/" + fwd + "/") +
|
||||||
|
'" hx-target="#lead-card-' + (typeof lead_card_id !== "undefined" ? lead_card_id : fwd) + '" hx-swap="innerHTML"' +
|
||||||
|
' style="margin-top:0.75rem">' +
|
||||||
|
'<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">' +
|
||||||
|
'<p style="font-size:0.8125rem;color:#475569;margin:0 0 0.5rem">{{ t.sd_guarantee_contact_label | e }}</p>' +
|
||||||
|
'<div style="display:flex;flex-direction:column;gap:6px;margin-bottom:0.75rem">' +
|
||||||
|
'<label style="font-size:0.8125rem"><input type="radio" name="contact_method" value="email" required> {{ t.sd_guarantee_contact_email | e }}</label>' +
|
||||||
|
'<label style="font-size:0.8125rem"><input type="radio" name="contact_method" value="phone"> {{ t.sd_guarantee_contact_phone | e }}</label>' +
|
||||||
|
'<label style="font-size:0.8125rem"><input type="radio" name="contact_method" value="both"> {{ t.sd_guarantee_contact_both | e }}</label>' +
|
||||||
|
'</div>' +
|
||||||
|
'<button type="submit" style="width:100%;padding:8px;font-size:0.8125rem;font-weight:600;background:#16A34A;color:white;border:none;border-radius:8px;cursor:pointer;font-family:inherit">{{ t.sd_guarantee_submit | e }}</button>' +
|
||||||
|
'</form></details>';
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
{% elif _claimed_at %}
|
||||||
|
<p style="font-size:0.75rem;color:#94A3B8;margin-top:0.5rem;text-align:center">{{ t.sd_guarantee_already_claimed }}</p>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if credit_cost is defined %}
|
{% if credit_cost is defined %}
|
||||||
|
|||||||
@@ -64,12 +64,22 @@
|
|||||||
{% if key == 'supplier_growth' %}<div class="s-plan-card__popular">{{ t.sup_step1_popular }}</div>{% endif %}
|
{% if key == 'supplier_growth' %}<div class="s-plan-card__popular">{{ t.sup_step1_popular }}</div>{% endif %}
|
||||||
<h3>{{ plan.name }}</h3>
|
<h3>{{ plan.name }}</h3>
|
||||||
<div class="price-yearly">
|
<div class="price-yearly">
|
||||||
|
{% if plan.yearly_price == 0 %}
|
||||||
|
<div class="price">{{ t.sup_basic_free_label }}</div>
|
||||||
|
<div style="font-size:0.6875rem;color:#16A34A;margin-top:2px">{{ t.sup_step1_free_forever }}</div>
|
||||||
|
{% else %}
|
||||||
<div class="price">€{{ plan.yearly_monthly_equivalent }} <span>/mo</span></div>
|
<div class="price">€{{ plan.yearly_monthly_equivalent }} <span>/mo</span></div>
|
||||||
<div style="font-size:0.6875rem;color:#94A3B8;margin-top:2px">{{ t.sup_step1_billed_yearly | tformat(price=plan.yearly_price) }}</div>
|
<div style="font-size:0.6875rem;color:#94A3B8;margin-top:2px">{{ t.sup_step1_billed_yearly | tformat(price=plan.yearly_price) }}</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="price-monthly">
|
<div class="price-monthly">
|
||||||
|
{% if plan.monthly_price == 0 %}
|
||||||
|
<div class="price">{{ t.sup_basic_free_label }}</div>
|
||||||
|
<div style="font-size:0.6875rem;color:#16A34A;margin-top:2px">{{ t.sup_step1_free_forever }}</div>
|
||||||
|
{% else %}
|
||||||
<div class="price">€{{ plan.monthly_price }} <span>/mo</span></div>
|
<div class="price">€{{ plan.monthly_price }} <span>/mo</span></div>
|
||||||
<div style="font-size:0.6875rem;color:#94A3B8;margin-top:2px">{{ t.sup_step1_billed_monthly }}</div>
|
<div style="font-size:0.6875rem;color:#94A3B8;margin-top:2px">{{ t.sup_step1_billed_monthly }}</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
{% for key in plan.feature_keys %}
|
{% for key in plan.feature_keys %}
|
||||||
|
|||||||
Reference in New Issue
Block a user