SaaS — Productivity / Collaboration Landing
A calm, organized landing page for a fictional productivity and collaboration SaaS. It pairs a soft product mockup with use-case tabs that live-swap between a Kanban board, task list, timeline, and docs view, then layers in a collaboration feature band, an integrations strip, a delightful five-star testimonial, and a free-to-start CTA. Built with semantic landmarks, scroll-reveal motion, validated email capture, and a fully responsive layout that collapses gracefully on mobile.
MCP
Code
/* ===== Flowdesk — Productivity / Collaboration Landing ===== */
:root {
--bg: #f7f8fb;
--surface: #ffffff;
--surface-2: #fbfcfe;
--ink: #0f1222;
--muted: #646b85;
--brand: #4f8af0;
--brand-d: #2f6fd6;
--brand-soft: #eaf2fe;
--ok: #16a34a;
--warn: #d97706;
--danger: #dc2626;
--line: rgba(15, 18, 34, .1);
--line-soft: rgba(15, 18, 34, .06);
--shadow-sm: 0 1px 2px rgba(15, 18, 34, .06), 0 1px 3px rgba(15, 18, 34, .05);
--shadow-md: 0 10px 30px -12px rgba(15, 18, 34, .18);
--shadow-lg: 0 30px 60px -22px rgba(31, 51, 92, .28);
--radius: 14px;
--radius-lg: 22px;
--ring: 0 0 0 3px rgba(79, 138, 240, .4);
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
html { scroll-behavior: auto; }
*, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}
body {
margin: 0;
font-family: "Inter", system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
background: var(--bg);
color: var(--ink);
line-height: 1.55;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
.wrap { width: min(1140px, 92vw); margin-inline: auto; }
.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
position: absolute; left: 12px; top: -50px; z-index: 100;
background: var(--ink); color: #fff; padding: 10px 16px; border-radius: 10px;
transition: top .2s;
}
.skip-link:focus { top: 12px; }
:focus-visible { outline: none; box-shadow: var(--ring); border-radius: 8px; }
/* ===== Buttons ===== */
.btn {
display: inline-flex; align-items: center; justify-content: center; gap: 8px;
font: inherit; font-weight: 600; font-size: .95rem;
padding: 11px 20px; border-radius: 11px; border: 1px solid transparent;
cursor: pointer; transition: transform .15s ease, background .2s, box-shadow .2s, border-color .2s;
white-space: nowrap;
}
.btn:active { transform: translateY(1px); }
.btn-solid {
background: var(--brand); color: #fff; box-shadow: 0 6px 16px -6px rgba(47, 111, 214, .6);
}
.btn-solid:hover { background: var(--brand-d); box-shadow: 0 10px 22px -8px rgba(47, 111, 214, .7); }
.btn-ghost { background: transparent; color: var(--ink); border-color: var(--line); }
.btn-ghost:hover { background: var(--surface); border-color: rgba(15, 18, 34, .2); }
/* ===== Header ===== */
.site-header {
position: sticky; top: 0; z-index: 40;
background: rgba(247, 248, 251, .82);
backdrop-filter: saturate(180%) blur(12px);
border-bottom: 1px solid var(--line-soft);
}
.header-inner {
display: flex; align-items: center; gap: 18px;
height: 66px;
}
.brand { display: inline-flex; align-items: center; gap: 10px; font-weight: 800; font-size: 1.12rem; }
.brand-mark {
display: grid; place-items: center; width: 34px; height: 34px; border-radius: 10px;
background: var(--brand-soft); color: var(--brand-d);
}
.brand-mark.sm { width: 28px; height: 28px; border-radius: 8px; }
.main-nav { display: flex; gap: 26px; margin-inline: auto; }
.main-nav a { color: var(--muted); font-weight: 500; font-size: .95rem; transition: color .2s; }
.main-nav a:hover { color: var(--ink); }
.header-cta { display: flex; align-items: center; gap: 10px; }
.nav-toggle {
display: none; flex-direction: column; gap: 5px; background: none; border: 0;
padding: 8px; cursor: pointer; margin-left: auto;
}
.nav-toggle span { width: 22px; height: 2px; background: var(--ink); border-radius: 2px; transition: .25s; }
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
.mobile-nav { display: none; flex-direction: column; gap: 4px; padding: 12px 4vw 20px; border-bottom: 1px solid var(--line-soft); }
.mobile-nav a { padding: 12px 10px; border-radius: 10px; color: var(--ink); font-weight: 500; }
.mobile-nav a:hover { background: var(--surface); }
.mobile-nav .btn-solid { margin-top: 8px; }
/* ===== Hero ===== */
.hero { padding: clamp(40px, 7vw, 84px) 0 clamp(30px, 5vw, 60px); position: relative; overflow: hidden; }
.hero::before {
content: ""; position: absolute; inset: -20% -10% auto -10%; height: 520px; z-index: -1;
background:
radial-gradient(50% 60% at 78% 20%, rgba(79, 138, 240, .16), transparent 70%),
radial-gradient(45% 55% at 12% 0%, rgba(124, 197, 255, .14), transparent 70%);
}
.hero-grid {
display: grid; grid-template-columns: 1.05fr 1fr; gap: clamp(30px, 5vw, 64px); align-items: center;
}
.eyebrow {
display: inline-flex; align-items: center; gap: 8px;
background: var(--surface); border: 1px solid var(--line);
padding: 6px 13px; border-radius: 999px; font-size: .82rem; font-weight: 600; color: var(--brand-d);
box-shadow: var(--shadow-sm);
}
.eyebrow .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ok); box-shadow: 0 0 0 4px rgba(22, 163, 74, .18); }
.hero h1 {
font-size: clamp(2.05rem, 4.6vw, 3.3rem); line-height: 1.08; letter-spacing: -.02em;
margin: 18px 0 14px; font-weight: 800;
}
.lede { font-size: clamp(1.02rem, 1.6vw, 1.18rem); color: var(--muted); max-width: 38ch; margin: 0 0 26px; }
.hero-form { display: flex; gap: 10px; max-width: 440px; flex-wrap: wrap; }
.hero-form input, .cta-form input {
flex: 1 1 220px; min-width: 0; padding: 12px 15px; font: inherit; font-size: .98rem;
border: 1px solid var(--line); border-radius: 11px; background: var(--surface); color: var(--ink);
transition: border-color .2s, box-shadow .2s;
}
.hero-form input::placeholder, .cta-form input::placeholder { color: #9aa1b8; }
.hero-form input:focus-visible, .cta-form input:focus-visible { border-color: var(--brand); box-shadow: var(--ring); }
.hero-form input[aria-invalid="true"], .cta-form input[aria-invalid="true"] { border-color: var(--danger); box-shadow: 0 0 0 3px rgba(220, 38, 38, .18); }
.hero-note, .cta-note { font-size: .85rem; color: var(--muted); margin: 14px 0 0; }
/* ===== Hero mockup ===== */
.hero-mock { position: relative; }
.mock-window {
background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-lg);
box-shadow: var(--shadow-lg); overflow: hidden;
transform: perspective(1400px) rotateY(-4deg) rotateX(2deg);
transition: transform .4s ease;
}
.hero-mock:hover .mock-window { transform: perspective(1400px) rotateY(0) rotateX(0); }
.mock-bar {
display: flex; align-items: center; gap: 7px; padding: 12px 16px;
background: var(--surface-2); border-bottom: 1px solid var(--line-soft);
}
.tl { width: 11px; height: 11px; border-radius: 50%; }
.tl-r { background: #ff6058; } .tl-y { background: #ffbd2e; } .tl-g { background: #28c840; }
.mock-title { margin-left: 10px; font-size: .85rem; font-weight: 600; color: var(--muted); }
.mock-body { padding: 18px; min-height: 318px; }
/* Board view */
.board-cols { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.board-col h5 {
margin: 0 0 10px; font-size: .76rem; text-transform: uppercase; letter-spacing: .05em;
color: var(--muted); display: flex; align-items: center; gap: 6px;
}
.board-col h5 .cnt { background: var(--brand-soft); color: var(--brand-d); border-radius: 999px; padding: 1px 7px; font-size: .7rem; }
.card-mini {
background: var(--surface); border: 1px solid var(--line-soft); border-radius: 10px;
padding: 10px; margin-bottom: 9px; box-shadow: var(--shadow-sm);
}
.card-mini .cm-tag { display: inline-block; font-size: .66rem; font-weight: 600; padding: 2px 7px; border-radius: 6px; margin-bottom: 6px; }
.tag-design { background: #fdeef4; color: #b03a73; }
.tag-eng { background: #eaf2fe; color: var(--brand-d); }
.tag-ops { background: #fef3e2; color: var(--warn); }
.card-mini p { margin: 0 0 8px; font-size: .82rem; font-weight: 500; line-height: 1.35; }
.cm-foot { display: flex; align-items: center; justify-content: space-between; }
.cm-avatar { width: 22px; height: 22px; border-radius: 50%; display: grid; place-items: center; font-size: .62rem; font-weight: 700; color: #fff; }
.cm-bar { height: 5px; width: 46px; border-radius: 999px; background: var(--line); overflow: hidden; }
.cm-bar i { display: block; height: 100%; background: var(--brand); border-radius: 999px; }
/* List view */
.list-rows { display: flex; flex-direction: column; gap: 2px; }
.list-row {
display: grid; grid-template-columns: 22px 1fr auto auto; gap: 12px; align-items: center;
padding: 11px 8px; border-radius: 10px; border-bottom: 1px solid var(--line-soft);
}
.list-row:hover { background: var(--surface-2); }
.lr-check { width: 18px; height: 18px; border-radius: 6px; border: 2px solid var(--line); }
.lr-check.done { background: var(--ok); border-color: var(--ok); position: relative; }
.lr-check.done::after { content: "✓"; color: #fff; font-size: .7rem; position: absolute; inset: 0; display: grid; place-items: center; }
.lr-title { font-size: .88rem; font-weight: 500; }
.lr-title.done { color: var(--muted); text-decoration: line-through; }
.lr-pill { font-size: .68rem; font-weight: 600; padding: 3px 9px; border-radius: 999px; }
.pill-todo { background: var(--line); color: var(--muted); }
.pill-prog { background: #fef3e2; color: var(--warn); }
.pill-done { background: #e7f6ec; color: var(--ok); }
.lr-avatar { width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center; font-size: .64rem; font-weight: 700; color: #fff; }
/* Timeline view */
.timeline { display: flex; flex-direction: column; gap: 14px; }
.tl-track-head { display: grid; grid-template-columns: 96px 1fr; gap: 10px; align-items: center; font-size: .82rem; }
.tl-name { font-weight: 600; color: var(--ink); }
.tl-grid { position: relative; height: 22px; background: repeating-linear-gradient(90deg, var(--line-soft) 0 1px, transparent 1px 25%); border-radius: 6px; }
.tl-bar { position: absolute; top: 3px; height: 16px; border-radius: 8px; display: flex; align-items: center; padding-left: 8px; font-size: .64rem; font-weight: 600; color: #fff; }
/* Docs view */
.docs-view { font-size: .86rem; }
.docs-view h4 { margin: 0 0 6px; font-size: 1.05rem; }
.docs-meta { color: var(--muted); font-size: .78rem; margin: 0 0 14px; display: flex; align-items: center; gap: 8px; }
.docs-line { height: 9px; border-radius: 6px; background: var(--line-soft); margin-bottom: 9px; }
.docs-line.w90 { width: 90%; } .docs-line.w75 { width: 75%; } .docs-line.w60 { width: 60%; }
.docs-callout { background: var(--brand-soft); border-left: 3px solid var(--brand); border-radius: 8px; padding: 11px 13px; margin: 14px 0; color: var(--brand-d); font-weight: 500; }
.docs-check { display: flex; align-items: center; gap: 9px; padding: 6px 0; color: var(--ink); }
.docs-check span.box { width: 17px; height: 17px; border-radius: 5px; background: var(--ok); color: #fff; display: grid; place-items: center; font-size: .62rem; }
/* Float cards */
.float-card {
position: absolute; background: var(--surface); border: 1px solid var(--line);
border-radius: 13px; padding: 11px 14px; box-shadow: var(--shadow-md);
display: flex; align-items: center; gap: 10px; font-size: .8rem;
}
.float-card strong { display: block; font-size: .84rem; }
.float-card span { color: var(--muted); font-size: .76rem; }
.float-card div span { display: block; }
.float-a { left: -26px; top: 30px; animation: floaty 5s ease-in-out infinite; }
.float-b { right: -20px; bottom: 26px; animation: floaty 6s ease-in-out infinite .8s; }
.fc-avatar { width: 30px; height: 30px; border-radius: 50%; background: linear-gradient(135deg, #7c5cff, #4f8af0); color: #fff; display: grid; place-items: center; font-size: .72rem; font-weight: 700; }
.fc-dot { width: 12px; height: 12px; border-radius: 50%; }
.fc-dot.ok { background: var(--ok); box-shadow: 0 0 0 4px rgba(22, 163, 74, .16); }
@keyframes floaty { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-9px); } }
/* ===== Logos ===== */
.logos { padding: 14px 0 8px; text-align: center; }
.logos-label { font-size: .8rem; color: var(--muted); text-transform: uppercase; letter-spacing: .08em; margin: 0 0 16px; }
.logo-row { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; justify-content: center; gap: clamp(22px, 5vw, 56px); }
.logo-row li { font-weight: 700; font-size: 1.18rem; color: #aab1c6; letter-spacing: -.01em; transition: color .2s; }
.logo-row li:hover { color: var(--muted); }
/* ===== Section heads ===== */
.section-head { max-width: 620px; margin: 0 auto clamp(28px, 4vw, 44px); text-align: center; }
.kicker { font-size: .82rem; font-weight: 700; color: var(--brand-d); text-transform: uppercase; letter-spacing: .07em; }
.section-head h2 { font-size: clamp(1.6rem, 3.2vw, 2.3rem); line-height: 1.15; letter-spacing: -.02em; margin: 10px 0 12px; }
.section-head p { color: var(--muted); font-size: 1.05rem; margin: 0; }
/* ===== Use cases ===== */
.usecases { padding: clamp(40px, 6vw, 70px) 0 clamp(20px, 3vw, 30px); }
.uc-tabs {
display: flex; flex-wrap: wrap; justify-content: center; gap: 8px;
background: var(--surface); border: 1px solid var(--line); border-radius: 999px;
padding: 6px; width: max-content; max-width: 100%; margin: 0 auto; box-shadow: var(--shadow-sm);
}
.uc-tab {
font: inherit; font-weight: 600; font-size: .92rem; color: var(--muted);
background: transparent; border: 0; padding: 9px 18px; border-radius: 999px; cursor: pointer;
transition: color .2s, background .2s;
}
.uc-tab:hover { color: var(--ink); }
.uc-tab.is-active { background: var(--brand); color: #fff; box-shadow: 0 4px 12px -4px rgba(47, 111, 214, .6); }
.uc-hint { text-align: center; color: var(--muted); font-size: .9rem; margin: 18px 0 0; }
.uc-current { display: inline-block; margin-left: 4px; font-weight: 600; color: var(--brand-d); }
/* ===== Features ===== */
.features { padding: clamp(40px, 6vw, 76px) 0; }
.feature-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; }
.feature {
background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--radius);
padding: 24px 22px; box-shadow: var(--shadow-sm); transition: transform .2s, box-shadow .2s, border-color .2s;
}
.feature:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: var(--line); }
.feat-ico {
display: grid; place-items: center; width: 46px; height: 46px; border-radius: 12px;
background: var(--brand-soft); color: var(--brand-d); margin-bottom: 16px;
}
.feature h3 { margin: 0 0 8px; font-size: 1.08rem; letter-spacing: -.01em; }
.feature p { margin: 0; color: var(--muted); font-size: .92rem; }
/* ===== Integrations ===== */
.integrations { padding: clamp(30px, 5vw, 60px) 0; }
.int-grid {
list-style: none; margin: 0; padding: 0;
display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px;
}
.int-grid li {
display: flex; align-items: center; gap: 12px; font-weight: 600; font-size: 1rem;
background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--radius);
padding: 16px 18px; box-shadow: var(--shadow-sm); transition: transform .2s, box-shadow .2s;
}
.int-grid li:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
.int-emoji { font-size: 1.4rem; line-height: 1; }
/* ===== Testimonial ===== */
.testimonial { padding: clamp(40px, 6vw, 80px) 0; }
.quote {
position: relative; max-width: 760px; margin: 0 auto; text-align: center;
background: var(--surface); border: 1px solid var(--line-soft); border-radius: var(--radius-lg);
padding: clamp(34px, 5vw, 56px); box-shadow: var(--shadow-md);
}
.quote-mark { position: absolute; top: 6px; left: 28px; font-size: 5rem; color: var(--brand-soft); font-weight: 800; line-height: 1; }
.quote blockquote { margin: 0 0 26px; font-size: clamp(1.15rem, 2.4vw, 1.6rem); font-weight: 600; line-height: 1.4; letter-spacing: -.01em; }
.quote figcaption { display: flex; align-items: center; justify-content: center; gap: 12px; flex-wrap: wrap; }
.q-avatar { width: 44px; height: 44px; border-radius: 50%; background: linear-gradient(135deg, #e36bb0, #7c5cff); color: #fff; display: grid; place-items: center; font-weight: 700; }
.q-meta { text-align: left; }
.q-meta strong { display: block; }
.q-meta span { color: var(--muted); font-size: .88rem; }
.q-stars { color: #f5a623; letter-spacing: 2px; font-size: 1.05rem; }
/* ===== CTA ===== */
.cta { padding: clamp(20px, 4vw, 50px) 0 clamp(50px, 7vw, 90px); }
.cta-card {
text-align: center; border-radius: var(--radius-lg); padding: clamp(40px, 6vw, 72px) 24px;
background:
radial-gradient(60% 120% at 50% 0%, rgba(255, 255, 255, .14), transparent 60%),
linear-gradient(135deg, var(--brand-d), var(--brand));
color: #fff; box-shadow: var(--shadow-lg);
}
.cta-card h2 { font-size: clamp(1.7rem, 3.4vw, 2.4rem); margin: 0 0 12px; letter-spacing: -.02em; }
.cta-card p { color: rgba(255, 255, 255, .9); margin: 0 0 24px; font-size: 1.08rem; }
.cta-form { display: flex; gap: 10px; max-width: 440px; margin: 0 auto; flex-wrap: wrap; justify-content: center; }
.cta-form input { background: rgba(255, 255, 255, .95); border-color: transparent; }
.cta-form .btn-solid { background: var(--ink); box-shadow: 0 8px 20px -8px rgba(0, 0, 0, .5); }
.cta-form .btn-solid:hover { background: #1d2238; }
.cta .cta-note { color: rgba(255, 255, 255, .82); }
/* ===== Footer ===== */
.site-footer { border-top: 1px solid var(--line-soft); background: var(--surface); padding: clamp(36px, 5vw, 56px) 0 26px; }
.footer-inner { display: flex; justify-content: space-between; gap: 32px; flex-wrap: wrap; }
.footer-brand { display: flex; align-items: center; gap: 10px; font-weight: 800; font-size: 1.1rem; }
.footer-cols { display: flex; gap: clamp(28px, 5vw, 64px); flex-wrap: wrap; }
.footer-cols h4 { font-size: .82rem; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin: 0 0 12px; }
.footer-cols a { display: block; color: var(--ink); font-size: .92rem; padding: 4px 0; transition: color .2s; }
.footer-cols a:hover { color: var(--brand-d); }
.footer-base {
display: flex; justify-content: space-between; gap: 14px; flex-wrap: wrap;
margin-top: 30px; padding-top: 22px; border-top: 1px solid var(--line-soft);
color: var(--muted); font-size: .85rem;
}
.footer-legal a:hover { color: var(--brand-d); }
/* ===== Toast ===== */
.toast {
position: fixed; left: 50%; bottom: 28px; transform: translate(-50%, 24px);
background: var(--ink); color: #fff; padding: 13px 20px; border-radius: 12px;
font-size: .92rem; font-weight: 500; box-shadow: var(--shadow-lg);
opacity: 0; pointer-events: none; transition: opacity .28s, transform .28s; z-index: 80;
max-width: 90vw;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
/* ===== Scroll reveal ===== */
.reveal { opacity: 0; transform: translateY(22px); transition: opacity .6s ease, transform .6s ease; }
.reveal.in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) { .reveal { opacity: 1; transform: none; } }
/* ===== Responsive ===== */
@media (max-width: 920px) {
.main-nav, .header-cta { display: none; }
.nav-toggle { display: flex; }
.mobile-nav { display: none; }
.mobile-nav.open { display: flex; }
.hero-grid { grid-template-columns: 1fr; }
.hero-mock { max-width: 540px; margin: 0 auto; }
.mock-window { transform: none; }
.feature-grid { grid-template-columns: repeat(2, 1fr); }
.int-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 600px) {
.feature-grid { grid-template-columns: 1fr; }
.int-grid { grid-template-columns: repeat(2, 1fr); }
.float-a { left: 0; top: 12px; }
.float-b { right: 0; bottom: 12px; }
.footer-base { flex-direction: column; }
}
@media (max-width: 380px) {
.uc-tabs { width: 100%; }
.board-cols { grid-template-columns: 1fr; }
}/* ===== Flowdesk landing — vanilla JS ===== */
(function () {
"use strict";
/* ---- Toast helper ---- */
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () { toastEl.classList.remove("show"); }, 2600);
}
/* ---- Mobile nav ---- */
var navToggle = document.querySelector(".nav-toggle");
var mobileNav = document.getElementById("mobile-nav");
if (navToggle && mobileNav) {
navToggle.addEventListener("click", function () {
var open = navToggle.getAttribute("aria-expanded") === "true";
navToggle.setAttribute("aria-expanded", String(!open));
mobileNav.classList.toggle("open", !open);
mobileNav.hidden = open;
});
mobileNav.querySelectorAll("a").forEach(function (a) {
a.addEventListener("click", function () {
navToggle.setAttribute("aria-expanded", "false");
mobileNav.classList.remove("open");
mobileNav.hidden = true;
});
});
}
/* ---- Mockup view templates ---- */
var AV = {
JK: "#7c5cff", MR: "#e36bb0", DT: "#4f8af0", LS: "#16a34a", PN: "#d97706"
};
function avatar(initials, size) {
return '<span class="' + (size || "cm-avatar") + '" style="background:' + (AV[initials] || "#4f8af0") + '">' + initials + "</span>";
}
var views = {
board: {
title: "Q3 Launch Board",
label: "Kanban board",
html:
'<div class="board-cols">' +
'<div class="board-col"><h5>To do <span class="cnt">2</span></h5>' +
'<div class="card-mini"><span class="cm-tag tag-design">Design</span><p>Refine onboarding flow</p><div class="cm-foot">' + avatar("MR") + '<span class="cm-bar"><i style="width:20%"></i></span></div></div>' +
'<div class="card-mini"><span class="cm-tag tag-ops">Ops</span><p>Draft launch checklist</p><div class="cm-foot">' + avatar("PN") + '<span class="cm-bar"><i style="width:10%"></i></span></div></div>' +
'</div>' +
'<div class="board-col"><h5>In progress <span class="cnt">2</span></h5>' +
'<div class="card-mini"><span class="cm-tag tag-eng">Eng</span><p>Build async standups</p><div class="cm-foot">' + avatar("DT") + '<span class="cm-bar"><i style="width:65%"></i></span></div></div>' +
'<div class="card-mini"><span class="cm-tag tag-design">Design</span><p>Marketing site polish</p><div class="cm-foot">' + avatar("LS") + '<span class="cm-bar"><i style="width:48%"></i></span></div></div>' +
'</div>' +
'<div class="board-col"><h5>Done <span class="cnt">1</span></h5>' +
'<div class="card-mini"><span class="cm-tag tag-eng">Eng</span><p>Set up CI pipeline</p><div class="cm-foot">' + avatar("JK") + '<span class="cm-bar"><i style="width:100%"></i></span></div></div>' +
'</div>' +
'</div>'
},
list: {
title: "All tasks · Q3",
label: "Task list",
html:
'<div class="list-rows">' +
row(true, "Set up CI pipeline", "done", "Done", "JK") +
row(false, "Build async standups", "prog", "In progress", "DT") +
row(false, "Marketing site polish", "prog", "In progress", "LS") +
row(false, "Refine onboarding flow", "todo", "To do", "MR") +
row(false, "Draft launch checklist", "todo", "To do", "PN") +
'</div>'
},
timeline: {
title: "Roadmap · Aug–Sep",
label: "Timeline",
html:
'<div class="timeline">' +
track("Discovery", 0, 35, "#7c5cff") +
track("Design", 18, 40, "#e36bb0") +
track("Engineering", 40, 55, "#4f8af0") +
track("Beta", 70, 28, "#16a34a") +
'</div>'
},
docs: {
title: "Launch plan.doc",
label: "Docs",
html:
'<div class="docs-view">' +
'<h4>Q3 Launch Plan</h4>' +
'<p class="docs-meta">' + avatar("MR", "lr-avatar") + ' Edited by Mara · 2h ago</p>' +
'<div class="docs-line w90"></div><div class="docs-line w75"></div><div class="docs-line w60"></div>' +
'<div class="docs-callout">🎯 Goal: ship the async standups beta to 50 teams by Sep 30.</div>' +
'<div class="docs-check"><span class="box">✓</span> Finalize pricing tiers</div>' +
'<div class="docs-check"><span class="box">✓</span> Brief the support team</div>' +
'<div class="docs-line w75" style="margin-top:10px"></div><div class="docs-line w90"></div>' +
'</div>'
}
};
function row(done, title, pillCls, pillTxt, av) {
return '<div class="list-row">' +
'<span class="lr-check ' + (done ? "done" : "") + '"></span>' +
'<span class="lr-title ' + (done ? "done" : "") + '">' + title + '</span>' +
'<span class="lr-pill pill-' + pillCls + '">' + pillTxt + '</span>' +
avatar(av, "lr-avatar") +
'</div>';
}
function track(name, start, width, color) {
return '<div class="tl-track-head"><span class="tl-name">' + name + '</span>' +
'<span class="tl-grid"><span class="tl-bar" style="left:' + start + '%;width:' + width + '%;background:' + color + '">' + width + '%</span></span></div>';
}
var mockBody = document.getElementById("mock-body");
var mockTitle = document.getElementById("mock-title");
var ucCurrent = document.getElementById("uc-current");
function setView(key) {
var v = views[key];
if (!v || !mockBody) return;
mockBody.style.opacity = "0";
setTimeout(function () {
mockBody.innerHTML = v.html;
if (mockTitle) mockTitle.textContent = v.title;
if (ucCurrent) ucCurrent.textContent = "Showing: " + v.label;
mockBody.style.opacity = "1";
}, 140);
}
if (mockBody) {
mockBody.style.transition = "opacity .2s ease";
setView("board");
}
/* ---- Use-case tabs ---- */
var tabs = Array.prototype.slice.call(document.querySelectorAll(".uc-tab"));
function activate(tab) {
tabs.forEach(function (t) {
var on = t === tab;
t.classList.toggle("is-active", on);
t.setAttribute("aria-selected", String(on));
});
setView(tab.dataset.view);
}
tabs.forEach(function (tab, i) {
tab.addEventListener("click", function () { activate(tab); });
tab.addEventListener("keydown", function (e) {
var dir = e.key === "ArrowRight" ? 1 : e.key === "ArrowLeft" ? -1 : 0;
if (!dir) return;
e.preventDefault();
var next = tabs[(i + dir + tabs.length) % tabs.length];
next.focus();
activate(next);
});
});
/* ---- Email forms ---- */
function isEmail(v) { return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v); }
function wireForm(id) {
var form = document.getElementById(id);
if (!form) return;
var input = form.querySelector("input[type=email]");
form.addEventListener("submit", function (e) {
e.preventDefault();
var val = (input.value || "").trim();
if (!isEmail(val)) {
input.setAttribute("aria-invalid", "true");
input.focus();
toast("Please enter a valid work email.");
return;
}
input.removeAttribute("aria-invalid");
input.value = "";
toast("🎉 You're in! Check " + val + " to set up your workspace.");
});
if (input) {
input.addEventListener("input", function () { input.removeAttribute("aria-invalid"); });
}
}
wireForm("hero-form");
wireForm("cta-form");
/* ---- Scroll reveal ---- */
var reveals = Array.prototype.slice.call(document.querySelectorAll(".reveal"));
if ("IntersectionObserver" in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting) {
en.target.classList.add("in");
io.unobserve(en.target);
}
});
}, { threshold: 0.12, rootMargin: "0px 0px -40px 0px" });
reveals.forEach(function (el) { io.observe(el); });
} else {
reveals.forEach(function (el) { el.classList.add("in"); });
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Flowdesk — Plan, collaborate, ship</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<header class="site-header" role="banner">
<div class="wrap header-inner">
<a class="brand" href="#main" aria-label="Flowdesk home">
<span class="brand-mark" aria-hidden="true">
<svg viewBox="0 0 24 24" width="22" height="22" fill="none">
<rect x="3" y="3" width="7" height="7" rx="2" fill="currentColor"/>
<rect x="14" y="3" width="7" height="11" rx="2" fill="currentColor" opacity=".6"/>
<rect x="3" y="14" width="7" height="7" rx="2" fill="currentColor" opacity=".6"/>
<rect x="14" y="18" width="7" height="3" rx="1.5" fill="currentColor"/>
</svg>
</span>
<span class="brand-name">Flowdesk</span>
</a>
<nav class="main-nav" aria-label="Primary">
<a href="#features">Features</a>
<a href="#usecases">Use cases</a>
<a href="#integrations">Integrations</a>
<a href="#testimonial">Customers</a>
</nav>
<div class="header-cta">
<a class="btn btn-ghost" href="#main">Sign in</a>
<a class="btn btn-solid" href="#cta">Start free</a>
</div>
<button class="nav-toggle" aria-expanded="false" aria-controls="mobile-nav" aria-label="Open menu">
<span></span><span></span><span></span>
</button>
</div>
<div class="mobile-nav" id="mobile-nav" hidden>
<a href="#features">Features</a>
<a href="#usecases">Use cases</a>
<a href="#integrations">Integrations</a>
<a href="#testimonial">Customers</a>
<a class="btn btn-solid" href="#cta">Start free</a>
</div>
</header>
<main id="main">
<!-- HERO -->
<section class="hero" aria-labelledby="hero-title">
<div class="wrap hero-grid">
<div class="hero-copy reveal">
<span class="eyebrow"><span class="dot" aria-hidden="true"></span> New · Async standups</span>
<h1 id="hero-title">Where your team plans, collaborates, and ships — calmly.</h1>
<p class="lede">Flowdesk brings boards, docs, and updates into one organized workspace, so every project moves forward without the noise.</p>
<form class="hero-form" id="hero-form" novalidate>
<label class="sr-only" for="hero-email">Work email</label>
<input id="hero-email" type="email" name="email" placeholder="[email protected]" autocomplete="email" required />
<button class="btn btn-solid" type="submit">Start free</button>
</form>
<p class="hero-note">Free for up to 5 teammates · No credit card required</p>
</div>
<div class="hero-mock reveal" aria-label="Product preview">
<div class="mock-window">
<div class="mock-bar" aria-hidden="true">
<span class="tl tl-r"></span><span class="tl tl-y"></span><span class="tl tl-g"></span>
<span class="mock-title" id="mock-title">Q3 Launch Board</span>
</div>
<div class="mock-body" id="mock-body">
<!-- injected by JS -->
</div>
</div>
<div class="float-card float-a" aria-hidden="true">
<span class="fc-avatar">JK</span>
<div><strong>Jules</strong><span>moved a card to Done</span></div>
</div>
<div class="float-card float-b" aria-hidden="true">
<span class="fc-dot ok"></span>
<div><strong>3 tasks shipped</strong><span>this week</span></div>
</div>
</div>
</div>
</section>
<!-- LOGOS -->
<section class="logos reveal" aria-label="Trusted by teams">
<p class="logos-label">Trusted by organized teams at</p>
<ul class="logo-row">
<li>Northwind</li><li>Cobalt</li><li>Lumen Labs</li><li>Fernweh</li><li>Riverstone</li>
</ul>
</section>
<!-- USE CASES (tabbed mockup swap) -->
<section class="usecases" id="usecases" aria-labelledby="uc-title">
<div class="wrap">
<div class="section-head reveal">
<span class="kicker">One workspace, many workflows</span>
<h2 id="uc-title">Pick a starting point. Flowdesk adapts.</h2>
<p>Switch the layout to match how your team thinks — the same data, the right view.</p>
</div>
<div class="uc-tabs reveal" role="tablist" aria-label="Use cases">
<button class="uc-tab is-active" role="tab" aria-selected="true" id="tab-board" aria-controls="mock-body" data-view="board">Kanban board</button>
<button class="uc-tab" role="tab" aria-selected="false" id="tab-list" aria-controls="mock-body" data-view="list">Task list</button>
<button class="uc-tab" role="tab" aria-selected="false" id="tab-timeline" aria-controls="mock-body" data-view="timeline">Timeline</button>
<button class="uc-tab" role="tab" aria-selected="false" id="tab-docs" aria-controls="mock-body" data-view="docs">Docs</button>
</div>
<p class="uc-hint reveal">Choose a view above to update the preview in the hero. <span class="uc-current" id="uc-current">Showing: Kanban board</span></p>
</div>
</section>
<!-- COLLABORATION FEATURE BAND -->
<section class="features" id="features" aria-labelledby="feat-title">
<div class="wrap">
<div class="section-head reveal">
<span class="kicker">Built for collaboration</span>
<h2 id="feat-title">Move together, not in circles.</h2>
</div>
<div class="feature-grid">
<article class="feature reveal">
<span class="feat-ico" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
</span>
<h3>Real-time presence</h3>
<p>See who is editing what, live. Comments thread inline so context never gets lost in a chat scroll.</p>
</article>
<article class="feature reveal">
<span class="feat-ico" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"/><path d="M22 4 12 14.01l-3-3"/></svg>
</span>
<h3>Async updates</h3>
<p>Replace status meetings with tidy, scheduled standups. Everyone reads on their own time.</p>
</article>
<article class="feature reveal">
<span class="feat-ico" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v18h18"/><path d="M18.7 8 13 13.5l-3-3L7 14"/></svg>
</span>
<h3>Calm analytics</h3>
<p>Lightweight burn-down and workload charts that nudge — never overwhelm — your weekly planning.</p>
</article>
<article class="feature reveal">
<span class="feat-ico" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m13 2-3 7h6l-3 7"/><circle cx="12" cy="12" r="10" opacity=".35"/></svg>
</span>
<h3>Smart automations</h3>
<p>Rule-based flows move cards, assign owners, and ping the right person — without a single line of code.</p>
</article>
</div>
</div>
</section>
<!-- INTEGRATIONS STRIP -->
<section class="integrations" id="integrations" aria-labelledby="int-title">
<div class="wrap">
<div class="section-head reveal">
<span class="kicker">Plays well with your stack</span>
<h2 id="int-title">Connect the tools you already use.</h2>
</div>
<ul class="int-grid reveal" aria-label="Integrations">
<li><span class="int-emoji" aria-hidden="true">💬</span> Slack</li>
<li><span class="int-emoji" aria-hidden="true">📅</span> Calendar</li>
<li><span class="int-emoji" aria-hidden="true">🐙</span> GitHub</li>
<li><span class="int-emoji" aria-hidden="true">🎨</span> Figma</li>
<li><span class="int-emoji" aria-hidden="true">📄</span> Drive</li>
<li><span class="int-emoji" aria-hidden="true">📊</span> Sheets</li>
<li><span class="int-emoji" aria-hidden="true">⚡</span> Zapier</li>
<li><span class="int-emoji" aria-hidden="true">📧</span> Email</li>
</ul>
</div>
</section>
<!-- TESTIMONIAL -->
<section class="testimonial" id="testimonial" aria-labelledby="test-title">
<div class="wrap">
<figure class="quote reveal">
<h2 id="test-title" class="sr-only">What customers say</h2>
<span class="quote-mark" aria-hidden="true">“</span>
<blockquote>
We replaced three tools and a weekly meeting with one Flowdesk board. The team is calmer, and we actually ship on the date we said we would.
</blockquote>
<figcaption>
<span class="q-avatar" aria-hidden="true">MR</span>
<span class="q-meta">
<strong>Mara Reyes</strong>
<span>Head of Product, Lumen Labs</span>
</span>
<span class="q-stars" aria-label="Rated 5 out of 5">★★★★★</span>
</figcaption>
</figure>
</div>
</section>
<!-- CTA -->
<section class="cta" id="cta" aria-labelledby="cta-title">
<div class="wrap cta-card reveal">
<h2 id="cta-title">Free to start. Calm by design.</h2>
<p>Spin up your first board in under a minute. Invite the team when you're ready.</p>
<form class="cta-form" id="cta-form" novalidate>
<label class="sr-only" for="cta-email">Work email</label>
<input id="cta-email" type="email" name="email" placeholder="[email protected]" autocomplete="email" required />
<button class="btn btn-solid" type="submit">Start free</button>
</form>
<p class="cta-note">No credit card · Cancel anytime · 5 teammates free forever</p>
</div>
</section>
</main>
<footer class="site-footer" role="contentinfo">
<div class="wrap footer-inner">
<div class="footer-brand">
<span class="brand-mark sm" aria-hidden="true">
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><rect x="3" y="3" width="7" height="7" rx="2"/><rect x="14" y="3" width="7" height="11" rx="2" opacity=".6"/><rect x="3" y="14" width="7" height="7" rx="2" opacity=".6"/></svg>
</span>
<span>Flowdesk</span>
</div>
<nav class="footer-cols" aria-label="Footer">
<div><h4>Product</h4><a href="#features">Features</a><a href="#integrations">Integrations</a><a href="#cta">Pricing</a></div>
<div><h4>Company</h4><a href="#main">About</a><a href="#testimonial">Customers</a><a href="#main">Careers</a></div>
<div><h4>Resources</h4><a href="#main">Docs</a><a href="#main">Changelog</a><a href="#main">Support</a></div>
</nav>
</div>
<div class="wrap footer-base">
<span>© 2026 Flowdesk · Fictional product for demo purposes.</span>
<span class="footer-legal"><a href="#main">Privacy</a> · <a href="#main">Terms</a></span>
</div>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Productivity / Collaboration Landing
A friendly, white-and-blue marketing page for Flowdesk, a fictional collaboration tool. The hero pairs a confident headline and an email-capture form with a tilted product window that floats gentle activity cards (“Jules moved a card to Done”, “3 tasks shipped this week”). The whole page is built on neutral SaaS surfaces, a single calm accent, soft 1px lines, and intentional shadows.
The standout interaction is the use-case tab strip: choosing Kanban board, Task list, Timeline, or Docs re-renders the hero mockup in place with a soft fade, swapping in realistic but clearly fictional data — cards with progress bars, a checkbox task list, a colored Gantt-style timeline, and a doc with callouts and checked items. Keyboard arrow keys move between tabs, ARIA selection state stays in sync, and a live “Showing:” label confirms the current view.
Below the fold, a collaboration feature band, an integrations strip, and a five-star testimonial scroll-reveal into view via IntersectionObserver. Both email forms validate input, surface an inline invalid state, and confirm with a toast. Everything is vanilla HTML, CSS, and JS — no frameworks, no external images, fully responsive down to ~360px with a working mobile menu.
Illustrative SaaS UI only — fictional product, metrics, and billing. No real backend.