| 1 |
<div class="tab-docs"><a href="/docs/getting-started">Docs: Getting Started →</a></div> |
| 2 |
|
| 3 |
{% if can_create_projects %} |
| 4 |
<div class="creator-tab-section creator-tab-section--centered"> |
| 5 |
<h2 class="creator-h2">You have creator access</h2> |
| 6 |
<p class="muted mb-pane">You can create projects and publish content.</p> |
| 7 |
<a href="/dashboard?tab=projects" class="btn-primary">Go to Projects</a> |
| 8 |
</div> |
| 9 |
|
| 10 |
<div class="creator-tab-section creator-tab-section--no-top"> |
| 11 |
<h2 class="creator-h2 creator-h2--sm mb-section">Creator Plan</h2> |
| 12 |
{% if let Some(label) = creator_tier_label %} |
| 13 |
<div class="creator-plan-box mb-section"> |
| 14 |
<div class="creator-plan-head"> |
| 15 |
<span class="creator-plan-name">{{ label }}</span> |
| 16 |
{% if let Some(status) = creator_sub_status %} |
| 17 |
{% if status == "active" %} |
| 18 |
<span class="badge" data-tone="success">Active</span> |
| 19 |
{% else if status == "past_due" %} |
| 20 |
<span class="badge" data-tone="warning">Past Due</span> |
| 21 |
{% else %} |
| 22 |
<span class="badge">{{ status }}</span> |
| 23 |
{% endif %} |
| 24 |
{% endif %} |
| 25 |
{% if is_founder_locked %} |
| 26 |
<span class="badge badge--founder-locked" title="Founder pricing is locked in for the life of this account.">Founder · locked for life</span> |
| 27 |
{% else if is_founder && founder_window_open %} |
| 28 |
<span class="badge badge--founder-pending" title="Founder pricing applies while the window is open. Stay subscribed until the window closes to lock it in for life.">Founder · pending lock-in</span> |
| 29 |
{% endif %} |
| 30 |
</div> |
| 31 |
{% if let Some(period_end) = creator_period_end %} |
| 32 |
<p class="muted text-sm">Current period ends {{ period_end }}</p> |
| 33 |
{% endif %} |
| 34 |
<p class="muted text-sm mt-peer">{{ storage_total }} of {{ storage_max }} storage used ({{ storage_pct }}%). <a href="/docs/tiers" class="muted">Plan limits →</a></p> |
| 35 |
</div> |
| 36 |
{% else %} |
| 37 |
<p class="muted text-sm mb-section"> |
| 38 |
No active plan. During alpha, all creator features are available without a membership. |
| 39 |
</p> |
| 40 |
{% if founder_window_open || is_founder_locked %} |
| 41 |
<div class="founder-callout mb-section"> |
| 42 |
{% if is_founder_locked %} |
| 43 |
<p class="m-0 text-sm"><strong>Your founder pricing is locked in.</strong> The rates below show the founder rate for each tier: you can subscribe (or upgrade later) at this rate for the life of your account.</p> |
| 44 |
{% else %} |
| 45 |
<p class="m-0 text-sm"><strong>Founder pricing window is open.</strong> Subscribe now to lock in half-price creator memberships for life. Window closes at 1,000 creators or exit-beta, whichever first. <a href="/docs/pricing">How it works →</a></p> |
| 46 |
{% endif %} |
| 47 |
</div> |
| 48 |
{% endif %} |
| 49 |
{% if creator_tiers_configured %} |
| 50 |
{% let show_founder_rate = founder_window_open || is_founder_locked %} |
| 51 |
<div class="creator-tier-grid mb-section"> |
| 52 |
{% for card in tier_cards %} |
| 53 |
<div class="creator-tier-card"> |
| 54 |
<div class="creator-tier-name"><a href="/docs/tiers" class="creator-tier-link">{{ card.label }}</a></div> |
| 55 |
{% if show_founder_rate %} |
| 56 |
<div class="meta mb-peer"><strong>${{ card.founder_monthly }}/mo</strong> <span class="strike-dim">${{ card.standard_monthly }}</span></div> |
| 57 |
<div class="meta creator-tier-sub mb-peer">or ${{ card.founder_annual }}/yr (10% off)</div> |
| 58 |
{% else %} |
| 59 |
<div class="meta mb-peer">${{ card.standard_monthly }}/mo</div> |
| 60 |
<div class="meta creator-tier-sub mb-peer">or ${{ card.standard_annual }}/yr (10% off)</div> |
| 61 |
{% endif %} |
| 62 |
<div class="meta creator-tier-storage mb-section">{{ card.storage }}</div> |
| 63 |
<div class="creator-tier-buttons"> |
| 64 |
<form method="post" action="/stripe/creator-tier" class="m-0"> |
| 65 |
{% if let Some(token) = csrf_token %}<input type="hidden" name="_csrf" value="{{ token }}">{% endif %} |
| 66 |
<input type="hidden" name="tier" value="{{ card.key }}"> |
| 67 |
<input type="hidden" name="interval" value="monthly"> |
| 68 |
<button type="submit" class="btn-primary creator-tier-btn" data-loading-text="Redirecting to Stripe...">Monthly</button> |
| 69 |
</form> |
| 70 |
<form method="post" action="/stripe/creator-tier" class="m-0"> |
| 71 |
{% if let Some(token) = csrf_token %}<input type="hidden" name="_csrf" value="{{ token }}">{% endif %} |
| 72 |
<input type="hidden" name="tier" value="{{ card.key }}"> |
| 73 |
<input type="hidden" name="interval" value="annual"> |
| 74 |
<button type="submit" class="btn-secondary creator-tier-btn" data-loading-text="Redirecting to Stripe...">Annual (save 10%)</button> |
| 75 |
</form> |
| 76 |
</div> |
| 77 |
</div> |
| 78 |
{% endfor %} |
| 79 |
</div> |
| 80 |
<p class="text-center mb-section"><a href="/docs/tiers" class="text-sm">Full tier details →</a></p> |
| 81 |
<p class="text-center text-sm muted mb-section"> |
| 82 |
Upgrades take effect immediately and are prorated. Downgrades take effect at the next billing period; if you're over the new tier's storage cap, new uploads stay blocked until you're under it, but your existing content stays published either way. |
| 83 |
</p> |
| 84 |
{% endif %} |
| 85 |
{% endif %} |
| 86 |
</div> |
| 87 |
|
| 88 |
<div class="section-group-label creator-section-label">Usage</div> |
| 89 |
|
| 90 |
<details class="form-section creator-form-section"> |
| 91 |
<summary><h2 class="creator-h2 creator-h2--sm">Storage ({{ storage_total }} / {{ storage_max }})</h2></summary> |
| 92 |
<div class="storage-box mb-section"> |
| 93 |
<div class="storage-row"> |
| 94 |
<span>{{ storage_total }} of {{ storage_max }} used</span> |
| 95 |
<span class="muted">{{ storage_pct }}%</span> |
| 96 |
</div> |
| 97 |
<div class="progress-bar-container"> |
| 98 |
<div class="progress-bar progress-bar--highlight{% if !storage_tier.is_empty() %} progress-bar--{{ storage_tier }}{% endif %}" style="--fill: {{ storage_pct }}%"></div> |
| 99 |
</div> |
| 100 |
<div class="storage-breakdown"> |
| 101 |
<span class="muted">Audio: {{ storage_audio }}</span> |
| 102 |
<span class="muted">Covers: {{ storage_covers }}</span> |
| 103 |
<span class="muted">Downloads: {{ storage_downloads }}</span> |
| 104 |
<span class="muted">Clips: {{ storage_insertions }}</span> |
| 105 |
<span class="muted">Video: {{ storage_video }}</span> |
| 106 |
<span class="muted">Media: {{ storage_media }}</span> |
| 107 |
<span class="muted">Gallery: {{ storage_gallery }}</span> |
| 108 |
</div> |
| 109 |
</div> |
| 110 |
</details> |
| 111 |
|
| 112 |
{% if let Some(mail) = mail_allowance %} |
| 113 |
<details class="form-section creator-form-section"{% if mail.warning || mail.complaints.elevated %} open{% endif %}> |
| 114 |
<summary><h2 class="creator-h2 creator-h2--sm">Email allowance ({{ mail.sent }} / {{ mail.cap }})</h2></summary> |
| 115 |
<div class="storage-box mb-section"> |
| 116 |
<div class="storage-row"> |
| 117 |
<span>{{ mail.sent }} of {{ mail.cap }} emails sent this billing month</span> |
| 118 |
<span class="muted">{{ mail.percent }}%</span> |
| 119 |
</div> |
| 120 |
<div class="progress-bar-container"> |
| 121 |
<div class="progress-bar progress-bar--highlight{% if !mail.tier.is_empty() %} progress-bar--{{ mail.tier }}{% endif %}" style="--fill: {{ mail.percent }}%"></div> |
| 122 |
</div> |
| 123 |
<p class="muted text-sm mt-peer"> |
| 124 |
{{ mail.remaining }} left. Resets {{ mail.resets_on }}. |
| 125 |
{% if mail.warning %} |
| 126 |
You are close to the allowance. A send that would cross it is refused rather than trimmed, so ask for more before you need it. |
| 127 |
{% endif %} |
| 128 |
</p> |
| 129 |
<p class="muted text-sm"> |
| 130 |
The allowance covers announcements to your subscribers and broadcasts to your followers. It exists so one large send cannot degrade delivery for everyone on the shared sending pool, and it is set where ordinary use never meets it. |
| 131 |
</p> |
| 132 |
<div class="storage-row mt-section"> |
| 133 |
<span> |
| 134 |
Marked as spam: |
| 135 |
{% if mail.complaints.measurable %} |
| 136 |
{{ mail.complaints.rate_display }} |
| 137 |
{% else %} |
| 138 |
too little sending to measure |
| 139 |
{% endif %} |
| 140 |
</span> |
| 141 |
<span class="muted">{{ mail.complaints.complaints }} of {{ mail.complaints.sent }} in the last {{ mail.complaints.window_days }} days</span> |
| 142 |
</div> |
| 143 |
<p class="muted text-sm mt-peer"> |
| 144 |
A healthy list stays under {{ mail.complaints.healthy_display }}. {{ mail.complaints.limit_display }} is the line mail providers act on, and it is the one we watch. Under {{ mail.complaints.min_sent }} emails in the window the number is too small a sample to say anything, so we do not read it as good or bad. |
| 145 |
</p> |
| 146 |
{% if let Some(complaint_warning) = mail.complaints.warning %} |
| 147 |
<div class="alert alert-warning">{{ complaint_warning }}</div> |
| 148 |
{% endif %} |
| 149 |
</div> |
| 150 |
|
| 151 |
{% match mail.request %} |
| 152 |
{% when Some with (request) %} |
| 153 |
<p class="text-sm muted"> |
| 154 |
{% if request.status == "pending" %} |
| 155 |
You asked for {{ request.requested_cap }} a month on {{ request.created_at }}. We will come back to you. |
| 156 |
{% else if request.status == "granted" %} |
| 157 |
Your request from {{ request.created_at }} was granted{% if let Some(granted) = request.granted_cap %} at {{ granted }} a month{% endif %}. |
| 158 |
{% else %} |
| 159 |
Your request from {{ request.created_at }} was not granted. Ask again if the shape of your sending has changed. |
| 160 |
{% endif %} |
| 161 |
</p> |
| 162 |
{% if request.status != "pending" %} |
| 163 |
<form hx-post="/api/mail-cap/request" hx-target="#mail-cap-status" hx-swap="innerHTML" class="stack"> |
| 164 |
{% if let Some(token) = csrf_token %}<input type="hidden" name="_csrf" value="{{ token }}">{% endif %} |
| 165 |
<label class="text-sm" for="mail-cap-requested">Emails per month</label> |
| 166 |
<input type="number" id="mail-cap-requested" name="requested_cap" min="1" max="1000000" required> |
| 167 |
<label class="text-sm" for="mail-cap-reason">Why you need it</label> |
| 168 |
<textarea id="mail-cap-reason" name="reason" rows="3" maxlength="1000" required></textarea> |
| 169 |
<button type="submit" class="btn">Request an increase</button> |
| 170 |
</form> |
| 171 |
{% endif %} |
| 172 |
{% when None %} |
| 173 |
<form hx-post="/api/mail-cap/request" hx-target="#mail-cap-status" hx-swap="innerHTML" class="stack"> |
| 174 |
{% if let Some(token) = csrf_token %}<input type="hidden" name="_csrf" value="{{ token }}">{% endif %} |
| 175 |
<label class="text-sm" for="mail-cap-requested">Emails per month</label> |
| 176 |
<input type="number" id="mail-cap-requested" name="requested_cap" min="1" max="1000000" required> |
| 177 |
<label class="text-sm" for="mail-cap-reason">Why you need it</label> |
| 178 |
<textarea id="mail-cap-reason" name="reason" rows="3" maxlength="1000" required></textarea> |
| 179 |
<button type="submit" class="btn">Request an increase</button> |
| 180 |
</form> |
| 181 |
{% endmatch %} |
| 182 |
<div id="mail-cap-status"></div> |
| 183 |
</details> |
| 184 |
{% endif %} |
| 185 |
|
| 186 |
<div class="section-group-label creator-section-label">Outreach</div> |
| 187 |
|
| 188 |
<details class="form-section creator-form-section"> |
| 189 |
<summary><h2 class="creator-h2 creator-h2--sm">Broadcast to Followers</h2></summary> |
| 190 |
<div class="broadcast-head mb-section"> |
| 191 |
<p class="text-sm muted m-0 broadcast-desc"> |
| 192 |
Send a plain-text announcement to everyone who follows you or your projects. |
| 193 |
Limited to one broadcast per 24 hours. |
| 194 |
{% if follower_count > 0 %} |
| 195 |
<strong>{{ follower_count }}</strong> follower{% if follower_count != 1 %}s{% endif %} will receive this email. |
| 196 |
{% else %} |
| 197 |
You don't have any followers yet. |
| 198 |
{% endif %} |
| 199 |
</p> |
| 200 |
{% if follower_count > 0 %} |
| 201 |
{# broadcast-export and nowrap are this page's layout, not the control's, |
| 202 |
so they stay on a wrapper the description does not own. #} |
| 203 |
<span class="broadcast-export nowrap">{{ crate::quasi::export_act::html("/api/export/followers", "followers.csv")|safe }}</span> |
| 204 |
{% endif %} |
| 205 |
</div> |
| 206 |
<form hx-post="/api/broadcast" hx-target="#broadcast-result" hx-swap="innerHTML" |
| 207 |
hx-confirm="Send this broadcast to {{ follower_count }} follower{% if follower_count != 1 %}s{% endif %}?"> |
| 208 |
<div class="form-group"> |
| 209 |
<label for="broadcast-subject">Subject</label> |
| 210 |
<input type="text" id="broadcast-subject" name="subject" required maxlength="200" |
| 211 |
placeholder="Subject line for your announcement"> |
| 212 |
</div> |
| 213 |
<div class="form-group"> |
| 214 |
<label for="broadcast-body">Message</label> |
| 215 |
<textarea id="broadcast-body" name="body" rows="6" required maxlength="5000" |
| 216 |
placeholder="Your announcement..."></textarea> |
| 217 |
</div> |
| 218 |
<div id="broadcast-result"></div> |
| 219 |
<button type="submit" class="btn-primary">Send Broadcast</button> |
| 220 |
</form> |
| 221 |
</details> |
| 222 |
|
| 223 |
{% if invites_enabled %} |
| 224 |
<details class="form-section creator-form-section"> |
| 225 |
<summary><h2 class="creator-h2 creator-h2--sm">Invite Codes</h2></summary> |
| 226 |
<p class="muted text-sm mb-section"> |
| 227 |
Invite people to Makenotwork. They skip the pitch but still need admin approval. |
| 228 |
{{ active_invite_count }}/{{ invite_limit }} active. |
| 229 |
</p> |
| 230 |
|
| 231 |
{% if active_invite_count < invite_limit %} |
| 232 |
<div id="invite-result" class="mb-section"></div> |
| 233 |
<button class="btn-primary mb-pane" |
| 234 |
hx-post="/api/invites/create" |
| 235 |
hx-target="#invite-result" |
| 236 |
hx-swap="innerHTML">Generate Invite Code</button> |
| 237 |
{% else %} |
| 238 |
<p class="dimmed text-sm mb-pane"> |
| 239 |
You have reached the limit of {{ invite_limit }} active codes. Codes free up when redeemed. |
| 240 |
</p> |
| 241 |
{% endif %} |
| 242 |
|
| 243 |
{% if !invite_codes.is_empty() %} |
| 244 |
<div class="scroll-x"> |
| 245 |
<table class="compact-table" aria-label="Invite codes"> |
| 246 |
<thead> |
| 247 |
<tr> |
| 248 |
<th>Code</th> |
| 249 |
<th>Status</th> |
| 250 |
<th>Created</th> |
| 251 |
</tr> |
| 252 |
</thead> |
| 253 |
<tbody> |
| 254 |
{% for code in invite_codes %} |
| 255 |
<tr> |
| 256 |
<td class="mono-sm">{{ code.formatted_code }}</td> |
| 257 |
<td> |
| 258 |
{% if code.redeemed %} |
| 259 |
<span class="badge dimmed">Redeemed</span> |
| 260 |
{% else %} |
| 261 |
<span class="badge" data-tone="success">Available</span> |
| 262 |
{% endif %} |
| 263 |
</td> |
| 264 |
<td class="text-sm">{{ code.created_at }}</td> |
| 265 |
</tr> |
| 266 |
{% endfor %} |
| 267 |
</tbody> |
| 268 |
</table> |
| 269 |
</div> |
| 270 |
{% endif %} |
| 271 |
</details> |
| 272 |
{% endif %} |
| 273 |
|
| 274 |
{% else %} |
| 275 |
|
| 276 |
<div class="creator-tab-section"> |
| 277 |
<h2 class="creator-h2">Become a Creator</h2> |
| 278 |
<p class="creator-intro mb-pane"> |
| 279 |
Apply for creator access to start publishing and selling your work. |
| 280 |
Tell us what you make and which tier fits. Most applications are approved within a few days. |
| 281 |
</p> |
| 282 |
</div> |
| 283 |
|
| 284 |
<hr class="creator-divider"> |
| 285 |
|
| 286 |
{% if let Some(entry) = waitlist_entry %} |
| 287 |
<div class="creator-tab-section"> |
| 288 |
<h2 class="creator-h2 mb-section">Application Submitted</h2> |
| 289 |
{% if let Some(pitch_text) = entry.pitch %} |
| 290 |
<div class="creator-pitch-box mb-section"> |
| 291 |
<div class="meta mb-peer">Your pitch</div> |
| 292 |
<p>{{ pitch_text }}</p> |
| 293 |
</div> |
| 294 |
{% else %} |
| 295 |
<div class="creator-pitch-box mb-section"> |
| 296 |
<p class="muted">You were invited: no pitch required.</p> |
| 297 |
</div> |
| 298 |
{% endif %} |
| 299 |
<div class="muted text-sm"> |
| 300 |
Applied {{ entry.created_at }} |
| 301 |
{% if entry.status == "pending" %} |
| 302 |
· Status: <strong>Pending review</strong> |
| 303 |
{% else if entry.status == "approved" %} |
| 304 |
· Status: <strong>Approved</strong> |
| 305 |
{% else %} |
| 306 |
· Status: {{ entry.status }} |
| 307 |
{% endif %} |
| 308 |
</div> |
| 309 |
<p class="text-sm mt-section"> |
| 310 |
We review applications individually, usually within a few days. You'll get an email when you're approved. |
| 311 |
</p> |
| 312 |
</div> |
| 313 |
{% else %} |
| 314 |
{% if email_verified %} |
| 315 |
<div class="creator-tab-section"> |
| 316 |
<h2 class="creator-h2 mb-section">Apply for Creator Access</h2> |
| 317 |
<p class="mb-pane"> |
| 318 |
Tell us what you make and which tier you need. If you have existing work online, |
| 319 |
link to it: a portfolio, channel, or profile elsewhere helps us review faster. |
| 320 |
</p> |
| 321 |
<form hx-post="/api/waitlist/apply" |
| 322 |
hx-target="closest div" |
| 323 |
hx-swap="innerHTML" |
| 324 |
id="waitlist-form"> |
| 325 |
<div class="form-group"> |
| 326 |
<label for="pitch">What do you make?</label> |
| 327 |
<textarea id="pitch" name="pitch" rows="4" |
| 328 |
minlength="20" maxlength="500" required |
| 329 |
placeholder="Briefly describe your work. A link to your portfolio, channel, or existing storefront is helpful."></textarea> |
| 330 |
<div class="hint">20-500 characters.</div> |
| 331 |
</div> |
| 332 |
<div class="form-group"> |
| 333 |
<label for="preferred-tier">Which tier fits?</label> |
| 334 |
<select id="preferred-tier" name="preferred_tier"> |
| 335 |
<option value="">Not sure yet</option> |
| 336 |
{# Guard the positional indexing: Askama `[N]` panics (raw 500) if the |
| 337 |
card list ever shrinks. Safe today (four fixed tiers); the length |
| 338 |
check keeps it from becoming a 500 if tiers go data-driven. #} |
| 339 |
{% if tier_cards.len() >= 4 %} |
| 340 |
<option value="basic">Basic (${{ tier_cards[0].standard_monthly }}/mo): text, blogs, newsletters</option> |
| 341 |
<option value="small_files">Small Files (${{ tier_cards[1].standard_monthly }}/mo): audio, software, plugins</option> |
| 342 |
<option value="big_files">Big Files (${{ tier_cards[2].standard_monthly }}/mo): video, games, large software</option> |
| 343 |
<option value="everything">Everything (${{ tier_cards[3].standard_monthly }}/mo): all features, current and future</option> |
| 344 |
{% endif %} |
| 345 |
</select> |
| 346 |
</div> |
| 347 |
<div class="form-group"> |
| 348 |
<label> |
| 349 |
<input type="checkbox" id="free-trial" name="free_trial" value="yes" |
| 350 |
data-change="toggleTrialDetails"> |
| 351 |
I'd like to request a free trial |
| 352 |
</label> |
| 353 |
</div> |
| 354 |
<div id="trial-details" class="hidden"> |
| 355 |
<div class="form-group"> |
| 356 |
<label for="trial-length">How long do you need?</label> |
| 357 |
<select id="trial-length" name="trial_length"> |
| 358 |
<option value="2 weeks">2 weeks</option> |
| 359 |
<option value="1 month" selected>1 month</option> |
| 360 |
<option value="2 months">2 months</option> |
| 361 |
<option value="3 months">3 months</option> |
| 362 |
</select> |
| 363 |
</div> |
| 364 |
<div class="form-group"> |
| 365 |
<label for="trial-reason">What do you want to test?</label> |
| 366 |
<textarea id="trial-reason" name="trial_reason" rows="3" maxlength="500" |
| 367 |
placeholder="What feature or workflow are you unsure about? What do you need to try before committing?"></textarea> |
| 368 |
<div class="hint">Longer trials should have a specific reason: a feature you need to evaluate, a workflow you want to test with real content, etc.</div> |
| 369 |
</div> |
| 370 |
</div> |
| 371 |
<div id="apply-result"></div> |
| 372 |
<button type="submit" class="btn-primary">Submit Application</button> |
| 373 |
</form> |
| 374 |
</div> |
| 375 |
{% else %} |
| 376 |
<div class="creator-tab-section creator-tab-section--centered"> |
| 377 |
<h2 class="creator-h2 mb-section">Verify Your Email First</h2> |
| 378 |
<p class="muted mb-pane"> |
| 379 |
You need a verified email address to apply for creator access. |
| 380 |
</p> |
| 381 |
<button class="btn-primary" |
| 382 |
hx-post="/api/resend-verification" |
| 383 |
hx-target="#verify-result" |
| 384 |
hx-swap="innerHTML">Resend Verification Email</button> |
| 385 |
<div id="verify-result" class="mt-section"></div> |
| 386 |
</div> |
| 387 |
{% endif %} |
| 388 |
{% endif %} |
| 389 |
{% endif %} |
| 390 |
|