|
|
|
|
|
|
|
import gradio as gr |
|
import plotly.graph_objects as go |
|
import plotly.express as px |
|
from plotly.subplots import make_subplots |
|
import numpy as np |
|
from dataclasses import dataclass |
|
from typing import Dict, List, Tuple |
|
|
|
|
|
|
|
|
|
|
|
OPTIMIZED_CSS = """ |
|
:root { |
|
--primary: #2563eb; |
|
--primary-dark: #1d4ed8; |
|
--success: #10b981; |
|
--warning: #f59e0b; |
|
--error: #ef4444; |
|
--surface: #ffffff; |
|
--text: #1e293b; |
|
--text-muted: #64748b; |
|
--border: #e2e8f0; |
|
--radius: 12px; |
|
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); |
|
--gradient: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%); |
|
} |
|
|
|
.gradio-container { |
|
max-width: 1200px !important; |
|
margin: 0 auto !important; |
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important; |
|
} |
|
|
|
.hero-section { |
|
background: var(--gradient); |
|
color: white; |
|
padding: 3rem 2rem; |
|
border-radius: var(--radius); |
|
margin-bottom: 2rem; |
|
text-align: center; |
|
} |
|
|
|
.hero-title { |
|
font-size: 2.5rem; |
|
font-weight: 800; |
|
margin-bottom: 1rem; |
|
} |
|
|
|
.hero-subtitle { |
|
font-size: 1.2rem; |
|
opacity: 0.9; |
|
margin-bottom: 2rem; |
|
} |
|
|
|
.social-proof { |
|
background: rgba(255,255,255,0.1); |
|
padding: 1rem; |
|
border-radius: 8px; |
|
display: inline-block; |
|
} |
|
|
|
.profile-grid { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); |
|
gap: 1rem; |
|
margin: 2rem 0; |
|
} |
|
|
|
.profile-card { |
|
background: var(--surface); |
|
border: 2px solid var(--border); |
|
border-radius: var(--radius); |
|
padding: 1.5rem; |
|
text-align: center; |
|
cursor: pointer; |
|
transition: all 0.3s ease; |
|
position: relative; |
|
} |
|
|
|
.profile-card:hover, .profile-card.selected { |
|
border-color: var(--primary); |
|
transform: translateY(-2px); |
|
box-shadow: var(--shadow); |
|
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); |
|
} |
|
|
|
.profile-icon { |
|
font-size: 2.5rem; |
|
margin-bottom: 1rem; |
|
} |
|
|
|
.profile-name { |
|
font-weight: 600; |
|
font-size: 1rem; |
|
margin-bottom: 0.5rem; |
|
} |
|
|
|
.kpi-grid { |
|
display: grid; |
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); |
|
gap: 1.5rem; |
|
margin: 2rem 0; |
|
} |
|
|
|
.kpi-card { |
|
background: var(--surface); |
|
border-radius: var(--radius); |
|
padding: 2rem; |
|
text-align: center; |
|
box-shadow: var(--shadow); |
|
border-top: 4px solid var(--primary); |
|
} |
|
|
|
.kpi-label { |
|
font-size: 0.9rem; |
|
color: var(--text-muted); |
|
margin-bottom: 0.5rem; |
|
text-transform: uppercase; |
|
font-weight: 600; |
|
letter-spacing: 0.5px; |
|
} |
|
|
|
.kpi-value { |
|
font-size: 2.5rem; |
|
font-weight: 800; |
|
color: var(--primary); |
|
margin-bottom: 0.5rem; |
|
} |
|
|
|
.kpi-note { |
|
font-size: 0.85rem; |
|
color: var(--text-muted); |
|
} |
|
|
|
.kpi-card.success { border-top-color: var(--success); } |
|
.kpi-card.success .kpi-value { color: var(--success); } |
|
|
|
.kpi-card.warning { border-top-color: var(--warning); } |
|
.kpi-card.warning .kpi-value { color: var(--warning); } |
|
|
|
.kpi-card.error { border-top-color: var(--error); } |
|
.kpi-card.error .kpi-value { color: var(--error); } |
|
|
|
.insight-card { |
|
background: var(--surface); |
|
border-left: 4px solid var(--primary); |
|
border-radius: var(--radius); |
|
padding: 1.5rem; |
|
margin: 1rem 0; |
|
box-shadow: var(--shadow); |
|
} |
|
|
|
.insight-title { |
|
font-weight: 600; |
|
color: var(--primary); |
|
margin-bottom: 0.5rem; |
|
} |
|
|
|
.offer-modal { |
|
background: var(--surface); |
|
border: 2px solid var(--primary); |
|
border-radius: var(--radius); |
|
padding: 2rem; |
|
margin: 2rem 0; |
|
text-align: center; |
|
position: relative; |
|
overflow: hidden; |
|
} |
|
|
|
.offer-modal::before { |
|
content: ''; |
|
position: absolute; |
|
top: -50%; |
|
left: -50%; |
|
width: 200%; |
|
height: 200%; |
|
background: conic-gradient(from 0deg, transparent, var(--primary), transparent); |
|
animation: rotate 4s linear infinite; |
|
z-index: -1; |
|
} |
|
|
|
@keyframes rotate { |
|
from { transform: rotate(0deg); } |
|
to { transform: rotate(360deg); } |
|
} |
|
|
|
.value-badge { |
|
background: var(--success); |
|
color: white; |
|
padding: 0.5rem 1rem; |
|
border-radius: 50px; |
|
display: inline-block; |
|
font-weight: 600; |
|
margin-bottom: 1rem; |
|
font-size: 0.9rem; |
|
} |
|
|
|
.cta-button { |
|
background: var(--gradient) !important; |
|
border: none !important; |
|
border-radius: var(--radius) !important; |
|
padding: 1rem 3rem !important; |
|
font-weight: 700 !important; |
|
font-size: 1.1rem !important; |
|
color: white !important; |
|
cursor: pointer !important; |
|
transition: all 0.3s ease !important; |
|
text-transform: uppercase !important; |
|
letter-spacing: 1px !important; |
|
box-shadow: 0 4px 15px rgba(37, 99, 235, 0.4) !important; |
|
} |
|
|
|
.cta-button:hover { |
|
transform: translateY(-3px) !important; |
|
box-shadow: 0 8px 25px rgba(37, 99, 235, 0.6) !important; |
|
} |
|
|
|
.comparison-table { |
|
background: var(--surface); |
|
border-radius: var(--radius); |
|
overflow: hidden; |
|
box-shadow: var(--shadow); |
|
margin: 2rem 0; |
|
} |
|
|
|
.country-highlight { |
|
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%); |
|
border-left: 4px solid var(--primary); |
|
padding: 1rem; |
|
margin: 1rem 0; |
|
border-radius: var(--radius); |
|
} |
|
|
|
@media (max-width: 768px) { |
|
.hero-title { font-size: 1.8rem; } |
|
.hero-subtitle { font-size: 1rem; } |
|
.profile-grid { grid-template-columns: 1fr 1fr; } |
|
.kpi-grid { grid-template-columns: 1fr; } |
|
} |
|
""" |
|
|
|
|
|
|
|
|
|
|
|
@dataclass |
|
class ProfileData: |
|
id: str |
|
name: str |
|
icon: str |
|
revenue: int |
|
margin: int |
|
risk_level: str |
|
growth_potential: float |
|
|
|
@dataclass |
|
class CountryData: |
|
name: str |
|
corp_tax: float |
|
pers_tax: float |
|
living_cost: int |
|
setup_cost: int |
|
growth_multiplier: float |
|
ease_score: float |
|
key_benefit: str |
|
|
|
|
|
PROFILES = { |
|
"startup": ProfileData("startup", "Tech Startup", "🚀", 50000, 20, "High", 2.8), |
|
"crypto": ProfileData("crypto", "Crypto/Web3", "₿", 80000, 35, "Very High", 3.5), |
|
"consulting": ProfileData("consulting", "Consultant", "💼", 30000, 60, "Low", 1.8), |
|
"ecommerce": ProfileData("ecommerce", "E-commerce", "🛒", 45000, 15, "Medium", 2.2) |
|
} |
|
|
|
|
|
COUNTRIES = { |
|
"UAE": CountryData("UAE (Dubai)", 0.09, 0.00, 8500, 45000, 2.4, 9.4, "0% personal tax"), |
|
"Singapore": CountryData("Singapore", 0.17, 0.22, 7200, 38000, 2.1, 9.6, "Asian gateway"), |
|
"Estonia": CountryData("Estonia", 0.20, 0.20, 2800, 8000, 1.8, 9.0, "Digital nomad visa"), |
|
"Portugal": CountryData("Portugal", 0.21, 0.48, 2200, 12000, 1.6, 7.8, "NHR tax regime"), |
|
"USA": CountryData("USA", 0.21, 0.37, 8800, 65000, 2.0, 8.4, "Largest market") |
|
} |
|
|
|
|
|
|
|
|
|
|
|
class SimpleROICalculator: |
|
@staticmethod |
|
def calculate_roi(profile: ProfileData, country: CountryData, |
|
custom_revenue: float = None, years: int = 5) -> Dict: |
|
"""Simplified ROI calculation focused on key metrics""" |
|
|
|
|
|
monthly_revenue = custom_revenue or profile.revenue |
|
|
|
|
|
current_profit = monthly_revenue * (profile.margin / 100) |
|
current_after_tax = current_profit * 0.75 * 0.85 |
|
current_net = current_after_tax - 4500 |
|
|
|
|
|
new_revenue = monthly_revenue * country.growth_multiplier * profile.growth_potential |
|
new_profit = new_revenue * (min(profile.margin + 10, 70) / 100) |
|
new_after_tax = new_profit * (1 - country.corp_tax) * (1 - country.pers_tax) |
|
new_net = new_after_tax - country.living_cost |
|
|
|
|
|
monthly_improvement = new_net - current_net |
|
annual_improvement = monthly_improvement * 12 |
|
total_benefit = annual_improvement * years |
|
setup_cost = country.setup_cost |
|
|
|
|
|
roi = ((total_benefit - setup_cost) / setup_cost) * 100 if setup_cost > 0 else 0 |
|
payback_months = setup_cost / monthly_improvement if monthly_improvement > 0 else float('inf') |
|
|
|
|
|
risk_factors = {"Low": 0.9, "Medium": 0.8, "High": 0.7, "Very High": 0.6} |
|
risk_multiplier = risk_factors.get(profile.risk_level, 0.8) |
|
adjusted_roi = roi * risk_multiplier |
|
|
|
return { |
|
"roi": roi, |
|
"adjusted_roi": adjusted_roi, |
|
"annual_savings": annual_improvement, |
|
"monthly_improvement": monthly_improvement, |
|
"payback_months": payback_months, |
|
"total_benefit": total_benefit, |
|
"setup_cost": setup_cost, |
|
"success_probability": min(90, country.ease_score * 10), |
|
"risk_level": profile.risk_level |
|
} |
|
|
|
|
|
|
|
|
|
|
|
class ChartBuilder: |
|
@staticmethod |
|
def create_comparison_chart(results: Dict, countries: List[str]) -> go.Figure: |
|
"""Simple comparison chart""" |
|
|
|
fig = make_subplots( |
|
rows=1, cols=2, |
|
subplot_titles=("ROI Comparison", "Payback Period"), |
|
specs=[[{"type": "bar"}, {"type": "bar"}]] |
|
) |
|
|
|
rois = [results[c]["adjusted_roi"] for c in countries] |
|
paybacks = [min(results[c]["payback_months"], 60) for c in countries] |
|
|
|
|
|
fig.add_trace( |
|
go.Bar(x=countries, y=rois, name="ROI (%)", |
|
marker_color=['#10b981' if r > 100 else '#f59e0b' if r > 50 else '#ef4444' for r in rois]), |
|
row=1, col=1 |
|
) |
|
|
|
|
|
fig.add_trace( |
|
go.Bar(x=countries, y=paybacks, name="Payback (months)", |
|
marker_color='#2563eb'), |
|
row=1, col=2 |
|
) |
|
|
|
fig.update_layout(height=400, showlegend=False, template="plotly_white") |
|
return fig |
|
|
|
@staticmethod |
|
def create_timeline_chart(result: Dict) -> go.Figure: |
|
"""Cash flow timeline""" |
|
|
|
months = list(range(1, 61)) |
|
monthly_cf = result["monthly_improvement"] |
|
cumulative = [-result["setup_cost"]] |
|
|
|
for month in months: |
|
cumulative.append(cumulative[-1] + monthly_cf) |
|
|
|
fig = go.Figure() |
|
|
|
|
|
fig.add_hline(y=0, line_dash="dash", line_color="red", |
|
annotation_text="Break-even point") |
|
|
|
|
|
fig.add_trace(go.Scatter( |
|
x=months, y=cumulative[1:], |
|
mode='lines+markers', |
|
name='Cumulative Cash Flow', |
|
line=dict(color='#2563eb', width=3), |
|
fill='tonexty' if any(cf > 0 for cf in cumulative[1:]) else None |
|
)) |
|
|
|
fig.update_layout( |
|
title="Cash Flow Projection Over 5 Years", |
|
xaxis_title="Months", |
|
yaxis_title="Cumulative Cash Flow (€)", |
|
template="plotly_white", |
|
height=400 |
|
) |
|
|
|
return fig |
|
|
|
|
|
|
|
|
|
|
|
def create_optimized_app(): |
|
with gr.Blocks(css=OPTIMIZED_CSS, title="VisaTier 4.0", theme=gr.themes.Soft()) as app: |
|
|
|
|
|
selected_profile = gr.State("startup") |
|
calculation_results = gr.State({}) |
|
|
|
|
|
gr.HTML(""" |
|
<div class="hero-section"> |
|
<h1 class="hero-title">Immigration ROI Calculator</h1> |
|
<p class="hero-subtitle">Make data-driven decisions about business relocation</p> |
|
<div class="social-proof"> |
|
<strong>2,100+ entrepreneurs</strong> used our calculator to optimize their relocations |
|
</div> |
|
</div> |
|
""") |
|
|
|
|
|
gr.Markdown("### Select Your Business Profile") |
|
|
|
profile_html = '<div class="profile-grid">' |
|
for pid, profile in PROFILES.items(): |
|
profile_html += f""" |
|
<div class="profile-card" onclick="selectProfile('{pid}', this)" id="card-{pid}"> |
|
<div class="profile-icon">{profile.icon}</div> |
|
<div class="profile-name">{profile.name}</div> |
|
<div style="font-size: 0.85rem; color: var(--text-muted);"> |
|
€{profile.revenue:,}/mo • {profile.margin}% margin |
|
</div> |
|
</div>""" |
|
profile_html += """ |
|
</div> |
|
<script> |
|
function selectProfile(profileId, element) { |
|
document.querySelectorAll('.profile-card').forEach(card => |
|
card.classList.remove('selected')); |
|
element.classList.add('selected'); |
|
|
|
// Update hidden dropdown |
|
const dropdown = document.querySelector('select[data-testid="dropdown"]'); |
|
if (dropdown) { |
|
dropdown.value = profileId; |
|
dropdown.dispatchEvent(new Event('change')); |
|
} |
|
} |
|
// Auto-select first profile |
|
document.addEventListener('DOMContentLoaded', () => { |
|
const firstCard = document.querySelector('#card-startup'); |
|
if (firstCard) selectProfile('startup', firstCard); |
|
}); |
|
</script>""" |
|
|
|
gr.HTML(profile_html) |
|
|
|
profile_dropdown = gr.Dropdown( |
|
choices=list(PROFILES.keys()), |
|
value="startup", |
|
visible=False |
|
) |
|
|
|
|
|
with gr.Row(): |
|
custom_revenue = gr.Number( |
|
label="Monthly Revenue (€)", |
|
value=50000, |
|
info="Leave empty to use profile default" |
|
) |
|
target_countries = gr.CheckboxGroup( |
|
choices=list(COUNTRIES.keys()), |
|
value=["UAE", "Singapore", "Estonia"], |
|
label="Countries to Compare" |
|
) |
|
|
|
|
|
calculate_btn = gr.Button( |
|
"Calculate ROI for All Countries", |
|
variant="primary", |
|
elem_classes=["cta-button"], |
|
size="lg" |
|
) |
|
|
|
|
|
results_display = gr.HTML(visible=False) |
|
comparison_chart = gr.Plot(visible=False) |
|
timeline_chart = gr.Plot(visible=False) |
|
recommendations = gr.HTML(visible=False) |
|
|
|
def calculate_all_rois(profile_id, revenue, countries): |
|
"""Calculate ROI for all selected countries""" |
|
if not countries or profile_id not in PROFILES: |
|
return [gr.update()] * 4 |
|
|
|
profile = PROFILES[profile_id] |
|
calculator = SimpleROICalculator() |
|
results = {} |
|
|
|
|
|
for country_id in countries: |
|
if country_id in COUNTRIES: |
|
country = COUNTRIES[country_id] |
|
results[country_id] = calculator.calculate_roi(profile, country, revenue) |
|
|
|
if not results: |
|
return [gr.update()] * 4 |
|
|
|
|
|
best_country = max(results.keys(), key=lambda c: results[c]["adjusted_roi"]) |
|
best_roi = results[best_country]["adjusted_roi"] |
|
|
|
|
|
kpi_html = f""" |
|
<div class="kpi-grid"> |
|
<div class="kpi-card {'success' if best_roi > 150 else 'warning' if best_roi > 75 else 'error'}"> |
|
<div class="kpi-label">Best ROI</div> |
|
<div class="kpi-value">{best_roi:.0f}%</div> |
|
<div class="kpi-note">{COUNTRIES[best_country].name}</div> |
|
</div> |
|
<div class="kpi-card"> |
|
<div class="kpi-label">Annual Savings</div> |
|
<div class="kpi-value">€{results[best_country]['annual_savings']:,.0f}</div> |
|
<div class="kpi-note">Per year after setup</div> |
|
</div> |
|
<div class="kpi-card"> |
|
<div class="kpi-label">Payback Time</div> |
|
<div class="kpi-value">{results[best_country]['payback_months']:.1f}</div> |
|
<div class="kpi-note">Months to break even</div> |
|
</div> |
|
<div class="kpi-card"> |
|
<div class="kpi-label">Setup Investment</div> |
|
<div class="kpi-value">€{results[best_country]['setup_cost']:,}</div> |
|
<div class="kpi-note">Initial investment required</div> |
|
</div> |
|
</div> |
|
""" |
|
|
|
|
|
comparison = ChartBuilder.create_comparison_chart(results, countries) |
|
timeline = ChartBuilder.create_timeline_chart(results[best_country]) |
|
|
|
|
|
country_data = COUNTRIES[best_country] |
|
rec_html = f""" |
|
<div class="country-highlight"> |
|
<h3>🏆 Recommended: {country_data.name}</h3> |
|
<p><strong>Key advantage:</strong> {country_data.key_benefit}</p> |
|
<p><strong>Why it works for {profile.name}s:</strong> |
|
{best_roi:.0f}% ROI with {results[best_country]['payback_months']:.1f} month payback period.</p> |
|
</div> |
|
|
|
<div class="offer-modal"> |
|
<div class="value-badge">Limited Time: 50% Off</div> |
|
<h3>Get Your Complete {country_data.name} Migration Plan</h3> |
|
<p style="font-size: 1.1rem; margin: 1rem 0;"> |
|
Detailed roadmap, legal requirements, tax optimization strategies |
|
</p> |
|
<div style="font-size: 1.2rem; margin: 1rem 0;"> |
|
<span style="text-decoration: line-through; color: #64748b;">€997</span> |
|
<strong style="color: #10b981; font-size: 1.5rem;"> €497</strong> |
|
</div> |
|
<button class="cta-button">Get Migration Plan Now</button> |
|
<p style="font-size: 0.8rem; color: #64748b; margin-top: 1rem;"> |
|
30-day money-back guarantee • Secure payment |
|
</p> |
|
</div> |
|
""" |
|
|
|
return ( |
|
gr.update(value=kpi_html, visible=True), |
|
gr.update(value=comparison, visible=True), |
|
gr.update(value=timeline, visible=True), |
|
gr.update(value=rec_html, visible=True) |
|
) |
|
|
|
|
|
calculate_btn.click( |
|
calculate_all_rois, |
|
inputs=[profile_dropdown, custom_revenue, target_countries], |
|
outputs=[results_display, comparison_chart, timeline_chart, recommendations] |
|
) |
|
|
|
|
|
def update_revenue(profile_id): |
|
if profile_id in PROFILES: |
|
return PROFILES[profile_id].revenue |
|
return 50000 |
|
|
|
profile_dropdown.change( |
|
update_revenue, |
|
inputs=[profile_dropdown], |
|
outputs=[custom_revenue] |
|
) |
|
|
|
|
|
gr.HTML(""" |
|
<div style="text-align: center; margin-top: 3rem; padding: 2rem; |
|
border-top: 1px solid var(--border); color: var(--text-muted);"> |
|
<p><strong>Disclaimer:</strong> Estimates for planning purposes only. |
|
Consult qualified professionals for legal and tax advice.</p> |
|
<p>© 2025 VisaTier • <a href="#" style="color: var(--primary);">Privacy</a> • |
|
<a href="#" style="color: var(--primary);">Terms</a></p> |
|
</div> |
|
""") |
|
|
|
return app |
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
app = create_optimized_app() |
|
app.launch(server_name="0.0.0.0", server_port=7860, share=False) |