public: landing + pricing + legal pages, apex-ready, lawyer-reviewed

Adds the unauthenticated surface that's needed to invite outsiders:

  - Landing (/) — dual-purpose root: dashboard for logged-in users,
    landing for everyone else. New maybe_current_user soft-auth helper
    in app/auth.py supports it without disturbing the per-route
    require_token deps on /news, /log, /upload, /settings.
  - About, Pricing, Disclaimer, Terms, Privacy — own router
    (app/routers/public.py), no auth dep, shared public_base layout
    (brand link, thin nav, footer with legal links + ICO ref + date).
  - Editorial positioning: news aggregator with a macro brain; tagline
    "Understand markets. Don't gamble on them."; anti-trading-as-gambling
    stance carried through About and Landing.

Legal pass following an independent lawyer-style review:

  - Privacy: explicit UK-GDPR Art. 6 lawful-basis section; Art. 22
    automated-decision line; explicit consent for sessionStorage sync
    key (PECR); 30-day IP-log retention; Art. 21 objection right;
    Children clause; Art. 33/34 breach-notification clause;
    international-transfer mechanism (IDTA + UK Addendum). ICO
    registration ZC098928 surfaced at the top.
  - Pricing: paid-card AI-portfolio-analysis bullet rewritten to remove
    advice-shaped wording ("what would invalidate the posture" gone);
    added italic carve-out citing FSMA / FCA COBS.
  - Disclaimer: separate EU/EEA carve-out + MAR 596/2014 Art. 3(1)(34)
    commentator safe-harbour; "qualifies the Terms" line; hallucination
    wording fixed.
  - Terms: cl.4 explicit AI-training prohibition + harassment line;
    cl.5 CCR 2013 14-day cancellation; cl.7 softened AI copyright
    claim under CDPA s.9(3) ambiguity; cl.8 proportionate suspension +
    pro-rata refund for paid users; cl.10 CRA 2015 Pt 1 statutory-rights
    carve-out from the liability cap; cl.11 right to close account on
    material change; cl.12 non-exclusive jurisdiction + UK consumer
    local courts.

Code-side enforcement of the Privacy claim:

  - openrouter.py: outbound OpenRouter calls now carry
    X-OR-Allow-Training: false. DeepSeek doesn't expose a per-request
    flag; the Privacy page discloses this caveat verbatim.

Apex domain prep:

  - branding.APP_URL flipped to https://read.markets (was app.). DNS for
    the apex already resolves; pending operator NPM step is a cert that
    covers the bare apex + a 301 from app.read.markets. No hard-coded
    subdomain references remain in code (verified with grep).

Nav + chrome:

  - app dropdown gains Pricing / Terms / Privacy / Disclaimer links.
  - login.html gains a small legal-links footer for the
    highest-leverage moment to surface them.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Giorgio Gilestro 2026-05-24 00:08:02 +02:00
parent 6f9a710726
commit f1903e1e61
17 changed files with 1436 additions and 10 deletions

View file

@ -0,0 +1,96 @@
{% extends "public_base.html" %}
{% block title %}{{ BRAND_NAME }} &middot; Pricing{% endblock %}
{% block main %}
<section class="public-section">
<h1 class="public-section__head">Pricing</h1>
<p>
Two tiers. The news aggregator and the hourly macro interpretation
are free for everyone &mdash; we want the read out where people can use
it. The paid tier extends the same editorial commentary to the
specific tickers in a portfolio you upload &mdash; an educational read
of public data, not advice on whether to hold them.
</p>
</section>
<section class="tier-grid">
<div class="tier-card">
<div class="tier-card__name">Free</div>
<div class="tier-card__price">&pound;0</div>
<div class="tier-card__price-hint">Forever. No card needed.</div>
<ul>
<li>News aggregator &mdash; auto-tagged by theme</li>
<li>Cross-asset macro signals across every asset class</li>
<li>Hourly AI interpretation of the news + the tape</li>
<li>Per-group cross-asset summaries</li>
<li>Novice / Intermediate reading levels</li>
<li class="tier-card__excluded">Portfolio import &amp; analysis</li>
<li class="tier-card__excluded">Encrypted cloud sync</li>
</ul>
<div class="tier-card__cta">
{% if cu and (cu.user or cu.is_admin) %}
<a class="btn-secondary" href="/">Open dashboard</a>
{% else %}
<a class="btn-primary" href="/login">Sign up free</a>
{% endif %}
</div>
</div>
<div class="tier-card tier-card--featured">
<div class="tier-card__name">Paid</div>
<div class="tier-card__price">Coming soon</div>
<div class="tier-card__price-hint">Checkout opens with our payments rollout.</div>
<ul>
<li>Everything in Free</li>
<li>Portfolio import (Trading 212 CSV)</li>
<li>AI commentary on diversification, sector and currency concentration, and macro-regime context for the holdings you upload</li>
<li>Optional encrypted cloud sync across devices</li>
<li>Priority email when something material changes (later)</li>
</ul>
<div class="tier-card__cta">
{% if cu and (cu.user or cu.is_admin) %}
<a class="btn-secondary" href="/settings">Manage account</a>
{% else %}
<a class="btn-primary" href="/login">Sign up &mdash; paid unlocks soon</a>
{% endif %}
</div>
<p style="margin-top:14px; font-size:11.5px; color: var(--muted); font-style: italic; line-height:1.55;">
The portfolio feature does not produce buy, sell or hold
recommendations. It does not consider your wider finances, debts,
tax position or objectives. It is not regulated investment advice
or a personal recommendation under FSMA / FCA COBS.
</p>
</div>
</section>
<section class="public-section">
<h2 class="public-section__head">How the data is handled</h2>
<p>
Your portfolio holdings live in your browser&rsquo;s local storage by
default. The server only learns which Yahoo tickers appear across the
user base &mdash; an anonymous union, with no link back to any specific
user.
</p>
<p>
If you opt in to <strong>encrypted cloud sync</strong>, your pie is
encrypted in your browser with a PIN you choose, then sent to the
server. We add a second layer of encryption with a key only the
server holds. We never see your holdings as plaintext, and forgetting
the PIN means we can&rsquo;t recover it for you. Full details on the
<a href="/privacy">privacy page</a>.
</p>
</section>
<section class="public-section public-section--callout">
<p style="margin:0;">
<strong>Not investment advice.</strong> Every output here is an
interpretation of public data &mdash; not personalised advice, not a
recommendation, and not produced by a regulated entity. Read the full
<a href="/disclaimer">disclaimer</a> before relying on anything you see.
</p>
</section>
{% endblock %}