Files
simplex-chat/website/src/index.html
T
EvgenyandEvgeny @ SimpleX Chat 41f0c429b3 website: update home page (#7724)
* website: update home page

* update

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-10-15 12:50:38 +01:00

444 lines
25 KiB
HTML

---
title: "SimpleX Chat: private and secure messenger without any user IDs (not even random)"
description: "SimpleX Chat - a private and encrypted messenger without any user IDs (not even random ones)! Make a private connection via link / QR code to send messages and make calls."
templateEngineOverride: njk
active_home: true
---
<!DOCTYPE html>
<html class="bg-[#C1E3FE] dark:bg-[#050920]" lang="{{ page.url | getlang }}"
{% for language in languages.languages %}
{% if language.label == page.url | getlang %}
dir="{{ "rtl" if language.rtl else "ltr" }}"
{% endif %}
{% endfor %}>
<head>
<meta charset="UTF-8">
{% include "dark-mode.html" %}
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>{{ title }}</title>
<meta name="description" content="{{ description }}"/>
<meta name="Content-Type" content="text/html;charset=utf-8"/>
{% if path %}
<meta http-equiv="onion-location" content="{% cfg 'onionLocation' %}{{ path }}" />
<meta property="og:url" content="{% cfg 'siteLocation' %}{{ path }}" />
{% else %}
<meta http-equiv="onion-location" content="{% cfg 'onionLocation' %}/" />
<meta property="og:url" content="{% cfg 'siteLocation' %}/" />
{% endif %}
<meta property="og:type" content="website"/>
<meta property="og:title" content="{{ title }}"/>
<meta property="og:description" content="{{ description }}"/>
<meta property="og:image" content="{% cfg 'siteLocation' %}/img/share_simplex.jpg"/>
<meta name="twitter:card" content="summary"/>
<link rel="icon" type="image/png" sizes="96x96" href="/img/favicon.ico"/>
{% include "markdown-alternate.html" %}
<meta name="theme-color" content="#C1E3FE">
<link rel="preload" href="/img/design_3/cover.webp" as="image" type="image/webp">
<link rel="preload" href="/img/design_3/cover-light.webp" as="image" type="image/webp" media="(prefers-color-scheme: light)">
<link rel="preload" href="/img/new/logo-dark.png" as="image">
<link rel="preload" href="/img/new/logo-light.png" as="image" media="(prefers-color-scheme: light)">
<link rel="preload" href="/img/design_3/publications/trail-of-bits.png" as="image">
<link rel="preload" href="/fonts/GT-Walsheim/GT-Walsheim-LC-Bold.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/GT-Walsheim/GT-Walsheim-LC-Regular.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/fonts/Manrope/Manrope-ExtraLight.ttf" as="font" type="font/ttf" crossorigin>
<link rel="preload" href="/fonts/Manrope/Manrope-Light.ttf" as="font" type="font/ttf" crossorigin>
<link rel="preload" href="/fonts/Manrope/Manrope-Medium.ttf" as="font" type="font/ttf" crossorigin>
<link rel="preload" href="/fonts/Manrope/Manrope-Bold.ttf" as="font" type="font/ttf" crossorigin>
<link rel="preload" href="/img/new/apple_store.svg" as="image">
<link rel="preload" href="/img/new/google_play.svg" as="image">
<link rel="preload" href="/img/new/f_droid_icon.svg" as="image" media="(max-width: 1015px)">
<link rel="preload" href="/img/design_3/testflight-dark.png" as="image" media="(max-width: 1015px)">
<link rel="preload" href="/img/design_3/socials/linux.png" as="image" media="(min-width: 1016px)">
<link rel="preload" href="/img/design_3/socials/apple.png" as="image" media="(min-width: 1016px)">
<link rel="preload" href="/img/design_3/socials/windows.png" as="image" media="(min-width: 1016px)">
<link href="/css/tailwind.css" rel="stylesheet"/>
<link rel="stylesheet" href="/css/design3.css">
<link rel="stylesheet" href="/css/design3-nav.css">
<style>
.sd-sale-banner {
position: absolute;
top: 60.4px;
left: 50%;
transform: translateX(-50%);
z-index: 12;
white-space: nowrap;
font-family: "Manrope", sans-serif;
font-weight: 400;
font-size: clamp(18px, calc(var(--sec-vwu) * 2.14), 34.92px);
line-height: 1.3;
text-decoration: none;
background-image: linear-gradient(90deg, #001aa7 0%, #0095e7 100%);
}
.sd-digits { font-variant-numeric: tabular-nums; }
.sd-icon { display: inline-block; width: 0.85em; height: 0.85em; vertical-align: -0.08em; margin-left: 0.35em; }
.sd-icon path, .sd-icon polygon { fill: #0095e7; }
.dark .sd-icon path, .dark .sd-icon polygon { fill: #c8feff; }
@media screen and (min-width: 1016px), screen and (max-width: 1015px) and (orientation: landscape) {
.sd-sale-banner { top: auto; bottom: calc(22px + var(--cover-bottom-ext, 0px)); transform: translateX(calc(-50% - max(0px, 50% + var(--cover-socials-w, 435px) + 54px - 50vw))); font-size: 16px; text-align: center; background-image: none; -webkit-text-fill-color: #ffffff; color: #ffffff; }
[dir="rtl"] .sd-sale-banner { transform: translateX(calc(-50% + max(0px, 50% + var(--cover-socials-w, 435px) + 54px - 50vw))); }
.sd-lead, .sd-tail { display: block; }
.sd-icon path, .sd-icon polygon { fill: #ffffff; }
}
@media screen and (max-width: 1015px) and (orientation: portrait), screen and (max-width: 1015px) and (orientation: landscape) and (max-height: 500px) {
.sd-sale-banner { display: none; }
}
</style>
<script src="/js/flag-anchor.js"></script>
<script>
function detectMobileBrowser() {
const ua = navigator.userAgent;
// Chrome on iOS
if (/CriOS/.test(ua)) {
document.documentElement.classList.add('chrome-ios');
}
// Safari on iOS
else if (/Safari/.test(ua) && /iPhone|iPad|iPod/.test(ua) && !/CriOS|FxiOS|EdgiOS/.test(ua)) {
document.documentElement.classList.add('safari-ios');
}
// Chrome on Android
else if (/Chrome/.test(ua) && /Android/.test(ua)) {
document.documentElement.classList.add('chrome-android');
}
}
detectMobileBrowser();
</script>
</head>
<body class="bg-[#C1E3FE] dark:bg-[#050920]">
{% include "navbar.html" %}
{% set lang = page.url | getlang %}
<div class="screen">
<section class="page-1 cover page">
<a class="sd-sale-banner gradient-text" href="https://simplex.domains/" target="_blank" rel="noopener"><span class="sd-lead" id="sd-lead">{{ "index-hero-countdown-lead" | i18n({}, lang) }} </span><span class="sd-tail" id="sd-tail"><span class="sd-digits" id="sd-cd"></span>&#8288;<svg class="sd-icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" viewBox="0 0 100 100"><path d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path><polygon points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg></span></a>
<script>
(function () {
var target = Date.UTC(2026, 11, 12, 18, 0, 0);
var lead = document.getElementById("sd-lead");
var cd = document.getElementById("sd-cd");
var days = new Intl.RelativeTimeFormat({{ lang | replace("_", "-") | dump | safe }} + "-u-nu-latn", { numeric: "always" });
var timer;
function pad(n) { return String(n).padStart(2, "0"); }
function tick() {
var ms = target - Date.now();
if (ms <= 0) {
clearInterval(timer);
lead.style.display = "none";
cd.textContent = {{ "index-hero-countdown-done" | i18n({}, lang) | dump | safe }};
return;
}
var s = Math.floor(ms / 1000);
var d = Math.floor(s / 86400);
cd.textContent = days.format(d, "day") + " " + pad(Math.floor((s % 86400) / 3600)) + ":" + pad(Math.floor((s % 3600) / 60)) + ":" + pad(s % 60);
}
timer = setInterval(tick, 1000);
tick();
})();
</script>
<div class="cover-art" aria-hidden="true">
<img class="cover-phone" src="/img/crowdfunding/phone.png" alt="">
<div class="cover-fade"></div>
</div>
<div class="area">
<div class="content">
<h1>{{ "index-hero-h1" | i18n({}, lang) | replace("<br>", " <br>") | safe }}</h1>
<h2 class="gradient-text">{{ "index-hero-h2" | i18n({}, lang) | safe }}</h2>
<p>{{ "index-hero-p1" | i18n({}, lang) | safe }}</p>
<p class="invest">{{ "index-hero-own-part" | i18n({}, lang) | safe }} <a class="gradient-text" href="https://wefunder.com/simplex.chat?utm_source=home" target="_blank" rel="noopener">{{ "index-hero-invest" | i18n({}, lang) | safe }}</a></p>
<div class="cover-stats">
<div class="cover-stat"><b>3M</b><span>{{ "index-hero-downloads" | i18n({}, lang) }}</span></div>
<div class="cover-stat"><b>600K</b><span>{{ "index-hero-monthly-users" | i18n({}, lang) }}</span></div>
</div>
<div class="socials">
<a href="/downloads" class="desktop-app-btn hidden" title="{{ 'index-hero-download-desktop-btn-title' | i18n({}, lang) }}">
<div class="btn-content">
<div>
<img class="no-border" src="/img/design_3/socials/linux.png">
<img class="no-border" src="/img/design_3/socials/apple.png" alt="">
<img class="no-border" src="/img/design_3/socials/windows.png" alt="">
</div>
<p>Download<br>Desktop App</p>
</div>
</a>
<a class="apple-store-btn hidden" href="https://apps.apple.com/us/app/simplex-chat/id1605771084" target="_blank" rel="noopener"><img src="/img/new/apple_store.svg" alt="App Store"></a>
<a class="google-play-btn hidden" href="https://play.google.com/store/apps/details?id=chat.simplex.app" target="_blank" rel="noopener"><img src="/img/new/google_play.svg" alt="Google Play"></a>
<a class="f-droid-btn hidden" href="{{ '' if lang == 'en' else '/' ~ lang }}/fdroid" title="{{ 'index-f-droid-title' | i18n({}, lang) }}"><img src="/img/new/f_droid_icon.svg" alt="F-Droid"></a>
<a class="testflight-btn hidden" href="https://testflight.apple.com/join/DWuT2LQu" target="_blank" rel="noopener"><img class="no-border" src="/img/design_3/testflight-dark.png" title="{{ 'index-testflight-title' | i18n({}, lang) }}" alt="TestFlight"></a>
</div>
<div class="security-btns">
<a title="{{ 'index-security-assessment-title' | i18n({}, lang) }}" href="https://www.trailofbits.com/about" target="_blank" rel="noopener">
<img src="/img/design_3/publications/trail-of-bits.png" alt="Trail of Bits">
</a>
<a class="box-btn btn-1" title="{{ 'index-security-review-2022-title' | i18n({}, lang) }}" href="/blog/20221108-simplex-chat-v4.2-security-audit-new-website.html">
2022
</a>
<a class="box-btn gold-gradient" title="{{ 'index-security-review-2024-title' | i18n({}, lang) }}" href="/blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.html">
2024
</a>
<p class="security-audits">
{{ 'index-security-audits-label' | i18n({}, lang) | safe }}
</p>
</div>
</div>
</div>
</section>
<main>
<div class="section-bg"></div>
<section id="messaging" class="page-2 page">
<div class="area">
<div class="text-container">
<h2>{{ "worlds-most-secure-messaging" | i18n({}, lang ) | safe }}</h2>
<p>{{ "index-messaging-p1" | i18n({}, lang) }}</p>
<p>{{ "index-messaging-p2" | i18n({}, lang) | safe }}</p>
<a class="gradient-text" href="{{ '' if lang == 'en' else '/' ~ lang }}/messaging">{{ "index-messaging-cta" | i18n({}, lang) }}</a>
</div>
</div>
</section>
<section id="nextweb" class="page-3 page">
<div class="area">
<div class="text-container">
<h2>{{ "index-nextweb-h2" | i18n({}, lang) | safe }}</h2>
<p>{{ "index-nextweb-p1" | i18n({}, lang) | safe }}</p>
<p>{{ "index-nextweb-p2" | i18n({}, lang) }}</p>
<a class="gradient-text" href="{{ '' if lang == 'en' else '/' ~ lang }}/why">{{ "why-footer-link" | i18n({}, lang) | safe }}</a>
</div>
</div>
</section>
<section id="token" class="page-4 page">
<div class="area">
<div class="text-container">
<h2>{{ "index-token-h2" | i18n({}, lang) }}</h2>
<p>{{ "index-token-p1" | i18n({}, lang) }}</p>
<p>{{ "index-token-p2-cf" | i18n({}, lang) | safe }}</p>
<a class="gradient-text" href="https://wefunder.com/simplex.chat?utm_source=home" target="_blank" rel="noopener">{{ "index-token-cta-cf" | i18n({}, lang) | safe }}</a>
</div>
</div>
</section>
<section id="roadmap" class="page-5 page">
<div class="area">
<div class="roadmap-labels" data-markdown-skip>
<span class="roadmap-label roadmap-label-now">{{ "index-roadmap-now" | i18n({}, lang) }}</span>
<span class="roadmap-label roadmap-label-1">{{ "index-roadmap-1" | i18n({}, lang) }}</span>
<span class="roadmap-label roadmap-label-2">{{ "index-roadmap-2" | i18n({}, lang) }}</span>
<span class="roadmap-label roadmap-label-3">{{ "index-roadmap-3" | i18n({}, lang) }}</span>
</div>
<div class="text-container">
<h2>{{ "index-roadmap-h2" | i18n({}, lang) }}</h2>
<div class="roadmap">
<p>{{ "index-roadmap-1" | i18n({}, lang) }} </p>
<p class="title"><span class="ltr:order-first rtl:order-last">: </span>{{ "index-roadmap-1-title" | i18n({}, lang) }}</p>
<p>{{ "index-roadmap-1-desc" | i18n({}, lang) }}</p>
</div>
<div class="roadmap">
<p>{{ "index-roadmap-2" | i18n({}, lang) }} </p>
<p class="title"><span class="ltr:order-first rtl:order-last">: </span>{{ "index-roadmap-2-title" | i18n({}, lang) }}</p>
<p>{{ "index-roadmap-2-desc" | i18n({}, lang) }}</p>
</div>
<div class="roadmap">
<p>{{ "index-roadmap-3" | i18n({}, lang) }} </p>
<p class="title"><span class="ltr:order-first rtl:order-last">: </span>{{ "index-roadmap-3-title" | i18n({}, lang) }}</p>
<p>{{ "index-roadmap-3-desc" | i18n({}, lang) }}</p>
</div>
</div>
</div>
</section>
</main>
<section id="directory" class="page-6 page">
<div class="area">
<div class="group-images">
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="https://smp4.simplex.im/g#hr4lvFeBmndWMKTwqiodPz3VBo_6UmdGWocXd1SupsM" class="simplex-image" title="{{ 'index-directory-users-group-title' | i18n({}, lang) }}" target="_blank" rel="noopener">
<img class="block dark:hidden" src="/img/new/logo-symbol-light.svg">
<img class="hidden dark:block" src="/img/new/logo-symbol-dark.svg">
</a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
<a href="/directory" class="group-image" target="_blank" rel="noopener"><img src="/img/group.svg"></a>
</div>
<div class="text-container">
<h2>{{ "index-directory-h2" | i18n({}, lang) }}</h2>
<p>{{ "index-directory-p1" | i18n({}, lang) }}</p>
<p>{{ "index-directory-p2" | i18n({}, lang) }}</p>
<a class="gradient-text" href="/directory">{{ "index-directory-cta" | i18n({}, lang) }}</a>
</div>
</div>
</section>
{% include "footer.html" %}
</div>
<script src="/js/directory.js"></script>
<script src="/js/design3.js"></script>
<script>
(function navColorOnScroll(){
const header = document.querySelector('header#navbar');
const cover = document.querySelector('.page-1.cover');
const footer = document.querySelector('.footer.page');
if (!header || !cover || !footer || !('IntersectionObserver' in window)) {
window.addEventListener('load', () => header && document.body.classList.toggle('change-nav-color', window.scrollY > 10));
window.addEventListener('scroll', () => header && document.body.classList.toggle('change-nav-color', window.scrollY > 10));
return;
}
const io = new IntersectionObserver((entries) => {
// if cover or footer is mostly visible, keep white nav; else switch to #001796
const onObserved = entries.some((e) => e.isIntersecting && e.intersectionRatio >= 0.02);
document.body.classList.toggle('change-nav-color', !onObserved);
}, { threshold: [0, 0.02, 1] });
io.observe(cover);
io.observe(footer);
})();
</script>
<script>
(function coverMetrics(){
const cover = document.querySelector('.page-1.cover');
const invest = cover.querySelector('.content > p.invest');
const socials = cover.querySelector('.socials');
const stats = cover.querySelector('.cover-stats');
const observer = new ResizeObserver(() => {
cover.style.setProperty('--cover-text-b', `${invest.getBoundingClientRect().bottom - cover.getBoundingClientRect().top}px`);
cover.style.setProperty('--cover-socials-w', `${socials.offsetWidth}px`);
cover.style.setProperty('--cover-stats-w', `${stats.offsetWidth}px`);
});
cover.querySelectorAll('.content > *').forEach((el) => observer.observe(el));
})();
</script>
<script>
(function sectionHeaderState(){
const mobileHeader = document.getElementById('mobile-header');
if (!mobileHeader) return;
const cover = document.querySelector('.page-1.cover');
const messaging = document.getElementById('messaging');
const nextweb = document.getElementById('nextweb');
const token = document.getElementById('token');
const roadmap = document.getElementById('roadmap');
const directory = document.getElementById('directory');
const footer = document.querySelector('footer, .footer, #footer');
const observed = [
{ key: 'cover', el: cover },
{ key: 'main', el: messaging },
{ key: 'main', el: nextweb },
{ key: 'main', el: token },
{ key: 'main', el: roadmap },
{ key: 'main', el: directory },
{ key: 'footer', el: footer }
].filter(t => t.el);
if (!observed.length) return;
const MIN_COVER = 0.02;
function clear(){
mobileHeader.classList.remove('cover','main','footer');
}
function apply(key){
clear();
if (key) mobileHeader.classList.add(key);
}
function viewportCoverageY(el){
const r = el.getBoundingClientRect();
const vh = window.innerHeight || document.documentElement.clientHeight;
const visibleY = Math.max(0, Math.min(vh, r.bottom) - Math.max(0, r.top));
return visibleY / Math.max(1, vh);
}
function computeKeyMax(){
const keyMax = { cover: 0, main: 0, footer: 0 };
for (const t of observed) {
const ratio = viewportCoverageY(t.el);
if (ratio > keyMax[t.key]) keyMax[t.key] = ratio;
}
return keyMax;
}
let lastKey = null;
let rafId = null;
function update(){
rafId = null; // Reset RAF flag
const keyMax = computeKeyMax();
let bestKey = null, best = MIN_COVER;
for (const [k, r] of Object.entries(keyMax)) {
if (r > best) { best = r; bestKey = k; }
}
// Only update DOM if state actually changed
if (bestKey !== lastKey) {
if (bestKey) {
apply(bestKey);
} else {
clear();
}
lastKey = bestKey;
}
}
function scheduleUpdate(){
if (rafId) return; // Already scheduled
rafId = requestAnimationFrame(update);
}
let observer = null;
if ('IntersectionObserver' in window) {
observer = new IntersectionObserver(scheduleUpdate, {
threshold: [0, 0.02, 0.1, 0.25, 0.5, 0.75, 1],
rootMargin: '0px 0px -35% 0px'
});
observed.forEach(t => observer.observe(t.el));
scheduleUpdate(); // Initial update
} else {
// Fallback for older browsers
['scroll','resize','load'].forEach(ev =>
window.addEventListener(ev, scheduleUpdate, { passive: true })
);
scheduleUpdate();
}
})();
</script>
</body>
</html>