Skip to main content

max / makenotwork

21.1 KB · 422 lines History Blame Raw
1 {% extends "base.html" %}
2
3 {% block title %}Pricing Calculator - Makenot.work{% endblock %}
4
5 {% block body_attrs %} class="centered-page"{% endblock %}
6
7 {% block content %}
8 <div class="landing-hero pricing-page">
9 <h1 class="page-title">Pricing Calculator<span class="dot">.</span></h1>
10 <p class="tagline">See what you keep on every sale<span class="dot">.</span></p>
11
12 <div class="tier-section">
13 <h2 class="section-label">Monthly revenue</h2>
14 <div class="pricing-input-wrap">
15 <span class="pricing-currency">$</span>
16 <input type="number" id="revenue" class="pricing-input" value="1000" min="0" max="999999" step="50" aria-label="Monthly revenue in dollars">
17 <span class="pricing-period">/mo</span>
18 </div>
19 </div>
20
21 <div class="tier-section">
22 <h2 class="section-label">Your content tier</h2>
23 <p class="tier-intro">Every tier is the complete platform: profile, project pages, forum, discovery, memberships, analytics, full data export. The tier picks the file-size envelope, not the feature set.</p>
24 <div class="tier-selector" role="radiogroup" aria-label="Content tier">
25 <label class="tier-card tier-option is-selected">
26 <input type="radio" name="tier" value="{{ tier_prices.basic_std }}" checked>
27 <div class="tier-name">Basic</div>
28 <div class="tier-price">${{ tier_prices.basic_std }}/mo</div>
29 <div class="tier-desc">{{ tier_prices.basic_per_file }}/file, {{ tier_prices.basic_total }} total. Fits text, blogs, newsletters.</div>
30 </label>
31 <label class="tier-card tier-option">
32 <input type="radio" name="tier" value="{{ tier_prices.small_files_std }}">
33 <div class="tier-name">Small Files</div>
34 <div class="tier-price">${{ tier_prices.small_files_std }}/mo</div>
35 <div class="tier-desc">{{ tier_prices.small_files_per_file }}/file, {{ tier_prices.small_files_total }} total. Fits audio, plugins, binaries.</div>
36 </label>
37 <label class="tier-card tier-option">
38 <input type="radio" name="tier" value="{{ tier_prices.big_files_std }}">
39 <div class="tier-name">Big Files</div>
40 <div class="tier-price">${{ tier_prices.big_files_std }}/mo</div>
41 <div class="tier-desc">{{ tier_prices.big_files_per_file }}/file, {{ tier_prices.big_files_total }} total. Fits video, games, large software.</div>
42 </label>
43 <label class="tier-card tier-option">
44 <input type="radio" name="tier" value="{{ tier_prices.everything_std }}">
45 <div class="tier-name">Everything</div>
46 <div class="tier-price">${{ tier_prices.everything_std }}/mo</div>
47 <div class="tier-desc">Big Files envelope plus first access to high-cost features as they ship.</div>
48 </label>
49 </div>
50 </div>
51
52 <div class="tier-section" id="results">
53 <h2 class="section-label">You keep</h2>
54 <div class="mnw-summary">
55 <div class="mnw-keep" id="mnw-keep">$943.00</div>
56 <div class="mnw-detail" id="mnw-detail">of every $1,000 after processing fees (~3%) and ${{ tier_prices.basic_std }}/mo membership</div>
57 </div>
58 </div>
59
60 <div class="tier-section">
61 <h2 class="section-label">Platform comparison</h2>
62 <table class="data-table" id="comparison-table" aria-label="Platform fee comparison">
63 <thead>
64 <tr>
65 <th>Platform</th>
66 <th>Fee structure</th>
67 <th>You keep</th>
68 <th>vs MNW</th>
69 </tr>
70 </thead>
71 <tbody id="comparison-body">
72 </tbody>
73 </table>
74 <p class="pricing-disclaimer">Fee structures sourced from each platform's public pricing page. Actual fees may vary by plan, region, or payment method. Verify on each platform's site before making decisions.</p>
75 <p class="pricing-disclaimer"><strong>Selling small-ticket items?</strong> Payment processors charge a fixed fee per transaction (~$0.30) that hits harder on $1-5 sales. This is an industry-wide constraint, not specific to any platform. Bundling items into collections lets your fans buy in groups at a single transaction cost instead of paying per-item processing fees.</p>
76 </div>
77
78 <div class="tier-section hidden" id="breakeven-section">
79 <div class="breakeven-note" id="breakeven-note"></div>
80 </div>
81
82 <div class="tier-section cost-allocation" id="cost-allocation">
83 <h2 class="section-label">Where your tier fee goes</h2>
84 <p class="cost-allocation-intro">Per typical creator, monthly. These are platform-wide budget allocations, not per-account accounting. Hover a segment for details.</p>
85
86 {# Basic — $16 = 0.76 + 0.40 + 5.00 + 6.00 + 2.00 + 1.84. Sub-amounts hardcoded; see launchplan §4.5.1 for the planned [cost_allocation] toml section. #}
87 <div class="cost-row">
88 <div class="cost-row-label"><strong>Basic</strong>${{ tier_prices.basic_std }}/mo</div>
89 <div class="cost-bar" role="img" aria-label="Basic tier ${{ tier_prices.basic_std }}/mo allocation: Stripe $0.76, Storage $0.40, Support $5.00, Engineering $6.00, Reserves $2.00, Earn-back $1.84">
90 <div class="cost-bar-seg cost-bar-seg-stripe" style="flex: 76" title="Stripe processing: $0.76. Stripe's fee on your monthly subscription.">$0.76</div>
91 <div class="cost-bar-seg cost-bar-seg-storage" style="flex: 40" title="Storage: $0.40. Object storage at your tier's typical fill, on Hetzner.">$0.40</div>
92 <div class="cost-bar-seg cost-bar-seg-support" style="flex: 500" title="Human support time: $5.00. Identity recovery, billing disputes, moderation, abuse, and legal — the work that can't be automated.">$5.00</div>
93 <div class="cost-bar-seg cost-bar-seg-engineering" style="flex: 600" title="Product engineering: $6.00. Bug fixes and ongoing product work. We treat fixing bugs as part of building the product, not as an ongoing support drag.">$6.00</div>
94 <div class="cost-bar-seg cost-bar-seg-reserves" style="flex: 200" title="Reserves: $2.00. Held against a bad month so a single incident doesn't force a price change or a shutdown.">$2.00</div>
95 <div class="cost-bar-seg cost-bar-seg-earnback" style="flex: 184" title="Earn-back surplus: $1.84. Earmarked to return to creators as earn-back credit (committed by 2027-01-01).">$1.84</div>
96 </div>
97 </div>
98
99 {# Small Files — $24 = 1.00 + 0.60 + 5.00 + 6.00 + 2.00 + 9.40 #}
100 <div class="cost-row">
101 <div class="cost-row-label"><strong>Small Files</strong>${{ tier_prices.small_files_std }}/mo</div>
102 <div class="cost-bar" role="img" aria-label="Small Files tier ${{ tier_prices.small_files_std }}/mo allocation: Stripe $1.00, Storage $0.60, Support $5.00, Engineering $6.00, Reserves $2.00, Earn-back $9.40">
103 <div class="cost-bar-seg cost-bar-seg-stripe" style="flex: 100" title="Stripe processing: $1.00.">$1.00</div>
104 <div class="cost-bar-seg cost-bar-seg-storage" style="flex: 60" title="Storage: $0.60. Object storage at your tier's typical fill.">$0.60</div>
105 <div class="cost-bar-seg cost-bar-seg-support" style="flex: 500" title="Human support time: $5.00.">$5.00</div>
106 <div class="cost-bar-seg cost-bar-seg-engineering" style="flex: 600" title="Product engineering: $6.00.">$6.00</div>
107 <div class="cost-bar-seg cost-bar-seg-reserves" style="flex: 200" title="Reserves: $2.00.">$2.00</div>
108 <div class="cost-bar-seg cost-bar-seg-earnback" style="flex: 940" title="Earn-back surplus: $9.40.">$9.40</div>
109 </div>
110 </div>
111
112 {# Big Files — $36 = 1.34 + 0.90 + 5.00 + 6.00 + 2.00 + 20.76 #}
113 <div class="cost-row">
114 <div class="cost-row-label"><strong>Big Files</strong>${{ tier_prices.big_files_std }}/mo</div>
115 <div class="cost-bar" role="img" aria-label="Big Files tier ${{ tier_prices.big_files_std }}/mo allocation: Stripe $1.34, Storage $0.90, Support $5.00, Engineering $6.00, Reserves $2.00, Earn-back $20.76">
116 <div class="cost-bar-seg cost-bar-seg-stripe" style="flex: 134" title="Stripe processing: $1.34.">$1.34</div>
117 <div class="cost-bar-seg cost-bar-seg-storage" style="flex: 90" title="Storage: $0.90.">$0.90</div>
118 <div class="cost-bar-seg cost-bar-seg-support" style="flex: 500" title="Human support time: $5.00.">$5.00</div>
119 <div class="cost-bar-seg cost-bar-seg-engineering" style="flex: 600" title="Product engineering: $6.00.">$6.00</div>
120 <div class="cost-bar-seg cost-bar-seg-reserves" style="flex: 200" title="Reserves: $2.00.">$2.00</div>
121 <div class="cost-bar-seg cost-bar-seg-earnback" style="flex: 2076" title="Earn-back surplus: $20.76.">$20.76</div>
122 </div>
123 </div>
124
125 {# Everything — $60 = 2.04 + 1.50 + 5.00 + 6.00 + 2.00 + 43.46 #}
126 <div class="cost-row">
127 <div class="cost-row-label"><strong>Everything</strong>${{ tier_prices.everything_std }}/mo</div>
128 <div class="cost-bar" role="img" aria-label="Everything tier ${{ tier_prices.everything_std }}/mo allocation: Stripe $2.04, Storage $1.50, Support $5.00, Engineering $6.00, Reserves $2.00, Earn-back $43.46">
129 <div class="cost-bar-seg cost-bar-seg-stripe" style="flex: 204" title="Stripe processing: $2.04.">$2.04</div>
130 <div class="cost-bar-seg cost-bar-seg-storage" style="flex: 150" title="Storage: $1.50.">$1.50</div>
131 <div class="cost-bar-seg cost-bar-seg-support" style="flex: 500" title="Human support time: $5.00.">$5.00</div>
132 <div class="cost-bar-seg cost-bar-seg-engineering" style="flex: 600" title="Product engineering: $6.00.">$6.00</div>
133 <div class="cost-bar-seg cost-bar-seg-reserves" style="flex: 200" title="Reserves: $2.00.">$2.00</div>
134 <div class="cost-bar-seg cost-bar-seg-earnback" style="flex: 4346" title="Earn-back surplus: $43.46.">$43.46</div>
135 </div>
136 </div>
137
138 <div class="cost-legend" aria-hidden="true">
139 <span class="cost-legend-item"><span class="cost-legend-swatch cost-bar-seg-stripe"></span>Stripe processing</span>
140 <span class="cost-legend-item"><span class="cost-legend-swatch cost-bar-seg-storage"></span>Storage (typical fill)</span>
141 <span class="cost-legend-item"><span class="cost-legend-swatch cost-bar-seg-support"></span>Human support time</span>
142 <span class="cost-legend-item"><span class="cost-legend-swatch cost-bar-seg-engineering"></span>Product engineering</span>
143 <span class="cost-legend-item"><span class="cost-legend-swatch cost-bar-seg-reserves"></span>Reserves</span>
144 <span class="cost-legend-item"><span class="cost-legend-swatch cost-bar-seg-earnback"></span>Earn-back surplus</span>
145 </div>
146
147 <p class="pricing-disclaimer">Founder-rate creators pay exactly 50% of the standard tier; that subsidy is absorbed by the standard cohort and platform reserves, so the breakdown above applies to standard rates.</p>
148 </div>
149
150 <div class="landing-cta">
151 <a class="btn-primary btn--large" href="/join">Join the Alpha</a>
152 </div>
153
154 <div class="secondary-links">
155 <a href="/">Home</a>
156 <a href="/discover">Browse as guest</a>
157 </div>
158 </div>
159
160 <footer>
161 <p>&copy; 2026 Make Creative</p>
162 </footer>
163 {% endblock %}
164
165 {% block scripts %}
166 <script>
167 (function() {
168 'use strict';
169
170 var STRIPE_PERCENT = 0.029;
171 var STRIPE_FIXED = 0.30;
172
173 // Average transactions per month at a given revenue level.
174 // Assumes ~$25 average transaction for simplicity.
175 function estimateTransactions(revenue) {
176 if (revenue <= 0) return 0;
177 return Math.max(1, Math.round(revenue / 25));
178 }
179
180 function stripeNet(revenue) {
181 if (revenue <= 0) return 0;
182 var txns = estimateTransactions(revenue);
183 return revenue - (revenue * STRIPE_PERCENT) - (txns * STRIPE_FIXED);
184 }
185
186 var competitors = [
187 {
188 name: 'Patreon',
189 url: 'https://www.patreon.com/pricing',
190 fee: '10% + ~3% + $0.30',
191 calc: function(rev) {
192 if (rev <= 0) return 0;
193 var txns = estimateTransactions(rev);
194 var afterPlatform = rev * 0.90;
195 return afterPlatform - (afterPlatform * STRIPE_PERCENT) - (txns * STRIPE_FIXED);
196 }
197 },
198 {
199 name: 'Gumroad',
200 url: 'https://gumroad.com/pricing',
201 fee: '10% + $0.50/tx + ~3%',
202 calc: function(rev) {
203 if (rev <= 0) return 0;
204 var txns = estimateTransactions(rev);
205 var afterPlatform = rev * 0.90 - (txns * 0.50);
206 return afterPlatform - (afterPlatform * STRIPE_PERCENT);
207 }
208 },
209 {
210 name: 'Bandcamp',
211 url: 'https://bandcamp.com/pricing',
212 fee: '15% (10% after $5k) + ~3%',
213 calc: function(rev) {
214 if (rev <= 0) return 0;
215 var rate = rev > 5000 ? 0.10 : 0.15;
216 var afterPlatform = rev * (1 - rate);
217 return afterPlatform - (afterPlatform * STRIPE_PERCENT);
218 }
219 },
220 {
221 name: 'Ko-fi Free',
222 url: 'https://ko-fi.com/pricing',
223 fee: '0% tips, 5% shop/memberships + ~3%',
224 calc: function(rev) {
225 if (rev <= 0) return 0;
226 var afterPlatform = rev * 0.95;
227 return afterPlatform - (afterPlatform * STRIPE_PERCENT);
228 }
229 },
230 {
231 name: 'Ko-fi Gold',
232 url: 'https://ko-fi.com/pricing',
233 fee: '$12/mo + 0% + ~3%',
234 calc: function(rev) {
235 if (rev <= 0) return -12;
236 return stripeNet(rev) - 12;
237 }
238 },
239 {
240 name: 'Itch.io',
241 url: 'https://itch.io/docs/creators/faq',
242 fee: '10% default + ~3%',
243 calc: function(rev) {
244 if (rev <= 0) return 0;
245 var afterPlatform = rev * 0.90;
246 return afterPlatform - (afterPlatform * STRIPE_PERCENT);
247 }
248 },
249 {
250 name: 'Lemon Squeezy',
251 url: 'https://www.lemonsqueezy.com/pricing',
252 fee: '5% + $0.50/tx (incl. processing)',
253 calc: function(rev) {
254 if (rev <= 0) return 0;
255 var txns = estimateTransactions(rev);
256 return rev * 0.95 - (txns * 0.50);
257 }
258 },
259 {
260 name: 'Buy Me a Coffee',
261 url: 'https://www.buymeacoffee.com/about',
262 fee: '5% + ~3%',
263 calc: function(rev) {
264 if (rev <= 0) return 0;
265 var afterPlatform = rev * 0.95;
266 return afterPlatform - (afterPlatform * STRIPE_PERCENT);
267 }
268 },
269 {
270 name: 'Substack',
271 url: 'https://substack.com/going-paid',
272 fee: '10% on paid subs',
273 calc: function(rev) {
274 if (rev <= 0) return 0;
275 var afterPlatform = rev * 0.90;
276 return afterPlatform - (afterPlatform * STRIPE_PERCENT);
277 }
278 }
279 ];
280
281 function fmt(n) {
282 return '$' + n.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
283 }
284
285 function fmtWhole(n) {
286 return '$' + Math.round(n).toLocaleString();
287 }
288
289 function update() {
290 var revenueInput = document.getElementById('revenue');
291 var revenue = parseFloat(revenueInput.value) || 0;
292
293 var tierRadios = document.querySelectorAll('input[name="tier"]');
294 // Default falls back to the Basic-tier radio value; templates render the canonical price.
295 var tierCost = {{ tier_prices.basic_std }};
296 for (var i = 0; i < tierRadios.length; i++) {
297 if (tierRadios[i].checked) {
298 tierCost = parseInt(tierRadios[i].value, 10);
299 break;
300 }
301 }
302
303 // MNW net: revenue minus processing fees minus monthly membership
304 var mnwNet = stripeNet(revenue) - tierCost;
305 if (revenue <= 0) mnwNet = -tierCost;
306
307 document.getElementById('mnw-keep').textContent = fmt(Math.max(0, mnwNet));
308 document.getElementById('mnw-detail').textContent =
309 'of every ' + fmtWhole(revenue) + ' after processing fees (~3%) and ' + fmt(tierCost) + '/mo membership';
310
311 // Build comparison rows sorted by net (descending)
312 var rows = [];
313 rows.push({
314 name: 'Makenot.work',
315 fee: fmt(tierCost) + '/mo flat + ~3% processing',
316 net: mnwNet,
317 isMnw: true
318 });
319
320 for (var j = 0; j < competitors.length; j++) {
321 var c = competitors[j];
322 rows.push({
323 name: c.name,
324 url: c.url,
325 fee: c.fee,
326 net: c.calc(revenue),
327 isMnw: false
328 });
329 }
330
331 rows.sort(function(a, b) { return b.net - a.net; });
332
333 var tbody = document.getElementById('comparison-body');
334 tbody.innerHTML = '';
335
336 var breakevenMessages = [];
337
338 for (var k = 0; k < rows.length; k++) {
339 var row = rows[k];
340 var tr = document.createElement('tr');
341 if (row.isMnw) tr.className = 'highlight';
342
343 var tdName = document.createElement('td');
344 if (row.isMnw) {
345 tdName.textContent = row.name;
346 tdName.style.fontWeight = '600';
347 } else {
348 var link = document.createElement('a');
349 link.href = row.url;
350 link.textContent = row.name;
351 link.target = '_blank';
352 link.rel = 'noopener';
353 tdName.appendChild(link);
354 }
355 tr.appendChild(tdName);
356
357 var tdFee = document.createElement('td');
358 tdFee.textContent = row.fee;
359 tr.appendChild(tdFee);
360
361 var tdKeep = document.createElement('td');
362 tdKeep.textContent = revenue > 0 ? fmt(Math.max(0, row.net)) : '--';
363 tr.appendChild(tdKeep);
364
365 var tdDiff = document.createElement('td');
366 if (row.isMnw) {
367 tdDiff.textContent = '--';
368 } else if (revenue > 0) {
369 var diff = mnwNet - row.net;
370 if (diff > 0.005) {
371 tdDiff.textContent = '+' + fmt(diff);
372 tdDiff.className = 'savings-negative';
373 } else if (diff < -0.005) {
374 tdDiff.textContent = fmt(diff);
375 tdDiff.className = 'savings-positive';
376 breakevenMessages.push(row.name);
377 } else {
378 tdDiff.textContent = '$0.00';
379 }
380 } else {
381 tdDiff.textContent = '--';
382 }
383 tr.appendChild(tdDiff);
384
385 tbody.appendChild(tr);
386 }
387
388 // Breakeven note
389 var breakevenSection = document.getElementById('breakeven-section');
390 var breakevenNote = document.getElementById('breakeven-note');
391 if (breakevenMessages.length > 0 && revenue > 0) {
392 breakevenSection.classList.remove('hidden');
393 breakevenNote.textContent =
394 'At ' + fmtWhole(revenue) + '/mo, some platforms with percentage-only fees cost less than MNW\'s flat rate. '
395 + 'MNW becomes cheaper as your revenue grows.';
396 } else {
397 breakevenSection.classList.add('hidden');
398 }
399 }
400
401 // Tier selector visual state. Listen on the radio inputs so keyboard
402 // navigation (arrow keys within the radiogroup) updates the styling.
403 var tierInputs = document.querySelectorAll('.tier-option input[type="radio"]');
404 var tierLabels = document.querySelectorAll('.tier-option');
405 for (var i = 0; i < tierInputs.length; i++) {
406 tierInputs[i].addEventListener('change', function() {
407 for (var j = 0; j < tierLabels.length; j++) {
408 tierLabels[j].classList.remove('is-selected');
409 }
410 this.closest('.tier-option').classList.add('is-selected');
411 update();
412 });
413 }
414
415 document.getElementById('revenue').addEventListener('input', update);
416
417 // Initial calculation
418 update();
419 })();
420 </script>
421 {% endblock %}
422