Spaces:
Running
Running
Commit ·
b51241a
1
Parent(s): 5aa1c76
Add Fleet Scale Economics section for volume advantage pitch
Browse filesShows 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>
app.py
CHANGED
|
@@ -1045,7 +1045,198 @@ st.markdown(f"""
|
|
| 1045 |
""")
|
| 1046 |
|
| 1047 |
# ============================================================
|
| 1048 |
-
# SECTION 7:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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")
|