read.markets/app/templates/landing.html
Giorgio Gilestro 47dce1a1a4 compliance: flag-gate AI portfolio + cloud sync + Stripe; de-risk prompts; harden reviewer
Implements docs/read-markets-compliance-changes.md as flag-gated changes
(no deletions) so paused features stay in the tree for future re-enable.
All four flags default False so a fresh deploy is compliance-safe.

- New env flags: PORTFOLIO_AI_ENABLED, PORTFOLIO_SYNC_ENABLED,
  TICKER_UNIVERSE_AGGREGATE_ENABLED, SUBSCRIPTIONS_ENABLED.
- Gates: /api/analyze, /api/portfolio/sync*, /api/stripe/*, /pricing,
  ticker_universe writes, portfolio_analysis.analyse(). is_paid_active()
  returns True for any auth'd user when subscriptions are paused.
- Prompts (PROMPT_VERSION 10): universal _COMPLIANCE_RIDER prepended to
  every system prompt; watch list removed; price-target / close-above-below
  / trigger / forward-state-as-description rules added; SPECULATIVE
  pivoted to regime-only scenarios; daily + weekly digests tightened.
- Reviewer: deterministic regex/lexicon pre-check fail-closed under the
  Haiku call; portfolio rider gated by PORTFOLIO_AI_ENABLED; base prompt
  sharpened for forward-state and MAR forward-opinion patterns;
  ReviewerVerdict audit table; generate_with_review retry helper.
- Migration 0026: purge portfolio_sync + ticker_universe; create
  reviewer_verdicts.
- Copy: MAR cite fixed to Art 3(1)(35) + Art 20 + Del Reg 2016/958;
  portfolio reframed as browser-only viewer in disclaimer / privacy /
  terms / about / pricing / landing (en + it). TODO(legal) marker for
  lawyer sign-off on disclaimer.
- Tests: 13 lexicon + 6 reviewer compliance regressions; conftest enables
  all flags so existing 402 tests still cover their code paths.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 19:57:12 +02:00

148 lines
6.1 KiB
HTML

{% extends "public_base.html" %}
{% block title %}{{ BRAND_NAME }} &middot; {{ t.hero.tagline }}{% endblock %}
{% block main %}
<section class="hero">
<div class="hero__brand">{{ BRAND_NAME }}</div>
<h1 class="hero__headline">{{ t.hero.tagline }}</h1>
<p class="hero__subhead">{{ t.hero.subhead | safe }}</p>
<div class="hero__ctas">
{% if cu and (cu.user or cu.is_admin) %}
<a class="btn-primary" href="/">{{ t.hero.cta_dashboard }}</a>
{% if SUBSCRIPTIONS_ENABLED %}<a class="btn-secondary" href="/pricing">{{ t.hero.cta_pricing }}</a>{% endif %}
{% else %}
<a class="btn-primary" href="/login">{{ t.hero.cta_signup }}</a>
{% if SUBSCRIPTIONS_ENABLED %}<a class="btn-secondary" href="/pricing">{{ t.hero.cta_pricing }}</a>{% endif %}
{% endif %}
</div>
</section>
<section class="shot-hero">
<button class="shot shot--hero"
data-full="{{ url_for('static', path='/images/dashboard.png') }}?v={{ ASSET_VERSION }}"
data-alt="{{ t.shot_dashboard.alt }}"
data-caption="{{ t.shot_dashboard.caption }}">
<img src="{{ url_for('static', path='/images/dashboard.png') }}?v={{ ASSET_VERSION }}"
alt="{{ t.shot_dashboard.alt }}" loading="lazy">
<span class="shot__zoom" aria-hidden="true">{{ t.shot_dashboard.zoom_hint }}</span>
</button>
</section>
<section class="feature-grid">
<div class="feature-card">
<div class="feature-card__tag">{{ t.features.news.tag }}</div>
<h3 class="feature-card__title">{{ t.features.news.title }}</h3>
<p class="feature-card__body">{{ t.features.news.body | safe }}</p>
<button class="shot feature-card__shot"
data-full="{{ url_for('static', path='/images/news-feed.png') }}?v={{ ASSET_VERSION }}"
data-alt="{{ t.features.news.shot_alt }}"
data-caption="{{ t.features.news.shot_caption }}">
<img src="{{ url_for('static', path='/images/news-feed.png') }}?v={{ ASSET_VERSION }}"
alt="{{ t.features.news.shot_alt }}" loading="lazy">
</button>
</div>
<div class="feature-card">
<div class="feature-card__tag">{{ t.features.indicators.tag }}</div>
<h3 class="feature-card__title">{{ t.features.indicators.title }}</h3>
<p class="feature-card__body">{{ t.features.indicators.body | safe }}</p>
<button class="shot feature-card__shot"
data-full="{{ url_for('static', path='/images/indicators-read.png') }}?v={{ ASSET_VERSION }}"
data-alt="{{ t.features.indicators.shot_alt }}"
data-caption="{{ t.features.indicators.shot_caption }}">
<img src="{{ url_for('static', path='/images/indicators-read.png') }}?v={{ ASSET_VERSION }}"
alt="{{ t.features.indicators.shot_alt }}" loading="lazy">
</button>
</div>
<div class="feature-card">
<div class="feature-card__tag">{{ t.features.strategic.tag }}</div>
<h3 class="feature-card__title">{{ t.features.strategic.title }}</h3>
<p class="feature-card__body">{{ t.features.strategic.body | safe }}</p>
<button class="shot feature-card__shot"
data-full="{{ url_for('static', path='/images/strategic-log.png') }}?v={{ ASSET_VERSION }}"
data-alt="{{ t.features.strategic.shot_alt }}"
data-caption="{{ t.features.strategic.shot_caption }}">
<img src="{{ url_for('static', path='/images/strategic-log.png') }}?v={{ ASSET_VERSION }}"
alt="{{ t.features.strategic.shot_alt }}" loading="lazy">
</button>
</div>
</section>
<section class="public-section" style="text-align:center;">
<p style="font-size: 14px; color: var(--muted); max-width: 60ch; margin: 0 auto;">
{{ t.multilang_callout | safe }}
</p>
</section>
<section class="public-section shots-section">
<h2 class="public-section__head">{{ t.more_views.head }}</h2>
<div class="shots-grid">
<button class="shot"
data-full="{{ url_for('static', path='/images/chat-with-log.png') }}?v={{ ASSET_VERSION }}"
data-alt="{{ t.more_views.chat.alt }}"
data-caption="{{ t.more_views.chat.caption }}">
<img src="{{ url_for('static', path='/images/chat-with-log.png') }}?v={{ ASSET_VERSION }}"
alt="{{ t.more_views.chat.alt }}" loading="lazy">
<div class="shot__caption">
<strong>{{ t.more_views.chat.caption_strong }}</strong>
<span>{{ t.more_views.chat.caption_span }}</span>
</div>
</button>
</div>
</section>
<section class="public-section">
<p style="font-size: 13.5px; color: var(--muted);">{{ t.portfolio_blurb | safe }}</p>
</section>
<section class="not-strip">
<strong>{{ t.not_strip.head }}</strong>
<ul>
{% for item in t.not_strip.items %}<li>{{ item }}</li>{% endfor %}
</ul>
</section>
<section class="public-section">
<p style="font-size: 13px; color: var(--muted);">{{ t.footer.legal | safe }}</p>
<div class="hero__ctas" style="margin-top:8px;">
{% if cu and (cu.user or cu.is_admin) %}
<a class="btn-primary" href="/">{{ t.hero.cta_dashboard }}</a>
{% else %}
<a class="btn-primary" href="/login">{{ t.hero.cta_signup }}</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">&times;</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 %}