Nonprofit — Pledge / Sponsorship Card
A warm set of four nonprofit sponsorship cards for sponsor-a-child and sponsor-a-cause programs, each with a photo placeholder, name, story snippet, monthly amount and a progress thermometer. Cards flip to reveal the impact details, facts and donor recognition, while a sponsor toggle adds each pledge to a running monthly total with animated counters and friendly toasts. Built with semantic HTML, responsive CSS and vanilla JavaScript, accessible and keyboard-friendly down to small screens.
MCP
Code
:root {
--brand: #1f7a6d;
--brand-d: #155e54;
--accent: #e8743b;
--accent-d: #cc5d28;
--ink: #2a2722;
--ink-2: #524d44;
--muted: #7a7368;
--bg: #faf6f0;
--surface: #ffffff;
--line: rgba(42, 39, 34, 0.1);
--line-2: rgba(42, 39, 34, 0.18);
--ok: #2f9e6f;
--warn: #d98a2b;
--danger: #d4503e;
--r-sm: 8px;
--r-md: 14px;
--r-lg: 22px;
--sh-sm: 0 1px 2px rgba(42, 39, 34, 0.06), 0 2px 6px rgba(42, 39, 34, 0.05);
--sh-md: 0 6px 18px rgba(42, 39, 34, 0.09), 0 2px 6px rgba(42, 39, 34, 0.06);
--sh-lg: 0 18px 44px rgba(42, 39, 34, 0.16);
}
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
body {
margin: 0;
font-family: "Inter", system-ui, -apple-system, sans-serif;
line-height: 1.6;
color: var(--ink);
background: var(--bg);
}
h1, h2, h3 {
font-family: "Fraunces", Georgia, serif;
font-weight: 600;
line-height: 1.15;
color: var(--ink);
}
.skip {
position: absolute;
left: -999px;
top: 0;
background: var(--brand);
color: #fff;
padding: 10px 16px;
border-radius: 0 0 var(--r-sm) 0;
z-index: 50;
}
.skip:focus { left: 0; }
/* Topbar */
.topbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 16px;
flex-wrap: wrap;
padding: 16px clamp(16px, 5vw, 48px);
background: var(--surface);
border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 10px; }
.logo {
display: grid;
place-items: center;
width: 38px; height: 38px;
border-radius: 12px;
background: linear-gradient(135deg, var(--brand), var(--brand-d));
color: #fff;
box-shadow: var(--sh-sm);
}
.brand-name { font-family: "Fraunces", serif; font-weight: 600; font-size: 1.15rem; }
.trust { display: flex; gap: 8px; flex-wrap: wrap; }
.badge {
font-size: 0.74rem;
font-weight: 600;
color: var(--brand-d);
background: rgba(31, 122, 109, 0.1);
border: 1px solid rgba(31, 122, 109, 0.22);
padding: 4px 10px;
border-radius: 999px;
}
/* Hero */
.hero {
max-width: 760px;
margin: 0 auto;
padding: clamp(36px, 7vw, 64px) clamp(16px, 5vw, 48px) 8px;
text-align: center;
}
.eyebrow {
text-transform: uppercase;
letter-spacing: 0.12em;
font-size: 0.78rem;
font-weight: 700;
color: var(--accent-d);
margin: 0 0 10px;
}
.hero h1 { font-size: clamp(1.9rem, 5vw, 3rem); margin: 0 0 14px; }
.lede { font-size: 1.06rem; color: var(--ink-2); margin: 0 auto; max-width: 600px; }
.lede strong { color: var(--brand-d); }
.impact-strip {
display: flex;
justify-content: center;
gap: clamp(18px, 5vw, 48px);
flex-wrap: wrap;
margin: 30px auto 0;
}
.impact { text-align: center; }
.impact strong {
display: block;
font-family: "Fraunces", serif;
font-size: clamp(1.6rem, 4vw, 2.1rem);
font-weight: 700;
color: var(--brand);
}
.impact span { font-size: 0.82rem; color: var(--muted); }
/* Grid */
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
max-width: 1120px;
margin: 0 auto;
padding: clamp(28px, 5vw, 48px) clamp(16px, 5vw, 48px);
}
/* Flip card scene */
.card {
perspective: 1400px;
min-height: 470px;
}
.card-inner {
position: relative;
width: 100%;
height: 100%;
min-height: 470px;
transition: transform 0.6s cubic-bezier(0.4, 0.1, 0.2, 1);
transform-style: preserve-3d;
}
.card.flipped .card-inner { transform: rotateY(180deg); }
.face {
position: absolute;
inset: 0;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
background: var(--surface);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--sh-md);
overflow: hidden;
display: flex;
flex-direction: column;
transition: box-shadow 0.25s ease, transform 0.25s ease, border-color 0.25s ease;
}
.card:hover .face { box-shadow: var(--sh-lg); transform: translateY(-3px); }
.card.sponsored .front { border-color: rgba(47, 158, 111, 0.5); }
.back { transform: rotateY(180deg); }
/* Photo block */
.photo {
position: relative;
height: 184px;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 14px;
color: #fff;
}
.photo::after {
content: "";
position: absolute;
inset: 0;
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 40%, rgba(0, 0, 0, 0.42) 100%);
}
.photo-cap {
position: relative;
z-index: 1;
font-size: 0.76rem;
font-weight: 500;
text-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}
.tag {
position: absolute;
top: 12px;
left: 12px;
z-index: 1;
font-size: 0.7rem;
font-weight: 700;
background: rgba(255, 255, 255, 0.92);
color: var(--ink);
padding: 3px 9px;
border-radius: 999px;
}
.flip-btn {
position: absolute;
top: 10px;
right: 10px;
z-index: 2;
border: none;
cursor: pointer;
width: 30px; height: 30px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.92);
color: var(--ink);
font-size: 0.95rem;
display: grid;
place-items: center;
box-shadow: var(--sh-sm);
transition: background 0.2s, transform 0.2s;
}
.flip-btn:hover { background: #fff; transform: scale(1.08); }
.flip-btn:focus-visible { outline: 3px solid rgba(31, 122, 109, 0.5); outline-offset: 2px; }
/* gradient placeholder palettes */
.ph-a { background: linear-gradient(135deg, #f0a868, #e8743b); }
.ph-b { background: linear-gradient(135deg, #4fb3a0, #1f7a6d); }
.ph-c { background: linear-gradient(135deg, #9c8bd1, #6f5fb0); }
.ph-d { background: linear-gradient(135deg, #e89bb0, #cc5d7e); }
/* Body */
.body {
padding: 16px 18px 18px;
display: flex;
flex-direction: column;
gap: 10px;
flex: 1;
}
.who { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.who h3 { font-size: 1.2rem; margin: 0; }
.who .meta { font-size: 0.8rem; color: var(--muted); }
.story { font-size: 0.9rem; color: var(--ink-2); margin: 0; flex: 1; }
/* Progress thermometer */
.therm-row { display: flex; align-items: center; justify-content: space-between; font-size: 0.76rem; color: var(--muted); }
.therm-row b { color: var(--brand-d); font-weight: 700; }
.therm {
height: 9px;
border-radius: 999px;
background: rgba(42, 39, 34, 0.08);
overflow: hidden;
}
.therm > span {
display: block;
height: 100%;
border-radius: 999px;
background: linear-gradient(90deg, var(--brand), var(--ok));
width: 0;
transition: width 0.9s cubic-bezier(0.4, 0.1, 0.2, 1);
}
.price-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-top: 2px; }
.price { font-family: "Fraunces", serif; }
.price b { font-size: 1.45rem; color: var(--ink); }
.price span { font-size: 0.82rem; color: var(--muted); }
/* Buttons */
.btn {
font-family: inherit;
font-weight: 600;
font-size: 0.92rem;
border: 1px solid transparent;
border-radius: var(--r-md);
padding: 10px 16px;
cursor: pointer;
transition: transform 0.12s ease, background 0.2s, box-shadow 0.2s, color 0.2s, border-color 0.2s;
}
.btn:active { transform: translateY(1px) scale(0.99); }
.btn:focus-visible { outline: 3px solid rgba(232, 116, 59, 0.45); outline-offset: 2px; }
.btn-accent { background: var(--accent); color: #fff; box-shadow: 0 4px 12px rgba(232, 116, 59, 0.28); }
.btn-accent:hover { background: var(--accent-d); }
.btn-lg { padding: 14px 26px; font-size: 1rem; }
.btn-lg:disabled { opacity: 0.45; cursor: not-allowed; box-shadow: none; }
.sponsor-btn { width: 100%; }
.card.sponsored .sponsor-btn {
background: rgba(47, 158, 111, 0.12);
color: var(--ok);
border-color: rgba(47, 158, 111, 0.5);
box-shadow: none;
}
.card.sponsored .sponsor-btn::before { content: "✔ "; }
/* Back face */
.back .body { gap: 12px; padding-top: 20px; }
.back h3 { font-size: 1.15rem; margin: 0; }
.back .detail { font-size: 0.88rem; color: var(--ink-2); margin: 0; }
.facts { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.facts li {
display: flex;
gap: 10px;
align-items: flex-start;
font-size: 0.85rem;
color: var(--ink-2);
}
.facts li b { color: var(--ink); }
.facts .ico {
flex: none;
width: 22px; height: 22px;
border-radius: 7px;
display: grid;
place-items: center;
background: rgba(31, 122, 109, 0.12);
color: var(--brand-d);
font-size: 0.8rem;
}
.donors {
margin-top: auto;
font-size: 0.76rem;
color: var(--muted);
border-top: 1px dashed var(--line-2);
padding-top: 10px;
}
.donors b { color: var(--brand-d); }
/* Pledge bar */
.pledge {
max-width: 1120px;
margin: 0 auto;
padding: 0 clamp(16px, 5vw, 48px) 56px;
}
.pledge-inner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
flex-wrap: wrap;
background: linear-gradient(135deg, #fff, #fdf3ea);
border: 1px solid var(--line);
border-radius: var(--r-lg);
box-shadow: var(--sh-md);
padding: clamp(20px, 4vw, 30px);
}
.pledge-label { text-transform: uppercase; letter-spacing: 0.1em; font-size: 0.74rem; font-weight: 700; color: var(--muted); margin: 0; }
.pledge-amount { margin: 4px 0 6px; font-family: "Fraunces", serif; }
.pledge-amount #pledgeTotal { font-size: clamp(2rem, 6vw, 2.8rem); font-weight: 700; color: var(--brand-d); }
.pledge-amount .per { font-size: 1rem; color: var(--muted); margin-left: 4px; }
.pledge-note { margin: 0; font-size: 0.9rem; color: var(--ink-2); }
.fineprint { text-align: center; font-size: 0.78rem; color: var(--muted); margin: 16px auto 0; max-width: 620px; }
/* Toast */
.toast-wrap {
position: fixed;
left: 50%;
bottom: 24px;
transform: translateX(-50%);
z-index: 80;
display: flex;
flex-direction: column;
gap: 10px;
align-items: center;
pointer-events: none;
}
.toast {
background: var(--ink);
color: #fff;
padding: 12px 18px;
border-radius: var(--r-md);
font-size: 0.9rem;
font-weight: 500;
box-shadow: var(--sh-lg);
display: flex;
align-items: center;
gap: 9px;
animation: toast-in 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.toast .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--ok); flex: none; }
.toast.warn .dot { background: var(--warn); }
@keyframes toast-in {
from { opacity: 0; transform: translateY(14px); }
to { opacity: 1; transform: translateY(0); }
}
@media (max-width: 520px) {
.grid { grid-template-columns: 1fr; gap: 18px; }
.trust { width: 100%; }
.pledge-inner { flex-direction: column; align-items: stretch; text-align: center; }
.btn-lg { width: 100%; }
.impact-strip { gap: 22px; }
}
@media (prefers-reduced-motion: reduce) {
.card-inner, .therm > span, .face, .toast { transition: none; animation: none; }
}(function () {
"use strict";
/* ---- Fictional sponsorship data ---- */
var CARDS = [
{
id: "amara",
name: "Amara",
meta: "Age 8 · Kenya",
tag: "Education",
ph: "ph-a",
photoCap: "Amara, walking 4km to her village school",
story: "Amara dreams of becoming a teacher, but her school of 60 children shares just one classroom and a handful of books.",
amount: 25,
raised: 34,
goal: 50,
detail: "Your monthly gift covers Amara's school fees, uniform, two daily meals and a place in the after-school reading club.",
facts: [
["📚", "Funds tuition, books & a uniform for the full year"],
["🍲", "Two nutritious meals every school day"],
["✉️", "Twice-yearly letters & a progress report from Amara"]
],
donors: "Joined by Priya N., Marcus T. and 32 others"
},
{
id: "river-school",
name: "Riverside School",
meta: "Community · Bangladesh",
tag: "Clean Water",
ph: "ph-b",
photoCap: "Children gathering at the new well in Riverside",
story: "A village school of 240 students relies on a river that floods each monsoon. A solar well would give them safe water all year.",
amount: 40,
raised: 188,
goal: 240,
detail: "Sponsoring Riverside funds a solar-powered well, hygiene training and yearly water testing for the whole school community.",
facts: [
["💧", "Shares the cost of a solar-powered village well"],
["🧼", "Hand-washing stations & hygiene workshops"],
["🔬", "Quarterly water-quality testing & maintenance"]
],
donors: "Powered by 188 monthly sponsors"
},
{
id: "diego",
name: "Diego",
meta: "Age 11 · Guatemala",
tag: "Health",
ph: "ph-c",
photoCap: "Diego at his first checkup in the mobile clinic",
story: "Diego loves football but lives three hours from the nearest clinic. A monthly sponsor keeps the mobile health van on the road.",
amount: 30,
raised: 41,
goal: 60,
detail: "Your pledge funds Diego's vaccinations, regular checkups and the fuel that keeps the mobile clinic reaching his mountain village.",
facts: [
["🩺", "Vaccinations & twice-yearly medical checkups"],
["🚐", "Keeps the mobile clinic visiting his village"],
["🦷", "Dental care & a vitamin program"]
],
donors: "Joined by Lena O., the Okafor family +39"
},
{
id: "maya-grove",
name: "Maya's Grove",
meta: "Family farm · Peru",
tag: "Livelihood",
ph: "ph-d",
photoCap: "Maya tending the new fruit-tree seedlings",
story: "Maya supports four children by farming. A small grove of fruit trees would triple her family's income within two seasons.",
amount: 35,
raised: 12,
goal: 80,
detail: "Sponsoring Maya's Grove plants fruit trees, funds farming tools and pays for a season of agronomy coaching for her cooperative.",
facts: [
["🌳", "Plants & cares for income-generating fruit trees"],
["🧑🌾", "Hands-on agronomy coaching each season"],
["📈", "Connects the co-op to fair-trade buyers"]
],
donors: "Be one of the first 12 sponsors"
}
];
var fmt = function (n) { return "$" + n.toLocaleString("en-US"); };
var selected = Object.create(null);
/* ---- Toast helper ---- */
var toastWrap = document.getElementById("toastWrap");
function toast(msg, kind) {
var el = document.createElement("div");
el.className = "toast" + (kind === "warn" ? " warn" : "");
el.innerHTML = '<span class="dot"></span><span></span>';
el.lastChild.textContent = msg;
toastWrap.appendChild(el);
setTimeout(function () {
el.style.transition = "opacity .35s, transform .35s";
el.style.opacity = "0";
el.style.transform = "translateY(10px)";
setTimeout(function () { el.remove(); }, 380);
}, 2600);
}
/* ---- Render cards ---- */
var grid = document.getElementById("cards");
function buildCard(c) {
var pct = Math.min(100, Math.round((c.raised / c.goal) * 100));
var card = document.createElement("article");
card.className = "card";
card.dataset.id = c.id;
var facts = c.facts.map(function (f) {
return '<li><span class="ico">' + f[0] + "</span><span>" + f[1] + "</span></li>";
}).join("");
card.innerHTML =
'<div class="card-inner">' +
'<div class="face front">' +
'<div class="photo ' + c.ph + '">' +
'<span class="tag">' + c.tag + "</span>" +
'<button class="flip-btn" type="button" aria-label="Read ' + c.name + "'s story" title="More details">↻</button>" +
'<span class="photo-cap">' + c.photoCap + "</span>" +
"</div>" +
'<div class="body">' +
'<div class="who"><h3>' + c.name + '</h3><span class="meta">' + c.meta + "</span></div>" +
'<p class="story">' + c.story + "</p>" +
'<div class="therm-row"><span><b class="raisedLabel">' + c.raised + "</b> of " + c.goal + " sponsors</span><span>" + pct + "%</span></div>" +
'<div class="therm"><span class="therm-fill" style="width:0"></span></div>' +
'<div class="price-row">' +
'<span class="price"><b>' + fmt(c.amount) + "</b><span>/month</span></span>" +
"</div>" +
'<button class="btn btn-accent sponsor-btn" type="button">Sponsor ' + c.name + "</button>" +
"</div>" +
"</div>" +
'<div class="face back">' +
'<div class="photo ' + c.ph + '" style="height:120px">' +
'<button class="flip-btn" type="button" aria-label="Back to ' + c.name + ' card" title="Back">↺</button>' +
'<span class="photo-cap">' + c.tag + " · " + c.meta + "</span>" +
"</div>" +
'<div class="body">' +
"<h3>About " + c.name + "</h3>" +
'<p class="detail">' + c.detail + "</p>" +
'<ul class="facts">' + facts + "</ul>" +
'<p class="donors">' + c.donors + "</p>" +
"</div>" +
"</div>" +
"</div>";
/* flip toggles */
card.querySelectorAll(".flip-btn").forEach(function (b) {
b.addEventListener("click", function (e) {
e.stopPropagation();
card.classList.toggle("flipped");
});
});
/* sponsor toggle */
var btn = card.querySelector(".sponsor-btn");
btn.addEventListener("click", function () {
if (selected[c.id]) {
delete selected[c.id];
card.classList.remove("sponsored");
btn.textContent = "Sponsor " + c.name;
toast("Removed " + c.name + " from your pledge", "warn");
bumpRaised(card, -1);
} else {
selected[c.id] = c.amount;
card.classList.add("sponsored");
btn.textContent = "Sponsoring " + c.name;
toast("You're sponsoring " + c.name + " — " + fmt(c.amount) + "/month 💚");
bumpRaised(card, 1);
}
updatePledge();
});
/* animate thermometer in */
requestAnimationFrame(function () {
requestAnimationFrame(function () {
card.querySelector(".therm-fill").style.width = pct + "%";
});
});
return card;
}
function bumpRaised(card, delta) {
var label = card.querySelector(".raisedLabel");
var n = parseInt(label.textContent, 10) + delta;
label.textContent = n;
var c = CARDS.filter(function (x) { return x.id === card.dataset.id; })[0];
var pct = Math.min(100, Math.round((n / c.goal) * 100));
card.querySelector(".therm-fill").style.width = pct + "%";
card.querySelector(".therm-row").lastElementChild.textContent = pct + "%";
}
CARDS.forEach(function (c) { grid.appendChild(buildCard(c)); });
/* ---- Pledge bar ---- */
var totalEl = document.getElementById("pledgeTotal");
var noteEl = document.getElementById("pledgeNote");
var checkout = document.getElementById("checkoutBtn");
function updatePledge() {
var ids = Object.keys(selected);
var total = ids.reduce(function (s, id) { return s + selected[id]; }, 0);
totalEl.textContent = fmt(total);
if (ids.length === 0) {
noteEl.textContent = "No sponsorships selected yet — choose a card to begin.";
checkout.disabled = true;
} else {
var names = ids.map(function (id) {
return CARDS.filter(function (c) { return c.id === id; })[0].name;
});
noteEl.textContent = "Sponsoring " + names.join(", ") + " · " + fmt(total) + " every month.";
checkout.disabled = false;
}
}
checkout.addEventListener("click", function () {
var count = Object.keys(selected).length;
var total = Object.keys(selected).reduce(function (s, id) { return s + selected[id]; }, 0);
toast("Thank you! " + count + " sponsorship" + (count > 1 ? "s" : "") + " confirmed — " + fmt(total) + "/month 🎉");
});
/* ---- Count-up impact numbers ---- */
function countUp(el) {
var target = parseInt(el.getAttribute("data-count"), 10);
var suffix = el.getAttribute("data-suffix") || "";
var start = null, dur = 1400;
function step(ts) {
if (start === null) start = ts;
var p = Math.min(1, (ts - start) / dur);
var eased = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(target * eased).toLocaleString("en-US") + suffix;
if (p < 1) requestAnimationFrame(step);
}
requestAnimationFrame(step);
}
var counters = document.querySelectorAll("[data-count]");
if ("IntersectionObserver" in window) {
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (en) {
if (en.isIntersecting) { countUp(en.target); io.unobserve(en.target); }
});
}, { threshold: 0.5 });
counters.forEach(function (el) { io.observe(el); });
} else {
counters.forEach(countUp);
}
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Brightpath Foundation — Sponsor a Future</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=Fraunces:opsz,wght@9..144,500;9..144,600;9..144,700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip" href="#cards">Skip to sponsorship cards</a>
<header class="topbar">
<div class="brand">
<span class="logo" aria-hidden="true">
<svg viewBox="0 0 24 24" width="22" height="22"><path d="M12 21s-7.5-4.6-7.5-10A4.2 4.2 0 0 1 12 7a4.2 4.2 0 0 1 7.5 4c0 5.4-7.5 10-7.5 10Z" fill="currentColor"/></svg>
</span>
<span class="brand-name">Brightpath Foundation</span>
</div>
<div class="trust">
<span class="badge" title="Registered nonprofit">✔ Registered Charity</span>
<span class="badge" title="Tax-deductible donations">Tax-Deductible</span>
</div>
</header>
<section class="hero">
<p class="eyebrow">Monthly Sponsorship Program</p>
<h1>Sponsor a future. Watch it grow.</h1>
<p class="lede">For as little as <strong>$25 a month</strong>, you give a child, family or community the consistent support that changes everything. Flip any card to read their story, then choose to sponsor.</p>
<div class="impact-strip" role="list" aria-label="Program impact">
<div class="impact" role="listitem"><strong data-count="14820">0</strong><span>Children sponsored</span></div>
<div class="impact" role="listitem"><strong data-count="38">0</strong><span>Communities served</span></div>
<div class="impact" role="listitem"><strong data-count="96" data-suffix="%">0</strong><span>Funds reach the field</span></div>
</div>
</section>
<main id="cards" class="grid" aria-label="Sponsorship cards"></main>
<section class="pledge" aria-label="Your monthly pledge">
<div class="pledge-inner">
<div>
<p class="pledge-label">Your monthly pledge</p>
<p class="pledge-amount"><span id="pledgeTotal">$0</span><span class="per">/month</span></p>
<p class="pledge-note" id="pledgeNote">No sponsorships selected yet — choose a card to begin.</p>
</div>
<button class="btn btn-accent btn-lg" id="checkoutBtn" disabled>Complete Sponsorship</button>
</div>
<p class="fineprint">Brightpath Foundation is a fictional 501(c)(3). Cancel or change your pledge any time. 96% of every dollar reaches the field.</p>
</section>
<div class="toast-wrap" id="toastWrap" aria-live="polite" aria-atomic="true"></div>
<script src="script.js"></script>
</body>
</html>Pledge / Sponsorship Card
A polished set of four monthly-sponsorship cards for the fictional Brightpath Foundation. Each card pairs a warm gradient photo placeholder (with a human, impact-focused caption) with the sponsoree’s name, location, a short personal story, a progress thermometer showing sponsors filled toward the goal, and a clear monthly amount. A prominent donate-orange Sponsor button is the primary call to action, and trust badges plus transparent impact numbers (“96% reaches the field”, “14,820 children sponsored”) build confidence.
Every card flips in 3D to a back face that reveals the full impact breakdown — what the pledge funds, three concrete facts, and donor recognition — so supporters can read the story before they commit. Sponsoring a card toggles its state to a confirmed look, nudges its thermometer up by one, fires a friendly toast, and adds the amount to a sticky monthly-pledge summary that tallies the total across all selected cards and enables the final “Complete Sponsorship” action.
The whole component is self-contained vanilla JavaScript: data-driven card rendering, an IntersectionObserver count-up for the hero impact stats, animated progress fills, and an accessible toast helper. It is responsive down to ~360px, respects reduced-motion preferences, and keeps buttons and inputs keyboard-usable with visible focus states.
Illustrative UI only — fictional organization, not a real charity or donation system.