mathiassandnes Claude Opus 4.5 commited on
Commit
b51241a
·
1 Parent(s): 5aa1c76

Add Fleet Scale Economics section for volume advantage pitch

Browse files

Shows circular vs linear comparison at 500, 1000, 2000, 5000 products:
- Profit at scale comparison chart
- Products needed to serve same customers
- Business case summary for Jonas's pitch (5k products = 15k customers)
- CO2 savings from fewer manufactured products

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Files changed (1) hide show
  1. app.py +192 -1
app.py CHANGED
@@ -1045,7 +1045,198 @@ st.markdown(f"""
1045
  """)
1046
 
1047
  # ============================================================
1048
- # SECTION 7: PRICING SENSITIVITY
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1049
  # ============================================================
1050
  st.markdown("---")
1051
  st.header("Pricing Analysis: Finding the Sweet Spot")
 
1045
  """)
1046
 
1047
  # ============================================================
1048
+ # SECTION 7: FLEET SCALE ECONOMICS
1049
+ # ============================================================
1050
+ st.markdown("---")
1051
+ st.header("Fleet Scale Economics: Volume Advantage")
1052
+
1053
+ st.markdown("""
1054
+ **The service model advantage:** With fewer products in circulation, serve more customers over time.
1055
+ This is the key to proving the circular business case at scale.
1056
+ """)
1057
+
1058
+ # Fleet sizes to analyze (Jonas's request: 500, 1000, 5000)
1059
+ fleet_sizes = [500, 1000, 2000, 5000]
1060
+
1061
+ scale_data = []
1062
+ for fleet in fleet_sizes:
1063
+ # Circular: fleet × cycles = total customers served
1064
+ customers_served = fleet * max_cycles
1065
+
1066
+ # Circular costs: first cycle mfg, subsequent cycles refurb
1067
+ circular_mfg_cost = fleet * new_unit_cost
1068
+ circular_refurb_cost = fleet * (max_cycles - 1) * refurb_unit_cost
1069
+ circular_total_cost = circular_mfg_cost + circular_refurb_cost
1070
+ circular_revenue = customers_served * revenue_per_customer
1071
+ circular_profit = circular_revenue - circular_total_cost
1072
+
1073
+ # Linear equivalent: to serve same customers, need to manufacture that many products
1074
+ linear_products_needed = customers_served # 1 product per customer
1075
+ linear_cost = linear_products_needed * new_unit_cost
1076
+ linear_revenue = customers_served * revenue_per_customer
1077
+ linear_profit = linear_revenue - linear_cost
1078
+
1079
+ # Savings
1080
+ products_saved = linear_products_needed - fleet
1081
+ cost_saved = linear_cost - circular_total_cost
1082
+ profit_advantage = circular_profit - linear_profit
1083
+
1084
+ # CO2 savings (fewer products manufactured)
1085
+ co2_per_unit = config["environmental"]["co2_manufacturing_kg"]
1086
+ co2_saved = products_saved * co2_per_unit
1087
+
1088
+ scale_data.append({
1089
+ "Fleet Size": fleet,
1090
+ "Fleet Label": f"{fleet:,}",
1091
+ "Customers Served": customers_served,
1092
+ "Circular Profit": circular_profit,
1093
+ "Linear Profit": linear_profit,
1094
+ "Profit Advantage": profit_advantage,
1095
+ "Products Saved": products_saved,
1096
+ "Cost Saved": cost_saved,
1097
+ "CO2 Saved (kg)": co2_saved,
1098
+ "Linear Products Needed": linear_products_needed,
1099
+ })
1100
+
1101
+ scale_df = pd.DataFrame(scale_data)
1102
+
1103
+ scale_col1, scale_col2 = st.columns(2)
1104
+
1105
+ with scale_col1:
1106
+ st.subheader("Profit at Scale: Circular vs Linear")
1107
+
1108
+ fig_scale = go.Figure()
1109
+
1110
+ # Circular profit
1111
+ fig_scale.add_trace(go.Bar(
1112
+ x=scale_df["Fleet Label"],
1113
+ y=scale_df["Circular Profit"],
1114
+ name=f"Circular ({max_cycles} cycles)",
1115
+ marker_color="#2ecc71",
1116
+ text=[f"€{v:,.0f}" for v in scale_df["Circular Profit"]],
1117
+ textposition="outside",
1118
+ ))
1119
+
1120
+ # Linear profit (same customers)
1121
+ fig_scale.add_trace(go.Bar(
1122
+ x=scale_df["Fleet Label"],
1123
+ y=scale_df["Linear Profit"],
1124
+ name="Linear (sell once)",
1125
+ marker_color="#e74c3c",
1126
+ text=[f"€{v:,.0f}" for v in scale_df["Linear Profit"]],
1127
+ textposition="outside",
1128
+ ))
1129
+
1130
+ fig_scale.update_layout(
1131
+ xaxis_title="Fleet Size (products in circulation)",
1132
+ yaxis_title="Total Profit (EUR)",
1133
+ barmode="group",
1134
+ legend=dict(orientation="h", yanchor="bottom", y=1.02),
1135
+ )
1136
+
1137
+ st.plotly_chart(fig_scale, width="stretch")
1138
+
1139
+ with scale_col2:
1140
+ st.subheader("Products Needed to Serve Same Customers")
1141
+
1142
+ fig_products = go.Figure()
1143
+
1144
+ # Circular: products needed
1145
+ fig_products.add_trace(go.Bar(
1146
+ x=scale_df["Fleet Label"],
1147
+ y=scale_df["Fleet Size"],
1148
+ name="Circular (reuse)",
1149
+ marker_color="#2ecc71",
1150
+ text=[f"{v:,}" for v in scale_df["Fleet Size"]],
1151
+ textposition="outside",
1152
+ ))
1153
+
1154
+ # Linear: products needed
1155
+ fig_products.add_trace(go.Bar(
1156
+ x=scale_df["Fleet Label"],
1157
+ y=scale_df["Linear Products Needed"],
1158
+ name="Linear (sell each)",
1159
+ marker_color="#e74c3c",
1160
+ text=[f"{v:,}" for v in scale_df["Linear Products Needed"]],
1161
+ textposition="outside",
1162
+ ))
1163
+
1164
+ fig_products.update_layout(
1165
+ xaxis_title="Fleet Size Target",
1166
+ yaxis_title="Products to Manufacture",
1167
+ barmode="group",
1168
+ legend=dict(orientation="h", yanchor="bottom", y=1.02),
1169
+ )
1170
+
1171
+ st.plotly_chart(fig_products, width="stretch")
1172
+
1173
+ # Key insight box for Jonas's pitch
1174
+ st.subheader("The Business Case")
1175
+
1176
+ # Use 5000 as the key example
1177
+ key_fleet = 5000
1178
+ key_data = scale_df[scale_df["Fleet Size"] == key_fleet].iloc[0]
1179
+
1180
+ metric_cols = st.columns(4)
1181
+ with metric_cols[0]:
1182
+ st.metric(
1183
+ "Fleet Size",
1184
+ f"{key_fleet:,}",
1185
+ help="Products in circulation"
1186
+ )
1187
+ with metric_cols[1]:
1188
+ st.metric(
1189
+ "Customers Served",
1190
+ f"{key_data['Customers Served']:,}",
1191
+ delta=f"{max_cycles} cycles",
1192
+ help="Total families served over product lifetime"
1193
+ )
1194
+ with metric_cols[2]:
1195
+ st.metric(
1196
+ "Products Saved",
1197
+ f"{key_data['Products Saved']:,}",
1198
+ delta=f"vs linear",
1199
+ help="Fewer products to manufacture"
1200
+ )
1201
+ with metric_cols[3]:
1202
+ st.metric(
1203
+ "Extra Profit",
1204
+ f"€{key_data['Profit Advantage']:,.0f}",
1205
+ delta=f"vs selling {key_data['Linear Products Needed']:,}",
1206
+ help="Circular profit advantage"
1207
+ )
1208
+
1209
+ st.success(f"""
1210
+ **Jonas's pitch:** With **{key_fleet:,} products** in our fleet, we serve **{key_data['Customers Served']:,} families**
1211
+ and generate **€{key_data['Circular Profit']:,.0f}** in profit.
1212
+
1213
+ A linear model would need to **manufacture {key_data['Linear Products Needed']:,} products** to serve the same customers,
1214
+ costing **€{key_data['Cost Saved']:,.0f} more** and generating **€{key_data['Profit Advantage']:,.0f} less profit**.
1215
+
1216
+ Environmental impact: **{key_data['CO2 Saved (kg)']:,.0f} kg CO2 saved** by manufacturing fewer products.
1217
+ """)
1218
+
1219
+ # Summary table
1220
+ with st.expander("Full Scale Comparison Table"):
1221
+ display_df = scale_df[["Fleet Label", "Customers Served", "Circular Profit", "Linear Profit",
1222
+ "Profit Advantage", "Products Saved", "CO2 Saved (kg)"]].copy()
1223
+ display_df.columns = ["Fleet", "Customers", "Circular Profit", "Linear Profit",
1224
+ "Advantage", "Products Saved", "CO2 Saved"]
1225
+ st.dataframe(
1226
+ display_df.style.format({
1227
+ "Customers": "{:,}",
1228
+ "Circular Profit": "€{:,.0f}",
1229
+ "Linear Profit": "€{:,.0f}",
1230
+ "Advantage": "€{:,.0f}",
1231
+ "Products Saved": "{:,}",
1232
+ "CO2 Saved": "{:,.0f} kg",
1233
+ }),
1234
+ width="stretch",
1235
+ hide_index=True,
1236
+ )
1237
+
1238
+ # ============================================================
1239
+ # SECTION 8: PRICING SENSITIVITY
1240
  # ============================================================
1241
  st.markdown("---")
1242
  st.header("Pricing Analysis: Finding the Sweet Spot")