323 lines
12 KiB
Python
323 lines
12 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Felt Pricing Model v2
|
||
|
||
Key insights from Mikkel:
|
||
- Offline (Leaf hardware) is a separate value from Pro features
|
||
- Display nodes: cost-recovery only, no recurring
|
||
- Casinos need tiered pricing (independent, small chain, large)
|
||
- €25/mo for offline capability alone
|
||
- €75/mo additional for full Pro features (cash, dealers, loyalty etc)
|
||
"""
|
||
|
||
EUR_TO_DKK = 7.45
|
||
|
||
print("=" * 70)
|
||
print("FELT PRICING MODEL v2")
|
||
print("=" * 70)
|
||
|
||
print("""
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ PRICING PHILOSOPHY │
|
||
│ │
|
||
│ FREE → Hook them. Full tournament engine in cloud. │
|
||
│ OFFLINE ADD-ON → They need reliability. Leaf hardware. │
|
||
│ PRO → They need operations. Cash, dealers, loyalty. │
|
||
│ CASINO TIERS → They need scale. Enterprise features. │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
""")
|
||
|
||
# ================================================================
|
||
# TIER DEFINITIONS
|
||
# ================================================================
|
||
print("=" * 70)
|
||
print("VENUE TIERS")
|
||
print("=" * 70)
|
||
|
||
tiers = [
|
||
{
|
||
"name": "Free",
|
||
"price": 0,
|
||
"target": "Enthusiast clubs, small bars, anyone starting",
|
||
"includes": [
|
||
"Full tournament engine (unlimited)",
|
||
"Player mobile access",
|
||
"Signage editor with AI assist",
|
||
"Leagues & seasons",
|
||
"Player database & history",
|
||
"Regional tournament participation",
|
||
],
|
||
"requires": "Internet connection (virtual Leaf in our cloud)",
|
||
},
|
||
{
|
||
"name": "Offline",
|
||
"price": 25,
|
||
"target": "Any venue that needs reliability / wants displays",
|
||
"includes": [
|
||
"Everything in Free",
|
||
"Dedicated Leaf node (hardware purchase required)",
|
||
"Full offline operation — runs without internet",
|
||
"Wireless display nodes (purchase separately)",
|
||
"Custom domain support",
|
||
"Remote admin access via Netbird",
|
||
],
|
||
"requires": "Leaf hardware (~€120) + display nodes (~€30 each)",
|
||
},
|
||
{
|
||
"name": "Pro",
|
||
"price": 100, # 25 offline + 75 pro features
|
||
"target": "Serious venues wanting the full platform",
|
||
"includes": [
|
||
"Everything in Offline",
|
||
"Cash game management (waitlists, sessions, rake)",
|
||
"Dealer scheduling & shift management",
|
||
"Player loyalty system",
|
||
"Membership management",
|
||
"Advanced analytics & reporting",
|
||
"TDD data import wizard",
|
||
"Priority support",
|
||
],
|
||
"requires": "Leaf hardware (included free with annual plan)",
|
||
},
|
||
]
|
||
|
||
for t in tiers:
|
||
print(f"\n {'━' * 60}")
|
||
if t['price'] == 0:
|
||
print(f" {t['name'].upper():30s} €0/mo")
|
||
elif t['name'] == 'Pro':
|
||
print(f" {t['name'].upper():30s} €{t['price']}/mo (€25 offline + €75 pro)")
|
||
else:
|
||
print(f" {t['name'].upper():30s} €{t['price']}/mo")
|
||
print(f" Target: {t['target']}")
|
||
print(f" Requires: {t['requires']}")
|
||
print(f" Includes:")
|
||
for item in t['includes']:
|
||
print(f" ✓ {item}")
|
||
|
||
# ================================================================
|
||
# CASINO / ENTERPRISE TIERS
|
||
# ================================================================
|
||
print(f"\n\n{'=' * 70}")
|
||
print("CASINO & ENTERPRISE TIERS")
|
||
print("=" * 70)
|
||
|
||
casino_tiers = [
|
||
{
|
||
"name": "Casino Starter",
|
||
"price": "€249/mo",
|
||
"target": "Independent casino with 1 poker room (5-15 tables)",
|
||
"tables": "Up to 15 tables",
|
||
"includes": [
|
||
"Everything in Pro",
|
||
"Multi-room support (tournament room + cash room)",
|
||
"Floor manager role with restricted permissions",
|
||
"Shift reporting & payroll export",
|
||
"Compliance-ready audit trail",
|
||
"8hr response SLA",
|
||
],
|
||
},
|
||
{
|
||
"name": "Casino Pro",
|
||
"price": "€499/mo per property",
|
||
"target": "Small casino chain (2-5 properties, 15-40 tables each)",
|
||
"tables": "Up to 40 tables per property",
|
||
"includes": [
|
||
"Everything in Casino Starter",
|
||
"Multi-property dashboard",
|
||
"Cross-property player tracking",
|
||
"Cross-property loyalty (play at A, redeem at B)",
|
||
"Centralized dealer pool management",
|
||
"API access for POS/CMS integration",
|
||
"Custom branding & white-label displays",
|
||
"4hr response SLA",
|
||
"Dedicated onboarding",
|
||
],
|
||
},
|
||
{
|
||
"name": "Casino Enterprise",
|
||
"price": "Custom (€999+/mo)",
|
||
"target": "Large operators (5+ properties, 40+ tables each)",
|
||
"tables": "Unlimited",
|
||
"includes": [
|
||
"Everything in Casino Pro",
|
||
"Unlimited properties",
|
||
"Fleet management (remote Leaf provisioning & updates)",
|
||
"Regional analytics & benchmarking",
|
||
"Casino management system API integration (Bally's, IGT, L&W)",
|
||
"Custom feature development",
|
||
"White-label everything (player app, venue pages)",
|
||
"2hr response SLA + dedicated account manager",
|
||
"On-site installation support",
|
||
],
|
||
},
|
||
]
|
||
|
||
for t in casino_tiers:
|
||
print(f"\n {'━' * 60}")
|
||
print(f" {t['name'].upper():30s} {t['price']}")
|
||
print(f" Target: {t['target']}")
|
||
print(f" Tables: {t['tables']}")
|
||
print(f" Includes:")
|
||
for item in t['includes']:
|
||
print(f" ✓ {item}")
|
||
|
||
# ================================================================
|
||
# HARDWARE PRICING
|
||
# ================================================================
|
||
print(f"\n\n{'=' * 70}")
|
||
print("HARDWARE PRICING (cost-recovery, no margin on displays)")
|
||
print("=" * 70)
|
||
|
||
hardware = [
|
||
("Leaf Node", 120, 120, "Venue brain, NVMe, locked down"),
|
||
("Display Node", 25, 30, "Pi Zero W2 + case + power, per unit"),
|
||
("Display Node (4-pack)", 90, 110, "4× display nodes, slight discount"),
|
||
]
|
||
|
||
print(f"\n {'Device':<25s} {'Our Cost':>10s} {'Sell Price':>12s} Notes")
|
||
print(f" {'─' * 65}")
|
||
for name, cost, price, notes in hardware:
|
||
margin = price - cost
|
||
print(f" {name:<25s} €{cost:>7} €{price:>9} {notes}")
|
||
|
||
print(f"""
|
||
|
||
Leaf hardware options:
|
||
• Buy outright: €120
|
||
• Free with 12-month Offline or Pro annual plan
|
||
• Casino tiers: hardware included in contract
|
||
|
||
Display nodes:
|
||
• Priced at cost + shipping
|
||
• No recurring fee — they're dumb render devices
|
||
• Venues buy as many as they need (most want 2-6)
|
||
""")
|
||
|
||
# ================================================================
|
||
# REVENUE MODEL WITH NEW PRICING
|
||
# ================================================================
|
||
print(f"\n{'=' * 70}")
|
||
print("REVISED FINANCIAL MODEL")
|
||
print("=" * 70)
|
||
|
||
# Infrastructure cost (from capacity analysis)
|
||
INFRA_PER_FREE = 0.45 # Actual cost per virtual Leaf (from capacity model)
|
||
INFRA_PER_PAID = 0.15 # Pro/Offline on Core (just sync)
|
||
INFRA_BASE = 100 # One Hetzner server
|
||
|
||
TARGET_ANNUAL_DKK = 60000 * 2 * 12 # 2× 60k DKK/mo
|
||
TARGET_ANNUAL_EUR = TARGET_ANNUAL_DKK / EUR_TO_DKK
|
||
|
||
print(f"\n Target: 2× {60000:,} DKK/mo = {TARGET_ANNUAL_DKK:,} DKK/yr = ~€{TARGET_ANNUAL_EUR:,.0f}/yr")
|
||
print(f" Infrastructure: €{INFRA_BASE}/mo base (real cost from capacity analysis)")
|
||
|
||
scenarios = [
|
||
# (name, free, offline, pro, casino_starter, casino_pro, casino_enterprise, casino_ent_price)
|
||
("Year 1: Denmark", 80, 8, 5, 0, 0, 0, 0),
|
||
("Year 2: Nordics + first casino", 250, 20, 15, 2, 0, 0, 0),
|
||
("Year 3: N. Europe + UK", 500, 40, 35, 5, 1, 0, 0),
|
||
("Year 4: International", 800, 60, 60, 8, 3, 1, 1499),
|
||
]
|
||
|
||
print(f"\n {'─' * 66}")
|
||
|
||
for name, free, offline, pro, cs, cp, ce, ce_price in scenarios:
|
||
# Revenue
|
||
rev_offline = offline * 25
|
||
rev_pro = pro * 100
|
||
rev_cs = cs * 249
|
||
rev_cp = cp * 499
|
||
rev_ce = ce * (ce_price if ce_price else 999)
|
||
total_rev = rev_offline + rev_pro + rev_cs + rev_cp + rev_ce
|
||
|
||
# Costs
|
||
cost_free = free * INFRA_PER_FREE
|
||
cost_paid = (offline + pro + cs + cp + ce) * INFRA_PER_PAID
|
||
total_cost = INFRA_BASE + cost_free + cost_paid
|
||
|
||
# Add second server if >400 free venues
|
||
if free > 400:
|
||
total_cost += 100
|
||
|
||
net = total_rev - total_cost
|
||
annual_net = net * 12
|
||
dkk = annual_net * EUR_TO_DKK
|
||
per_person = dkk / 2 / 12
|
||
pct = per_person / 60000 * 100
|
||
|
||
total_venues = free + offline + pro + cs + cp + ce
|
||
total_paying = offline + pro + cs + cp + ce
|
||
|
||
print(f"\n {name}")
|
||
print(f" Total venues: {total_venues} ({free} free, {total_paying} paying)")
|
||
detail = f" Paying: {offline} offline, {pro} pro"
|
||
if cs: detail += f", {cs} casino-start"
|
||
if cp: detail += f", {cp} casino-pro"
|
||
if ce: detail += f", {ce} casino-ent"
|
||
print(detail)
|
||
print(f" Revenue: €{total_rev:>7,.0f}/mo Costs: €{total_cost:>6,.0f}/mo Net: €{net:>7,.0f}/mo")
|
||
print(f" Annual: €{annual_net:>8,.0f} → {dkk:>10,.0f} DKK/yr → {per_person:>7,.0f} DKK/mo per person", end="")
|
||
if pct >= 95:
|
||
print(f" ✅")
|
||
else:
|
||
print(f" ({pct:.0f}%)")
|
||
|
||
# ================================================================
|
||
# THE CASINO MULTIPLIER EFFECT
|
||
# ================================================================
|
||
print(f"\n\n{'=' * 70}")
|
||
print("THE CASINO MULTIPLIER")
|
||
print("=" * 70)
|
||
print(f"""
|
||
One Casino Pro deal (5 properties × €499/mo) = €2,495/mo = €29,940/yr
|
||
|
||
That single deal is worth:
|
||
• 100 Offline venues (€25/mo each)
|
||
• 25 Pro venues (€100/mo each)
|
||
• 10 Casino Starter venues (€249/mo each)
|
||
|
||
Two Casino Pro deals + modest venue growth:
|
||
2× Casino Pro: €4,990/mo
|
||
30 Pro venues: €3,000/mo
|
||
20 Offline venues: €500/mo
|
||
────────────────────────────
|
||
Total: €8,490/mo → €101,880/yr → ~759,000 DKK/yr
|
||
Per person: ~31,600 DKK/mo (53% of target)
|
||
|
||
Add ONE Casino Enterprise deal and you're close to target
|
||
with under 100 total paying venues.
|
||
|
||
The path:
|
||
1. Saturate Denmark free tier (prove the product)
|
||
2. Convert enthusiast venues to Offline/Pro (prove revenue)
|
||
3. Use those as case studies to land first casino deal
|
||
4. Casino revenue subsidizes everything else
|
||
""")
|
||
|
||
# ================================================================
|
||
# PRICING COMPARISON
|
||
# ================================================================
|
||
print(f"{'=' * 70}")
|
||
print("WHAT VENUES PAY TODAY (for comparison)")
|
||
print(f"{'=' * 70}")
|
||
print(f"""
|
||
TDD License: $130 one-time (but stuck on Windows PC)
|
||
BravoPokerLive: $200-500/mo (US, waitlist-focused)
|
||
Casino CMS poker module: $2,000-10,000/mo (Bally's, IGT, L&W)
|
||
Digital signage software: €30-80/mo (separate subscription)
|
||
Generic waitlist apps: €20-50/mo
|
||
Spreadsheets: Free (but hours of manual work)
|
||
|
||
Felt Offline at €25/mo replaces TDD + adds wireless displays +
|
||
mobile + signage. That's a no-brainer for any venue currently
|
||
on TDD who spends any money on their operation.
|
||
|
||
Felt Pro at €100/mo replaces TDD + digital signage + waitlist
|
||
app + spreadsheets + manual comp tracking. Five tools for the
|
||
price of one.
|
||
|
||
Casino Starter at €249/mo is 1/10th what they pay for their
|
||
current casino management poker module — and it's better.
|
||
""")
|
||
|