Marketing + behaviour pass to get the site ready for Paddle approval.
Pricing page
- £7/month, £70/year headline (was "Coming soon").
- Bigger tier names (was 11px uppercase mono — looked like chips).
- Real CTAs (button base styles were only scoped to .hero__ctas).
- "Best value" badge + drop-shadow on the Paid card; full-width
block CTAs that align across both cards.
- "Free vs Paid at a glance" comparison table beneath the cards.
- Compact "Invite a friend — both get 50% off for 3 months"
callout with the detail explanation behind a <dialog> popup.
Tier copy + behaviour now consistent
- Free strategic-log refresh is every 6 hours, not hourly. New
read-side filter on /api/log/{latest,by-date} restricts free
users to logs at boundary hours (00/06/12/18 UTC); paid users
still see the most recent.
- Follow-up chat is paid-only. /api/chat returns 402 for free;
the chat sidebar on /log is replaced with a locked aside and
chat.js no longer loads at all for free users.
- Dashboard meta lines + landing copy softened so they no longer
promise hourly to everyone.
Future-proofing copy on public pages
- Dropped "free forever" wording (we may close the free tier).
- "Trading 212 CSV" became "broker CSV (Trading 212 today; more
planned)" on pricing + landing; the actual import UIs stay
T212-specific.
Terms
- Renamed Terms of Service -> Terms and Conditions (Paddle
expectation), bumped last-updated to 2026-05-26.
- New §6 Refunds covering the 14-day cooling off, post-window
cancellation, termination-by-us refunds, statutory rights, and
how to request a refund.
- Renumbered §7-§14 and fixed the disclaimer link labels.
Tests
- 6 new tests in tests/test_chat_and_log_gates.py cover the
chat 402 + the boundary-hour filter on both log endpoints.
- Full suite: 205 passed, 5 skipped, 0 failed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
183 lines
8.3 KiB
HTML
183 lines
8.3 KiB
HTML
{% extends "public_base.html" %}
|
|
{% block title %}{{ BRAND_NAME }} · {{ TAGLINE }}{% endblock %}
|
|
|
|
{% block main %}
|
|
|
|
<section class="hero">
|
|
<div class="hero__brand">{{ BRAND_NAME }}</div>
|
|
<h1 class="hero__headline">{{ TAGLINE }}</h1>
|
|
<p class="hero__subhead">
|
|
Built for investors who want to <strong>act rationally</strong> and
|
|
tune out the high-frequency noise that comes from treating markets
|
|
like a casino. We aggregate cross-asset news and macro signals,
|
|
then write a plain-English read of what the underlying fundamentals
|
|
justify versus what the crowd is doing. Refreshed through the
|
|
trading day. A media service, not a financial one.
|
|
</p>
|
|
<div class="hero__ctas">
|
|
{% if cu and (cu.user or cu.is_admin) %}
|
|
<a class="btn-primary" href="/">Open dashboard</a>
|
|
<a class="btn-secondary" href="/pricing">See pricing</a>
|
|
{% else %}
|
|
<a class="btn-primary" href="/login">Sign up free</a>
|
|
<a class="btn-secondary" href="/pricing">See pricing</a>
|
|
{% endif %}
|
|
</div>
|
|
</section>
|
|
|
|
<section class="shot-hero">
|
|
<button class="shot shot--hero"
|
|
data-full="{{ url_for('static', path='/images/dashboard.png') }}"
|
|
data-alt="Read the Markets dashboard"
|
|
data-caption="The dashboard. An aggregate cross-asset read at the top, hand-picked indicator groups underneath. Reading level toggle (Novice / Intermediate) flips every AI-generated panel between plain-English and terse-pro framing.">
|
|
<img src="{{ url_for('static', path='/images/dashboard.png') }}"
|
|
alt="Dashboard preview" loading="lazy">
|
|
<span class="shot__zoom" aria-hidden="true">Click to enlarge</span>
|
|
</button>
|
|
</section>
|
|
|
|
<section class="feature-grid">
|
|
<div class="feature-card">
|
|
<div class="feature-card__tag">News, aggregated</div>
|
|
<h3 class="feature-card__title">Headlines from across the macro universe</h3>
|
|
<p class="feature-card__body">
|
|
RSS and per-ticker feeds covering equities, rates, credit, FX,
|
|
commodities, and geopolitics. Every headline is auto-tagged by
|
|
theme so the noise stays as noise and the fundamentals-relevant
|
|
stuff is easy to find. Ingestion follows the trading calendar —
|
|
off-hours stay quiet.
|
|
</p>
|
|
<button class="shot feature-card__shot"
|
|
data-full="{{ url_for('static', path='/images/news-feed.png') }}"
|
|
data-alt="News feed with auto-tagged headlines"
|
|
data-caption="The news feed. Each headline carries one or more theme tags (rates, AI, energy, geopolitics, …) so you can keep the threads you care about and mute the ones you don't. Click a tag to include; shift-click to exclude.">
|
|
<img src="{{ url_for('static', path='/images/news-feed.png') }}"
|
|
alt="News feed thumbnail" loading="lazy">
|
|
</button>
|
|
</div>
|
|
|
|
<div class="feature-card">
|
|
<div class="feature-card__tag">Macro signals</div>
|
|
<h3 class="feature-card__title">A curated cross-asset tape</h3>
|
|
<p class="feature-card__body">
|
|
A hand-picked set of indicators across every asset class, refreshed
|
|
hourly during market hours. Each group gets a short read that
|
|
explains what the move <em>means</em>, not what it was. Anchored
|
|
in earnings, policy, valuation — not chart patterns.
|
|
</p>
|
|
<button class="shot feature-card__shot"
|
|
data-full="{{ url_for('static', path='/images/indicators-read.png') }}"
|
|
data-alt="Indicators panel with AI commentary"
|
|
data-caption="The indicators panel. Tabs across asset classes (equity, rates, commodities, FX, bonds, …); each tab carries a one-paragraph 'read' written by the model on top of the live prices. The numbers anchor the prose so the commentary is checkable, not floating.">
|
|
<img src="{{ url_for('static', path='/images/indicators-read.png') }}"
|
|
alt="Indicators panel thumbnail" loading="lazy">
|
|
</button>
|
|
</div>
|
|
|
|
<div class="feature-card">
|
|
<div class="feature-card__tag">The strategic read</div>
|
|
<h3 class="feature-card__title">Rational vs irrational, every paragraph</h3>
|
|
<p class="feature-card__body">
|
|
We tie the day’s headlines and the cross-asset signals into
|
|
a single short interpretation. Each paragraph separates
|
|
<strong>rational drivers</strong> (earnings, policy, valuation)
|
|
from <strong>irrational ones</strong> (positioning, narrative,
|
|
flows) and names the gap. Two reading levels: novice and
|
|
intermediate. This is editorial commentary on public data —
|
|
not a forecast and not advice on any investment decision.
|
|
</p>
|
|
<button class="shot feature-card__shot"
|
|
data-full="{{ url_for('static', path='/images/strategic-log.png') }}"
|
|
data-alt="Strategic log — the editorial AI read"
|
|
data-caption="The strategic log. The model writes a fresh interpretation through the trading day, taking the previous draft as context so it updates rather than starts over. Sections are typed: date header, TL;DR, what moved, what to watch, system temperature. Paid users get a refresh every hour; free users get one every six.">
|
|
<img src="{{ url_for('static', path='/images/strategic-log.png') }}"
|
|
alt="Strategic log thumbnail" loading="lazy">
|
|
</button>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="public-section shots-section">
|
|
<h2 class="public-section__head">More views</h2>
|
|
<div class="shots-grid">
|
|
<button class="shot"
|
|
data-full="{{ url_for('static', path='/images/chat-with-log.png') }}"
|
|
data-alt="Ask follow-up questions against any past log"
|
|
data-caption="Ask follow-up questions against any past log. The chat panel inherits the log's full context, so you can pull on a thread without re-pasting headlines or re-explaining the setup.">
|
|
<img src="{{ url_for('static', path='/images/chat-with-log.png') }}"
|
|
alt="Chat-with-log thumbnail" loading="lazy">
|
|
<div class="shot__caption">
|
|
<strong>Ask anything about a log</strong>
|
|
<span>Conversational follow-ups with the day's context loaded.</span>
|
|
</div>
|
|
</button>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="public-section">
|
|
<p style="font-size: 13.5px; color: var(--muted);">
|
|
Paid users can also drop a portfolio CSV from their broker
|
|
(Trading 212 today, more brokers planned) for an AI sense-check on
|
|
concentration, regime fit, and currency exposure. Holdings stay in
|
|
your browser by default; opt in to encrypted cloud sync to restore
|
|
on another device.
|
|
</p>
|
|
</section>
|
|
|
|
<section class="not-strip">
|
|
<strong>What this isn’t.</strong>
|
|
<ul>
|
|
<li>Not investment advice.</li>
|
|
<li>Not trading signals.</li>
|
|
<li>Not a day-trading tool.</li>
|
|
<li>No buy/sell calls, ever.</li>
|
|
<li>No chart-pattern predictions.</li>
|
|
<li>Not a regulated service.</li>
|
|
</ul>
|
|
</section>
|
|
|
|
<section class="public-section">
|
|
<p style="font-size: 13px; color: var(--muted);">
|
|
By signing up you agree to our <a href="/terms">Terms</a> and
|
|
<a href="/privacy">Privacy notice</a>, and confirm you’ve read
|
|
the <a href="/disclaimer">financial disclaimer</a>.
|
|
</p>
|
|
<div class="hero__ctas" style="margin-top:8px;">
|
|
{% if cu and (cu.user or cu.is_admin) %}
|
|
<a class="btn-primary" href="/">Open dashboard</a>
|
|
{% else %}
|
|
<a class="btn-primary" href="/login">Sign up free</a>
|
|
{% endif %}
|
|
</div>
|
|
</section>
|
|
|
|
<dialog id="shot-modal" class="shot-modal" aria-label="Screenshot preview">
|
|
<button class="shot-modal__close" type="button" aria-label="Close">×</button>
|
|
<img id="shot-modal__img" alt="">
|
|
<p id="shot-modal__caption"></p>
|
|
</dialog>
|
|
|
|
<script>
|
|
(function () {
|
|
var dlg = document.getElementById('shot-modal');
|
|
var img = document.getElementById('shot-modal__img');
|
|
var cap = document.getElementById('shot-modal__caption');
|
|
if (!dlg || !dlg.showModal) return; // gracefully skip on ancient browsers
|
|
document.querySelectorAll('.shot').forEach(function (btn) {
|
|
btn.addEventListener('click', function () {
|
|
img.src = btn.dataset.full;
|
|
img.alt = btn.dataset.alt || '';
|
|
cap.textContent = btn.dataset.caption || '';
|
|
dlg.showModal();
|
|
});
|
|
});
|
|
// Backdrop click closes; clicking the image itself does not.
|
|
dlg.addEventListener('click', function (e) {
|
|
if (e.target === dlg) dlg.close();
|
|
});
|
|
dlg.querySelector('.shot-modal__close').addEventListener('click', function () {
|
|
dlg.close();
|
|
});
|
|
})();
|
|
</script>
|
|
|
|
{% endblock %}
|