212 lines
10 KiB
Plaintext
212 lines
10 KiB
Plaintext
@model PricingViewModel
|
|
@{
|
|
ViewData["Title"] = "Pricing";
|
|
ViewData["ShellClass"] = "marketing-shell";
|
|
var paidPlans = Model.Plans.Where(x => x.RequiresStripeSubscription).ToList();
|
|
var suggestedPlanId = paidPlans.Count >= 2 ? paidPlans[paidPlans.Count / 2].SubscriptionLevelID : (int?)null;
|
|
}
|
|
|
|
<article class="marketing-home pricing-page">
|
|
<section class="pricing-hero">
|
|
<div class="marketing-container pricing-hero__inner">
|
|
<p class="marketing-eyebrow">Pricing</p>
|
|
<h1>Simple pricing for stories of every size.</h1>
|
|
<p class="marketing-lead">Start free, subscribe when you're ready, or choose a plan straight away.</p>
|
|
<div class="marketing-actions">
|
|
<a class="marketing-btn marketing-btn--primary" asp-controller="Pricing" asp-action="StartTrial">Start Free Trial</a>
|
|
<a class="marketing-btn marketing-btn--secondary" href="#plans">Compare Plans</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
@if (TempData["PricingMessage"] is string pricingMessage)
|
|
{
|
|
<div class="marketing-container">
|
|
<div class="pricing-message" role="status">@pricingMessage</div>
|
|
</div>
|
|
}
|
|
|
|
<section class="pricing-plans" id="plans" aria-labelledby="plans-heading">
|
|
<div class="marketing-container">
|
|
<div class="section-heading">
|
|
<p class="marketing-eyebrow">Plans</p>
|
|
<h2 id="plans-heading">Choose the space your story needs.</h2>
|
|
</div>
|
|
|
|
@if (!Model.Plans.Any())
|
|
{
|
|
<div class="pricing-empty">
|
|
<h2>Plans are being prepared.</h2>
|
|
<p>Active subscription levels are not available yet. Please check back soon.</p>
|
|
</div>
|
|
}
|
|
else
|
|
{
|
|
<div class="pricing-card-grid">
|
|
@foreach (var plan in Model.Plans)
|
|
{
|
|
var isCurrent = Model.CurrentSubscriptionLevelID == plan.SubscriptionLevelID;
|
|
var isSuggested = suggestedPlanId == plan.SubscriptionLevelID;
|
|
<article class="pricing-card @(isSuggested ? "pricing-card--featured" : string.Empty)">
|
|
@if (isSuggested)
|
|
{
|
|
<p class="pricing-badge">Most Popular</p>
|
|
}
|
|
<div class="pricing-card__heading">
|
|
<h3>@plan.DisplayName</h3>
|
|
<p>@PlanTagline(plan)</p>
|
|
</div>
|
|
|
|
<div class="pricing-price">
|
|
@if (plan.RequiresStripeSubscription)
|
|
{
|
|
<strong>@FormatMoney(plan.MonthlyPricePence, plan.CurrencyCode)</strong>
|
|
<span>per month</span>
|
|
@if (plan.AnnualPricePence.HasValue)
|
|
{
|
|
<small>@FormatMoney(plan.AnnualPricePence, plan.CurrencyCode) billed annually</small>
|
|
}
|
|
}
|
|
else
|
|
{
|
|
<strong>Free</strong>
|
|
<span>trial workspace</span>
|
|
<small>Assigned when you create an account.</small>
|
|
}
|
|
</div>
|
|
|
|
<ul class="pricing-feature-list">
|
|
<li>@FormatLimit(plan.MaxBooks) books</li>
|
|
<li>@FormatLimit(plan.MaxCharactersPerProject) characters per project</li>
|
|
<li>@FormatLimit(plan.MaxScenesPerBook) scenes per book</li>
|
|
<li>@FormatStorageAllowance(plan.MaxStorageMB) storage</li>
|
|
<li>@FormatLimit(plan.MaxCollaborators) collaborators</li>
|
|
</ul>
|
|
|
|
<div class="pricing-card__actions">
|
|
@if (!plan.RequiresStripeSubscription)
|
|
{
|
|
<a class="marketing-btn marketing-btn--primary" asp-controller="Pricing" asp-action="StartTrial">Start Free Trial</a>
|
|
}
|
|
else
|
|
{
|
|
<a class="marketing-btn marketing-btn--primary" asp-controller="Pricing" asp-action="Checkout" asp-route-id="@plan.SubscriptionLevelID" asp-route-billingInterval="Monthly">
|
|
@(isCurrent ? "Current Plan" : "Subscribe Now")
|
|
</a>
|
|
@if (plan.AnnualPricePence.HasValue)
|
|
{
|
|
<a class="pricing-link" asp-controller="Pricing" asp-action="Checkout" asp-route-id="@plan.SubscriptionLevelID" asp-route-billingInterval="Annual">Choose annual billing</a>
|
|
}
|
|
}
|
|
</div>
|
|
</article>
|
|
}
|
|
</div>
|
|
}
|
|
</div>
|
|
</section>
|
|
|
|
<section class="pricing-compare" aria-labelledby="compare-heading">
|
|
<div class="marketing-container">
|
|
<div class="section-heading">
|
|
<p class="marketing-eyebrow">What is included</p>
|
|
<h2 id="compare-heading">Everything you need to keep a complex novel coherent.</h2>
|
|
</div>
|
|
<div class="comparison-grid">
|
|
<article>
|
|
<h3>Planning capacity</h3>
|
|
<p>Database-backed limits for books, chapters, scenes, characters, storage, and collaborators scale with the plan you choose.</p>
|
|
</article>
|
|
<article>
|
|
<h3>Story organisation</h3>
|
|
<p>Characters, plot threads, story assets, locations, and the story bible help keep the moving pieces of your manuscript in one place.</p>
|
|
</article>
|
|
<article>
|
|
<h3>Structure and flow</h3>
|
|
<p>Timeline, scene metrics, story health, and analytics views help you understand pacing, pressure, and revision priorities.</p>
|
|
</article>
|
|
<article>
|
|
<h3>Continuity confidence</h3>
|
|
<p>Relationship mapping, continuity warnings, import, export, backups, and restore points support serious drafting and revision work.</p>
|
|
</article>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="pricing-faq" aria-labelledby="faq-heading">
|
|
<div class="marketing-container">
|
|
<div class="section-heading">
|
|
<p class="marketing-eyebrow">Questions</p>
|
|
<h2 id="faq-heading">Pricing FAQ</h2>
|
|
</div>
|
|
<div class="faq-grid">
|
|
<article>
|
|
<h3>Can I start with a free trial?</h3>
|
|
<p>Yes. Creating an account starts you on the Draft Desk evaluation plan, so you can try the workspace before choosing a paid subscription.</p>
|
|
</article>
|
|
<article>
|
|
<h3>Can I subscribe without using the trial?</h3>
|
|
<p>Yes. Choose a paid plan from this page. If you are not signed in, PlotWeaver will ask you to log in before continuing to checkout.</p>
|
|
</article>
|
|
<article>
|
|
<h3>Can I change plan later?</h3>
|
|
<p>Yes. Signed-in users can manage subscription changes from the account subscription area.</p>
|
|
</article>
|
|
<article>
|
|
<h3>What happens to my work if I cancel?</h3>
|
|
<p>Your existing story data remains in PlotWeaver. Your account may return to the application-managed Draft Desk level after the paid subscription ends.</p>
|
|
</article>
|
|
<article>
|
|
<h3>Is PlotWeaver suitable for a series?</h3>
|
|
<p>Yes. The higher plan limits are designed for authors managing multiple books, long arcs, and larger casts.</p>
|
|
</article>
|
|
<article>
|
|
<h3>Do I need a credit card for the trial?</h3>
|
|
<p>The registration flow creates the evaluation workspace. Payment details are only handled when you choose a paid subscription through Stripe Checkout.</p>
|
|
</article>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="final-cta">
|
|
<div class="marketing-container final-cta__inner">
|
|
<p class="marketing-eyebrow">Begin with clarity</p>
|
|
<h2>Ready to take control of your story?</h2>
|
|
<p>Try PlotWeaver Studio free, or choose the paid plan that fits the novel you are building.</p>
|
|
<div class="marketing-actions">
|
|
<a class="marketing-btn marketing-btn--primary" asp-controller="Pricing" asp-action="StartTrial">Start Free Trial</a>
|
|
<a class="marketing-btn marketing-btn--secondary" href="#plans">View Plans</a>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</article>
|
|
|
|
@functions {
|
|
private const int PracticalUnlimited = 999999;
|
|
|
|
private static string FormatLimit(int value) =>
|
|
value >= PracticalUnlimited ? "Unlimited" : value.ToString("N0");
|
|
|
|
private static string FormatStorageAllowance(int megabytes) =>
|
|
megabytes >= PracticalUnlimited
|
|
? "Unlimited"
|
|
: StorageUsage.FormatBytes(megabytes * 1024L * 1024L);
|
|
|
|
private static string FormatMoney(int? pence, string? currencyCode)
|
|
{
|
|
if (!pence.HasValue)
|
|
{
|
|
return "Contact us";
|
|
}
|
|
|
|
return string.Equals(currencyCode, "GBP", StringComparison.OrdinalIgnoreCase)
|
|
? $"GBP {pence.Value / 100m:0.00}"
|
|
: $"{pence.Value / 100m:0.00} {(string.IsNullOrWhiteSpace(currencyCode) ? "GBP" : currencyCode.ToUpperInvariant())}";
|
|
}
|
|
|
|
private static string PlanTagline(SubscriptionLevel plan) =>
|
|
string.IsNullOrWhiteSpace(plan.Description)
|
|
? plan.RequiresStripeSubscription ? "For authors ready to build with confidence." : "A calm place to begin."
|
|
: plan.Description;
|
|
}
|