Museum — Donation / Support Panel
A refined, gallery-toned donation panel for a fictional art museum. Visitors pick a preset amount chip or type a custom gift, switch between one-time and monthly support, optionally cover the processing fee, and direct their gift to general funds, acquisitions, education, or conservation. A live impact line rewrites itself as the amount and designation change, while a running ledger tallies the gift, fee, and total today and updates the donate button label in real time, with a toast confirming each step.
MCP
Code
:root {
--paper: #f6f4ef;
--wall: #ffffff;
--charcoal: #1c1b19;
--ink: #2a2825;
--ink-2: #4a4640;
--muted: #8c857a;
--gold: #a98140;
--gold-d: #876631;
--gold-50: #f3ecdd;
--line: rgba(28, 27, 25, 0.12);
--line-2: rgba(28, 27, 25, 0.2);
--ok: #3f7d56;
--warn: #b8842c;
--danger: #b4493a;
--r-sm: 6px;
--r-md: 12px;
--r-lg: 18px;
--serif: "Cormorant Garamond", Georgia, serif;
--sans: "Inter", system-ui, -apple-system, sans-serif;
--shadow-sm: 0 1px 2px rgba(28, 27, 25, 0.05);
--shadow-md: 0 18px 48px -28px rgba(28, 27, 25, 0.45);
}
* {
box-sizing: border-box;
}
html {
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
background: var(--paper);
color: var(--ink);
font-family: var(--sans);
line-height: 1.55;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.page {
max-width: 1040px;
margin: 0 auto;
padding: clamp(24px, 5vw, 64px) clamp(16px, 4vw, 40px);
}
/* ---- Panel shell ---- */
.panel {
display: grid;
grid-template-columns: 1.05fr 1fr;
gap: 0;
background: var(--wall);
border: 1px solid var(--line);
border-radius: var(--r-lg);
overflow: hidden;
box-shadow: var(--shadow-md);
}
/* ---- Left rail ---- */
.rail {
padding: clamp(28px, 4vw, 44px);
background: linear-gradient(180deg, var(--gold-50), var(--wall) 70%);
border-right: 1px solid var(--line);
}
.eyebrow {
margin: 0 0 14px;
font-size: 0.72rem;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--gold-d);
font-weight: 600;
}
.rail-title {
margin: 0 0 14px;
font-family: var(--serif);
font-weight: 600;
font-size: clamp(1.7rem, 3.4vw, 2.35rem);
line-height: 1.12;
color: var(--charcoal);
letter-spacing: 0.01em;
}
.rail-lede {
margin: 0 0 24px;
color: var(--ink-2);
font-size: 0.97rem;
}
.rail-lede strong {
color: var(--ink);
}
.featured {
margin: 0 0 24px;
}
.art {
border: 1px solid var(--line-2);
padding: 8px;
background: var(--wall);
border-radius: var(--r-sm);
box-shadow: var(--shadow-sm);
}
.art svg {
display: block;
width: 100%;
height: auto;
aspect-ratio: 320 / 200;
border-radius: 2px;
}
.caption {
display: flex;
flex-direction: column;
gap: 2px;
margin-top: 12px;
font-size: 0.8rem;
}
.cap-title {
font-family: var(--serif);
font-weight: 600;
font-size: 1.05rem;
color: var(--charcoal);
}
.cap-meta {
color: var(--ink-2);
}
.cap-acc {
color: var(--muted);
font-size: 0.74rem;
letter-spacing: 0.04em;
}
.impact-list {
list-style: none;
margin: 0;
padding: 18px 0 0;
border-top: 1px solid var(--line);
display: grid;
gap: 8px;
font-size: 0.88rem;
color: var(--ink-2);
}
.impact-list .tick {
color: var(--gold);
margin-right: 8px;
font-size: 0.7rem;
}
/* ---- Right form ---- */
.give {
padding: clamp(28px, 4vw, 44px);
display: flex;
flex-direction: column;
gap: 22px;
}
.give-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 14px;
flex-wrap: wrap;
}
.give-title {
margin: 0;
font-family: var(--serif);
font-weight: 600;
font-size: 1.55rem;
color: var(--charcoal);
}
.cadence {
display: inline-flex;
background: var(--gold-50);
border: 1px solid var(--line);
border-radius: 999px;
padding: 3px;
}
.cad-btn {
border: 0;
background: transparent;
font-family: var(--sans);
font-size: 0.82rem;
font-weight: 500;
color: var(--ink-2);
padding: 7px 16px;
border-radius: 999px;
cursor: pointer;
transition: background 0.18s, color 0.18s, box-shadow 0.18s;
}
.cad-btn.is-on {
background: var(--wall);
color: var(--charcoal);
box-shadow: var(--shadow-sm);
font-weight: 600;
}
fieldset.amounts {
border: 0;
margin: 0;
padding: 0;
}
.field-label {
display: block;
font-size: 0.78rem;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--muted);
margin-bottom: 10px;
}
.cad-tag {
display: inline-block;
font-size: 0.66rem;
font-weight: 500;
letter-spacing: 0.08em;
color: var(--gold-d);
background: var(--gold-50);
border-radius: 999px;
padding: 2px 8px;
margin-left: 4px;
text-transform: none;
}
.chips {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 8px;
}
.chip {
font-family: var(--sans);
font-size: 0.95rem;
font-weight: 600;
color: var(--ink);
background: var(--wall);
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
padding: 12px 4px;
cursor: pointer;
transition: border-color 0.16s, background 0.16s, color 0.16s, transform 0.08s;
}
.chip:hover {
border-color: var(--gold);
background: var(--gold-50);
}
.chip:active {
transform: translateY(1px);
}
.chip.is-on {
background: var(--charcoal);
border-color: var(--charcoal);
color: var(--paper);
}
.custom {
margin-top: 10px;
display: flex;
align-items: center;
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
padding: 0 14px;
background: var(--wall);
transition: border-color 0.16s, box-shadow 0.16s;
}
.custom:focus-within {
border-color: var(--gold);
box-shadow: 0 0 0 3px var(--gold-50);
}
.custom.is-on {
border-color: var(--charcoal);
}
.cur {
color: var(--muted);
font-weight: 600;
margin-right: 4px;
}
#custom-amt {
flex: 1;
border: 0;
outline: 0;
background: transparent;
font-family: var(--sans);
font-size: 1rem;
color: var(--ink);
padding: 12px 0;
}
.row {
display: grid;
gap: 16px;
}
.field {
display: block;
}
select {
width: 100%;
font-family: var(--sans);
font-size: 0.95rem;
color: var(--ink);
background: var(--wall);
border: 1px solid var(--line-2);
border-radius: var(--r-sm);
padding: 12px 14px;
cursor: pointer;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%238c857a' stroke-width='1.5' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 14px center;
}
select:focus-visible {
outline: 0;
border-color: var(--gold);
box-shadow: 0 0 0 3px var(--gold-50);
}
.fee {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 14px 16px;
border: 1px solid var(--line);
border-radius: var(--r-md);
background: var(--paper);
cursor: pointer;
}
.fee input {
margin-top: 3px;
width: 18px;
height: 18px;
accent-color: var(--gold);
cursor: pointer;
flex: 0 0 auto;
}
.fee-text {
display: flex;
flex-direction: column;
gap: 2px;
font-size: 0.9rem;
}
.fee-text strong {
color: var(--ink);
}
.fee-sub {
font-size: 0.82rem;
color: var(--muted);
}
.impact-line {
display: flex;
align-items: center;
gap: 12px;
padding: 14px 16px;
border-radius: var(--r-md);
background: var(--gold-50);
border: 1px solid rgba(169, 129, 64, 0.28);
font-size: 0.9rem;
color: var(--ink-2);
}
.impact-line strong {
color: var(--gold-d);
}
.impact-icon {
color: var(--gold);
font-size: 1.1rem;
flex: 0 0 auto;
}
.summary {
border-top: 1px solid var(--line);
padding-top: 20px;
display: flex;
flex-direction: column;
gap: 16px;
}
.ledger {
margin: 0;
display: grid;
gap: 6px;
}
.ledger-row {
display: flex;
justify-content: space-between;
align-items: baseline;
font-size: 0.92rem;
color: var(--ink-2);
}
.ledger-row dt,
.ledger-row dd {
margin: 0;
}
.ledger-row dd {
font-variant-numeric: tabular-nums;
font-weight: 500;
}
.ledger-row.total {
margin-top: 4px;
padding-top: 10px;
border-top: 1px dashed var(--line-2);
}
.ledger-row.total dt {
font-family: var(--serif);
font-weight: 600;
font-size: 1.1rem;
color: var(--charcoal);
}
.ledger-row.total dd {
font-weight: 700;
font-size: 1.2rem;
color: var(--charcoal);
}
.donate {
width: 100%;
border: 0;
cursor: pointer;
font-family: var(--sans);
font-size: 1rem;
font-weight: 600;
letter-spacing: 0.01em;
color: var(--paper);
background: var(--charcoal);
padding: 16px;
border-radius: var(--r-md);
transition: background 0.18s, transform 0.08s, box-shadow 0.18s;
box-shadow: var(--shadow-sm);
}
.donate:hover {
background: var(--gold-d);
}
.donate:active {
transform: translateY(1px);
}
.donate.is-busy {
opacity: 0.75;
cursor: progress;
}
.reassure {
margin: 0;
text-align: center;
font-size: 0.76rem;
color: var(--muted);
}
/* ---- Focus ---- */
button:focus-visible,
.cad-btn:focus-visible,
.chip:focus-visible,
.donate:focus-visible {
outline: 2px solid var(--gold-d);
outline-offset: 2px;
}
.vis-hidden {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip: rect(0 0 0 0);
white-space: nowrap;
}
/* ---- Toast ---- */
.toast {
position: fixed;
left: 50%;
bottom: 28px;
transform: translate(-50%, 16px);
background: var(--charcoal);
color: var(--paper);
font-size: 0.9rem;
padding: 13px 20px;
border-radius: 999px;
box-shadow: var(--shadow-md);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s, transform 0.25s;
z-index: 50;
max-width: calc(100vw - 32px);
}
.toast.show {
opacity: 1;
transform: translate(-50%, 0);
}
.toast .t-mark {
color: var(--gold);
margin-right: 8px;
}
/* ---- Responsive ---- */
@media (max-width: 820px) {
.panel {
grid-template-columns: 1fr;
}
.rail {
border-right: 0;
border-bottom: 1px solid var(--line);
}
}
@media (max-width: 520px) {
.page {
padding: 16px 12px;
}
.chips {
grid-template-columns: repeat(3, 1fr);
}
.give-head {
flex-direction: column;
align-items: stretch;
}
.cadence {
align-self: flex-start;
}
.rail-title {
font-size: 1.7rem;
}
}
@media (prefers-reduced-motion: reduce) {
* {
transition: none !important;
}
}(function () {
"use strict";
var FEE_RATE = 0.025;
var state = {
cadence: "once", // "once" | "monthly"
amount: 75, // selected gift amount in dollars
coverFee: false,
};
var els = {
form: document.getElementById("give-form"),
cadBtns: Array.prototype.slice.call(document.querySelectorAll(".cad-btn")),
cadTag: document.getElementById("cad-tag"),
chips: Array.prototype.slice.call(document.querySelectorAll(".chip")),
custom: document.getElementById("custom-amt"),
customWrap: document.querySelector(".custom"),
coverFee: document.getElementById("cover-fee"),
feeSub: document.getElementById("fee-sub"),
feeRow: document.getElementById("fee-row"),
designation: document.getElementById("designation"),
impactLine: document.getElementById("impact-line"),
sumGift: document.getElementById("sum-gift"),
sumFee: document.getElementById("sum-fee"),
sumTotal: document.getElementById("sum-total"),
donateBtn: document.getElementById("donate-btn"),
donateLabel: document.getElementById("donate-label"),
toast: document.getElementById("toast"),
};
// ---- helpers ----
function fmt(n) {
return "$" + n.toLocaleString("en-US", { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
function fmtShort(n) {
if (n >= 1000 && n % 1000 === 0) return "$" + (n / 1000) + ",000";
return "$" + n.toLocaleString("en-US");
}
var toastTimer = null;
function toast(msg) {
if (!els.toast) return;
els.toast.innerHTML = '<span class="t-mark" aria-hidden="true">◆</span>' + msg;
els.toast.hidden = false;
// force reflow so the transition runs
void els.toast.offsetWidth;
els.toast.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
els.toast.classList.remove("show");
setTimeout(function () {
els.toast.hidden = true;
}, 280);
}, 3200);
}
// ---- impact copy ----
function impactFor(amount, designation) {
if (amount <= 0) return "Choose an amount to see the difference it makes.";
var tiers = {
general: [
[40, "keeps the galleries open and free on Thursday evenings"],
[100, "sponsors a class field trip for ten students"],
[250, "supports a week of free community programming"],
[600, "underwrites a visiting-artist talk"],
[Infinity, "names you a Curator's Circle supporter for the year"],
],
acquisitions: [
[75, "helps frame and mat a new work on paper"],
[200, "supports research for a future acquisition"],
[750, "contributes toward acquiring an emerging artist's print"],
[Infinity, "joins the Acquisitions Council shaping the collection"],
],
education: [
[40, "covers art supplies for a children's workshop"],
[100, "sponsors a class field trip for ten students"],
[300, "funds a teaching-artist residency day"],
[Infinity, "endows a full season of school visits"],
],
conservation: [
[60, "supplies archival materials for the studio"],
[180, "funds an hour with a paintings conservator"],
[800, "supports the conservation of a fragile work on canvas"],
[Infinity, "helps restore a major work for return to the gallery"],
],
};
var table = tiers[designation] || tiers.general;
for (var i = 0; i < table.length; i++) {
if (amount <= table[i][0]) {
return "A <strong>" + fmt(amount) + "</strong> " + cadenceWord() + " gift " + table[i][1] + ".";
}
}
return "A <strong>" + fmt(amount) + "</strong> gift makes a lasting difference.";
}
function cadenceWord() {
return state.cadence === "monthly" ? "monthly" : "one-time";
}
// ---- recompute & render ----
function render() {
var amount = state.amount > 0 ? state.amount : 0;
var fee = state.coverFee ? Math.round(amount * FEE_RATE * 100) / 100 : 0;
var total = amount + fee;
els.sumGift.textContent = fmt(amount);
els.sumFee.textContent = fmt(fee);
els.sumTotal.textContent = fmt(total);
els.feeRow.hidden = !state.coverFee;
var suffix = state.cadence === "monthly" ? " / month" : "";
if (amount > 0) {
els.donateLabel.textContent = "Donate " + fmt(total) + suffix;
els.donateBtn.disabled = false;
} else {
els.donateLabel.textContent = "Enter an amount";
els.donateBtn.disabled = false;
}
els.feeSub.textContent = state.coverFee
? "Adding " + fmt(fee) + " (2.5%) so 100% of your gift reaches the museum."
: "Add 2.5% so 100% of your gift reaches the museum.";
els.cadTag.textContent = state.cadence === "monthly" ? "per month" : "per gift";
els.impactLine.querySelector(".impact-text").innerHTML = impactFor(amount, els.designation.value);
}
// ---- preset chips ----
function selectChip(btn) {
els.chips.forEach(function (c) {
var on = c === btn;
c.classList.toggle("is-on", on);
c.setAttribute("aria-pressed", on ? "true" : "false");
});
els.custom.value = "";
els.customWrap.classList.remove("is-on");
state.amount = parseFloat(btn.getAttribute("data-amount")) || 0;
render();
}
els.chips.forEach(function (btn) {
btn.addEventListener("click", function () {
selectChip(btn);
});
});
// ---- custom amount ----
els.custom.addEventListener("input", function () {
// strip everything except digits and a single dot
var raw = els.custom.value.replace(/[^0-9.]/g, "");
var parts = raw.split(".");
if (parts.length > 2) raw = parts[0] + "." + parts.slice(1).join("");
els.custom.value = raw;
var val = parseFloat(raw);
if (raw !== "" && !isNaN(val) && val > 0) {
els.chips.forEach(function (c) {
c.classList.remove("is-on");
c.setAttribute("aria-pressed", "false");
});
els.customWrap.classList.add("is-on");
state.amount = Math.round(val * 100) / 100;
} else {
els.customWrap.classList.remove("is-on");
state.amount = 0;
}
render();
});
els.custom.addEventListener("blur", function () {
var val = parseFloat(els.custom.value);
if (!isNaN(val) && val > 0) {
els.custom.value = (Math.round(val * 100) / 100).toString();
}
});
// ---- cadence toggle ----
els.cadBtns.forEach(function (btn) {
btn.addEventListener("click", function () {
state.cadence = btn.getAttribute("data-cadence");
els.cadBtns.forEach(function (b) {
var on = b === btn;
b.classList.toggle("is-on", on);
b.setAttribute("aria-selected", on ? "true" : "false");
});
render();
});
});
// ---- cover fee ----
els.coverFee.addEventListener("change", function () {
state.coverFee = els.coverFee.checked;
render();
if (state.coverFee) toast("Thank you — you're covering the processing fee.");
});
// ---- designation ----
els.designation.addEventListener("change", render);
// ---- submit ----
els.form.addEventListener("submit", function (e) {
e.preventDefault();
if (state.amount <= 0) {
els.custom.focus();
toast("Please choose or enter a gift amount.");
return;
}
var fee = state.coverFee ? Math.round(state.amount * FEE_RATE * 100) / 100 : 0;
var total = state.amount + fee;
var desigText = els.designation.options[els.designation.selectedIndex].text;
els.donateBtn.classList.add("is-busy");
els.donateBtn.disabled = true;
var prev = els.donateLabel.textContent;
els.donateLabel.textContent = "Processing…";
setTimeout(function () {
els.donateBtn.classList.remove("is-busy");
els.donateBtn.disabled = false;
els.donateLabel.textContent = prev;
var word = state.cadence === "monthly" ? "monthly gift" : "gift";
toast("Thank you! Your " + fmt(total) + " " + word + " to " + desigText + " is confirmed (demo).");
}, 900);
});
// initial paint
render();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Meridian Museum of Art — Support the Collection</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=Cormorant+Garamond:wght@500;600;700&family=Inter:wght@400;500;600&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="page">
<section class="panel" aria-labelledby="give-title">
<!-- Left rail: campaign narrative -->
<aside class="rail">
<p class="eyebrow">Annual Fund · 2026</p>
<h1 id="give-title" class="rail-title">Help keep the galleries open to all.</h1>
<p class="rail-lede">
Your gift to the <strong>Meridian Museum of Art</strong> sustains free Thursday
evenings, conserves fragile works, and brings school groups face-to-face with
five centuries of art.
</p>
<figure class="featured">
<div class="art" role="img" aria-label="Featured acquisition — abstract gold and charcoal field">
<svg viewBox="0 0 320 200" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
<defs>
<linearGradient id="g1" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#f3ecdd" />
<stop offset="0.55" stop-color="#a98140" />
<stop offset="1" stop-color="#1c1b19" />
</linearGradient>
</defs>
<rect width="320" height="200" fill="url(#g1)" />
<circle cx="232" cy="64" r="34" fill="#f6f4ef" opacity="0.85" />
<rect x="28" y="120" width="120" height="10" fill="#f6f4ef" opacity="0.7" />
<rect x="28" y="142" width="78" height="10" fill="#f6f4ef" opacity="0.5" />
</svg>
</div>
<figcaption class="caption">
<span class="cap-title">Field, No. 9</span>
<span class="cap-meta">Ada Renström · 1968 · Oil & gold leaf on linen</span>
<span class="cap-acc">Acquired with donor support · Acc. 2024.117</span>
</figcaption>
</figure>
<ul class="impact-list" aria-label="What your support makes possible">
<li><span class="tick" aria-hidden="true">◆</span> 41,000 students reached last year</li>
<li><span class="tick" aria-hidden="true">◆</span> 9 works conserved & returned to view</li>
<li><span class="tick" aria-hidden="true">◆</span> 52 free community evenings</li>
</ul>
</aside>
<!-- Right: donation form -->
<form class="give" id="give-form" novalidate>
<header class="give-head">
<h2 class="give-title">Make a gift</h2>
<div class="cadence" role="tablist" aria-label="Gift frequency">
<button type="button" class="cad-btn is-on" role="tab" aria-selected="true" data-cadence="once">
One-time
</button>
<button type="button" class="cad-btn" role="tab" aria-selected="false" data-cadence="monthly">
Monthly
</button>
</div>
</header>
<fieldset class="amounts">
<legend class="field-label">
Choose an amount <span class="cad-tag" id="cad-tag">per gift</span>
</legend>
<div class="chips" role="group" aria-label="Preset amounts">
<button type="button" class="chip" data-amount="25">$25</button>
<button type="button" class="chip is-on" data-amount="75" aria-pressed="true">$75</button>
<button type="button" class="chip" data-amount="150">$150</button>
<button type="button" class="chip" data-amount="500">$500</button>
<button type="button" class="chip" data-amount="1500">$1,500</button>
</div>
<label class="custom" for="custom-amt">
<span class="vis-hidden">Custom amount in US dollars</span>
<span class="cur">$</span>
<input
type="text"
inputmode="decimal"
id="custom-amt"
name="custom-amt"
placeholder="Other amount"
autocomplete="off"
/>
</label>
</fieldset>
<div class="row">
<label class="field" for="designation">
<span class="field-label">Direct my gift to</span>
<select id="designation" name="designation">
<option value="general">Where it's needed most</option>
<option value="acquisitions">Acquisitions Fund</option>
<option value="education">Education & Access</option>
<option value="conservation">Conservation Studio</option>
</select>
</label>
</div>
<label class="fee" for="cover-fee">
<input type="checkbox" id="cover-fee" name="cover-fee" />
<span class="fee-text">
<strong>Cover the processing fee</strong>
<span class="fee-sub" id="fee-sub">Add 2.5% so 100% of your gift reaches the museum.</span>
</span>
</label>
<div class="impact-line" id="impact-line" role="status" aria-live="polite">
<span class="impact-icon" aria-hidden="true">◇</span>
<span class="impact-text">A <strong>$75</strong> gift sponsors a class field trip for ten students.</span>
</div>
<div class="summary">
<dl class="ledger">
<div class="ledger-row">
<dt>Gift</dt>
<dd id="sum-gift">$75.00</dd>
</div>
<div class="ledger-row" id="fee-row" hidden>
<dt>Processing fee</dt>
<dd id="sum-fee">$0.00</dd>
</div>
<div class="ledger-row total">
<dt>Total today</dt>
<dd id="sum-total">$75.00</dd>
</div>
</dl>
<button type="submit" class="donate" id="donate-btn">
<span id="donate-label">Donate $75.00</span>
</button>
<p class="reassure">
<span aria-hidden="true">🔒</span> Secure, demo-only checkout · The Museum is a registered nonprofit (EIN 00-0000000).
</p>
</div>
</form>
</section>
</main>
<div class="toast" id="toast" role="status" aria-live="polite" hidden></div>
<script src="script.js"></script>
</body>
</html>Donation / Support Panel
A self-contained support panel for the fictional Meridian Museum of Art, paired with a curatorial left rail that frames a featured acquisition — Field, No. 9 by Ada Renström (1968, Acc. 2024.117) — and three lines of annual impact. The form favors generous wall space, a Cormorant Garamond and Inter pairing, and a quiet gold-and-charcoal palette to feel like a real cultural institution rather than a generic checkout.
Donors choose from five preset amount chips or type a custom figure, then toggle between a one-time and a monthly gift; the frequency tag and button label adjust accordingly. An optional cover the processing fee checkbox adds 2.5% and reveals a fee line in the ledger, and a designation dropdown routes the gift to general support, the Acquisitions Fund, Education & Access, or the Conservation Studio.
Everything is reactive: a live impact line rewrites itself for the current amount and designation, the ledger tallies gift, fee, and total today, and the donate button shows the exact amount with a per-month suffix when relevant. Submitting runs a short processing state and fires a confirmation toast. The panel is keyboard-usable with visible focus, meets AA contrast, respects reduced-motion, and reflows cleanly down to 360px.
Illustrative UI only — demo data; not a real museum system.