Dating — Landing Lgbtq
A warm, pride-forward dating landing page built for LGBTQ+ folks: a phone-framed hero with an animated match card, a pronoun and identity picker that updates a live preview, safe-space and moderation messaging, community stat counters, member spotlight cards, and an inclusive signup form with orientation and pronoun selectors. Rounded gradient surfaces, coral-to-violet accents, and smooth micro-interactions give it a genuine mobile-app feel.
MCP
Code
:root {
--bg: #fff5f6;
--surface: #ffffff;
--ink: #2a1a2e;
--ink-2: #5a4a55;
--muted: #8a7a85;
--coral: #ff5e6c;
--coral-d: #e63950;
--violet: #8b5cf6;
--violet-d: #7c3aed;
--pink: #ff8fb1;
--grad: linear-gradient(135deg, #ff5e6c, #8b5cf6);
--line: rgba(42, 26, 46, 0.10);
--white: #fff;
--r-sm: 12px;
--r-md: 20px;
--r-lg: 28px;
--shadow: 0 18px 50px -20px rgba(124, 58, 237, 0.35);
--shadow-sm: 0 8px 24px -12px rgba(42, 26, 46, 0.25);
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
font-family: "Inter", system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--ink);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
h1, h2, h3 { margin: 0; line-height: 1.15; letter-spacing: -0.02em; }
p { margin: 0; }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
.skip-link {
position: absolute;
left: -999px;
top: 8px;
background: var(--ink);
color: #fff;
padding: 10px 16px;
border-radius: var(--r-sm);
z-index: 200;
}
.skip-link:focus { left: 12px; }
:focus-visible {
outline: 3px solid var(--violet);
outline-offset: 2px;
border-radius: 6px;
}
.wrap { width: min(1120px, 92vw); margin: 0 auto; }
.wrap-narrow { width: min(760px, 92vw); }
.grad-text {
background: var(--grad);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
/* ---------- Buttons ---------- */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 8px;
font-weight: 700;
font-size: 15px;
padding: 14px 26px;
border-radius: 999px;
border: 0;
cursor: pointer;
transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.2s ease;
font-family: inherit;
}
.btn-sm { padding: 10px 20px; font-size: 14px; }
.btn-block { width: 100%; }
.btn-primary {
background: var(--grad);
color: #fff;
box-shadow: 0 10px 26px -10px rgba(230, 57, 80, 0.7);
}
.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 16px 32px -12px rgba(124, 58, 237, 0.6); }
.btn-primary:active { transform: translateY(0); }
.btn-ghost {
background: var(--white);
color: var(--ink);
border: 1.5px solid var(--line);
}
.btn-ghost:hover { border-color: var(--violet); color: var(--violet-d); transform: translateY(-2px); }
/* ---------- Nav ---------- */
.nav {
position: sticky;
top: 0;
z-index: 100;
background: rgba(255, 245, 246, 0.82);
backdrop-filter: blur(12px);
border-bottom: 1px solid var(--line);
}
.nav-inner {
display: flex;
align-items: center;
gap: 20px;
padding: 14px 0;
}
.brand {
display: inline-flex;
align-items: center;
gap: 9px;
font-weight: 800;
font-size: 20px;
letter-spacing: -0.02em;
}
.brand-dot {
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--grad);
box-shadow: 0 0 0 4px rgba(139, 92, 246, 0.16);
}
.nav-links {
display: flex;
gap: 26px;
margin-left: auto;
font-weight: 600;
font-size: 15px;
color: var(--ink-2);
}
.nav-links a { position: relative; transition: color 0.15s; }
.nav-links a:hover { color: var(--violet-d); }
.nav-links a::after {
content: "";
position: absolute;
left: 0; bottom: -6px;
width: 0; height: 2px;
background: var(--grad);
border-radius: 2px;
transition: width 0.2s;
}
.nav-links a:hover::after { width: 100%; }
/* ---------- Hero ---------- */
.hero { padding: 60px 0 40px; position: relative; overflow: hidden; }
.hero::before {
content: "";
position: absolute;
top: -120px; right: -100px;
width: 420px; height: 420px;
background: radial-gradient(circle, rgba(255, 143, 177, 0.5), transparent 70%);
filter: blur(10px);
z-index: 0;
}
.hero-grid {
display: grid;
grid-template-columns: 1.05fr 0.95fr;
gap: 48px;
align-items: center;
position: relative;
z-index: 1;
}
.pill-badge {
display: inline-flex;
align-items: center;
gap: 10px;
background: var(--white);
border: 1px solid var(--line);
padding: 7px 14px 7px 8px;
border-radius: 999px;
font-weight: 600;
font-size: 13px;
color: var(--ink-2);
box-shadow: var(--shadow-sm);
}
.flag-strip {
width: 34px;
height: 18px;
border-radius: 5px;
background: linear-gradient(
to bottom,
#ff5e6c 0 20%, #ff9f45 20% 40%, #ffd93d 40% 60%,
#4ade80 60% 80%, #8b5cf6 80% 100%
);
}
.flag-strip.lg { width: 60px; height: 30px; border-radius: 8px; margin: 0 auto 6px; }
.hero h1 {
font-family: "Fraunces", "Inter", serif;
font-size: clamp(38px, 5.5vw, 62px);
font-weight: 700;
margin: 18px 0 16px;
}
.lead { font-size: 18px; color: var(--ink-2); max-width: 44ch; }
.hero-cta { display: flex; gap: 14px; margin: 26px 0 22px; flex-wrap: wrap; }
.trust-row {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: 8px 22px;
font-size: 14px;
font-weight: 600;
color: var(--ink-2);
}
.trust-row li { display: inline-flex; align-items: center; gap: 7px; }
.check {
display: inline-grid;
place-items: center;
width: 20px; height: 20px;
border-radius: 50%;
background: rgba(74, 222, 128, 0.2);
color: #16a34a;
font-size: 12px;
font-weight: 800;
}
/* ---------- Phone frame ---------- */
.phone-col { position: relative; display: grid; place-items: center; }
.phone-glow {
position: absolute;
inset: 10% 15%;
background: var(--grad);
filter: blur(70px);
opacity: 0.35;
border-radius: 50%;
z-index: 0;
}
.phone {
position: relative;
z-index: 1;
width: min(340px, 82vw);
background: #1a0f1e;
border-radius: 44px;
padding: 12px;
box-shadow: var(--shadow), inset 0 0 0 2px rgba(255, 255, 255, 0.06);
}
.phone-notch {
position: absolute;
top: 14px; left: 50%;
transform: translateX(-50%);
width: 110px; height: 22px;
background: #1a0f1e;
border-radius: 0 0 14px 14px;
z-index: 3;
}
.phone-screen {
background: linear-gradient(180deg, #fff, #fff5f6);
border-radius: 34px;
overflow: hidden;
height: 560px;
display: flex;
flex-direction: column;
}
.app-topbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30px 20px 12px;
}
.app-title { font-weight: 800; font-size: 20px; }
.app-live {
font-size: 12px;
font-weight: 600;
color: #16a34a;
display: inline-flex;
align-items: center;
gap: 6px;
}
.app-live::before {
content: "";
width: 8px; height: 8px;
border-radius: 50%;
background: #22c55e;
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.6);
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.5); }
70% { box-shadow: 0 0 0 8px rgba(34, 197, 94, 0); }
100% { box-shadow: 0 0 0 0 rgba(34, 197, 94, 0); }
}
.match-stack {
flex: 1;
position: relative;
margin: 6px 16px 0;
}
.match-card {
position: absolute;
inset: 0;
border-radius: var(--r-md);
overflow: hidden;
box-shadow: var(--shadow-sm);
display: flex;
flex-direction: column;
justify-content: flex-end;
color: #fff;
animation: cardIn 0.45s cubic-bezier(0.2, 0.8, 0.3, 1.2);
}
.match-card::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg, transparent 35%, rgba(0, 0, 0, 0.72));
}
.match-card.leaving { animation: cardOut 0.35s ease forwards; }
@keyframes cardIn {
from { opacity: 0; transform: scale(0.94) translateY(8px); }
to { opacity: 1; transform: none; }
}
@keyframes cardOut {
to { opacity: 0; transform: translateX(60px) rotate(9deg); }
}
.match-meta {
position: relative;
z-index: 2;
padding: 18px;
}
.match-verify {
position: absolute;
top: 14px; left: 14px;
z-index: 2;
background: rgba(255, 255, 255, 0.92);
color: var(--violet-d);
font-size: 11px;
font-weight: 700;
padding: 5px 10px;
border-radius: 999px;
display: inline-flex;
gap: 5px;
align-items: center;
}
.match-name { font-size: 22px; font-weight: 800; display: flex; align-items: baseline; gap: 8px; }
.match-name .mp {
font-size: 12px;
font-weight: 600;
background: rgba(255, 255, 255, 0.22);
padding: 3px 9px;
border-radius: 999px;
backdrop-filter: blur(4px);
}
.match-sub { font-size: 13px; opacity: 0.9; margin-top: 3px; }
.match-tags { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
.match-tags span {
font-size: 11px;
font-weight: 600;
padding: 4px 10px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.2);
backdrop-filter: blur(4px);
}
.swipe-row {
display: flex;
justify-content: center;
gap: 18px;
padding: 16px 0 24px;
}
.swipe-btn {
width: 54px; height: 54px;
border-radius: 50%;
border: 0;
cursor: pointer;
font-size: 20px;
display: grid;
place-items: center;
background: var(--white);
box-shadow: var(--shadow-sm);
transition: transform 0.15s, box-shadow 0.2s;
}
.swipe-btn:hover { transform: translateY(-3px) scale(1.06); }
.swipe-btn:active { transform: scale(0.92); }
.swipe-btn.pass { color: var(--coral-d); }
.swipe-btn.star { color: var(--violet-d); font-size: 22px; }
.swipe-btn.like {
color: #fff;
background: var(--grad);
width: 62px; height: 62px;
font-size: 24px;
}
/* ---------- Sections ---------- */
.section { padding: 76px 0; }
.section-tint { background: linear-gradient(180deg, #fff, #fdeef1); }
.section-head { text-align: center; max-width: 600px; margin: 0 auto 44px; }
.eyebrow {
display: inline-block;
font-size: 13px;
font-weight: 800;
letter-spacing: 0.08em;
text-transform: uppercase;
color: var(--violet-d);
margin-bottom: 10px;
}
.section-head h2 {
font-family: "Fraunces", serif;
font-size: clamp(28px, 4vw, 40px);
font-weight: 700;
}
.section-sub { color: var(--ink-2); font-size: 17px; margin-top: 12px; }
/* ---------- Identity picker ---------- */
.identity-grid {
display: grid;
grid-template-columns: 1.1fr 0.9fr;
gap: 32px;
align-items: start;
}
.picker-card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 28px;
box-shadow: var(--shadow-sm);
}
.chip-group { border: 0; padding: 0; margin: 0 0 24px; }
.chip-group:last-child { margin-bottom: 0; }
.chip-group legend {
font-weight: 700;
font-size: 15px;
padding: 0;
margin-bottom: 12px;
}
.chips { display: flex; flex-wrap: wrap; gap: 10px; }
.chip {
font-family: inherit;
font-size: 14px;
font-weight: 600;
padding: 10px 16px;
border-radius: 999px;
border: 1.5px solid var(--line);
background: var(--bg);
color: var(--ink-2);
cursor: pointer;
transition: all 0.15s ease;
}
.chip:hover { border-color: var(--pink); transform: translateY(-1px); }
.chip.is-active {
background: var(--grad);
color: #fff;
border-color: transparent;
box-shadow: 0 8px 18px -8px rgba(230, 57, 80, 0.6);
}
.chip-custom { border-style: dashed; }
.custom-input {
margin-top: 12px;
width: 100%;
padding: 11px 14px;
border-radius: var(--r-sm);
border: 1.5px solid var(--line);
font-family: inherit;
font-size: 14px;
}
.custom-input:focus-visible { border-color: var(--violet); }
/* ---------- Preview card ---------- */
.preview-card {
background: var(--white);
border-radius: var(--r-lg);
overflow: hidden;
box-shadow: var(--shadow);
border: 1px solid var(--line);
position: sticky;
top: 90px;
}
.preview-photo {
height: 200px;
background: linear-gradient(135deg, #ff8fb1, #8b5cf6 70%, #7c3aed);
position: relative;
}
.preview-status {
position: absolute;
bottom: 12px; left: 14px;
background: rgba(255, 255, 255, 0.9);
color: #16a34a;
font-size: 12px;
font-weight: 700;
padding: 5px 11px;
border-radius: 999px;
}
.preview-body { padding: 20px 22px 24px; }
.preview-name-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.preview-name-row h3 { font-size: 22px; }
.preview-pronoun {
font-size: 12px;
font-weight: 700;
color: var(--violet-d);
background: rgba(139, 92, 246, 0.12);
padding: 4px 10px;
border-radius: 999px;
}
.preview-loc { color: var(--muted); font-size: 14px; margin-top: 4px; }
.preview-tags { display: flex; gap: 7px; flex-wrap: wrap; margin: 12px 0; }
.tag {
font-size: 12px;
font-weight: 700;
padding: 5px 12px;
border-radius: 999px;
background: rgba(255, 94, 108, 0.12);
color: var(--coral-d);
}
.tag-sm { font-size: 11px; padding: 3px 9px; }
.preview-bio { color: var(--ink-2); font-size: 14px; }
/* ---------- Features ---------- */
.feature-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
}
.feature-card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 26px 22px;
transition: transform 0.2s, box-shadow 0.2s;
}
.feature-card:hover { transform: translateY(-5px); box-shadow: var(--shadow); }
.feature-icon {
display: inline-grid;
place-items: center;
width: 52px; height: 52px;
border-radius: 16px;
background: linear-gradient(135deg, rgba(255, 94, 108, 0.16), rgba(139, 92, 246, 0.16));
font-size: 24px;
margin-bottom: 16px;
}
.feature-card h3 { font-size: 18px; margin-bottom: 8px; }
.feature-card p { color: var(--ink-2); font-size: 14px; }
.feature-badge {
display: inline-block;
margin-top: 14px;
font-size: 11px;
font-weight: 700;
color: var(--violet-d);
background: rgba(139, 92, 246, 0.12);
padding: 4px 11px;
border-radius: 999px;
}
/* ---------- Stats ---------- */
.stats-row {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 18px;
margin-bottom: 52px;
}
.stat {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-md);
padding: 26px 18px;
text-align: center;
box-shadow: var(--shadow-sm);
}
.stat-num {
display: block;
font-family: "Fraunces", serif;
font-size: 34px;
font-weight: 700;
background: var(--grad);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.stat-label { font-size: 13px; color: var(--ink-2); font-weight: 600; }
/* ---------- Spotlight ---------- */
.spotlight-row {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 22px;
}
.spot-card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-md);
overflow: hidden;
transition: transform 0.2s, box-shadow 0.2s;
}
.spot-card:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
.spot-photo { height: 150px; }
.spot-a { background: linear-gradient(135deg, #ff5e6c, #ff9f45); }
.spot-b { background: linear-gradient(135deg, #8b5cf6, #ff8fb1); }
.spot-c { background: linear-gradient(135deg, #4ade80, #8b5cf6); }
.spot-info { padding: 18px 20px 22px; }
.spot-name { font-weight: 800; font-size: 17px; display: flex; align-items: baseline; gap: 8px; }
.spot-pron {
font-size: 12px;
font-weight: 600;
color: var(--violet-d);
}
.spot-badges { display: flex; gap: 6px; margin: 10px 0; flex-wrap: wrap; }
.spot-quote { color: var(--ink-2); font-size: 14px; font-style: italic; }
/* ---------- FAQ ---------- */
.faq { display: flex; flex-direction: column; gap: 12px; }
.faq-item {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-md);
overflow: hidden;
}
.faq-q {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
padding: 20px 24px;
background: none;
border: 0;
font-family: inherit;
font-size: 16px;
font-weight: 700;
color: var(--ink);
cursor: pointer;
text-align: left;
}
.faq-icon {
flex-shrink: 0;
width: 28px; height: 28px;
display: grid;
place-items: center;
border-radius: 50%;
background: rgba(139, 92, 246, 0.12);
color: var(--violet-d);
font-size: 18px;
font-weight: 700;
transition: transform 0.25s;
}
.faq-item.open .faq-icon { transform: rotate(45deg); }
.faq-a {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.faq-a p { padding: 0 24px 20px; color: var(--ink-2); font-size: 15px; }
/* ---------- Join ---------- */
.join { padding-top: 40px; }
.join-card {
background: var(--white);
border: 1px solid var(--line);
border-radius: var(--r-lg);
padding: 40px;
box-shadow: var(--shadow);
position: relative;
overflow: hidden;
}
.join-card::before {
content: "";
position: absolute;
top: 0; left: 0; right: 0;
height: 6px;
background: var(--grad);
}
.join-head { text-align: center; margin-bottom: 26px; }
.join-head h2 { font-family: "Fraunces", serif; font-size: 30px; font-weight: 700; }
.join-head p { color: var(--ink-2); margin-top: 6px; }
.join-form {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.field { display: flex; flex-direction: column; gap: 6px; }
.field label { font-weight: 600; font-size: 14px; }
.field input, .field select {
padding: 13px 14px;
border-radius: var(--r-sm);
border: 1.5px solid var(--line);
font-family: inherit;
font-size: 15px;
background: var(--bg);
color: var(--ink);
transition: border-color 0.15s;
}
.field input:focus-visible, .field select:focus-visible { border-color: var(--violet); }
.field.invalid input, .field.invalid select { border-color: var(--coral-d); background: rgba(255, 94, 108, 0.06); }
.err { color: var(--coral-d); font-size: 12px; font-weight: 600; min-height: 14px; }
.consent {
grid-column: 1 / -1;
display: flex;
align-items: flex-start;
gap: 10px;
font-size: 14px;
color: var(--ink-2);
cursor: pointer;
}
.consent input { margin-top: 3px; width: 18px; height: 18px; accent-color: var(--violet); flex-shrink: 0; }
.join-form .btn-block { grid-column: 1 / -1; margin-top: 4px; }
.join-fine { grid-column: 1 / -1; text-align: center; font-size: 13px; color: var(--muted); }
/* ---------- Footer ---------- */
.footer { padding: 40px 0; border-top: 1px solid var(--line); background: var(--white); }
.footer-inner { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 14px; }
.footer-note { color: var(--muted); font-size: 14px; }
.footer-links { display: flex; gap: 22px; font-weight: 600; font-size: 14px; color: var(--ink-2); }
.footer-links a:hover { color: var(--violet-d); }
/* ---------- Toast ---------- */
.toast {
position: fixed;
left: 50%; bottom: 28px;
transform: translateX(-50%) translateY(140%);
background: var(--ink);
color: #fff;
padding: 14px 22px;
border-radius: 999px;
font-weight: 600;
font-size: 14px;
box-shadow: 0 16px 40px -12px rgba(0, 0, 0, 0.5);
z-index: 300;
transition: transform 0.35s cubic-bezier(0.2, 0.8, 0.3, 1.1);
max-width: 88vw;
text-align: center;
}
.toast.show { transform: translateX(-50%) translateY(0); }
/* ---------- Responsive ---------- */
@media (max-width: 900px) {
.hero-grid { grid-template-columns: 1fr; gap: 34px; }
.hero-copy { order: 1; }
.phone-col { order: 2; }
.identity-grid { grid-template-columns: 1fr; }
.preview-card { position: static; }
.feature-grid { grid-template-columns: repeat(2, 1fr); }
.stats-row { grid-template-columns: repeat(2, 1fr); }
.spotlight-row { grid-template-columns: 1fr; }
.nav-links { display: none; }
}
@media (max-width: 520px) {
.hero { padding: 40px 0 30px; }
.section { padding: 54px 0; }
.feature-grid { grid-template-columns: 1fr; }
.stats-row { grid-template-columns: 1fr 1fr; }
.join-card { padding: 26px 20px; }
.join-form { grid-template-columns: 1fr; }
.lead { font-size: 16px; }
.hero-cta { flex-direction: column; }
.hero-cta .btn { width: 100%; }
.footer-inner { flex-direction: column; text-align: center; }
}
@media (prefers-reduced-motion: reduce) {
* { animation: none !important; scroll-behavior: auto; transition: none !important; }
}(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);
}
/* ---------- Match card carousel (phone) ---------- */
var profiles = [
{ name: "Sam", pron: "they/them", loc: "Age 27 · 3 mi away", tags: ["Nonbinary", "Musician"], grad: "linear-gradient(135deg,#ff5e6c,#ff9f45)" },
{ name: "Priya", pron: "she/her", loc: "Age 29 · 5 mi away", tags: ["Bisexual", "Foodie"], grad: "linear-gradient(135deg,#8b5cf6,#ff8fb1)" },
{ name: "Devon", pron: "he/him", loc: "Age 31 · 2 mi away", tags: ["Gay", "Runner"], grad: "linear-gradient(135deg,#4ade80,#8b5cf6)" },
{ name: "Kai", pron: "ze/hir", loc: "Age 25 · 6 mi away", tags: ["Queer", "Artist"], grad: "linear-gradient(135deg,#ff8fb1,#7c3aed)" }
];
var stack = document.getElementById("matchStack");
var current = 0;
function renderCard(idx) {
var p = profiles[idx];
var card = document.createElement("div");
card.className = "match-card";
card.style.background = p.grad;
card.innerHTML =
'<span class="match-verify">✓ Verified</span>' +
'<div class="match-meta">' +
'<div class="match-name">' + p.name + ' <span class="mp">' + p.pron + '</span></div>' +
'<div class="match-sub">' + p.loc + '</div>' +
'<div class="match-tags">' + p.tags.map(function (t) { return "<span>" + t + "</span>"; }).join("") + '</div>' +
'</div>';
return card;
}
function showCard() {
if (!stack) return;
stack.innerHTML = "";
stack.appendChild(renderCard(current));
}
function advance(action) {
if (!stack) return;
var card = stack.querySelector(".match-card");
if (card) {
card.classList.add("leaving");
}
var name = profiles[current].name;
current = (current + 1) % profiles.length;
setTimeout(showCard, 320);
if (action === "like") toast("💜 You liked " + name);
else if (action === "star") toast("⭐ Super liked " + name + "!");
else if (action === "pass") toast("Passed on " + name);
}
showCard();
var likeBtn = document.getElementById("likeBtn");
var passBtn = document.getElementById("passBtn");
var starBtn = document.getElementById("starBtn");
if (likeBtn) likeBtn.addEventListener("click", function () { advance("like"); });
if (passBtn) passBtn.addEventListener("click", function () { advance("pass"); });
if (starBtn) starBtn.addEventListener("click", function () { advance("star"); });
/* Gently auto-rotate the deck */
var autoRotate = setInterval(function () { advance(); }, 5200);
if (stack) {
stack.addEventListener("pointerdown", function () { clearInterval(autoRotate); });
}
/* ---------- Live online counter jitter ---------- */
var onlineEl = document.getElementById("onlineCount");
if (onlineEl) {
var base = 1284;
setInterval(function () {
base += Math.floor(Math.random() * 9) - 4;
if (base < 1200) base = 1200;
onlineEl.textContent = base.toLocaleString() + " online";
}, 3000);
}
/* ---------- Identity / pronoun picker ---------- */
var pronounChips = document.getElementById("pronounChips");
var identityChips = document.getElementById("identityChips");
var customInput = document.getElementById("customPronoun");
var previewPronoun = document.getElementById("previewPronoun");
var previewTags = document.getElementById("previewTags");
function updatePronounPreview(val) {
if (previewPronoun) previewPronoun.textContent = val || "—";
}
if (pronounChips) {
pronounChips.addEventListener("click", function (e) {
var chip = e.target.closest(".chip");
if (!chip) return;
pronounChips.querySelectorAll(".chip").forEach(function (c) { c.classList.remove("is-active"); });
chip.classList.add("is-active");
var val = chip.getAttribute("data-pronoun");
if (val === "custom") {
customInput.hidden = false;
customInput.focus();
updatePronounPreview(customInput.value || "your pronouns");
} else {
customInput.hidden = true;
updatePronounPreview(val);
toast("Pronouns set to " + val);
}
});
}
if (customInput) {
customInput.addEventListener("input", function () {
updatePronounPreview(customInput.value || "your pronouns");
});
}
/* Identity multi-select (up to 3) */
var selectedIdentities = ["Queer"];
function renderIdentityTags() {
if (!previewTags) return;
previewTags.innerHTML = selectedIdentities.length
? selectedIdentities.map(function (t) { return '<span class="tag">' + t + "</span>"; }).join("")
: '<span class="tag">Add a label</span>';
}
if (identityChips) {
identityChips.addEventListener("click", function (e) {
var chip = e.target.closest(".chip");
if (!chip) return;
var val = chip.getAttribute("data-identity");
var idx = selectedIdentities.indexOf(val);
if (idx > -1) {
selectedIdentities.splice(idx, 1);
chip.classList.remove("is-active");
} else {
if (selectedIdentities.length >= 3) {
toast("You can pick up to 3 labels");
return;
}
selectedIdentities.push(val);
chip.classList.add("is-active");
}
renderIdentityTags();
});
}
/* ---------- Animated stat counters ---------- */
function formatCount(n, suffix) {
var out;
if (suffix === "+" && n >= 1000000) {
out = (n / 1000000).toFixed(1).replace(/\.0$/, "") + "M";
} else if (suffix === "k") {
out = n.toLocaleString();
} else {
out = n.toLocaleString();
}
if (suffix === "+" && n >= 1000000) return out + "+";
if (suffix === "k") return out + "k";
return out + (suffix === "%" ? "%" : suffix === "+" ? "+" : "");
}
function animateStat(el) {
var target = parseInt(el.getAttribute("data-count"), 10);
var suffix = el.getAttribute("data-suffix") || "";
var start = 0;
var dur = 1600;
var t0 = performance.now();
function tick(now) {
var p = Math.min((now - t0) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
var val = Math.round(start + (target - start) * eased);
el.textContent = formatCount(val, suffix);
if (p < 1) requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
}
var statsRow = document.getElementById("statsRow");
if (statsRow && "IntersectionObserver" in window) {
var statObs = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
statsRow.querySelectorAll(".stat-num").forEach(animateStat);
statObs.disconnect();
}
});
}, { threshold: 0.4 });
statObs.observe(statsRow);
} else if (statsRow) {
statsRow.querySelectorAll(".stat-num").forEach(animateStat);
}
/* ---------- FAQ accordion ---------- */
var faqItems = document.querySelectorAll(".faq-item");
faqItems.forEach(function (item) {
var btn = item.querySelector(".faq-q");
var ans = item.querySelector(".faq-a");
btn.addEventListener("click", function () {
var isOpen = item.classList.contains("open");
faqItems.forEach(function (other) {
other.classList.remove("open");
other.querySelector(".faq-q").setAttribute("aria-expanded", "false");
other.querySelector(".faq-a").style.maxHeight = null;
});
if (!isOpen) {
item.classList.add("open");
btn.setAttribute("aria-expanded", "true");
ans.style.maxHeight = ans.scrollHeight + "px";
}
});
});
/* ---------- Signup form validation ---------- */
var form = document.getElementById("joinForm");
function setError(id, msg) {
var field = document.getElementById(id).closest(".field");
var err = document.querySelector('.err[data-for="' + id + '"]');
if (msg) {
field.classList.add("invalid");
if (err) err.textContent = msg;
} else {
field.classList.remove("invalid");
if (err) err.textContent = "";
}
}
if (form) {
form.addEventListener("submit", function (e) {
e.preventDefault();
var ok = true;
var name = document.getElementById("fName");
var email = document.getElementById("fEmail");
var pronoun = document.getElementById("fPronoun");
var meet = document.getElementById("fMeet");
var consent = document.getElementById("fConsent");
if (!name.value.trim()) { setError("fName", "Please add your name"); ok = false; }
else setError("fName", "");
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value.trim())) { setError("fEmail", "Enter a valid email"); ok = false; }
else setError("fEmail", "");
if (!pronoun.value) { setError("fPronoun", "Pick your pronouns"); ok = false; }
else setError("fPronoun", "");
if (!meet.value) { setError("fMeet", "Choose who you'd like to meet"); ok = false; }
else setError("fMeet", "");
if (!consent.checked) { toast("Please confirm you're 18+ to continue"); ok = false; }
if (ok) {
toast("🎉 Welcome to Spectrum, " + name.value.trim() + "!");
form.reset();
form.querySelectorAll(".field").forEach(function (f) { f.classList.remove("invalid"); });
} else {
toast("Please fix the highlighted fields");
}
});
/* Clear errors as the user corrects them */
["fName", "fEmail", "fPronoun", "fMeet"].forEach(function (id) {
var el = document.getElementById(id);
el.addEventListener("input", function () { setError(id, ""); });
el.addEventListener("change", function () { setError(id, ""); });
});
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Spectrum — Dating for every kind of love</title>
<meta name="description" content="Spectrum is an LGBTQ+ dating app built on safety, pronoun-first profiles, and real community." />
<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&family=Fraunces:opsz,[email protected],600;9..144,700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip-link" href="#main">Skip to content</a>
<header class="nav" aria-label="Primary">
<div class="wrap nav-inner">
<a class="brand" href="#top" aria-label="Spectrum home">
<span class="brand-dot" aria-hidden="true"></span>
Spectrum
</a>
<nav class="nav-links" aria-label="Sections">
<a href="#identity">Your profile</a>
<a href="#safe">Safety</a>
<a href="#community">Community</a>
<a href="#faq">FAQ</a>
</nav>
<a href="#join" class="btn btn-primary btn-sm">Join free</a>
</div>
</header>
<main id="main">
<!-- HERO -->
<section class="hero" id="top">
<div class="wrap hero-grid">
<div class="hero-copy">
<span class="pill-badge">
<span class="flag-strip" aria-hidden="true"></span>
Proudly LGBTQ+ owned
</span>
<h1>Every kind of love<br /><span class="grad-text">belongs here.</span></h1>
<p class="lead">
Spectrum is the dating app where your pronouns come first, your safety is
non-negotiable, and your community actually shows up. Meet people who see
the whole you.
</p>
<div class="hero-cta">
<a href="#join" class="btn btn-primary">Create your profile</a>
<a href="#identity" class="btn btn-ghost">See how it works</a>
</div>
<ul class="trust-row" aria-label="Why members trust Spectrum">
<li><span class="check" aria-hidden="true">✓</span> Photo-verified members</li>
<li><span class="check" aria-hidden="true">✓</span> 24/7 human moderation</li>
<li><span class="check" aria-hidden="true">✓</span> Pronoun-first profiles</li>
</ul>
</div>
<!-- PHONE FRAME -->
<div class="phone-col">
<div class="phone" aria-label="App preview">
<div class="phone-notch" aria-hidden="true"></div>
<div class="phone-screen">
<div class="app-topbar">
<span class="app-title">Discover</span>
<span class="app-live" id="onlineCount">1,284 online</span>
</div>
<div class="match-stack" id="matchStack">
<!-- match card injected by JS -->
</div>
<div class="swipe-row">
<button class="swipe-btn pass" id="passBtn" aria-label="Pass">✕</button>
<button class="swipe-btn star" id="starBtn" aria-label="Super like">★</button>
<button class="swipe-btn like" id="likeBtn" aria-label="Like">♥</button>
</div>
</div>
</div>
<div class="phone-glow" aria-hidden="true"></div>
</div>
</div>
</section>
<!-- IDENTITY / PRONOUN PICKER -->
<section class="section" id="identity">
<div class="wrap">
<div class="section-head">
<span class="eyebrow">Pronoun-first</span>
<h2>Your identity, front and center</h2>
<p class="section-sub">
Pick the pronouns and labels that fit you — then watch your profile preview
update in real time. No hidden menus, no assumptions.
</p>
</div>
<div class="identity-grid">
<div class="picker-card">
<fieldset class="chip-group">
<legend>Choose your pronouns</legend>
<div class="chips" id="pronounChips" role="group" aria-label="Pronoun options">
<button type="button" class="chip is-active" data-pronoun="she/her">she / her</button>
<button type="button" class="chip" data-pronoun="he/him">he / him</button>
<button type="button" class="chip" data-pronoun="they/them">they / them</button>
<button type="button" class="chip" data-pronoun="ze/hir">ze / hir</button>
<button type="button" class="chip" data-pronoun="xe/xem">xe / xem</button>
<button type="button" class="chip chip-custom" data-pronoun="custom">+ Custom</button>
</div>
<input class="custom-input" id="customPronoun" type="text" placeholder="Type your pronouns" hidden aria-label="Custom pronouns" maxlength="24" />
</fieldset>
<fieldset class="chip-group">
<legend>How do you identify?</legend>
<div class="chips" id="identityChips" role="group" aria-label="Identity options">
<button type="button" class="chip is-active" data-identity="Queer">Queer</button>
<button type="button" class="chip" data-identity="Lesbian">Lesbian</button>
<button type="button" class="chip" data-identity="Gay">Gay</button>
<button type="button" class="chip" data-identity="Bisexual">Bisexual</button>
<button type="button" class="chip" data-identity="Pansexual">Pansexual</button>
<button type="button" class="chip" data-identity="Trans">Trans</button>
<button type="button" class="chip" data-identity="Nonbinary">Nonbinary</button>
<button type="button" class="chip" data-identity="Asexual">Asexual</button>
</div>
</fieldset>
</div>
<!-- LIVE PREVIEW -->
<div class="preview-card" aria-live="polite">
<div class="preview-photo" aria-hidden="true">
<span class="preview-status">Active now</span>
</div>
<div class="preview-body">
<div class="preview-name-row">
<h3 id="previewName">Alex</h3>
<span class="preview-pronoun" id="previewPronoun">she / her</span>
</div>
<p class="preview-loc">Brooklyn, NY · 4 mi away</p>
<div class="preview-tags" id="previewTags">
<span class="tag">Queer</span>
</div>
<p class="preview-bio">
Coffee-shop regular, weekend hiker, and unapologetic karaoke enthusiast.
Looking for someone to share playlists and quiet Sundays with.
</p>
</div>
</div>
</div>
</div>
</section>
<!-- SAFE SPACE -->
<section class="section section-tint" id="safe">
<div class="wrap">
<div class="section-head">
<span class="eyebrow">Safe by design</span>
<h2>A space that has your back</h2>
<p class="section-sub">Safety isn't a setting you dig for — it's built into every part of Spectrum.</p>
</div>
<div class="feature-grid">
<article class="feature-card">
<span class="feature-icon" aria-hidden="true">🛡️</span>
<h3>Photo verification</h3>
<p>Every profile can earn a verified badge with a quick selfie check, so you know who's real.</p>
<span class="feature-badge">Verified badge</span>
</article>
<article class="feature-card">
<span class="feature-icon" aria-hidden="true">👀</span>
<h3>24/7 moderation</h3>
<p>A real human team reviews reports around the clock and removes bad actors fast.</p>
<span class="feature-badge">Human review</span>
</article>
<article class="feature-card">
<span class="feature-icon" aria-hidden="true">🚫</span>
<h3>One-tap block</h3>
<p>Block, mute, or report anyone in a single tap — no explanation required, ever.</p>
<span class="feature-badge">Instant</span>
</article>
<article class="feature-card">
<span class="feature-icon" aria-hidden="true">🔒</span>
<h3>Private by default</h3>
<p>Control who sees your distance, your photos, and your identity details at all times.</p>
<span class="feature-badge">You decide</span>
</article>
</div>
</div>
</section>
<!-- COMMUNITY STATS + SPOTLIGHT -->
<section class="section" id="community">
<div class="wrap">
<div class="section-head">
<span class="eyebrow">Real community</span>
<h2>You're in good company</h2>
<p class="section-sub">Millions of queer folks connecting, dating, and building something real.</p>
</div>
<div class="stats-row" id="statsRow">
<div class="stat">
<span class="stat-num" data-count="3200000" data-suffix="+">0</span>
<span class="stat-label">Members worldwide</span>
</div>
<div class="stat">
<span class="stat-num" data-count="180" data-suffix="+">0</span>
<span class="stat-label">Cities with meetups</span>
</div>
<div class="stat">
<span class="stat-num" data-count="94" data-suffix="%">0</span>
<span class="stat-label">Feel safe on Spectrum</span>
</div>
<div class="stat">
<span class="stat-num" data-count="47" data-suffix="k">0</span>
<span class="stat-label">Matches made daily</span>
</div>
</div>
<div class="spotlight-row" aria-label="Member spotlights">
<article class="spot-card">
<div class="spot-photo spot-a" aria-hidden="true"></div>
<div class="spot-info">
<div class="spot-name">Rowan <span class="spot-pron">they/them</span></div>
<div class="spot-badges"><span class="tag tag-sm">Pansexual</span><span class="tag tag-sm">Verified</span></div>
<p class="spot-quote">“Found my partner here after two weeks. The pronoun-first thing genuinely mattered.”</p>
</div>
</article>
<article class="spot-card">
<div class="spot-photo spot-b" aria-hidden="true"></div>
<div class="spot-info">
<div class="spot-name">Nadia <span class="spot-pron">she/her</span></div>
<div class="spot-badges"><span class="tag tag-sm">Lesbian</span><span class="tag tag-sm">Verified</span></div>
<p class="spot-quote">“It's the first app where I didn't have to explain myself. Felt like home.”</p>
</div>
</article>
<article class="spot-card">
<div class="spot-photo spot-c" aria-hidden="true"></div>
<div class="spot-info">
<div class="spot-name">Marcus <span class="spot-pron">he/him</span></div>
<div class="spot-badges"><span class="tag tag-sm">Gay</span><span class="tag tag-sm">Verified</span></div>
<p class="spot-quote">“The community events turned a new city into a whole friend group.”</p>
</div>
</article>
</div>
</div>
</section>
<!-- FAQ -->
<section class="section section-tint" id="faq">
<div class="wrap wrap-narrow">
<div class="section-head">
<span class="eyebrow">Good to know</span>
<h2>Questions, answered</h2>
</div>
<div class="faq" id="faq">
<div class="faq-item">
<button class="faq-q" aria-expanded="false">
Is Spectrum free to use?
<span class="faq-icon" aria-hidden="true">+</span>
</button>
<div class="faq-a"><p>Yes. Creating a profile, matching, and messaging are all free. Spectrum+ adds optional extras like unlimited likes and travel mode.</p></div>
</div>
<div class="faq-item">
<button class="faq-q" aria-expanded="false">
Can I add custom pronouns?
<span class="faq-icon" aria-hidden="true">+</span>
</button>
<div class="faq-a"><p>Absolutely. Choose from our presets or type your own — your pronouns appear on your card exactly as you write them.</p></div>
</div>
<div class="faq-item">
<button class="faq-q" aria-expanded="false">
How do you keep the app safe?
<span class="faq-icon" aria-hidden="true">+</span>
</button>
<div class="faq-a"><p>Photo verification, 24/7 human moderation, one-tap blocking, and privacy controls that are on by default. You're always in charge of who sees what.</p></div>
</div>
<div class="faq-item">
<button class="faq-q" aria-expanded="false">
Who is Spectrum for?
<span class="faq-icon" aria-hidden="true">+</span>
</button>
<div class="faq-a"><p>Everyone across the LGBTQ+ spectrum — and allies welcome. Whatever your identity or orientation, there's space for you here.</p></div>
</div>
</div>
</div>
</section>
<!-- SIGNUP -->
<section class="section join" id="join">
<div class="wrap wrap-narrow">
<div class="join-card">
<div class="join-head">
<span class="flag-strip lg" aria-hidden="true"></span>
<h2>Find your people</h2>
<p>Join Spectrum in under a minute. Free, always.</p>
</div>
<form class="join-form" id="joinForm" novalidate>
<div class="field">
<label for="fName">First name</label>
<input id="fName" name="name" type="text" autocomplete="given-name" placeholder="Jordan" required />
<span class="err" data-for="fName"></span>
</div>
<div class="field">
<label for="fEmail">Email</label>
<input id="fEmail" name="email" type="email" autocomplete="email" placeholder="[email protected]" required />
<span class="err" data-for="fEmail"></span>
</div>
<div class="field">
<label for="fPronoun">Your pronouns</label>
<select id="fPronoun" name="pronoun" required>
<option value="">Select…</option>
<option>she / her</option>
<option>he / him</option>
<option>they / them</option>
<option>ze / hir</option>
<option>xe / xem</option>
<option>Prefer to self-describe</option>
</select>
<span class="err" data-for="fPronoun"></span>
</div>
<div class="field">
<label for="fMeet">I'd like to meet</label>
<select id="fMeet" name="meet" required>
<option value="">Select…</option>
<option>Women</option>
<option>Men</option>
<option>Nonbinary people</option>
<option>Everyone</option>
</select>
<span class="err" data-for="fMeet"></span>
</div>
<label class="consent">
<input type="checkbox" id="fConsent" required />
<span>I'm 18+ and agree to the community guidelines.</span>
</label>
<button type="submit" class="btn btn-primary btn-block">Join Spectrum</button>
<p class="join-fine">No spam, ever. Unsubscribe anytime.</p>
</form>
</div>
</div>
</section>
</main>
<footer class="footer">
<div class="wrap footer-inner">
<div class="brand"><span class="brand-dot" aria-hidden="true"></span> Spectrum</div>
<p class="footer-note">A fictional demo · Made with pride 🏳️🌈</p>
<nav class="footer-links" aria-label="Footer">
<a href="#safe">Safety</a>
<a href="#faq">Guidelines</a>
<a href="#join">Join</a>
</nav>
</div>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>An inclusive dating landing page for the fictional app Spectrum, designed around a coral-to-violet gradient palette and a rounded, mobile-first aesthetic. The hero pairs a benefit-driven headline and safe-space badges with a centered phone frame that shows a live-updating match card. Below it, a pronoun and identity picker lets visitors toggle chips (she/her, they/them, ze/hir, plus a custom field) and instantly see how their profile preview would read — reinforcing that pronouns are first-class, not an afterthought.
Further down, the page communicates trust: a safe-space section covers photo verification, proactive moderation, and one-tap blocking, while animated counters bring community stats to life on scroll. A member spotlight row shows fictional profiles with orientation and pronoun badges, and the signup form collects identity, pronouns, and who you’d like to meet with accessible, keyboard-usable controls.
Interactions are all vanilla JS: the identity chips update the preview and a toast, the match card cycles through profiles, counters animate via IntersectionObserver, the FAQ accordion expands with proper ARIA state, and the signup form validates inline and confirms with a celebratory toast. Everything is responsive down to ~360px and keyboard-navigable with visible focus rings.