Nonprofit — Animal Welfare Landing
A warm, friendly animal-welfare landing page for a fictional rescue shelter, built with plain HTML, CSS and vanilla JavaScript. It pairs a heart-warming hero and live adoption ticker with a filterable adoptable-animals grid, animated impact counters, a campaign progress thermometer wired to an interactive donate box, a sponsor-a-pet slider, success-story cards, and a volunteer sign-up — all responsive down to mobile with scroll reveals, toast feedback and accessible, keyboard-usable controls.
MCP
Code
:root {
/* Animal welfare palette — soft green + cream + warm orange */
--brand: #5a9e6f;
--brand-d: #3f7c54;
--accent: #f08a3c;
--accent-d: #d96f24;
--ink: #2f3a30;
--ink-2: #54614f;
--muted: #8a9183;
--bg: #fbf6ec;
--cream: #fff9ef;
--surface: #ffffff;
--line: rgba(47, 58, 48, 0.1);
--line-2: rgba(47, 58, 48, 0.18);
--ok: #2f9e6f;
--warn: #d98a2b;
--danger: #d4503e;
--r-sm: 10px;
--r-md: 16px;
--r-lg: 26px;
--shadow-sm: 0 2px 10px rgba(63, 124, 84, 0.08);
--shadow-md: 0 14px 34px rgba(63, 124, 84, 0.14);
--shadow-lg: 0 26px 60px rgba(47, 58, 48, 0.16);
--ring: 0 0 0 4px rgba(240, 138, 60, 0.28);
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
font-family: "Nunito", system-ui, -apple-system, sans-serif;
background: var(--bg);
color: var(--ink);
line-height: 1.6;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
h1, h2, h3, h4, .brand-name { font-family: "Baloo 2", system-ui, sans-serif; line-height: 1.12; }
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
.wrap { width: min(1140px, 92vw); margin-inline: auto; }
.sr-only {
position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip {
position: absolute; left: 12px; top: -60px; z-index: 200;
background: var(--accent); color: #fff; padding: 10px 16px;
border-radius: var(--r-sm); font-weight: 700; transition: top .2s;
}
.skip:focus { top: 12px; }
:focus-visible { outline: none; box-shadow: var(--ring); border-radius: var(--r-sm); }
/* ---------- Buttons ---------- */
.btn {
display: inline-flex; align-items: center; justify-content: center; gap: .5rem;
border: 0; cursor: pointer; font-family: inherit; font-weight: 800;
padding: .72rem 1.25rem; border-radius: 999px; font-size: .98rem;
transition: transform .15s ease, box-shadow .2s ease, background .2s ease;
text-align: center;
}
.btn:active { transform: translateY(1px) scale(.99); }
.btn-lg { padding: .92rem 1.7rem; font-size: 1.05rem; }
.btn-block { width: 100%; }
.btn-accent { background: var(--accent); color: #fff; box-shadow: 0 8px 20px rgba(240, 138, 60, 0.34); }
.btn-accent:hover { background: var(--accent-d); transform: translateY(-2px); }
.btn-brand { background: var(--brand); color: #fff; box-shadow: 0 8px 18px rgba(90, 158, 111, 0.3); }
.btn-brand:hover { background: var(--brand-d); transform: translateY(-2px); }
.btn-ghost { background: var(--surface); color: var(--brand-d); border: 2px solid var(--line-2); }
.btn-ghost:hover { border-color: var(--brand); transform: translateY(-2px); }
/* ---------- Nav ---------- */
.nav {
position: sticky; top: 0; z-index: 100;
background: rgba(251, 246, 236, 0.86);
backdrop-filter: blur(12px);
border-bottom: 1px solid var(--line);
}
.nav-inner { display: flex; align-items: center; gap: 1.2rem; height: 72px; }
.brand { display: inline-flex; align-items: center; gap: .5rem; font-weight: 800; }
.brand-mark {
width: 38px; height: 38px; display: grid; place-items: center; font-size: 1.2rem;
background: linear-gradient(135deg, #d7efdc, #ffe2c6); border-radius: 12px;
box-shadow: var(--shadow-sm);
}
.brand-name { font-size: 1.35rem; color: var(--ink); }
.brand-dot { color: var(--accent); }
.nav-links { display: flex; gap: 1.5rem; margin-left: auto; }
.nav-links a { font-weight: 700; color: var(--ink-2); position: relative; padding: 4px 0; }
.nav-links a::after {
content: ""; position: absolute; left: 0; bottom: 0; width: 0; height: 2.5px;
background: var(--accent); border-radius: 2px; transition: width .22s ease;
}
.nav-links a:hover { color: var(--ink); }
.nav-links a:hover::after { width: 100%; }
.nav-cta { display: flex; align-items: center; gap: .8rem; margin-left: 1rem; }
.badge-charity {
display: inline-flex; align-items: center; gap: .35rem;
font-size: .76rem; font-weight: 800; color: var(--brand-d);
background: #e4f2e7; padding: .35rem .7rem; border-radius: 999px;
border: 1px solid rgba(90, 158, 111, 0.28);
}
.nav-toggle { display: none; flex-direction: column; gap: 5px; background: none; border: 0; cursor: pointer; padding: 8px; }
.nav-toggle span { width: 24px; height: 2.6px; background: var(--ink); border-radius: 2px; transition: .25s; }
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7.6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7.6px) rotate(-45deg); }
/* ---------- Hero ---------- */
.hero { padding: clamp(2.5rem, 6vw, 5rem) 0 clamp(3rem, 7vw, 6rem); position: relative; overflow: hidden; }
.hero::before {
content: ""; position: absolute; inset: 0; z-index: -1;
background:
radial-gradient(60% 50% at 12% 8%, rgba(90, 158, 111, 0.12), transparent 70%),
radial-gradient(50% 50% at 92% 18%, rgba(240, 138, 60, 0.12), transparent 70%);
}
.hero-inner { display: grid; grid-template-columns: 1.05fr .95fr; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
.eyebrow {
display: inline-block; font-weight: 800; font-size: .85rem; color: var(--brand-d);
background: var(--cream); border: 1px solid var(--line); padding: .4rem .9rem;
border-radius: 999px; box-shadow: var(--shadow-sm); margin-bottom: 1.1rem;
}
.hero-copy h1 { font-size: clamp(2.3rem, 5.4vw, 3.7rem); margin: 0 0 1rem; color: var(--ink); }
.hero-copy h1 em { color: var(--accent); font-style: normal; position: relative; }
.hero-copy h1 em::after {
content: ""; position: absolute; left: -2%; right: -2%; bottom: 4px; height: 12px;
background: rgba(240, 138, 60, 0.22); border-radius: 8px; z-index: -1;
}
.lede { font-size: 1.12rem; color: var(--ink-2); max-width: 46ch; margin: 0 0 1.6rem; }
.hero-actions { display: flex; flex-wrap: wrap; gap: .8rem; margin-bottom: 2rem; }
.hero-trust { list-style: none; display: flex; gap: 1.8rem; padding: 0; margin: 0; flex-wrap: wrap; }
.hero-trust li { display: flex; flex-direction: column; }
.hero-trust strong { font-family: "Baloo 2", sans-serif; font-size: 1.55rem; color: var(--brand-d); }
.hero-trust span { font-size: .82rem; color: var(--muted); font-weight: 600; }
/* Hero media */
.hero-media { position: relative; min-height: 380px; }
.photo {
margin: 0; border-radius: var(--r-lg); position: relative; overflow: hidden;
box-shadow: var(--shadow-md); border: 5px solid #fff;
}
.photo .photo-cap {
position: absolute; left: 12px; bottom: 12px;
background: rgba(47, 58, 48, 0.62); color: #fff; backdrop-filter: blur(4px);
font-size: .78rem; font-weight: 700; padding: .35rem .7rem; border-radius: 999px;
}
.photo-a {
width: 72%; height: 320px; margin-left: auto;
background: linear-gradient(150deg, #cfe7d3, #f3c896 75%, #e89b5c);
}
.photo-b {
width: 52%; height: 190px; position: absolute; left: 0; bottom: -18px;
background: linear-gradient(150deg, #ffe0c2, #f4b27e, #cfe2c8);
animation: float 6s ease-in-out infinite;
}
.hero-float {
position: absolute; right: -6px; top: 28px;
background: var(--surface); border-radius: var(--r-md); padding: .7rem .95rem;
display: flex; align-items: center; gap: .7rem; box-shadow: var(--shadow-md);
border: 1px solid var(--line); animation: float 5s ease-in-out infinite reverse;
}
.hero-float strong { font-family: "Baloo 2", sans-serif; font-size: 1.4rem; color: var(--accent-d); display: block; line-height: 1; }
.hero-float small { color: var(--muted); font-size: .72rem; font-weight: 700; }
.float-pulse {
width: 12px; height: 12px; border-radius: 50%; background: var(--ok); position: relative;
}
.float-pulse::after {
content: ""; position: absolute; inset: -6px; border-radius: 50%;
border: 2px solid var(--ok); animation: pulse 1.8s ease-out infinite;
}
@keyframes float { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-12px); } }
@keyframes pulse { 0% { transform: scale(.7); opacity: .9; } 100% { transform: scale(1.7); opacity: 0; } }
/* ---------- Sections ---------- */
.section { padding: clamp(3rem, 7vw, 5.5rem) 0; }
.section-tint { background: linear-gradient(180deg, var(--cream), #f6efe0); border-block: 1px solid var(--line); }
.sec-head { max-width: 56ch; margin: 0 auto 2.5rem; text-align: center; }
.kicker {
display: inline-block; font-weight: 800; font-size: .8rem; letter-spacing: .08em;
text-transform: uppercase; color: var(--accent-d); margin-bottom: .6rem;
}
.kicker-light { color: #ffe7d3; }
.sec-head h2 { font-size: clamp(1.7rem, 3.6vw, 2.5rem); margin: 0 0 .6rem; }
.sec-head p { color: var(--ink-2); margin: 0; font-size: 1.05rem; }
/* ---------- Filters ---------- */
.filters { display: flex; flex-wrap: wrap; gap: .6rem; justify-content: center; margin-bottom: 2rem; }
.chip {
border: 1.5px solid var(--line-2); background: var(--surface); color: var(--ink-2);
font-family: inherit; font-weight: 800; font-size: .92rem; padding: .55rem 1.15rem;
border-radius: 999px; cursor: pointer; transition: .18s;
}
.chip:hover { border-color: var(--brand); color: var(--ink); }
.chip.is-active { background: var(--brand); border-color: var(--brand); color: #fff; box-shadow: var(--shadow-sm); }
/* ---------- Adopt grid ---------- */
.adopt-grid {
display: grid; grid-template-columns: repeat(auto-fill, minmax(245px, 1fr)); gap: 1.4rem;
}
.adopt-card {
background: var(--surface); border-radius: var(--r-lg); overflow: hidden;
box-shadow: var(--shadow-sm); border: 1px solid var(--line);
display: flex; flex-direction: column; transition: transform .2s ease, box-shadow .2s ease;
opacity: 0; transform: translateY(14px) scale(.98); animation: cardIn .45s ease forwards;
}
@keyframes cardIn { to { opacity: 1; transform: none; } }
.adopt-card:hover { transform: translateY(-6px); box-shadow: var(--shadow-md); }
.adopt-photo { height: 188px; position: relative; }
.adopt-tag {
position: absolute; top: 12px; left: 12px; background: rgba(255,255,255,.92);
color: var(--brand-d); font-weight: 800; font-size: .74rem; padding: .3rem .65rem;
border-radius: 999px; box-shadow: var(--shadow-sm);
}
.adopt-fav {
position: absolute; top: 10px; right: 10px; width: 38px; height: 38px;
border: 0; border-radius: 50%; background: rgba(255,255,255,.92); cursor: pointer;
font-size: 1.05rem; display: grid; place-items: center; box-shadow: var(--shadow-sm);
transition: .18s;
}
.adopt-fav:hover { transform: scale(1.12); }
.adopt-fav.is-fav { background: var(--accent); }
.adopt-body { padding: 1rem 1.1rem 1.2rem; display: flex; flex-direction: column; gap: .5rem; flex: 1; }
.adopt-name { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; }
.adopt-name h3 { margin: 0; font-size: 1.25rem; }
.adopt-name .age { font-size: .82rem; color: var(--muted); font-weight: 700; }
.adopt-meta { display: flex; flex-wrap: wrap; gap: .4rem; }
.adopt-meta span {
font-size: .74rem; font-weight: 700; color: var(--ink-2);
background: #eef4ea; padding: .25rem .6rem; border-radius: 999px;
}
.adopt-desc { font-size: .9rem; color: var(--ink-2); margin: 0; flex: 1; }
.adopt-card .btn { margin-top: .3rem; }
/* ---------- Impact ---------- */
.impact-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 1.2rem; margin-bottom: 3rem; }
.stat {
background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-md);
padding: 1.5rem 1.2rem; text-align: center; box-shadow: var(--shadow-sm);
}
.stat-ico { font-size: 1.8rem; display: block; margin-bottom: .4rem; }
.stat strong { font-family: "Baloo 2", sans-serif; font-size: 2.1rem; color: var(--brand-d); display: block; line-height: 1; }
.stat span { font-size: .88rem; color: var(--muted); font-weight: 700; }
/* Thermometer + donate */
.thermo {
background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
padding: clamp(1.4rem, 3vw, 2.2rem); box-shadow: var(--shadow-md);
}
.thermo-head { display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; align-items: flex-end; margin-bottom: 1rem; }
.thermo-head h3 { margin: 0 0 .2rem; font-size: 1.4rem; }
.thermo-head p { margin: 0; color: var(--ink-2); }
.thermo-figures { text-align: right; }
.thermo-figures strong { font-family: "Baloo 2", sans-serif; font-size: 1.7rem; color: var(--accent-d); display: block; line-height: 1; }
.thermo-figures small { color: var(--muted); font-weight: 700; }
.thermo-track { height: 16px; background: #eef0e7; border-radius: 999px; overflow: hidden; margin-bottom: 1.6rem; }
.thermo-fill {
height: 100%; border-radius: 999px;
background: linear-gradient(90deg, var(--brand), var(--accent));
transition: width 1.1s cubic-bezier(.22,.9,.3,1);
}
.donate-box { display: grid; grid-template-columns: 1fr auto auto; gap: .9rem; align-items: end; }
.donate-box fieldset { border: 0; padding: 0; margin: 0; }
.donate-box legend { font-weight: 800; font-size: .82rem; color: var(--ink-2); margin-bottom: .5rem; }
.amounts { display: flex; gap: .5rem; flex-wrap: wrap; }
.amt {
border: 1.5px solid var(--line-2); background: var(--surface); color: var(--ink);
font-family: inherit; font-weight: 800; padding: .6rem 1rem; border-radius: var(--r-sm);
cursor: pointer; transition: .16s; min-width: 64px;
}
.amt:hover { border-color: var(--accent); }
.amt.is-active { background: #fdeee0; border-color: var(--accent); color: var(--accent-d); }
.custom-amt { position: relative; display: flex; align-items: center; }
.custom-amt .dollar { position: absolute; left: 12px; color: var(--muted); font-weight: 800; }
.custom-amt input {
font-family: inherit; font-weight: 700; padding: .7rem .8rem .7rem 1.7rem; width: 150px;
border: 1.5px solid var(--line-2); border-radius: var(--r-sm); background: var(--surface); color: var(--ink);
}
.custom-amt input:focus { border-color: var(--accent); outline: none; }
.donate-note { grid-column: 1 / -1; margin: .3rem 0 0; font-size: .78rem; color: var(--muted); font-weight: 700; }
/* ---------- Sponsor ---------- */
.sponsor-inner { display: grid; grid-template-columns: 1fr .85fr; gap: clamp(2rem, 5vw, 4rem); align-items: center; }
.sponsor-copy h2 { font-size: clamp(1.7rem, 3.4vw, 2.4rem); margin: 0 0 .8rem; }
.sponsor-copy p { color: var(--ink-2); margin: 0 0 1.2rem; font-size: 1.05rem; }
.sponsor-perks { list-style: none; padding: 0; margin: 0; display: grid; gap: .6rem; }
.sponsor-perks li { font-weight: 700; color: var(--ink); background: var(--cream); border: 1px solid var(--line); padding: .65rem .9rem; border-radius: var(--r-sm); }
.sponsor-card { background: var(--surface); border-radius: var(--r-lg); overflow: hidden; box-shadow: var(--shadow-md); border: 1px solid var(--line); }
.photo-c { height: 230px; border: 0; border-radius: 0; box-shadow: none; background: linear-gradient(150deg, #d7ecd6, #f4c089, #ec9a52); }
.sponsor-pad { padding: 1.4rem 1.4rem 1.6rem; }
.sponsor-row { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: .8rem; font-weight: 700; }
.sponsor-row strong { font-family: "Baloo 2", sans-serif; font-size: 1.7rem; color: var(--accent-d); }
input[type="range"] { width: 100%; accent-color: var(--accent); cursor: pointer; }
.sponsor-hint { margin: .7rem 0 1.2rem; color: var(--ink-2); font-weight: 700; }
/* ---------- Stories ---------- */
.stories { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.4rem; }
.story {
margin: 0; background: var(--surface); border-radius: var(--r-lg); overflow: hidden;
border: 1px solid var(--line); box-shadow: var(--shadow-sm); display: flex; flex-direction: column;
transition: transform .2s ease, box-shadow .2s ease;
}
.story:hover { transform: translateY(-5px); box-shadow: var(--shadow-md); }
.story-photo { height: 160px; }
.story-1 { background: linear-gradient(150deg, #cfe7d3, #f3c896); }
.story-2 { background: linear-gradient(150deg, #ffe0c2, #f0b884); }
.story-3 { background: linear-gradient(150deg, #d9ead7, #e9c79a); }
.story blockquote { margin: 0; padding: 1.2rem 1.3rem .8rem; font-size: .98rem; color: var(--ink-2); flex: 1; font-style: italic; }
.story figcaption { padding: 0 1.3rem 1.3rem; }
.story figcaption strong { display: block; }
.story figcaption span { font-size: .85rem; color: var(--muted); font-weight: 700; }
/* ---------- Volunteer ---------- */
.volunteer { background: linear-gradient(135deg, var(--brand-d), var(--brand)); color: #fff; }
.volunteer-inner { display: grid; grid-template-columns: 1fr auto; gap: 2rem; align-items: center; }
.volunteer-copy h2 { font-size: clamp(1.6rem, 3.2vw, 2.3rem); margin: 0 0 .5rem; color: #fff; }
.volunteer-copy p { margin: 0; color: rgba(255,255,255,.9); max-width: 44ch; }
.volunteer-form { display: flex; gap: .7rem; flex-wrap: wrap; }
.volunteer-form input {
font-family: inherit; font-weight: 600; padding: .85rem 1rem; border: 0; border-radius: 999px;
background: rgba(255,255,255,.96); color: var(--ink); min-width: 200px;
}
.volunteer-form input:focus { outline: none; box-shadow: var(--ring); }
/* ---------- Footer ---------- */
.footer { background: #2c3a2e; color: #cdd6c9; padding: 3.5rem 0 1.5rem; }
.footer-inner { display: grid; grid-template-columns: 1.6fr 1fr 1fr 1.3fr; gap: 2rem; padding-bottom: 2rem; border-bottom: 1px solid rgba(255,255,255,.1); }
.footer .brand-name { color: #fff; }
.footer-brand p { margin: .8rem 0; max-width: 32ch; font-size: .92rem; }
.footer-badges { display: flex; gap: .5rem; flex-wrap: wrap; }
.footer-badges .badge-charity { background: rgba(255,255,255,.08); color: #d7e7d9; border-color: rgba(255,255,255,.14); }
.footer-col h4 { color: #fff; margin: 0 0 .9rem; font-size: 1.02rem; }
.footer-col a { display: block; padding: .3rem 0; color: #cdd6c9; font-weight: 600; transition: color .15s; }
.footer-col a:hover { color: var(--accent); }
.news { display: flex; gap: .5rem; margin-bottom: .7rem; }
.news input { flex: 1; min-width: 0; padding: .65rem .8rem; border: 0; border-radius: var(--r-sm); font-family: inherit; }
.news input:focus { outline: none; box-shadow: var(--ring); }
.footer-fine { font-size: .82rem; color: #9aa794; margin: 0; }
.footer-base { display: flex; justify-content: space-between; padding-top: 1.2rem; color: #9aa794; flex-wrap: wrap; gap: .5rem; }
/* ---------- Toast ---------- */
.toast {
position: fixed; left: 50%; bottom: 28px; transform: translate(-50%, 140%);
background: var(--ink); color: #fff; padding: .85rem 1.3rem; border-radius: 999px;
font-weight: 700; box-shadow: var(--shadow-lg); z-index: 300; max-width: 90vw;
transition: transform .35s cubic-bezier(.22,.9,.3,1); pointer-events: none;
}
.toast.show { transform: translate(-50%, 0); }
/* ---------- Reveal ---------- */
.reveal { opacity: 0; transform: translateY(22px); transition: opacity .6s ease, transform .6s ease; }
.reveal.in { opacity: 1; transform: none; }
/* ---------- Responsive ---------- */
@media (max-width: 900px) {
.hero-inner, .sponsor-inner { grid-template-columns: 1fr; }
.hero-media { min-height: 320px; max-width: 460px; }
.impact-grid { grid-template-columns: repeat(2, 1fr); }
.stories { grid-template-columns: 1fr; }
.footer-inner { grid-template-columns: 1fr 1fr; }
.volunteer-inner { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
.nav-links {
position: absolute; top: 72px; left: 0; right: 0; flex-direction: column; gap: 0;
background: var(--surface); border-bottom: 1px solid var(--line);
padding: .5rem 6vw 1rem; box-shadow: var(--shadow-md);
transform: translateY(-12px); opacity: 0; pointer-events: none; transition: .22s;
}
.nav-links.open { transform: none; opacity: 1; pointer-events: auto; }
.nav-links a { padding: .8rem 0; border-bottom: 1px solid var(--line); }
.nav-toggle { display: flex; }
.badge-charity { display: none; }
}
@media (max-width: 520px) {
.donate-box { grid-template-columns: 1fr; }
.custom-amt input { width: 100%; }
.impact-grid { grid-template-columns: 1fr; }
.footer-inner { grid-template-columns: 1fr; }
.hero-trust { gap: 1.2rem; }
.photo-a { width: 84%; }
.volunteer-form { flex-direction: column; }
.volunteer-form input { min-width: 0; width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; transition-duration: .001ms !important; }
html { scroll-behavior: auto; }
}(function () {
"use strict";
/* ---------- Toast helper ---------- */
var toastEl = document.getElementById("toast");
var toastTimer;
function toast(msg) {
if (!toastEl) return;
toastEl.textContent = msg;
toastEl.classList.add("show");
clearTimeout(toastTimer);
toastTimer = setTimeout(function () {
toastEl.classList.remove("show");
}, 2600);
}
/* ---------- Mobile nav ---------- */
var navToggle = document.getElementById("navToggle");
var navLinks = document.getElementById("navLinks");
if (navToggle && navLinks) {
navToggle.addEventListener("click", function () {
var open = navLinks.classList.toggle("open");
navToggle.setAttribute("aria-expanded", String(open));
});
navLinks.addEventListener("click", function (e) {
if (e.target.tagName === "A") {
navLinks.classList.remove("open");
navToggle.setAttribute("aria-expanded", "false");
}
});
}
/* ---------- Adoptable animals ---------- */
var ANIMALS = [
{ name: "Marble", type: "dog", age: "2 yr", tag: "Good with kids", grad: "#cfe7d3,#f3c896", emoji: "🐕",
meta: ["Labrador mix", "Medium", "Vaccinated"], desc: "Goofy, gentle and obsessed with tennis balls." },
{ name: "Clementine", type: "cat", age: "5 yr", tag: "Senior sweetheart", grad: "#ffe0c2,#f0b884", emoji: "🐈",
meta: ["Tabby", "Lap cat", "Spayed"], desc: "A calm cuddler who loves sunny windowsills." },
{ name: "Biscuit", type: "dog", age: "4 yr", tag: "Special needs", grad: "#d7ecd6,#ec9a52", emoji: "🦮",
meta: ["Terrier", "3 legs", "House-trained"], desc: "Doesn't let three legs slow down his zoomies." },
{ name: "Pip", type: "cat", age: "4 mo", tag: "Kitten", grad: "#e7f0d8,#f4c089", emoji: "😺",
meta: ["Domestic", "Playful", "Microchipped"], desc: "Tiny tornado of purrs and pounces." },
{ name: "Sprout", type: "small", age: "1 yr", tag: "Bonded pair", grad: "#d9ead7,#e9c79a", emoji: "🐰",
meta: ["Lop rabbit", "Litter-trained", "Neutered"], desc: "Comes with bestie Clover — adopt together!" },
{ name: "Maple", type: "dog", age: "7 yr", tag: "Calm companion", grad: "#cfe7d3,#e8b079", emoji: "🐶",
meta: ["Beagle", "Low energy", "Vaccinated"], desc: "An easygoing senior who adores slow walks." },
{ name: "Olive", type: "cat", age: "2 yr", tag: "Shy but sweet", grad: "#e3eed6,#f3c08a", emoji: "🐱",
meta: ["Tuxedo", "Indoor", "Spayed"], desc: "Warms up fast once she trusts you." },
{ name: "Pepper", type: "small", age: "8 mo", tag: "First-time friendly", grad: "#d7ecd6,#efc488", emoji: "🐹",
meta: ["Guinea pig", "Social", "Healthy"], desc: "Squeaks with joy at veggie time." }
];
var grid = document.getElementById("adoptGrid");
var favs = {};
function render(filter) {
if (!grid) return;
grid.innerHTML = "";
var list = ANIMALS.filter(function (a) {
return filter === "all" || a.type === filter;
});
list.forEach(function (a, i) {
var card = document.createElement("article");
card.className = "adopt-card";
card.style.animationDelay = i * 50 + "ms";
var faved = favs[a.name] ? " is-fav" : "";
card.innerHTML =
'<div class="adopt-photo" style="background:linear-gradient(150deg,' + a.grad + ')">' +
'<span class="adopt-tag">' + a.tag + "</span>" +
'<button class="adopt-fav' + faved + '" type="button" aria-pressed="' + !!favs[a.name] +
'" aria-label="Favourite ' + a.name + '">' + (favs[a.name] ? "❤️" : "🤍") + "</button>" +
"</div>" +
'<div class="adopt-body">' +
'<div class="adopt-name"><h3>' + a.emoji + " " + a.name + '</h3><span class="age">' + a.age + "</span></div>" +
'<div class="adopt-meta">' + a.meta.map(function (m) { return "<span>" + m + "</span>"; }).join("") + "</div>" +
'<p class="adopt-desc">' + a.desc + "</p>" +
'<button class="btn btn-brand" type="button" data-meet="' + a.name + '">Meet ' + a.name + " →</button>" +
"</div>";
var favBtn = card.querySelector(".adopt-fav");
favBtn.addEventListener("click", function () {
favs[a.name] = !favs[a.name];
favBtn.classList.toggle("is-fav", favs[a.name]);
favBtn.textContent = favs[a.name] ? "❤️" : "🤍";
favBtn.setAttribute("aria-pressed", String(!!favs[a.name]));
toast(favs[a.name] ? "Saved " + a.name + " to your favourites 💚" : "Removed " + a.name);
});
card.querySelector("[data-meet]").addEventListener("click", function () {
toast("Application started for " + a.name + " — our team will call you! 🐾");
});
grid.appendChild(card);
});
}
render("all");
/* ---------- Filters ---------- */
var chips = document.querySelectorAll(".chip");
chips.forEach(function (chip) {
chip.addEventListener("click", function () {
chips.forEach(function (c) {
c.classList.remove("is-active");
c.setAttribute("aria-selected", "false");
});
chip.classList.add("is-active");
chip.setAttribute("aria-selected", "true");
render(chip.dataset.filter);
});
});
/* ---------- Animated impact counters ---------- */
function animateCount(el) {
var target = parseInt(el.dataset.count, 10);
var dur = 1500, start = performance.now();
function step(now) {
var p = Math.min((now - start) / dur, 1);
var eased = 1 - Math.pow(1 - p, 3);
var val = Math.floor(eased * target);
el.textContent = val >= 1000 ? val.toLocaleString("en-US") : String(val);
if (p < 1) requestAnimationFrame(step);
else el.textContent = target.toLocaleString("en-US");
}
requestAnimationFrame(step);
}
/* ---------- Reveal + counters + thermometer via IntersectionObserver ---------- */
var thermoFill = document.getElementById("thermoFill");
var GOAL = 50000, RAISED = 38400;
var io = new IntersectionObserver(function (entries) {
entries.forEach(function (entry) {
if (!entry.isIntersecting) return;
var t = entry.target;
t.classList.add("in");
t.querySelectorAll("[data-count]").forEach(animateCount);
if (t.querySelector("#thermoFill") || t.id === "donate") {
if (thermoFill) thermoFill.style.width = Math.min((RAISED / GOAL) * 100, 100) + "%";
}
io.unobserve(t);
});
}, { threshold: 0.2 });
document.querySelectorAll(".reveal, .impact-grid, .thermo").forEach(function (el) {
io.observe(el);
});
/* ---------- Donate amount selection ---------- */
var selectedAmt = 50;
var donateBtn = document.getElementById("donateBtn");
var customAmt = document.getElementById("customAmt");
var amtBtns = document.querySelectorAll(".amt");
function syncDonateBtn() {
if (donateBtn) donateBtn.textContent = "Donate $" + selectedAmt + " →";
}
amtBtns.forEach(function (b) {
b.addEventListener("click", function () {
amtBtns.forEach(function (x) { x.classList.remove("is-active"); });
b.classList.add("is-active");
selectedAmt = parseInt(b.dataset.amt, 10);
if (customAmt) customAmt.value = "";
syncDonateBtn();
});
});
if (customAmt) {
customAmt.addEventListener("input", function () {
var v = parseInt(customAmt.value, 10);
if (!isNaN(v) && v > 0) {
amtBtns.forEach(function (x) { x.classList.remove("is-active"); });
selectedAmt = v;
syncDonateBtn();
}
});
}
var donateForm = document.getElementById("donateForm");
if (donateForm) {
donateForm.addEventListener("submit", function (e) {
e.preventDefault();
if (!selectedAmt || selectedAmt < 1) { toast("Please choose an amount 🐾"); return; }
// simulate the gift bumping the thermometer
RAISED = Math.min(RAISED + selectedAmt, GOAL);
if (thermoFill) thermoFill.style.width = Math.min((RAISED / GOAL) * 100, 100) + "%";
var raisedEl = document.getElementById("raised");
if (raisedEl) raisedEl.textContent = "$" + RAISED.toLocaleString("en-US");
toast("Thank you! Your $" + selectedAmt + " gift feeds rescues today 🧡");
});
}
/* ---------- Donate nav buttons scroll + nudge ---------- */
document.querySelectorAll("[data-donate]").forEach(function (btn) {
btn.addEventListener("click", function (e) {
var donate = document.getElementById("donate");
if (donate) {
e.preventDefault();
donate.scrollIntoView({ behavior: "smooth", block: "center" });
}
});
});
/* ---------- Sponsor slider ---------- */
var range = document.getElementById("sponsorRange");
var sponsorAmt = document.getElementById("sponsorAmt");
var sponsorHint = document.getElementById("sponsorHint");
var sponsorBtn = document.getElementById("sponsorBtn");
function sponsorTier(v) {
if (v <= 15) return "Feeds Biscuit for a whole month 🍖";
if (v <= 35) return "Covers food + a vet check-up 🩺";
if (v <= 65) return "Funds rehab + grooming + meals ✨";
return "Sponsors a full recovery journey 🌟";
}
if (range) {
range.addEventListener("input", function () {
var v = range.value;
if (sponsorAmt) sponsorAmt.textContent = "$" + v + "/mo";
if (sponsorHint) sponsorHint.textContent = sponsorTier(parseInt(v, 10));
});
}
if (sponsorBtn) {
sponsorBtn.addEventListener("click", function () {
toast("You're now sponsoring Biscuit at " + (range ? "$" + range.value : "$15") + "/mo 🦴");
});
}
/* ---------- Volunteer + newsletter forms ---------- */
var volForm = document.getElementById("volForm");
if (volForm) {
volForm.addEventListener("submit", function (e) {
e.preventDefault();
var name = document.getElementById("volName");
var email = document.getElementById("volEmail");
if (!name.value.trim()) { toast("Tell us your name 🙂"); name.focus(); return; }
if (!email.value.trim() || email.value.indexOf("@") === -1) { toast("Add a valid email 📧"); email.focus(); return; }
toast("Welcome aboard, " + name.value.trim().split(" ")[0] + "! We'll be in touch 🙌");
volForm.reset();
});
}
var newsForm = document.getElementById("newsForm");
if (newsForm) {
newsForm.addEventListener("submit", function (e) {
e.preventDefault();
var email = document.getElementById("newsEmail");
if (!email.value.trim() || email.value.indexOf("@") === -1) { toast("Add a valid email 📧"); email.focus(); return; }
toast("Subscribed! Watch for happy tails in your inbox 🐾");
newsForm.reset();
});
}
/* ---------- Live "adopted this week" ticker ---------- */
var liveAdopt = document.getElementById("liveAdopt");
if (liveAdopt) {
setInterval(function () {
if (Math.random() > 0.55) {
var n = parseInt(liveAdopt.textContent, 10) + 1;
liveAdopt.textContent = n;
}
}, 7000);
}
syncDonateBtn();
})();<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Pawhaven Rescue — Every Animal Deserves a Home</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=Baloo+2:wght@500;600;700;800&family=Nunito:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<a class="skip" href="#main">Skip to content</a>
<!-- NAV -->
<header class="nav" id="top">
<div class="wrap nav-inner">
<a class="brand" href="#top" aria-label="Pawhaven Rescue home">
<span class="brand-mark" aria-hidden="true">🐾</span>
<span class="brand-name">Pawhaven<span class="brand-dot">.</span></span>
</a>
<nav class="nav-links" id="navLinks" aria-label="Primary">
<a href="#adopt">Adopt</a>
<a href="#impact">Our Impact</a>
<a href="#sponsor">Sponsor</a>
<a href="#stories">Stories</a>
<a href="#volunteer">Volunteer</a>
</nav>
<div class="nav-cta">
<span class="badge-charity" title="Registered charity #PH-2014">
<span aria-hidden="true">✓</span> Registered Charity
</span>
<a class="btn btn-accent" href="#donate" data-donate>Donate</a>
<button class="nav-toggle" id="navToggle" aria-expanded="false" aria-controls="navLinks" aria-label="Toggle menu">
<span></span><span></span><span></span>
</button>
</div>
</div>
</header>
<main id="main">
<!-- HERO -->
<section class="hero">
<div class="wrap hero-inner">
<div class="hero-copy reveal">
<span class="eyebrow">🏡 No-kill shelter · Since 2014</span>
<h1>Every paw deserves<br />a place to call <em>home</em>.</h1>
<p class="lede">
We rescue, heal and rehome animals in need across the Greenvale region —
then find them families who love them for life. Your kindness is their second chance.
</p>
<div class="hero-actions">
<a class="btn btn-accent btn-lg" href="#adopt">🐶 Meet our animals</a>
<a class="btn btn-ghost btn-lg" href="#donate" data-donate>♥ Donate today</a>
</div>
<ul class="hero-trust">
<li><strong>4,820</strong><span>animals rehomed</span></li>
<li><strong>100%</strong><span>no-kill promise</span></li>
<li><strong>1,200+</strong><span>active foster homes</span></li>
</ul>
</div>
<div class="hero-media reveal">
<figure class="photo photo-a" role="img" aria-label="Volunteer cuddling a rescued golden dog">
<span class="photo-cap">Luna, rescued & recovering 🌿</span>
</figure>
<figure class="photo photo-b" role="img" aria-label="Tabby kitten napping in a warm blanket">
<span class="photo-cap">Pip, ready to adopt 😺</span>
</figure>
<div class="hero-float">
<span class="float-pulse" aria-hidden="true"></span>
<div>
<strong id="liveAdopt">12</strong>
<small>animals adopted this week</small>
</div>
</div>
</div>
</div>
</section>
<!-- ADOPT GRID -->
<section class="section" id="adopt">
<div class="wrap">
<header class="sec-head reveal">
<span class="kicker">Find a friend</span>
<h2>Meet animals looking for a home</h2>
<p>Each one is vaccinated, microchipped and ready for love. Filter to find your match.</p>
</header>
<div class="filters reveal" role="tablist" aria-label="Filter adoptable animals">
<button class="chip is-active" data-filter="all" role="tab" aria-selected="true">All</button>
<button class="chip" data-filter="dog" role="tab" aria-selected="false">🐕 Dogs</button>
<button class="chip" data-filter="cat" role="tab" aria-selected="false">🐈 Cats</button>
<button class="chip" data-filter="small" role="tab" aria-selected="false">🐰 Small pets</button>
</div>
<div class="adopt-grid" id="adoptGrid" aria-live="polite"><!-- JS injects cards --></div>
</div>
</section>
<!-- IMPACT -->
<section class="section section-tint" id="impact">
<div class="wrap">
<header class="sec-head reveal">
<span class="kicker">Transparency you can trust</span>
<h2>The difference we make together</h2>
<p>Every dollar is tracked. Here's what your support achieved this year.</p>
</header>
<div class="impact-grid">
<div class="stat reveal"><span class="stat-ico" aria-hidden="true">🏡</span><strong data-count="4820">0</strong><span>animals rehomed</span></div>
<div class="stat reveal"><span class="stat-ico" aria-hidden="true">🩺</span><strong data-count="9640">0</strong><span>vet treatments funded</span></div>
<div class="stat reveal"><span class="stat-ico" aria-hidden="true">🍲</span><strong data-count="312000">0</strong><span>meals served</span></div>
<div class="stat reveal"><span class="stat-ico" aria-hidden="true">🙌</span><strong data-count="1240">0</strong><span>volunteers & fosters</span></div>
</div>
<div class="thermo reveal" id="donate">
<div class="thermo-head">
<div>
<h3>Winter Warmth Campaign</h3>
<p>Help us build 50 insulated kennels before the cold sets in.</p>
</div>
<div class="thermo-figures">
<strong id="raised">$38,400</strong>
<small>raised of $50,000 goal</small>
</div>
</div>
<div class="thermo-track" role="progressbar" aria-valuemin="0" aria-valuemax="50000" aria-valuenow="38400" aria-label="Campaign progress">
<div class="thermo-fill" id="thermoFill" style="width:0%"></div>
</div>
<form class="donate-box" id="donateForm" novalidate>
<fieldset>
<legend>Choose your gift</legend>
<div class="amounts" id="amounts">
<button type="button" class="amt" data-amt="25">$25</button>
<button type="button" class="amt is-active" data-amt="50">$50</button>
<button type="button" class="amt" data-amt="100">$100</button>
<button type="button" class="amt" data-amt="250">$250</button>
</div>
</fieldset>
<label class="custom-amt">
<span class="sr-only">Custom amount in dollars</span>
<span class="dollar" aria-hidden="true">$</span>
<input type="number" id="customAmt" min="1" inputmode="numeric" placeholder="Other amount" />
</label>
<button type="submit" class="btn btn-accent btn-lg" id="donateBtn">Donate $50 →</button>
<p class="donate-note">🔒 Secure & tax-deductible. EIN 47-0192233.</p>
</form>
</div>
</div>
</section>
<!-- SPONSOR -->
<section class="section" id="sponsor">
<div class="wrap sponsor-inner">
<div class="sponsor-copy reveal">
<span class="kicker">Sponsor a pet</span>
<h2>Can't adopt? Sponsor a rescue instead.</h2>
<p>
For animals still healing or waiting on their family, monthly sponsors cover food,
shelter and medical care. You'll get photo updates and a thank-you from your buddy.
</p>
<ul class="sponsor-perks">
<li>📸 Monthly photo & progress updates</li>
<li>💌 Personalised sponsor certificate</li>
<li>🎟️ Invites to meet-and-greet days</li>
</ul>
</div>
<div class="sponsor-card reveal">
<figure class="photo photo-c" role="img" aria-label="Three-legged rescue dog smiling in the sun">
<span class="photo-cap">Sponsor me — Biscuit 🦴</span>
</figure>
<div class="sponsor-pad">
<div class="sponsor-row">
<span>Monthly sponsorship</span>
<strong id="sponsorAmt">$15/mo</strong>
</div>
<input type="range" id="sponsorRange" min="10" max="100" step="5" value="15" aria-label="Monthly sponsorship amount" />
<p class="sponsor-hint" id="sponsorHint">Feeds Biscuit for a whole month 🍖</p>
<button class="btn btn-brand btn-block" id="sponsorBtn">Sponsor Biscuit</button>
</div>
</div>
</div>
</section>
<!-- SUCCESS STORIES -->
<section class="section section-tint" id="stories">
<div class="wrap">
<header class="sec-head reveal">
<span class="kicker">Happy tails</span>
<h2>Success stories from our families</h2>
</header>
<div class="stories">
<figure class="story reveal">
<div class="story-photo story-1" role="img" aria-label="Happy family with adopted dog at the park"></div>
<blockquote>"Marble went from a frightened shelter pup to the heart of our home in weeks. The Pawhaven team checked in every step."</blockquote>
<figcaption><strong>The Alvarez Family</strong><span>Adopted Marble 🐕</span></figcaption>
</figure>
<figure class="story reveal">
<div class="story-photo story-2" role="img" aria-label="Senior cat curled on a sunny windowsill"></div>
<blockquote>"We sponsored Clementine for a year, then couldn't resist — now she rules our windowsill. Best decision ever."</blockquote>
<figcaption><strong>Dev & Priya</strong><span>Adopted Clementine 🐈</span></figcaption>
</figure>
<figure class="story reveal">
<div class="story-photo story-3" role="img" aria-label="Child reading a book beside a calm rescue rabbit"></div>
<blockquote>"Our volunteer days turned into fostering, which turned into forever. Pawhaven changed our whole family."</blockquote>
<figcaption><strong>The Okafor Family</strong><span>Fostered 9 · Adopted Sprout 🐰</span></figcaption>
</figure>
</div>
</div>
</section>
<!-- VOLUNTEER CTA -->
<section class="section volunteer" id="volunteer">
<div class="wrap volunteer-inner reveal">
<div class="volunteer-copy">
<span class="kicker kicker-light">Lend a hand</span>
<h2>Become a volunteer or foster</h2>
<p>Walk dogs, socialise kittens, or open your home to a foster pet. Two hours a week changes a life.</p>
</div>
<form class="volunteer-form" id="volForm" novalidate>
<label class="sr-only" for="volName">Your name</label>
<input id="volName" type="text" placeholder="Your name" autocomplete="name" required />
<label class="sr-only" for="volEmail">Your email</label>
<input id="volEmail" type="email" placeholder="[email protected]" autocomplete="email" required />
<button type="submit" class="btn btn-accent">Sign me up 🙌</button>
</form>
</div>
</section>
</main>
<!-- FOOTER -->
<footer class="footer">
<div class="wrap footer-inner">
<div class="footer-brand">
<span class="brand"><span class="brand-mark" aria-hidden="true">🐾</span><span class="brand-name">Pawhaven</span></span>
<p>A no-kill rescue giving every animal a second chance at Greenvale since 2014.</p>
<div class="footer-badges">
<span class="badge-charity">✓ Registered Charity</span>
<span class="badge-charity">🔒 Tax-deductible</span>
</div>
</div>
<nav class="footer-col" aria-label="Get involved">
<h4>Get involved</h4>
<a href="#adopt">Adopt</a>
<a href="#sponsor">Sponsor a pet</a>
<a href="#volunteer">Volunteer</a>
<a href="#donate" data-donate>Donate</a>
</nav>
<nav class="footer-col" aria-label="About">
<h4>About us</h4>
<a href="#impact">Our impact</a>
<a href="#stories">Success stories</a>
<a href="#">Annual report</a>
<a href="#">Contact</a>
</nav>
<div class="footer-col">
<h4>Stay in touch</h4>
<form class="news" id="newsForm" novalidate>
<label class="sr-only" for="newsEmail">Email for newsletter</label>
<input id="newsEmail" type="email" placeholder="Email address" />
<button class="btn btn-brand" type="submit">Join</button>
</form>
<p class="footer-fine">12 Maple Row, Greenvale · [email protected]</p>
</div>
</div>
<div class="wrap footer-base">
<small>© 2026 Pawhaven Rescue (fictional). Illustrative demo only.</small>
<small>Made with 🧡 by volunteers</small>
</div>
</footer>
<div class="toast" id="toast" role="status" aria-live="polite"></div>
<script src="script.js"></script>
</body>
</html>Animal Welfare Landing
A full single-page site for Pawhaven Rescue, a fictional no-kill animal shelter. A soft green, cream and warm-orange palette with a rounded, friendly sans-serif sets a caring tone, while a sticky nav carries a registered-charity trust badge and a prominent accent Donate call to action. The hero pairs overlapping rescue-photo placeholders with adopt and donate buttons, headline impact numbers, and a floating “animals adopted this week” card whose count ticks up live.
The adoptable-animals grid renders from data and can be filtered by dogs, cats or small pets; each card has a heart toggle to save favourites and a “Meet” button that simulates starting an application. Scrolling into the impact section animates the stat counters and fills a Winter Warmth campaign thermometer. The donate box lets you pick a preset or custom amount — submitting it bumps the thermometer and the raised total. A sponsor-a-pet slider updates the monthly amount and what it covers, success stories sit in hover-lift cards, and a volunteer form plus newsletter sign-up validate input and confirm with a toast.
Everything is self-contained vanilla JavaScript: an IntersectionObserver drives scroll reveals, counters and the thermometer; a small toast() helper gives feedback; and the mobile nav, forms and controls are all keyboard-usable with appropriate ARIA.
Illustrative UI only — fictional organization, not a real charity or donation system.