LiiS Strategy Dashboard
C-Level View โ Portfolio Economics & Break-Even Analysis
""" LiiS Circular Economy Simulator - C-Level View (Draft) Simplified executive dashboard focused on portfolio-level economics. Run with: streamlit run strategy_v2.py """ import streamlit as st import plotly.graph_objects as go import pandas as pd import plotly.io as pio # โโ LiiS Brand Palette โโ COLORS = { "bg": "#f9fcf3", "surface": "#ffffff", "surface_alt": "#eef5e6", "primary": "#124b5f", "secondary": "#785964", "text": "#1a1a2e", "text_muted": "#4a5568", "text_subtle": "#718096", "border": "#d1ddc8", "positive": "#124b5f", "negative": "#a0444a", "warning": "#c87941", "info": "#4a7c9b", } CHART_COLORS = ["#124b5f", "#785964", "#2a7f62", "#c87941", "#4a7c9b", "#a0444a"] LIIS_LAYOUT = dict( paper_bgcolor=COLORS["bg"], plot_bgcolor=COLORS["surface"], font=dict(family="Futura, Century Gothic, Arial, sans-serif", color=COLORS["text"]), xaxis=dict(gridcolor="#e2e8f0", zerolinecolor=COLORS["border"]), yaxis=dict(gridcolor="#e2e8f0", zerolinecolor=COLORS["border"]), colorway=CHART_COLORS, legend=dict(orientation="h", yanchor="bottom", y=1.02), hovermode="x unified", ) liis_template = pio.templates["plotly"] liis_template.layout.update(LIIS_LAYOUT) pio.templates["liis"] = liis_template pio.templates.default = "liis" # Page config st.set_page_config( page_title="LiiS Strategy", page_icon="๐ผ", layout="wide", ) # Custom CSS โ LiiS brand st.markdown(f""" """, unsafe_allow_html=True) # Header st.markdown("""
C-Level View โ Portfolio Economics & Break-Even Analysis
Tier 1 โ Primary Metrics
', unsafe_allow_html=True) hero_col1, hero_col2 = st.columns(2) with hero_col1: st.markdown(f"""BREAK EVEN
For entire portfolio of {portfolio_size:,} units
TOTAL PORTFOLIO PROFIT
Net revenue across {portfolio_size:,} units over {reuse_cycles} cycles
Tier 2 โ Unit Economics
', unsafe_allow_html=True) metric_col1, metric_col2, metric_col3 = st.columns(3) with metric_col1: st.metric( "Revenue per Product", f"{revenue_per_product:,.0f} NOK", delta=f"{monthly_lease} ร {lease_term_months}mo ร {reuse_cycles} cycles", help="Total revenue generated by one product over its lifetime" ) st.metric( "Revenue per Portfolio", f"{revenue_portfolio:,.0f} NOK", delta=f"{portfolio_size:,} units", help="Total portfolio revenue" ) with metric_col2: st.metric( "Blended Cost per Product", f"{blended_cost_per_product:,.0f} NOK", delta=f"Total {total_cost_per_product:,.0f} รท {reuse_cycles} cycles", help="Average cost per cycle (manufacturing + logistics + repair)" ) st.metric( "Blended Cost per Portfolio", f"{blended_cost_portfolio:,.0f} NOK", delta=f"{portfolio_size:,} units", help="Total portfolio blended cost" ) with metric_col3: margin_color = "normal" if operating_margin >= 50 else "off" st.metric( "Operating Margin", f"{operating_margin:.0f}%", delta="healthy" if operating_margin >= 50 else "review pricing", delta_color=margin_color, help="Profit / Revenue - same for all units" ) st.metric( "Profit per Product", f"{profit_per_product:,.0f} NOK", delta="lifetime profit", help="Revenue - Total Cost per product" ) st.markdown("---") # ============================================================ # CHART 1: Break Even Comparison # ============================================================ st.markdown('Tier 2 โ Model Comparison
', unsafe_allow_html=True) st.subheader("Break Even: Circular vs Linear Model") comp_col1, comp_col2 = st.columns(2) with comp_col1: st.markdown(f"""LINEAR MODEL
{linear_margin*100:.0f}% margin
Sell once, done
CIRCULAR MODEL
{operating_margin:.0f}% margin
Reuse {reuse_cycles}ร per product
Tier 3 โ Scale Understanding
', unsafe_allow_html=True) st.subheader("At Scale: Customer Base Projection") scale_col1, scale_col2 = st.columns([1, 2]) with scale_col1: customer_base = st.slider( "Customer Base", min_value=500, max_value=10000, value=1000, step=100, help="Target number of active customers" ) utilization_rate = 0.80 # 80% of fleet in use at any time fleet_needed = int(customer_base / utilization_rate) annual_revenue_scale = customer_base * monthly_lease * lease_term_months total_cost_scale = fleet_needed * blended_cost_per_product annual_profit_scale = annual_revenue_scale - total_cost_scale st.metric("Fleet Size Needed", f"{fleet_needed:,} units", delta=f"{utilization_rate*100:.0f}% utilization") st.metric("Annual Revenue", f"{annual_revenue_scale:,.0f} NOK", delta=f"{customer_base:,} customers") st.metric("Total Blended Cost", f"{total_cost_scale:,.0f} NOK", delta=f"{fleet_needed:,} units") profit_delta = "profit" if annual_profit_scale > 0 else "loss" st.metric("Annual Profit", f"{annual_profit_scale:,.0f} NOK", delta=profit_delta) with scale_col2: # Chart 3: 5-Year Cumulative Profit st.markdown("**5-Year Cumulative Profit Trajectory**") # Calculate with 10% utilization decline per cycle months = list(range(0, 61)) cumulative_profit = [] cumulative = -fleet_needed * mfg_cost # Initial investment current_utilization = utilization_rate for month in months: if month == 0: cumulative_profit.append(cumulative) continue # Decline utilization at each cycle end if month > 0 and month % lease_term_months == 0: current_utilization = max(0.5, current_utilization - 0.10) cumulative -= fleet_needed * logistics_per_cycle # Logistics at cycle transition active_customers = int(fleet_needed * current_utilization) monthly_rev = active_customers * monthly_lease monthly_cost = active_customers * (repair_per_cycle / lease_term_months) cumulative += monthly_rev - monthly_cost cumulative_profit.append(cumulative) fig_trajectory = go.Figure() fig_trajectory.add_trace(go.Scatter( x=months, y=cumulative_profit, mode="lines", name="Cumulative Profit", line=dict(color=COLORS["positive"], width=3), fill="tozeroy", fillcolor="rgba(18, 75, 95, 0.15)", )) # Add zero line fig_trajectory.add_hline(y=0, line_color=COLORS["border"], line_width=1) # Mark cycle transitions for cycle_month in range(lease_term_months, 61, lease_term_months): fig_trajectory.add_vline( x=cycle_month, line_dash="dot", line_color=COLORS["border"], ) # Find and mark breakeven for i, cp in enumerate(cumulative_profit): if cp >= 0 and i > 0: fig_trajectory.add_trace(go.Scatter( x=[i], y=[cp], mode="markers+text", name="Breakeven", marker=dict(size=12, color=COLORS["warning"], symbol="star"), text=["Breakeven"], textposition="top center", )) break fig_trajectory.update_layout( xaxis_title="Months", yaxis_title="Cumulative Profit (NOK)", showlegend=False, yaxis=dict(tickformat=","), ) st.plotly_chart(fig_trajectory, use_container_width=True) final_profit = cumulative_profit[-1] st.caption(f"5-year projection with {fleet_needed:,} unit fleet. Final cumulative profit: **{final_profit:,.0f} NOK**") # ============================================================ # FOOTER # ============================================================ st.markdown("---") st.caption(f"Portfolio: {portfolio_size:,} units | Lease: {monthly_lease} NOK/mo | Mfg: {mfg_cost:,} NOK | Cycles: {reuse_cycles}")