1<!DOCTYPE html> 2<html lang="en" id="html-root"> 3<head> 4 5 6 7
7<script> 8 /* Initialize dark theme */ 9 document.documentElement.classList.add('dark'); 10 </script>
10 11 <meta charset="UTF-8"> 12 <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover"> 13 <meta name="theme-color" content="#f5a623"> 14 <meta name="apple-mobile-web-app-capable" content="yes"> 15 <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> 16 <title>Privacy Policy - SaveToExo</title> 17 18 <!-- PWA Manifest & Icons --> 19 <link rel="manifest" href="/static/manifest.json"> 20 <link rel="icon" type="image/png" sizes="48x48" href="/static/images/icon-48.png"> 21 <link rel="icon" type="image/png" sizes="128x128" href="/static/images/icon-128.png"> 22 <link rel="apple-touch-icon" href="/static/images/apple-touch-icon.png"> 23 24 <!-- OpenGraph Meta Tags for WhatsApp/Social Sharing --> 25 26 <meta property="og:title" content="SaveToExo - save anything, ask it anything" /> 27 <meta property="og:description" content="Save a link from anywhere. Then ask your saves a question in plain English, or connect your memory to Claude and ChatGPT." /> 28 <meta property="og:image" content="https://www.savetoexo.com/static/images/og-image.png" /> 29 <meta property="og:url" content="https://www.savetoexo.com" /> 30 <meta property="og:type" content="website" /> 31 <meta property="og:site_name" content="SaveToExo" /> 32 33 34 <!-- Tailwind CSS --> 35 36
36<script src="https://cdn.tailwindcss.com"></script>
36 37 38 <!-- Tailwind Typography Plugin --> 39 <link rel="stylesheet" href="https://unpkg.com/@tailwindcss/[email protected]/dist/typography.min.css"> 40 41 <!-- Google Fonts --> 42 <link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Inter:wght@300;400;500;600&display=swap" rel="stylesheet"> 43 44 <!-- Alpine.js (CSP-safe build, self-hosted). 45 The page CSP deliberately omits 'unsafe-eval' (see main.py `_CSP`); 46 standard Alpine needs eval, this build does not. Components are 47 registered in exo-components.js, which MUST load before the Alpine 48 build, both are `defer` so they execute in document order. --> 49
49<script defer src="/static/js/exo-components.js"></script>
49 50
50<script defer src="/static/js/alpine-csp-3.14.1.min.js"></script>
50 51 52 <!-- HTMX --> 53
53<script src="https://unpkg.com/[email protected]"></script>
53 54 55 <!-- Tailwind Configuration --> 56
56<script> 57 tailwind.config = { 58 darkMode: 'class', 59 theme: { 60 extend: { 61 fontFamily: { 62 display: ['Space Grotesk', 'system-ui', 'sans-serif'], 63 body: ['Inter', 'system-ui', 'sans-serif'] 64 }, 65 colors: { 66 background: 'hsl(222, 47%, 6%)', 67 foreground: 'hsl(45, 20%, 95%)', 68 card: { 69 DEFAULT: 'hsl(222, 40%, 9%)', 70 foreground: 'hsl(45, 20%, 95%)' 71 }, 72 primary: { 73 DEFAULT: 'hsl(38, 92%, 55%)', 74 glow: 'hsl(38, 100%, 65%)', 75 foreground: 'hsl(222, 47%, 6%)' 76 }, 77 muted: { 78 DEFAULT: 'hsl(222, 25%, 15%)', 79 foreground: 'hsl(222, 15%, 55%)' 80 }, 81 accent: { 82 DEFAULT: 'hsl(222, 35%, 20%)', 83 foreground: 'hsl(45, 20%, 90%)' 84 }, 85 border: 'hsl(222, 30%, 18%)', 86 secondary: { 87 DEFAULT: 'hsl(222, 30%, 18%)', 88 foreground: 'hsl(45, 15%, 80%)' 89 } 90 }, 91 backdropBlur: { 92 'xl': '24px' 93 }, 94 boxShadow: { 95 'card': '0 4px 24px -4px rgba(0, 0, 0, 0.4)', 96 'card-hover': '0 8px 40px -8px rgba(0, 0, 0, 0.5)', 97 'glow': '0 0 40px -10px hsl(38 92% 55% / 0.3)' 98 } 99 } 100 } 101 } 102 </script>
102 103 104 <style> 105 /* Dark Theme Colors (default - only theme) */ 106 html, html.dark { 107 --background: hsl(222, 47%, 12%); 108 --foreground: hsl(45, 20%, 95%); 109 --card: hsl(222, 40%, 16%); 110 --card-hover: hsl(222, 40%, 20%); 111 --primary: hsl(38, 92%, 55%); 112 --primary-glow: hsl(38, 100%, 65%); 113 --muted: hsl(222, 25%, 22%); 114 --muted-foreground: hsl(222, 15%, 55%); 115 --border: hsl(222, 30%, 24%); 116 --secondary: hsl(222, 30%, 24%); 117 --accent: hsl(222, 35%, 26%); 118 } 119 120 /* Glass morphism effects - dark theme */ 121 html.dark .glass-card { 122 background: rgba(23, 32, 51, 0.8); 123 backdrop-filter: blur(24px); 124 -webkit-backdrop-filter: blur(24px); 125 border: 1px solid rgba(255, 255, 255, 0.05); 126 } 127 128 html.dark .glass-sidebar { 129 background: rgba(15, 23, 42, 0.3); 130 backdrop-filter: blur(16px); 131 -webkit-backdrop-filter: blur(16px); 132 } 133 134 html.dark .glass-nav { 135 background: rgba(23, 32, 51, 0.95); 136 backdrop-filter: blur(24px); 137 -webkit-backdrop-filter: blur(24px); 138 } 139 140 /* Animations */ 141 @keyframes fadeInUp { 142 from { 143 opacity: 0; 144 transform: translateY(20px); 145 } 146 to { 147 opacity: 1; 148 transform: translateY(0); 149 } 150 } 151 152 @keyframes pulseGlow { 153 0%, 100% { 154 box-shadow: 0 0 20px -10px hsl(38 92% 55%); 155 } 156 50% { 157 box-shadow: 0 0 40px -5px hsl(38 92% 55%); 158 } 159 } 160 161 .animate-pulse-glow { 162 animation: pulseGlow 2s infinite; 163 } 164 165 @keyframes shimmer { 166 100% { 167 transform: translateX(100%); 168 } 169 } 170 171 .hover-glow:hover { 172 box-shadow: 0 0 40px -10px hsl(38 92% 55% / 0.3); 173 transition: box-shadow 0.3s ease; 174 } 175 176 /* Custom scrollbar */ 177 ::-webkit-scrollbar {
178 width: 8px; 179 height: 8px; 180 } 181 182 ::-webkit-scrollbar-track { 183 background: hsl(222, 40%, 9%); 184 } 185 186 ::-webkit-scrollbar-thumb { 187 background: hsl(222, 30%, 18%); 188 border-radius: 4px; 189 } 190 191 ::-webkit-scrollbar-thumb:hover { 192 background: hsl(222, 15%, 55%); 193 } 194 195 /* Safe area for iOS */ 196 .pb-safe { 197 padding-bottom: max(1rem, env(safe-area-inset-bottom)); 198 } 199 200 /* Hide native search input clear button (we have custom one) */ 201 input[type="search"]::-webkit-search-cancel-button, 202 input[type="search"]::-webkit-search-decoration { 203 -webkit-appearance: none; 204 appearance: none; 205 } 206 207 /* Body base styles */ 208 body { 209 background-color: var(--background); 210 color: var(--foreground); 211 font-family: 'Inter', system-ui, sans-serif; 212 -webkit-font-smoothing: antialiased; 213 -moz-osx-font-smoothing: grayscale; 214 transition: background-color 0.3s ease, color 0.3s ease; 215 } 216 217 /* Prose styling for markdown content */ 218 .prose { 219 color: inherit; 220 } 221 222 .prose strong { 223 color: hsl(45, 20%, 95%); 224 font-weight: 600; 225 } 226 227 .prose p { 228 margin: 0.75rem 0; 229 } 230 231 .prose a { 232 color: hsl(38, 92%, 55%); 233 text-decoration: underline; 234 } 235 236 .prose a:hover { 237 color: hsl(38, 100%, 65%); 238 } 239 240 /* Utility classes */ 241 .text-primary-glow { 242 color: hsl(38, 100%, 65%); 243 } 244 245 .bg-primary-glow\/10 { 246 background-color: rgba(244, 164, 96, 0.1); 247 } 248 249 /* Prose typography overrides for markdown content */ 250 .prose :where(h1, h2, h3, h4, h5, h6):not(:where([class~="not-prose"] *)) { 251 color: hsl(38, 100%, 75%) !important; /* Bright amber */ 252 font-family: 'Space Grotesk', system-ui, sans-serif !important; /* Match display font */ 253 font-weight: 600 !important; 254 margin-top: 1.5rem; 255 margin-bottom: 0.75rem; 256 line-height: 1.3 !important; 257 } 258 259 .prose :where(h3):not(:where([class~="not-prose"] *)) { 260 font-size: 1rem !important; /* Match viewport text size */ 261 } 262 263 .prose :where(p):not(:where([class~="not-prose"] *)) { 264 color: hsl(45, 20%, 90%) !important; 265 line-height: 1.75 !important; 266 margin-top: 0.5rem; 267 margin-bottom: 0.5rem; 268 } 269 270 /* Tight prose variant for card teasers */ 271 .prose-tight :where(h1, h2, h3, h4, h5, h6):not(:where([class~="not-prose"] *)) { 272 font-size: 1rem; /* text-base = 16px */ 273 margin-top: 0.5rem; 274 margin-bottom: 0.25rem; 275 } 276 277 .prose-tight :where(p):not(:where([class~="not-prose"] *)) { 278 margin-top: 0.25rem; 279 margin-bottom: 0.25rem; 280 } 281 282 .prose :where(strong):not(:where([class~="not-prose"] *)) { 283 color: hsl(38, 100%, 75%) !important; 284 font-weight: 600 !important; 285 } 286 287 .prose :where(ul):not(:where([class~="not-prose"] *)) { 288 margin-top: 1rem; 289 margin-bottom: 1rem; 290 padding-left: 1rem !important; 291 border-left: 2px solid hsl(222, 30%, 24%) !important; 292 list-style-type: none !important; 293 } 294 295 .prose :where(li):not(:where([class~="not-prose"] *)) { 296 margin-top: 0.75rem; 297 margin-bottom: 0.75rem; 298 padding-left: 0.5rem !important; 299 line-height: 1.75 !important; 300 } 301 302 .prose :where(ol):not(:where([class~="not-prose"] *)) { 303 margin-top: 1rem; 304 margin-bottom: 1rem; 305 padding-left: 1rem !important; 306 } 307 </style> 308 309 310</head> 311<body class="min-h-screen"> 312 313<div class="min-h-screen bg-background px-4 py-12"> 314 <div class="max-w-2xl mx-auto"> 315 316 <!-- Header --> 317 <div class="mb-8"> 318 <a href="/" class="text-primary hover:text-primary-glow transition-colors text-sm"> 319 â Back to SaveToExo 320 </a> 321 <h1 class="font-display font-bold text-4xl text-foreground mt-4">Privacy Policy</h1> 322 <p class="text-muted-foreground mt-2">Last updated: August 2026</p> 323 </div> 324 325 <!-- Content --> 326 <div class="prose prose-invert max-w-none space-y-6 text-foreground"> 327 328 <section class="space-y-3"> 329 <h2 class="text-xl font-semibold text-primary">What we collect</h2> 330 <p class="text-muted-foreground leading-relaxed"> 331 SaveToExo collects only what's necessary to provide the service: 332 </p> 333 <ul class="list-disc list-inside text-muted-foreground space-y-2 ml-2"> 334 <li>Your Telegram user ID and username (for authentication)</li> 335 <li>Links you save and their content (articles, tweets, videos)</li> 336 <li>AI-generated summaries, tags, and insights from your saved content</li> 337 <li>Anonymous visits to our public landing page, including the page viewed, referring site, device type, and approximate country</li> 338 </ul> 339 </section> 340 341 <section class="space-y-3"> 342 <h2 class="text-xl font-semibold text-primary">How we use your data</h2> 343 <ul class="list-disc list-inside text-muted-foreground space-y-2 ml-2"> 344 <li>To save and process links you send us</li> 345 <li>To generate AI summaries and make your content searchable</li> 346 <li>To authenticate you via Telegram</li> 347 <li>To understand how people find our public landing page</li> 348 </ul> 349 <p class="text-muted-foreground leading-relaxed"> 350 We do not sell your data. We do not show you ads. Your saved content is private to you. 351 </p> 352 <p class="text-muted-foreground leading-relaxed"> 353 We use PostHog's EU-hosted service for cookieless, anonymous analytics on the public landing page only. 354 Session replay, interaction tracking, user identification, and analytics on signed-in pages are disabled. 355 </p> 356 </section> 357 358 <section class="space-y-3"> 359 <h2 class="text-xl font-semibold text-primary">Data retention</h2> 360 <p class="text-muted-foreground leading-relaxed"> 361 Your saved content is stored until you delete it. You can delete individual saves at any time from your feed. 362 If you want to delete your entire account and all associated data, contact us at 363 <a href="mailto:[email protected]" class="text-primary hover:underline">[email protected]</a>. 364 </p> 365 </section> 366 367 <section class="space-y-3"> 368 <h2 class="text-xl font-semibold text-primary">Security</h2> 369 <p class="text-muted-foreground leading-relaxed"> 370 We use industry-standard security practices including encrypted connections (HTTPS), 371 secure session tokens, and database-level access controls. Your data is hosted on
372 secure cloud infrastructure. 373 </p> 374 </section> 375 376 <section class="space-y-3"> 377 <h2 class="text-xl font-semibold text-primary">Contact</h2> 378 <p class="text-muted-foreground leading-relaxed"> 379 Questions about this policy? Email us at 380 <a href="mailto:[email protected]" class="text-primary hover:underline">[email protected]</a>. 381 </p> 382 </section> 383 384 </div> 385 </div> 386</div> 387 388</body> 389</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.