Files
gh-christianlouis-docuelevate/frontend/templates/pricing.html
T
copilot-swe-agent[bot] 7f521eb755 fix(subscriptions): address code review feedback
- Use shared _require_admin from admin_users in subscriptions API endpoint
- Remove unnecessary Alpine.js hidden-div workaround in pricing.html
- Replace fragile string replace for OCR page count with proper Jinja {:,} format
- Improve comment wording in upload quota cleanup code
- Extract _scalar_count() helper in subscription.py to reduce repetition
- Add aria-valuemin='0' to all progressbar elements in subscription/index templates

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
2026-03-06 16:10:00 +00:00

371 lines
18 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}Pricing & Plans DocuElevate{% endblock %}
{% block content %}
<div class="bg-gray-50 min-h-screen">
<!-- ── Hero ──────────────────────────────────────────────────────────────── -->
<div class="bg-gradient-to-br from-blue-700 via-indigo-700 to-purple-700 text-white py-20 px-4">
<div class="max-w-4xl mx-auto text-center">
<span class="inline-block bg-white/20 text-white text-xs font-semibold uppercase tracking-widest px-3 py-1 rounded-full mb-4">
Simple, transparent pricing
</span>
<h1 class="text-4xl sm:text-5xl font-extrabold mb-4 leading-tight">
Choose the plan that's right for you
</h1>
<p class="text-indigo-100 text-lg max-w-2xl mx-auto">
From free exploration to unlimited enterprise processing — scale as your document workflows grow.
</p>
<!-- Annual / Monthly toggle (cosmetic — actual billing handled separately) -->
<div class="mt-8 inline-flex items-center bg-white/10 rounded-full p-1 gap-1" x-data="{ annual: false }">
<button
@click="annual = false"
:class="!annual ? 'bg-white text-indigo-700 shadow' : 'text-white'"
class="px-5 py-2 rounded-full text-sm font-semibold transition-all duration-200"
>Monthly</button>
<button
@click="annual = true"
:class="annual ? 'bg-white text-indigo-700 shadow' : 'text-white'"
class="px-5 py-2 rounded-full text-sm font-semibold transition-all duration-200"
>
Annual <span class="ml-1 text-xs bg-green-400 text-green-900 rounded-full px-2 py-0.5 font-bold">Save ~17%</span>
</button>
</div>
</div>
</div>
<!-- ── Tier cards ────────────────────────────────────────────────────────── -->
<div class="max-w-7xl mx-auto px-4 -mt-10 pb-20" x-data="{ annual: false }">
<!-- Recreate the toggle state here so cards react to the hero toggle too -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 items-stretch">
{% for tier in tiers %}
<div class="relative flex flex-col rounded-2xl shadow-lg overflow-hidden
{% if tier.highlight %}ring-4 ring-indigo-500 scale-105 z-10 bg-white{% else %}bg-white{% endif %}
transition-transform duration-200 hover:shadow-xl"
>
<!-- Popular badge -->
{% if tier.badge %}
<div class="absolute top-0 right-0 mt-4 mr-4">
<span class="
{% if tier.badge == 'Most Popular' %}bg-indigo-600 text-white
{% elif tier.badge == 'Best Value' %}bg-green-600 text-white
{% else %}bg-gray-600 text-white{% endif %}
text-xs font-bold uppercase tracking-wide px-3 py-1 rounded-full shadow"
>
{{ tier.badge }}
</span>
</div>
{% endif %}
<!-- Card header -->
<div class="p-6 pb-4 border-b border-gray-100">
<h2 class="text-xl font-bold text-gray-900">{{ tier.name }}</h2>
<p class="text-gray-500 text-sm mt-1">{{ tier.tagline }}</p>
<!-- Price -->
<div class="mt-4">
<template x-if="!annual">
<div>
{% if tier.price_monthly == 0 %}
<span class="text-4xl font-extrabold text-gray-900">Free</span>
{% else %}
<span class="text-4xl font-extrabold text-gray-900">${{ tier.price_monthly }}</span>
<span class="text-gray-500 text-sm">/month</span>
{% endif %}
</div>
</template>
<template x-if="annual">
<div>
{% if tier.price_yearly == 0 %}
<span class="text-4xl font-extrabold text-gray-900">Free</span>
{% else %}
<span class="text-4xl font-extrabold text-gray-900">${{ tier.price_yearly }}</span>
<span class="text-gray-500 text-sm">/year</span>
<div class="text-xs text-green-600 font-semibold mt-0.5">
~${{ (tier.price_yearly / 12) | round(0) | int }}/month billed annually
</div>
{% endif %}
</div>
</template>
</div>
</div>
<!-- Features list -->
<div class="p-6 flex-1">
<ul class="space-y-3 text-sm text-gray-600">
{% for feature in tier.features %}
<li class="flex items-start gap-2">
<i class="fas fa-check-circle text-green-500 mt-0.5 flex-shrink-0" aria-hidden="true"></i>
<span>{{ feature }}</span>
</li>
{% endfor %}
</ul>
</div>
<!-- CTA button -->
<div class="p-6 pt-0">
{% if tier.id == 'free' %}
<a href="/login"
class="block w-full text-center py-3 px-4 rounded-lg border-2 border-blue-600 text-blue-600 font-semibold hover:bg-blue-50 transition"
>{{ tier.cta }}</a>
{% elif tier.id == 'business' %}
<a href="mailto:sales@docuelevate.io?subject=Business+Plan+Enquiry"
class="block w-full text-center py-3 px-4 rounded-lg bg-gray-800 text-white font-semibold hover:bg-gray-700 transition"
>{{ tier.cta }}</a>
{% elif tier.highlight %}
<a href="/login"
class="block w-full text-center py-3 px-4 rounded-lg bg-indigo-600 text-white font-semibold hover:bg-indigo-700 transition shadow-md"
>{{ tier.cta }}</a>
{% else %}
<a href="/login"
class="block w-full text-center py-3 px-4 rounded-lg bg-blue-600 text-white font-semibold hover:bg-blue-700 transition"
>{{ tier.cta }}</a>
{% endif %}
</div>
</div>
{% endfor %}
</div>
<!-- ── Comparison table ───────────────────────────────────────────────── -->
<div class="mt-20">
<h2 class="text-2xl font-bold text-gray-900 text-center mb-8">Full feature comparison</h2>
<div class="overflow-x-auto rounded-2xl shadow">
<table class="min-w-full bg-white divide-y divide-gray-200" aria-label="Plan comparison">
<thead>
<tr class="bg-gray-50">
<th scope="col" class="px-6 py-4 text-left text-sm font-semibold text-gray-700 w-1/3">Feature</th>
{% for tier in tiers %}
<th scope="col" class="px-4 py-4 text-center text-sm font-semibold
{% if tier.highlight %}text-indigo-700{% else %}text-gray-700{% endif %}">
{{ tier.name }}
</th>
{% endfor %}
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
<tr class="bg-gray-50/50">
<th scope="row" colspan="5" class="px-6 py-2 text-xs font-bold text-gray-400 uppercase tracking-wider text-left">
File Processing
</th>
</tr>
<tr class="hover:bg-indigo-50/30 transition">
<td class="px-6 py-3 text-sm text-gray-700">Lifetime file limit</td>
{% for tier in tiers %}
<td class="px-4 py-3 text-center text-sm">
{% if tier.lifetime_file_limit == 0 %}
<span class="font-semibold text-green-600">Unlimited</span>
{% else %}
<span class="font-medium text-gray-800">{{ tier.lifetime_file_limit }}</span>
{% endif %}
</td>
{% endfor %}
</tr>
<tr class="hover:bg-indigo-50/30 transition">
<td class="px-6 py-3 text-sm text-gray-700">Files per day</td>
{% for tier in tiers %}
<td class="px-4 py-3 text-center text-sm">
{% if tier.daily_upload_limit == 0 %}
<span class="font-semibold text-green-600">Unlimited</span>
{% else %}
<span class="font-medium text-gray-800">{{ tier.daily_upload_limit }}</span>
{% endif %}
</td>
{% endfor %}
</tr>
<tr class="hover:bg-indigo-50/30 transition">
<td class="px-6 py-3 text-sm text-gray-700">Files per month</td>
{% for tier in tiers %}
<td class="px-4 py-3 text-center text-sm">
{% if tier.monthly_upload_limit == 0 %}
<span class="font-semibold text-green-600">Unlimited</span>
{% else %}
<span class="font-medium text-gray-800">{{ tier.monthly_upload_limit }}</span>
{% endif %}
</td>
{% endfor %}
</tr>
<tr class="hover:bg-indigo-50/30 transition">
<td class="px-6 py-3 text-sm text-gray-700">Max file size</td>
{% for tier in tiers %}
<td class="px-4 py-3 text-center text-sm">
{% if tier.max_file_size_mb == 0 %}
<span class="font-semibold text-green-600">Unlimited</span>
{% else %}
<span class="font-medium text-gray-800">{{ tier.max_file_size_mb }} MB</span>
{% endif %}
</td>
{% endfor %}
</tr>
<tr class="bg-gray-50/50">
<th scope="row" colspan="5" class="px-6 py-2 text-xs font-bold text-gray-400 uppercase tracking-wider text-left">
OCR & AI
</th>
</tr>
<tr class="hover:bg-indigo-50/30 transition">
<td class="px-6 py-3 text-sm text-gray-700">OCR pages / month</td>
{% for tier in tiers %}
<td class="px-4 py-3 text-center text-sm">
{% if tier.max_ocr_pages_monthly == 0 %}
<span class="font-semibold text-green-600">Unlimited</span>
{% else %}
<span class="font-medium text-gray-800">{{ "{:,}".format(tier.max_ocr_pages_monthly) }}</span>
{% endif %}
</td>
{% endfor %}
</tr>
<tr class="hover:bg-indigo-50/30 transition">
<td class="px-6 py-3 text-sm text-gray-700">AI metadata extraction</td>
<td class="px-4 py-3 text-center">
<span class="text-xs text-gray-500">Basic</span>
</td>
{% for tier in tiers[1:] %}
<td class="px-4 py-3 text-center">
<i class="fas fa-check text-green-500" aria-label="Included"></i>
</td>
{% endfor %}
</tr>
<tr class="bg-gray-50/50">
<th scope="row" colspan="5" class="px-6 py-2 text-xs font-bold text-gray-400 uppercase tracking-wider text-left">
Integrations
</th>
</tr>
<tr class="hover:bg-indigo-50/30 transition">
<td class="px-6 py-3 text-sm text-gray-700">Storage destinations</td>
{% for tier in tiers %}
<td class="px-4 py-3 text-center text-sm">
{% if tier.max_storage_destinations == 0 %}
<span class="font-semibold text-green-600">Unlimited</span>
{% else %}
<span class="font-medium text-gray-800">{{ tier.max_storage_destinations }}</span>
{% endif %}
</td>
{% endfor %}
</tr>
<tr class="hover:bg-indigo-50/30 transition">
<td class="px-6 py-3 text-sm text-gray-700">Email ingestion</td>
<td class="px-4 py-3 text-center">
<i class="fas fa-times text-gray-300" aria-label="Not included"></i>
</td>
{% for tier in tiers[1:] %}
<td class="px-4 py-3 text-center">
<i class="fas fa-check text-green-500" aria-label="Included"></i>
</td>
{% endfor %}
</tr>
<tr class="hover:bg-indigo-50/30 transition">
<td class="px-6 py-3 text-sm text-gray-700">Webhooks</td>
<td class="px-4 py-3 text-center">
<i class="fas fa-times text-gray-300" aria-label="Not included"></i>
</td>
<td class="px-4 py-3 text-center">
<i class="fas fa-times text-gray-300" aria-label="Not included"></i>
</td>
<td class="px-4 py-3 text-center">
<i class="fas fa-check text-green-500" aria-label="Included"></i>
</td>
<td class="px-4 py-3 text-center">
<i class="fas fa-check text-green-500" aria-label="Included"></i>
</td>
</tr>
<tr class="hover:bg-indigo-50/30 transition">
<td class="px-6 py-3 text-sm text-gray-700">API access</td>
<td class="px-4 py-3 text-center">
<i class="fas fa-times text-gray-300" aria-label="Not included"></i>
</td>
{% for tier in tiers[1:] %}
<td class="px-4 py-3 text-center">
<i class="fas fa-check text-green-500" aria-label="Included"></i>
</td>
{% endfor %}
</tr>
<tr class="bg-gray-50/50">
<th scope="row" colspan="5" class="px-6 py-2 text-xs font-bold text-gray-400 uppercase tracking-wider text-left">
Support
</th>
</tr>
<tr class="hover:bg-indigo-50/30 transition">
<td class="px-6 py-3 text-sm text-gray-700">Support level</td>
<td class="px-4 py-3 text-center text-xs text-gray-500">Community</td>
<td class="px-4 py-3 text-center text-xs text-gray-700">Email</td>
<td class="px-4 py-3 text-center text-xs text-gray-700 font-medium">Priority email</td>
<td class="px-4 py-3 text-center text-xs text-indigo-700 font-bold">Dedicated</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- ── FAQ ───────────────────────────────────────────────────────────── -->
<div class="mt-20 max-w-3xl mx-auto">
<h2 class="text-2xl font-bold text-gray-900 text-center mb-8">Frequently asked questions</h2>
<div class="space-y-4" x-data="{ open: null }">
{% set faqs = [
("What counts as a 'processed file'?",
"Every document you upload and run through the DocuElevate pipeline counts as one processed file — including OCR, AI metadata extraction, and storage delivery."),
("What happens when I reach the Free tier lifetime limit?",
"Once your 25-file lifetime quota is reached you will see a friendly upgrade prompt on the upload page and any further upload attempts will return a payment-required error until you upgrade to a paid plan."),
("Can I change my plan at any time?",
"Yes. Upgrades take effect immediately. Downgrades take effect at the start of the next billing cycle. Unused quota does not roll over between billing periods."),
("Is there an annual discount?",
"Yes — paying annually saves approximately 17% compared to monthly billing. The savings are shown in the annual pricing above."),
("Do you offer a trial for paid plans?",
"The Free tier lets you try DocuElevate with up to 25 files at no cost and with no credit card required. Paid trials can be arranged — contact sales."),
("What is a 'storage destination'?",
"A storage destination is any cloud or self-hosted storage you configure as an output — Dropbox, Google Drive, OneDrive, Nextcloud, S3, SFTP, FTP, WebDAV, or Paperless-ngx each count as one destination."),
] %}
{% for q, a in faqs %}
<div class="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
<button
@click="open = (open === {{ loop.index0 }}) ? null : {{ loop.index0 }}"
class="w-full flex justify-between items-center px-6 py-4 text-left text-sm font-semibold text-gray-800 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500"
:aria-expanded="open === {{ loop.index0 }}"
>
<span>{{ q }}</span>
<i class="fas text-indigo-400 ml-4 flex-shrink-0 transition-transform duration-200"
:class="open === {{ loop.index0 }} ? 'fa-chevron-up' : 'fa-chevron-down'"
aria-hidden="true"></i>
</button>
<div x-show="open === {{ loop.index0 }}" x-transition class="px-6 pb-4 text-sm text-gray-600">
{{ a }}
</div>
</div>
{% endfor %}
</div>
</div>
<!-- ── CTA strip ─────────────────────────────────────────────────────── -->
<div class="mt-20 rounded-2xl bg-gradient-to-r from-blue-600 to-indigo-700 text-white p-10 text-center shadow-xl">
<h2 class="text-2xl font-bold mb-2">Ready to get started?</h2>
<p class="text-indigo-100 mb-6">Start with the free tier — no credit card required.</p>
<a href="/login"
class="inline-block bg-white text-indigo-700 font-bold px-8 py-3 rounded-lg hover:bg-indigo-50 transition shadow">
Create your free account
</a>
</div>
</div>
</div>
{% endblock %}