<!doctype html>
<html lang="en">
<head>
<meta
charset="utf-8" />
<meta
name="viewport" content="width=device-width,
initial-scale=1" />
<title>Thanks
for Mansplaining — TFM</title>
<meta
name="description" content="TFM: Thanks for Mansplaining. Submit
examples to help document and educate." />
<style>
:root{
--bg:#0b0f14;
/* deep blue/black */
--card:#111827; /* slate-900
*/
--ink:#e5e7eb; /* gray-200
*/
--muted:#9ca3af; /* gray-400
*/
--accent:#ff3cac; /* playful
gradient start */
--accent-2:#784ba0; /* gradient
middle */
--accent-3:#2b86c5; /* gradient
end */
--good:#22c55e; /* green */
--warn:#f59e0b; /* amber */
--danger:#ef4444; /* red */
--focus:#60a5fa; /* blue-400
*/
--radius:20px;
--shadow:0 20px
45px rgba(0,0,0,.35);
}
*{box-sizing:border-box}
html,body{height:100%}
body{
margin:0;
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial,
"Apple Color Emoji", "Segoe UI Emoji";
color:var(--ink); background: radial-gradient(1200px 600px
at 20% -10%, rgba(255,60,172,.25), transparent 60%),
radial-gradient(1000px 600px at 120% 10%, rgba(43,134,197,.2),
transparent 60%),
var(--bg);
line-height:1.5;
}
.wrap{max-width:1100px; margin:0 auto; padding:32px 20px 80px}
header{
display:flex; align-items:center;
justify-content:space-between; gap:16px;
margin-bottom:26px
}
.brand{display:flex; align-items:center;
gap:14px; text-decoration:none}
.logo{
width:54px;
height:54px; border-radius:16px; background:
linear-gradient(135deg, var(--accent), var(--accent-2),
var(--accent-3));
box-shadow:0 8px
20px rgba(123,97,255,.35), inset 0 0 0 3px rgba(255,255,255,.2);
display:grid; place-items:center;
font-weight:900; color:white; letter-spacing:.5px;
}
.brand
h1{font-size:20px; margin:0; color:var(--ink);
opacity:.9}
.stars{font-size:20px; filter: drop-shadow(0 2px 4px rgba(0,0,0,.35))}
.hero{
border-radius:var(--radius); padding:34px; background:linear-gradient(180deg, rgba(255,255,255,.04),
rgba(255,255,255,.02));
box-shadow:var(--shadow);
text-align:center; margin-bottom:22px
}
.hero h2{
font-size:
clamp(36px, 7vw, 72px);
margin:6px 0
12px; font-weight:900; line-height:1.05;
background:linear-gradient(90deg,var(--accent),
var(--accent-2), var(--accent-3));
-webkit-background-clip:text; background-clip:text;
color:transparent;
letter-spacing:.5px;
}
.tag{
display:inline-block; padding:6px 12px;
border-radius:999px; font-size:12px; letter-spacing:.4px;
background:rgba(255,255,255,.08); color:var(--muted)
}
.lead{max-width:62ch; margin:10px auto 0; color:var(--ink);
opacity:.85}
.grid{display:grid; grid-template-columns: 1fr; gap:22px}
@media (min-width:
900px){ .grid{ grid-template-columns: 1.2fr .8fr } }
.card{
background:var(--card); border:1px solid rgba(255,255,255,.06); border-radius:var(--radius);
box-shadow:var(--shadow);
padding:24px
}
.card h3{margin:0
0 6px; font-size:22px}
.card p.helper{margin:4px 0 18px; color:var(--muted)}
label{display:block; font-size:14px; color:var(--muted);
margin:14px 0 6px}
input[type="text"], input[type="email"],
input[type="url"], textarea,
select{
width:100%;
padding:12px 14px; border-radius:12px; border:1px solid rgba(255,255,255,.12);
background:#0b1220; color:var(--ink); outline:none; transition:border
.15s, box-shadow .15s
}
textarea{min-height:140px; resize:vertical}
input:focus, textarea:focus, select:focus{ border-color: var(--focus); box-shadow:0 0 0
3px rgba(96,165,250,.2) }
.row{display:grid; grid-template-columns:1fr; gap:12px}
@media (min-width:
700px){ .row{ grid-template-columns:1fr 1fr } }
.drop{
position:relative; display:flex; align-items:center; justify-content:center;
text-align:center; gap:12px; cursor:pointer;
border:1.5px
dashed rgba(255,255,255,.25); color:var(--muted);
padding:18px; border-radius:14px; min-height:120px;
background:linear-gradient(180deg, rgba(255,255,255,.02),
rgba(255,255,255,.03));
}
.drop input{position:absolute; inset:0; opacity:0; cursor:pointer}
.actions{display:flex; gap:12px; flex-wrap:wrap;
align-items:center; margin-top:14px}
.btn{
padding:12px
16px; border-radius:12px; font-weight:700; letter-spacing:.2px; border:0; cursor:pointer;
color:#0b0f14; background:white; transition: transform .06s ease, filter
.2s ease
}
.btn:hover{filter:brightness(1.05)}
.btn:active{transform:translateY(1px)}
.btn.alt{ background:linear-gradient(90deg,var(--accent),var(--accent-2),var(--accent-3));
color:white }
.btn.ghost{ background:transparent;
color:var(--ink); border:1px solid rgba(255,255,255,.18) }
.success, .error{display:none; margin-top:10px; padding:12px 14px;
border-radius:10px; font-size:14px}
.success{background: rgba(34,197,94,.15);
border:1px solid rgba(34,197,94,.35)}
.error{background:
rgba(239,68,68,.15); border:1px solid rgba(239,68,68,.35)}
.footer{margin-top:26px; color:var(--muted);
font-size:13px}
.pill{display:inline-block; padding:4px 8px; border:1px solid rgba(255,255,255,.15); border-radius:999px;
margin-right:6px}
.qr-hint{font-size:12px; color:var(--muted)}
</style>
</head>
<body>
<div
class="wrap">
<header>
<a
class="brand" href="#"
aria-label="TFM Home">
<div
class="logo">TFM</div>
<h1>Thanks for Mansplaining</h1>
</a>
<div
class="stars" aria-hidden="true">⭐️⭐️⭐️</div>
</header>
<section
class="hero" role="banner">
<span
class="tag">Public service with ✨sparkle✨</span>
<h2>Thanks
for Mansplaining</h2>
<p
class="lead">You found the QR code. Leave an example to help
document the phenomenon, educate gently, and maybe raise a smile. Screenshots
welcome. 🚀</p>
<p
class="qr-hint">Share this page via your
QR code link: <span id="share-url"></span></p>
</section>
<div
class="grid">
<!--
Submission form card -->
<section
class="card" aria-labelledby="formTitle">
<h3
id="formTitle">Submit an
example</h3>
<p
class="helper">No doxxing, keep it
safe-for-work, and redact personal info if needed. By submitting you confirm
you have the rights to share the content.</p>
<!--
Netlify-compatible form. If not hosted on Netlify, we fall back to local
save/download. -->
<form
id="mansplainForm"
name="mansplain" method="POST" data-netlify="true"
enctype="multipart/form-data">
<input
type="hidden" name="form-name" value="mansplain"
/>
<!--
honeypot for bots -->
<input
type="text" name="company" style="display:none"
tabindex="-1" autocomplete="off"
/>
<label
for="title">Title</label>
<input
id="title" name="title" type="text"
placeholder="e.g., 'Explaining my own paper to me'" required />
<div
class="row">
<div>
<label for="platform">Platform</label>
<select id="platform" name="platform">
<option>In person</option>
<option>Email</option>
<option>Meeting</option>
<option>Conference</option>
<option>Social media</option>
<option>Other</option>
</select>
</div>
<div>
<label for="date">Date</label>
<input id="date" name="date"
type="text" placeholder="YYYY-MM-DD (optional)" />
</div>
</div>
<label
for="link">Link (optional)</label>
<input
id="link" name="link" type="url"
placeholder="https://example.com/post" />
<label
for="details">What happened?</label>
<textarea id="details" name="details"
placeholder="Tell us the story (redact names & identifiers)."
required></textarea>
<label>Upload screenshot (PNG/JPG/GIF, optional)</label>
<div
class="drop" role="button" aria-label="Upload
screenshot">
<input
id="file" name="file" type="file"
accept="image/*" />
<span>Drag & drop or click to upload</span>
</div>
<div
class="actions">
<button
class="btn alt"
type="submit">Submit</button>
<button
class="btn ghost" type="button"
id="saveLocal">Save
locally</button>
<button
class="btn" type="button"
id="downloadJson">Download
JSON</button>
</div>
<div
id="msgOk" class="success"
role="status">Thanks! Your submission was received.</div>
<div
id="msgLocal" class="success"
role="status">Saved locally in your browser.</div>
<div
id="msgErr" class="error"
role="alert">Hmm, that didn't submit. You can still save locally
or download JSON.</div>
</form>
</section>
<!-- About /
guidance card -->
<aside
class="card" aria-labelledby="aboutTitle">
<h3
id="aboutTitle">What is this?</h3>
<p
class="helper">TFM is a tiny, tongue‑in‑cheek project to log everyday
micro‑patronising moments and build better norms. Use the
sticker, link here, and let the data speak.</p>
<p><span class="pill">Respect privacy</span>
<span class="pill">Redact names</span> <span
class="pill">No harassment</span></p>
<p
class="helper">Hosting options:
<strong>Netlify</strong> (submissions auto‑store),
<strong>GitHub Pages</strong> (use local save/JSON), or wire the
form action to your own backend/Google Form.</p>
<p
style="margin-top:18px; color:var(--muted);
font-size:12px">Made with ♥. CC BY‑SA.
Swap brand colours in <code>:root</code>
to match your vibe.</p>
</aside>
</div>
<footer
class="footer">© <span id="year"></span> TFM
• <a href="#" style="color:var(--ink)">Privacy</a> • <a href="#" style="color:var(--ink)">Contact</a></footer>
</div>
<script>
// Share URL
display
(function(){
const el = document.getElementById('share-url');
try { el.textContent = window.location.href;
} catch(e) { el.textContent = '' }
})();
//
Netlify-friendly submit with graceful fallback
const form = document.getElementById('mansplainForm');
const ok = document.getElementById('msgOk');
const err = document.getElementById('msgErr');
const localMsg = document.getElementById('msgLocal');
async function postForm(data){
const url = '/'; // Netlify captures POST to current path when
data-netlify="true"
return await
fetch(url, { method:'POST',
headers:{ 'Content-Type':'application/x-www-form-urlencoded' }, body: data });
}
function
encode(data){
const pairs =
[];
for(const [k,v] of data.entries()){
if(v instanceof File){ continue; } // Netlify handles files;
fallback ignores
pairs.push(encodeURIComponent(k)+'='+encodeURIComponent(v));
}
return pairs.join('&');
}
form.addEventListener('submit', async (e)=>{
ok.style.display = 'none'; err.style.display='none';
// If Netlify
isn't present, the fetch below will still work on Netlify but fail elsewhere →
we then show error.
const data = new
FormData(form);
try{
const res =
await postForm(encode(data));
if(res.ok){ ok.style.display='block';
form.reset(); return; }
throw new
Error('non-200');
}catch(_){ err.style.display='block'; }
});
// Save locally (LocalStorage)
document.getElementById('saveLocal').addEventListener('click', ()=>{
const payload =
collect();
const list = JSON.parse(localStorage.getItem('tfmSubmissions')||'[]');
list.push(payload); localStorage.setItem('tfmSubmissions', JSON.stringify(list));
localMsg.style.display='block';
});
// Download JSON
document.getElementById('downloadJson').addEventListener('click',
()=>{
const payload =
collect();
const blob = new
Blob([JSON.stringify(payload, null, 2)], {type:'application/json'});
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = `tfm_submission_${Date.now()}.json`;
a.click();
URL.revokeObjectURL(a.href);
});
function
collect(){
const obj = {
title: document.getElementById('title').value,
platform: document.getElementById('platform').value,
date: document.getElementById('date').value,
link: document.getElementById('link').value,
details: document.getElementById('details').value,
timestamp: new
Date().toISOString()
};
return obj;
}
// year
document.getElementById('year').textContent
= new Date().getFullYear();
</script>
</body>
</html>