feat: standalone "Coming Soon" landing (single HTML, dockerized)

RTL Persian landing matching the FlatRender brand (blue + LogoMark), with a live
countdown to ۱ تیر ۱۴۰۵ (1 Tir 1405 = 22 Jun 2026, Iran time), feature chips, and a
notify-me capture. One static index.html served by nginx (coming-soon/Dockerfile);
stack-agnostic, no app dependencies.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-02 15:44:44 +03:30
parent e7cdf35b65
commit 814eb8d54e
3 changed files with 262 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
# Standalone "Coming Soon" landing — a single static HTML page served by nginx.
# Stack-agnostic: build & run anywhere, no app dependencies.
#
# docker build -t flatrender-coming-soon ./coming-soon
# docker run -d -p 8090:80 --name flatrender-coming-soon flatrender-coming-soon
# → http://localhost:8090
FROM nginx:1.27-alpine
# Single-page site: replace the default nginx root with our one HTML file.
RUN rm -f /usr/share/nginx/html/*
COPY index.html /usr/share/nginx/html/index.html
# Serve index.html for every path (so deep links still land on the page).
RUN printf 'server {\n listen 80;\n root /usr/share/nginx/html;\n location / {\n try_files $uri /index.html;\n }\n}\n' > /etc/nginx/conf.d/default.conf
EXPOSE 80
+34
View File
@@ -0,0 +1,34 @@
# FlatRender — Coming Soon landing
A **single, self-contained HTML page** (no build step, no app dependencies) packaged as
a tiny nginx Docker image. RTL Persian, matches the FlatRender brand (blue + Vazirmatn +
LogoMark), with a **live countdown to ۱ تیر ۱۴۰۵ (1 Tir 1405 = 22 June 2026, Iran time)**.
## Run
```bash
docker build -t flatrender-coming-soon ./coming-soon
docker run -d -p 8090:80 --name flatrender-coming-soon flatrender-coming-soon
# → http://localhost:8090
```
Or just open `index.html` directly in a browser — it works with zero server.
## What it includes
- Animated brand-gradient background + grid overlay
- LogoMark + Persian wordmark (فلت‌رندر)
- Headline + lead reflecting the app's value (AI video & image maker)
- Live countdown (days / hours / minutes / seconds) in Persian digits
- Feature chips mirroring the app: video maker, image editor, 1200+ templates,
music & voiceover, HD/4K export
- "Notify me" email capture (posts to `/subscribe` if a backend is wired; otherwise
shows a thank-you locally)
## Change the launch date
Edit one line in `index.html`:
```js
var TARGET = new Date("2026-06-22T00:00:00+03:30").getTime();
```
+212
View File
@@ -0,0 +1,212 @@
<!DOCTYPE html>
<html lang="fa" dir="rtl">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>فلت‌رندر — به‌زودی | FlatRender — Coming Soon</title>
<meta name="description" content="فلت‌رندر؛ ساخت ویدیو و تصویر حرفه‌ای با هوش مصنوعی در چند دقیقه. به‌زودی." />
<meta property="og:title" content="فلت‌رندر — به‌زودی" />
<meta property="og:description" content="ساخت ویدیو و تصویر حرفه‌ای با هوش مصنوعی. به‌زودی راه‌اندازی می‌شود." />
<meta property="og:type" content="website" />
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Crect width='40' height='40' rx='9' fill='%232563EB'/%3E%3Cpath d='M12 12.5L12 27.5L24.5 20L12 12.5Z' fill='white'/%3E%3C/svg%3E" />
<style>
:root {
--blue: #2563eb;
--blue-light: #3b82f6;
--violet: #7c3aed;
--ink: #0b1020;
--muted: #94a3b8;
--card: rgba(255, 255, 255, 0.04);
--border: rgba(255, 255, 255, 0.08);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; }
body {
font-family: "Vazirmatn", Tahoma, "Segoe UI", system-ui, -apple-system, sans-serif;
background: var(--ink);
color: #e6ecff;
min-height: 100vh;
overflow-x: hidden;
position: relative;
display: flex;
flex-direction: column;
}
/* Animated brand gradient blobs */
.bg { position: fixed; inset: 0; z-index: 0; overflow: hidden; }
.blob {
position: absolute; border-radius: 50%; filter: blur(90px); opacity: 0.55;
animation: float 16s ease-in-out infinite;
}
.blob.b1 { width: 520px; height: 520px; background: var(--blue); top: -160px; right: -120px; }
.blob.b2 { width: 460px; height: 460px; background: var(--violet); bottom: -160px; left: -120px; animation-delay: -5s; }
.blob.b3 { width: 360px; height: 360px; background: #0ea5e9; top: 40%; left: 30%; opacity: 0.3; animation-delay: -9s; }
@keyframes float {
0%, 100% { transform: translate(0, 0) scale(1); }
50% { transform: translate(0, -40px) scale(1.08); }
}
.grid-overlay {
position: fixed; inset: 0; z-index: 0; opacity: 0.4;
background-image: linear-gradient(var(--border) 1px, transparent 1px),
linear-gradient(90deg, var(--border) 1px, transparent 1px);
background-size: 48px 48px;
mask-image: radial-gradient(ellipse 80% 60% at 50% 35%, #000 40%, transparent 100%);
}
main {
position: relative; z-index: 1; flex: 1;
display: flex; flex-direction: column; align-items: center; justify-content: center;
text-align: center; padding: 48px 20px;
}
.logo { display: flex; align-items: center; gap: 12px; margin-bottom: 28px; }
.logo svg { width: 52px; height: 52px; filter: drop-shadow(0 8px 24px rgba(37,99,235,.5)); }
.logo-name { font-size: 26px; font-weight: 800; letter-spacing: -0.5px; }
.badge {
display: inline-flex; align-items: center; gap: 8px;
background: var(--card); border: 1px solid var(--border);
padding: 8px 16px; border-radius: 999px; font-size: 13px; color: #c7d2fe; margin-bottom: 24px;
backdrop-filter: blur(8px);
}
.badge .dot { width: 8px; height: 8px; border-radius: 50%; background: #22c55e; box-shadow: 0 0 12px #22c55e; animation: pulse 2s infinite; }
@keyframes pulse { 50% { opacity: .4; } }
h1 {
font-size: clamp(34px, 7vw, 68px); font-weight: 800; line-height: 1.15; letter-spacing: -1px;
margin-bottom: 18px;
}
h1 .grad {
background: linear-gradient(120deg, var(--blue-light), var(--violet));
-webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent;
}
.lead { font-size: clamp(16px, 2.4vw, 21px); color: #b6c2e0; max-width: 640px; line-height: 1.9; margin-bottom: 40px; }
/* Countdown */
.countdown { display: flex; gap: 14px; justify-content: center; flex-wrap: wrap; margin-bottom: 14px; }
.unit {
background: var(--card); border: 1px solid var(--border); border-radius: 18px;
width: 104px; padding: 18px 10px; backdrop-filter: blur(10px);
}
.unit .num {
font-size: clamp(34px, 6vw, 48px); font-weight: 800; line-height: 1;
font-variant-numeric: tabular-nums; color: #fff;
}
.unit .lbl { font-size: 13px; color: var(--muted); margin-top: 10px; }
.target { font-size: 14px; color: var(--muted); margin-bottom: 44px; }
.target b { color: #c7d2fe; font-weight: 600; }
/* Features */
.features { display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; max-width: 860px; margin-bottom: 44px; }
.feat {
display: flex; align-items: center; gap: 10px;
background: var(--card); border: 1px solid var(--border); border-radius: 14px;
padding: 12px 18px; font-size: 14.5px; color: #cdd8f3; backdrop-filter: blur(8px);
}
.feat svg { width: 20px; height: 20px; color: var(--blue-light); flex-shrink: 0; }
/* Notify form */
.notify { display: flex; gap: 10px; width: 100%; max-width: 440px; }
.notify input {
flex: 1; background: var(--card); border: 1px solid var(--border); color: #fff;
padding: 14px 18px; border-radius: 14px; font-size: 15px; font-family: inherit; outline: none;
}
.notify input::placeholder { color: var(--muted); }
.notify input:focus { border-color: var(--blue); }
.notify button {
background: linear-gradient(120deg, var(--blue), var(--violet)); color: #fff; border: 0;
padding: 14px 26px; border-radius: 14px; font-size: 15px; font-weight: 700; font-family: inherit;
cursor: pointer; transition: transform .15s, box-shadow .15s; white-space: nowrap;
}
.notify button:hover { transform: translateY(-2px); box-shadow: 0 12px 30px rgba(37,99,235,.45); }
.notify-msg { font-size: 14px; color: #34d399; margin-top: 14px; min-height: 20px; }
footer { position: relative; z-index: 1; text-align: center; padding: 24px; color: var(--muted); font-size: 13px; }
footer a { color: #c7d2fe; text-decoration: none; }
@media (max-width: 480px) { .unit { width: 76px; } }
</style>
</head>
<body>
<div class="bg">
<div class="blob b1"></div><div class="blob b2"></div><div class="blob b3"></div>
</div>
<div class="grid-overlay"></div>
<main>
<div class="logo">
<svg viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<rect width="40" height="40" rx="9" fill="#2563EB" />
<path d="M12 12.5L12 27.5L24.5 20L12 12.5Z" fill="white" />
<rect x="27" y="13" width="7" height="2.5" rx="1.25" fill="white" fill-opacity="0.9" />
<rect x="27" y="18.75" width="5.5" height="2.5" rx="1.25" fill="white" fill-opacity="0.75" />
<rect x="27" y="24.5" width="4" height="2.5" rx="1.25" fill="white" fill-opacity="0.6" />
</svg>
<span class="logo-name">فلت‌رندر</span>
</div>
<div class="badge"><span class="dot"></span> به‌زودی راه‌اندازی می‌شود</div>
<h1>ساخت <span class="grad">ویدیو و تصویر</span><br />با هوش مصنوعی، در چند دقیقه</h1>
<p class="lead">
استودیوی آنلاین فلت‌رندر؛ ساخت ویدیوهای حرفه‌ای، ویرایش تصویر، و بیش از ۱۲۰۰ قالب آمادهٔ ساخته‌شده توسط طراحان موشن — همه در مرورگر شما. به‌زودی…
</p>
<div class="countdown" id="countdown" aria-live="polite">
<div class="unit"><div class="num" id="d">۰۰</div><div class="lbl">روز</div></div>
<div class="unit"><div class="num" id="h">۰۰</div><div class="lbl">ساعت</div></div>
<div class="unit"><div class="num" id="m">۰۰</div><div class="lbl">دقیقه</div></div>
<div class="unit"><div class="num" id="s">۰۰</div><div class="lbl">ثانیه</div></div>
</div>
<p class="target">زمان راه‌اندازی: <b>۱ تیر ۱۴۰۵</b></p>
<div class="features">
<div class="feat"><svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2"/></svg> ویدیوساز هوش مصنوعی</div>
<div class="feat"><svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="M21 15l-5-5L5 21"/></svg> ویرایشگر تصویر</div>
<div class="feat"><svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M12 2l2.4 7.4H22l-6 4.6 2.3 7.4L12 17l-6.3 4.4L8 14 2 9.4h7.6z"/></svg> بیش از ۱۲۰۰ قالب</div>
<div class="feat"><svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M9 18V5l12-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="18" cy="16" r="3"/></svg> موسیقی و صداگذاری</div>
<div class="feat"><svg fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path d="M4 4h16v12H4z"/><path d="M2 20h20"/></svg> خروجی HD و ۴K</div>
</div>
<form class="notify" id="notify" onsubmit="return notifyMe(event)">
<input type="email" id="email" placeholder="ایمیل شما برای اطلاع از راه‌اندازی" required />
<button type="submit">به من خبر بده</button>
</form>
<p class="notify-msg" id="notifyMsg"></p>
</main>
<footer>
© <span id="year"></span> فلت‌رندر — تمامی حقوق محفوظ است. ·
<a href="mailto:hello@flatrender.com">hello@flatrender.com</a>
</footer>
<script>
// ── Countdown to 1 Tir 1405 (22 June 2026, 00:00 Iran time, UTC+03:30) ──
var TARGET = new Date("2026-06-22T00:00:00+03:30").getTime();
var faDigits = function (n) {
return String(n).padStart(2, "0").replace(/[0-9]/g, function (d) {
return "۰۱۲۳۴۵۶۷۸۹"[d];
});
};
function tick() {
var diff = TARGET - Date.now();
if (diff < 0) diff = 0;
var s = Math.floor(diff / 1000);
document.getElementById("d").textContent = faDigits(Math.floor(s / 86400));
document.getElementById("h").textContent = faDigits(Math.floor((s % 86400) / 3600));
document.getElementById("m").textContent = faDigits(Math.floor((s % 3600) / 60));
document.getElementById("s").textContent = faDigits(s % 60);
}
tick();
setInterval(tick, 1000);
document.getElementById("year").textContent = "۱۴۰۵";
// ── Notify-me (stores locally; POSTs to /subscribe if a backend is wired) ──
function notifyMe(e) {
e.preventDefault();
var email = document.getElementById("email").value;
var msg = document.getElementById("notifyMsg");
try {
fetch("/subscribe", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: email }),
}).catch(function () {});
} catch (_) {}
msg.textContent = "ممنون! به‌محض راه‌اندازی به شما اطلاع می‌دهیم. ✓";
document.getElementById("notify").reset();
return false;
}
</script>
</body>
</html>