Sessions 5-7a: 955 tests, deployment ready

This commit is contained in:
Kev
2026-06-08 18:35:13 -04:00
parent 06b82624a2
commit 1fa04dc776
371 changed files with 49366 additions and 955 deletions
+134
View File
@@ -0,0 +1,134 @@
import type { Metadata } from 'next';
export const metadata: Metadata = {
title: 'Privacy Policy',
description: 'How VYNDR handles your data. Plain English.',
};
const SECTIONS: { title: string; body: string[]; emphasized?: boolean }[] = [
{
title: 'We never sell your personal data.',
body: [
'VYNDR Intelligence LLC does not sell, rent, or trade your personal data to anyone. Ever. Our business is a subscription to an analytics product — not a data resale operation.',
],
emphasized: true,
},
{
title: 'Data we collect',
body: [
'Account data: email, password hash, age confirmation, signup timestamp.',
'Usage data: reads you run (player, stat, line, sport, grade), parlays you build, page views.',
'Payment data: NexaPay processes all card data — we never see or store your card. We retain a NexaPay customer ID and your subscription status.',
'Device data: IP address, browser type, basic device info (for fraud prevention and analytics).',
],
},
{
title: 'How we use your data',
body: [
'To provide the service: sign you in, gate reads by tier, deliver grades.',
'To improve the model: aggregate, anonymized read data helps calibration. Individual reads are never used to identify you to third parties.',
'Aggregate trend features: "Most Read Tonight" and "Most Parlayed Tonight" use anonymized counts across all users. Your individual reads are not exposed.',
'To communicate: account confirmation, payment receipts, renewal reminders, critical service notices.',
],
},
{
title: 'What we do not do',
body: [
'We do not sell your personal data. Ever.',
'We do not share your individual betting activity with sportsbooks, advertisers, or data brokers.',
'We do not run ads. The business model is subscription, period.',
],
},
{
title: 'Data retention',
body: [
'Account data: retained while your account is active. Deleted on request.',
'Read history: retained indefinitely (anonymized after account deletion) for model calibration.',
'Payment records: retained for 7 years per US tax and accounting requirements.',
],
},
{
title: 'Cookies and analytics',
body: [
'Essential cookies: auth session, read counter (cannot be disabled — the service does not work without them).',
'Analytics: PostHog (anonymized IPs, no third-party trackers). You can opt out by setting your browser to "Do Not Track" or contacting us.',
],
},
{
title: 'Notifications',
body: [
'We send push notifications and emails based on your preferences in Settings. You can disable any notification type at any time. Transactional messages (receipts, password resets, security alerts) cannot be disabled while your account is active.',
],
},
{
title: 'Your rights',
body: [
'Access: request a copy of your data at any time.',
'Deletion: request account and data deletion at privacy@vyndr.app — we comply within 30 days.',
'Correction: update your email and other profile data from the profile page.',
'Export: request a JSON export of your read history.',
],
},
{
title: 'Children',
body: [
'VYNDR is not for anyone under 21. We do not knowingly collect data from minors. If we discover an account belongs to a minor, we will delete it immediately.',
],
},
{
title: 'Changes',
body: [
'If we make material changes to this policy, we will notify you by email at least 30 days before they take effect.',
],
},
{
title: 'Contact',
body: [
'Privacy questions or data requests: privacy@vyndr.app',
],
},
];
export default function PrivacyPage() {
return (
<section style={{ maxWidth: 720, margin: '0 auto', padding: '48px 16px 120px' }}>
<header style={{ marginBottom: 32 }}>
<h1 style={{ fontSize: 36, fontWeight: 700, letterSpacing: '-0.03em', marginBottom: 8 }}>Privacy Policy</h1>
<p className="mono" style={{ fontSize: 12, color: 'var(--text-tertiary)', letterSpacing: '0.05em' }}>
EFFECTIVE: MAY 2026
</p>
</header>
{SECTIONS.map((s) => (
<section
key={s.title}
style={{
marginBottom: 32,
...(s.emphasized
? {
borderLeft: '3px solid var(--grade-a)',
background: 'rgba(0, 212, 160, 0.04)',
borderRadius: 4,
padding: '12px 16px',
}
: {}),
}}
>
<h2 style={{ fontSize: s.emphasized ? 20 : 18, fontWeight: 700, marginBottom: 12 }}>{s.title}</h2>
{s.body.map((p, i) => (
<p
key={i}
style={{
color: s.emphasized ? 'var(--text-0)' : 'var(--text-secondary)',
fontSize: s.emphasized ? 16 : 15,
lineHeight: 1.7,
marginBottom: 12,
}}
>
{p}
</p>
))}
</section>
))}
</section>
);
}