Source Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PawHouse — Premium Pet Store</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link href="https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,700;1,400&family=DM+Sans:wght@300;400;500;600&display=swap" rel="stylesheet" />
<style>
/* ─────────────────────────────────────────
TOKENS & RESET
───────────────────────────────────────── */
:root {
--cream: #FAF7F2;
--warm: #F2EDE4;
--sand: #E8DDD0;
--bark: #8B6F47;
--bark-dk: #5C4A2A;
--leaf: #4A7C59;
--leaf-lt: #D6E9DC;
--blush: #E8A598;
--text: #1C1612;
--muted: #7A6E65;
--white: #FFFFFF;
--shadow: 0 4px 24px rgba(0,0,0,.09);
--shadow-lg: 0 16px 56px rgba(0,0,0,.14);
--r: 16px;
--r-sm: 10px;
--transition: .3s cubic-bezier(.4,0,.2,1);
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }
body {
font-family: 'DM Sans', sans-serif;
background: var(--cream);
color: var(--text);
overflow-x: hidden;
}
img { display: block; max-width: 100%; }
button { cursor: pointer; font-family: inherit; border: none; background: none; }
a { text-decoration: none; color: inherit; }
ul { list-style: none; }
/* ─────────────────────────────────────────
NAV
───────────────────────────────────────── */
.nav {
position: fixed; top: 0; left: 0; right: 0; z-index: 900;
display: flex; align-items: center; justify-content: space-between;
padding: 18px 5vw;
background: rgba(250,247,242,.88);
backdrop-filter: blur(14px);
-webkit-backdrop-filter: blur(14px);
border-bottom: 1px solid var(--sand);
transition: box-shadow var(--transition);
}
.nav.scrolled { box-shadow: var(--shadow); }
.nav-logo {
font-family: 'Playfair Display', serif;
font-size: 1.5rem;
font-weight: 700;
color: var(--bark-dk);
letter-spacing: -.5px;
}
.nav-logo span { color: var(--leaf); }
.nav-links {
display: flex; gap: 32px;
}
.nav-links a {
font-size: .9rem; font-weight: 500; color: var(--muted);
transition: color var(--transition); letter-spacing: .02em;
}
.nav-links a:hover { color: var(--bark-dk); }
.nav-actions { display: flex; align-items: center; gap: 16px; }
.cart-btn {
position: relative;
display: flex; align-items: center; gap: 8px;
background: var(--bark-dk); color: var(--white);
padding: 10px 20px; border-radius: 50px;
font-size: .88rem; font-weight: 600;
transition: background var(--transition), transform var(--transition);
}
.cart-btn:hover { background: var(--bark); transform: translateY(-1px); }
.cart-badge {
position: absolute; top: -6px; right: -6px;
background: var(--blush); color: var(--white);
font-size: .7rem; font-weight: 700;
width: 20px; height: 20px; border-radius: 50%;
display: flex; align-items: center; justify-content: center;
opacity: 0; transform: scale(0);
transition: opacity var(--transition), transform var(--transition);
}
.cart-badge.active { opacity: 1; transform: scale(1); }
.nav-hamburger { display: none; flex-direction: column; gap: 5px; padding: 4px; }
.nav-hamburger span { display: block; width: 22px; height: 2px; background: var(--text); border-radius: 2px; }
/* ─────────────────────────────────────────
HERO
───────────────────────────────────────── */
.hero {
min-height: 100vh;
display: flex; align-items: center;
padding: 120px 5vw 80px;
position: relative; overflow: hidden;
background: linear-gradient(135deg, var(--cream) 0%, var(--warm) 60%, #EDE4D8 100%);
}
/* decorative blobs */
.hero::before, .hero::after {
content: ''; position: absolute; border-radius: 50%;
filter: blur(80px); opacity: .35; pointer-events: none;
}
.hero::before {
width: 600px; height: 600px;
background: radial-gradient(circle, var(--sand) 0%, transparent 70%);
top: -100px; right: -100px;
}
.hero::after {
width: 400px; height: 400px;
background: radial-gradient(circle, var(--leaf-lt) 0%, transparent 70%);
bottom: -80px; left: 10%;
}
.hero-content { max-width: 560px; position: relative; z-index: 1; }
.hero-eyebrow {
display: inline-flex; align-items: center; gap: 8px;
background: var(--leaf-lt); color: var(--leaf);
font-size: .8rem; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
padding: 6px 14px; border-radius: 50px; margin-bottom: 24px;
}
.hero-eyebrow::before { content: '🐾'; font-size: 1rem; }
.hero h1 {
font-family: 'Playfair Display', serif;
font-size: clamp(2.6rem, 5.5vw, 4.2rem);
line-height: 1.12; color: var(--bark-dk);
margin-bottom: 20px;
}
.hero h1 em { font-style: italic; color: var(--leaf); }
.hero-desc {
font-size: 1.05rem; color: var(--muted);
line-height: 1.7; max-width: 440px; margin-bottom: 40px;
}
.hero-cta {
display: flex; gap: 14px; flex-wrap: wrap;
}
.btn-primary {
background: var(--bark-dk); color: var(--white);
padding: 14px 32px; border-radius: 50px;
font-size: .95rem; font-weight: 600;
transition: background var(--transition), transform var(--transition), box-shadow var(--transition);
box-shadow: 0 4px 18px rgba(92,74,42,.28);
}
.btn-primary:hover { background: var(--bark); transform: translateY(-2px); box-shadow: 0 8px 28px rgba(92,74,42,.28); }
.btn-outline {
background: transparent; color: var(--bark-dk);
padding: 14px 32px; border-radius: 50px;
border: 2px solid var(--bark-dk);
font-size: .95rem; font-weight: 600;
transition: all var(--transition);
}
.btn-outline:hover { background: var(--bark-dk); color: var(--white); transform: translateY(-2px); }
.hero-visual {
position: absolute; right: 5vw; top: 50%;
transform: translateY(-50%);
width: clamp(280px, 40vw, 500px);
z-index: 1;
}
.hero-visual-inner {
border-radius: 40% 60% 55% 45% / 50% 45% 55% 50%;
overflow: hidden;
box-shadow: var(--shadow-lg);
animation: heroFloat 6s ease-in-out infinite;
aspect-ratio: 4/5;
}
.hero-visual-inner img {
width: 100%; height: 100%; object-fit: cover;
transition: transform 8s ease;
}
@keyframes heroFloat {
0%,100% { transform: translateY(0); }
50% { transform: translateY(-16px); }
}
.hero-stats {
display: flex; gap: 32px; margin-top: 48px;
flex-wrap: wrap;
}
.hero-stat-num {
font-family: 'Playfair Display', serif;
font-size: 1.8rem; font-weight: 700; color: var(--bark-dk);
}
.hero-stat-lbl { font-size: .8rem; color: var(--muted); font-weight: 500; }
/* ─────────────────────────────────────────
SECTION SHARED
───────────────────────────────────────── */
.section-header {
text-align: center; margin-bottom: 56px;
}
.section-tag {
display: inline-block;
font-size: .78rem; font-weight: 600; letter-spacing: .1em;
text-transform: uppercase; color: var(--leaf);
margin-bottom: 12px;
}
.section-header h2 {
font-family: 'Playfair Display', serif;
font-size: clamp(1.9rem, 3.5vw, 2.8rem);
color: var(--bark-dk); line-height: 1.2;
margin-bottom: 14px;
}
.section-header p {
color: var(--muted); font-size: 1rem; max-width: 500px; margin: 0 auto; line-height: 1.7;
}
/* ─────────────────────────────────────────
SHOP SECTION
───────────────────────────────────────── */
#shop {
padding: 100px 5vw;
background: var(--white);
}
.category-tabs {
display: flex; justify-content: center; gap: 10px;
margin-bottom: 48px; flex-wrap: wrap;
}
.tab-btn {
padding: 10px 24px; border-radius: 50px;
font-size: .9rem; font-weight: 600;
border: 2px solid var(--sand);
color: var(--muted); background: transparent;
transition: all var(--transition);
}
.tab-btn.active, .tab-btn:hover {
background: var(--bark-dk); color: var(--white); border-color: var(--bark-dk);
}
.category-section { margin-bottom: 64px; }
.category-section.hidden { display: none; }
.category-title {
font-family: 'Playfair Display', serif;
font-size: 1.5rem; color: var(--bark-dk);
margin-bottom: 28px; padding-bottom: 14px;
border-bottom: 2px solid var(--warm);
display: flex; align-items: center; gap: 10px;
}
.pets-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
gap: 24px;
}
.pet-card {
background: var(--cream);
border-radius: var(--r);
overflow: hidden;
box-shadow: var(--shadow);
transition: transform var(--transition), box-shadow var(--transition);
cursor: pointer;
}
.pet-card:hover {
transform: translateY(-6px);
box-shadow: var(--shadow-lg);
}
.pet-card-img {
aspect-ratio: 4/3;
overflow: hidden;
}
.pet-card-img img {
width: 100%; height: 100%; object-fit: cover;
transition: transform .6s ease;
}
.pet-card:hover .pet-card-img img { transform: scale(1.06); }
.pet-card-body { padding: 18px; }
.pet-card-name {
font-family: 'Playfair Display', serif;
font-size: 1.15rem; color: var(--bark-dk); margin-bottom: 4px;
}
.pet-card-color {
font-size: .82rem; color: var(--muted); margin-bottom: 12px;
display: flex; align-items: center; gap: 6px;
}
.color-dot {
width: 10px; height: 10px; border-radius: 50%;
border: 1px solid rgba(0,0,0,.1);
}
.pet-card-footer {
display: flex; align-items: center; justify-content: space-between;
}
.pet-price {
font-size: 1.1rem; font-weight: 700; color: var(--bark-dk);
}
.btn-view {
background: var(--bark-dk); color: var(--white);
padding: 8px 16px; border-radius: var(--r-sm);
font-size: .82rem; font-weight: 600;
transition: background var(--transition);
}
.btn-view:hover { background: var(--bark); }
/* ─────────────────────────────────────────
PET DETAIL MODAL
───────────────────────────────────────── */
.overlay {
position: fixed; inset: 0; z-index: 1000;
background: rgba(28,22,18,.55);
backdrop-filter: blur(4px);
display: flex; align-items: center; justify-content: center;
padding: 20px;
opacity: 0; pointer-events: none;
transition: opacity var(--transition);
}
.overlay.open { opacity: 1; pointer-events: all; }
.modal {
background: var(--white); border-radius: 24px;
width: 100%; max-width: 780px; max-height: 90vh;
overflow-y: auto;
transform: scale(.92) translateY(20px);
transition: transform var(--transition);
box-shadow: var(--shadow-lg);
position: relative;
}
.overlay.open .modal { transform: scale(1) translateY(0); }
.modal-close {
position: absolute; top: 16px; right: 16px; z-index: 10;
width: 36px; height: 36px; border-radius: 50%;
background: var(--warm); color: var(--bark-dk);
font-size: 1.1rem; display: flex; align-items: center; justify-content: center;
transition: background var(--transition);
}
.modal-close:hover { background: var(--sand); }
.modal-inner { display: grid; grid-template-columns: 1fr 1fr; }
.modal-img { aspect-ratio: 1; overflow: hidden; border-radius: 24px 0 0 24px; }
.modal-img img { width: 100%; height: 100%; object-fit: cover; }
.modal-info { padding: 36px 28px; display: flex; flex-direction: column; gap: 16px; }
.modal-category { font-size: .78rem; text-transform: uppercase; letter-spacing: .1em; color: var(--leaf); font-weight: 600; }
.modal-name {
font-family: 'Playfair Display', serif;
font-size: 1.9rem; color: var(--bark-dk); line-height: 1.1;
}
.modal-desc { font-size: .92rem; color: var(--muted); line-height: 1.7; }
.modal-label { font-size: .8rem; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin-bottom: 8px; }
.color-chips { display: flex; gap: 10px; flex-wrap: wrap; }
.color-chip {
padding: 6px 14px; border-radius: 50px;
border: 2px solid var(--sand); font-size: .83rem; font-weight: 500; color: var(--muted);
transition: all var(--transition); cursor: pointer;
}
.color-chip.selected, .color-chip:hover {
border-color: var(--bark-dk); color: var(--bark-dk); background: var(--warm);
}
.modal-price {
font-family: 'Playfair Display', serif;
font-size: 2rem; color: var(--bark-dk); font-weight: 700;
}
.btn-add-cart {
background: var(--bark-dk); color: var(--white);
padding: 14px 28px; border-radius: 50px;
font-size: .95rem; font-weight: 600;
transition: all var(--transition);
display: flex; align-items: center; gap: 8px; justify-content: center;
box-shadow: 0 4px 16px rgba(92,74,42,.25);
}
.btn-add-cart:hover { background: var(--bark); transform: translateY(-2px); }
/* ─────────────────────────────────────────
CART SIDEBAR
───────────────────────────────────────── */
.cart-overlay {
position: fixed; inset: 0; z-index: 1100;
background: rgba(28,22,18,.4);
backdrop-filter: blur(3px);
opacity: 0; pointer-events: none;
transition: opacity var(--transition);
}
.cart-overlay.open { opacity: 1; pointer-events: all; }
.cart-sidebar {
position: fixed; top: 0; right: 0; bottom: 0; z-index: 1101;
width: min(420px, 100vw);
background: var(--white);
box-shadow: -8px 0 40px rgba(0,0,0,.12);
transform: translateX(100%);
transition: transform var(--transition);
display: flex; flex-direction: column;
}
.cart-overlay.open .cart-sidebar { transform: translateX(0); }
.cart-head {
padding: 24px; border-bottom: 1px solid var(--warm);
display: flex; align-items: center; justify-content: space-between;
}
.cart-head h3 {
font-family: 'Playfair Display', serif;
font-size: 1.4rem; color: var(--bark-dk);
}
.cart-close {
width: 36px; height: 36px; border-radius: 50%; background: var(--warm);
display: flex; align-items: center; justify-content: center;
font-size: 1.1rem; color: var(--bark-dk);
transition: background var(--transition);
}
.cart-close:hover { background: var(--sand); }
.cart-items { flex: 1; overflow-y: auto; padding: 20px 24px; display: flex; flex-direction: column; gap: 16px; }
.cart-item {
display: flex; gap: 14px; align-items: center;
background: var(--cream); padding: 14px; border-radius: var(--r-sm);
}
.cart-item-img {
width: 64px; height: 64px; border-radius: 10px; overflow: hidden; flex-shrink: 0;
}
.cart-item-img img { width: 100%; height: 100%; object-fit: cover; }
.cart-item-info { flex: 1; }
.cart-item-name { font-weight: 600; font-size: .92rem; color: var(--bark-dk); }
.cart-item-price { font-size: .85rem; color: var(--muted); margin-top: 2px; }
.qty-ctrl { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.qty-btn {
width: 26px; height: 26px; border-radius: 50%;
background: var(--sand); color: var(--bark-dk);
font-size: 1rem; display: flex; align-items: center; justify-content: center;
transition: background var(--transition);
}
.qty-btn:hover { background: var(--bark-dk); color: var(--white); }
.qty-num { font-size: .9rem; font-weight: 600; min-width: 16px; text-align: center; color: var(--text); }
.cart-item-remove {
color: var(--muted); font-size: .8rem; padding: 4px;
transition: color var(--transition);
}
.cart-item-remove:hover { color: #c0392b; }
.cart-empty {
text-align: center; padding: 60px 20px;
color: var(--muted); font-size: .95rem;
}
.cart-empty-icon { font-size: 3rem; margin-bottom: 12px; }
.cart-foot {
padding: 24px; border-top: 1px solid var(--warm);
}
.cart-total {
display: flex; justify-content: space-between; margin-bottom: 16px;
font-size: 1rem; font-weight: 600; color: var(--bark-dk);
}
.cart-total-price { font-family: 'Playfair Display', serif; font-size: 1.3rem; }
.btn-checkout {
width: 100%; background: var(--bark-dk); color: var(--white);
padding: 16px; border-radius: 50px;
font-size: .95rem; font-weight: 700;
transition: background var(--transition), transform var(--transition);
box-shadow: 0 4px 18px rgba(92,74,42,.28);
}
.btn-checkout:hover { background: var(--bark); transform: translateY(-1px); }
/* ─────────────────────────────────────────
CHECKOUT MODAL
───────────────────────────────────────── */
#checkoutOverlay .modal { max-width: 560px; }
.checkout-body { padding: 36px 32px; }
.checkout-body h2 {
font-family: 'Playfair Display', serif;
font-size: 1.7rem; color: var(--bark-dk); margin-bottom: 6px;
}
.checkout-sub { color: var(--muted); font-size: .9rem; margin-bottom: 28px; }
.order-summary-items { margin-bottom: 20px; }
.order-row {
display: flex; justify-content: space-between; align-items: center;
padding: 10px 0; border-bottom: 1px dashed var(--sand);
font-size: .9rem; color: var(--text);
}
.order-row:last-child { border-bottom: none; }
.order-row span:first-child { color: var(--muted); }
.order-total-row {
display: flex; justify-content: space-between; align-items: center;
padding: 14px 0; border-top: 2px solid var(--bark-dk);
font-weight: 700;
}
.order-total-row .amount {
font-family: 'Playfair Display', serif;
font-size: 1.3rem; color: var(--bark-dk);
}
.payment-options { margin: 24px 0; }
.payment-label { font-size: .8rem; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); margin-bottom: 12px; }
.pay-methods { display: flex; gap: 10px; }
.pay-method {
flex: 1; padding: 14px 10px; border-radius: var(--r-sm);
border: 2px solid var(--sand); text-align: center;
font-size: .85rem; font-weight: 600; color: var(--muted);
transition: all var(--transition); cursor: pointer;
}
.pay-method .pay-icon { font-size: 1.4rem; display: block; margin-bottom: 4px; }
.pay-method.selected, .pay-method:hover {
border-color: var(--bark-dk); color: var(--bark-dk); background: var(--warm);
}
.btn-confirm {
width: 100%; background: var(--leaf); color: var(--white);
padding: 16px; border-radius: 50px;
font-size: .95rem; font-weight: 700;
transition: all var(--transition);
box-shadow: 0 4px 18px rgba(74,124,89,.28);
margin-top: 8px;
}
.btn-confirm:hover { background: #3a6247; transform: translateY(-1px); }
/* ─────────────────────────────────────────
SUCCESS MODAL
───────────────────────────────────────── */
#successOverlay .modal { max-width: 480px; }
.success-body {
padding: 48px 36px; text-align: center;
}
.success-icon {
font-size: 4rem; margin-bottom: 16px;
animation: successBounce .6s cubic-bezier(.36,.07,.19,.97) both;
}
@keyframes successBounce {
0% { transform: scale(0) rotate(-10deg); }
70% { transform: scale(1.15) rotate(4deg); }
100%{ transform: scale(1) rotate(0); }
}
.success-body h2 {
font-family: 'Playfair Display', serif;
font-size: 1.9rem; color: var(--bark-dk); margin-bottom: 10px;
}
.success-body p { color: var(--muted); font-size: .95rem; line-height: 1.7; margin-bottom: 28px; }
.tracking-code-box {
background: var(--warm); border: 2px dashed var(--sand);
border-radius: var(--r); padding: 20px;
margin-bottom: 24px;
}
.tracking-code-label { font-size: .78rem; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin-bottom: 8px; }
.tracking-code-value {
font-family: monospace; font-size: 1.3rem; font-weight: 700;
color: var(--bark-dk); letter-spacing: 2px;
}
.btn-track { margin-top: 4px; }
/* ─────────────────────────────────────────
TRACKING SECTION
───────────────────────────────────────── */
#tracking {
padding: 100px 5vw;
background: var(--warm);
}
.tracking-card {
background: var(--white); border-radius: 24px;
padding: 48px 40px; max-width: 640px; margin: 0 auto;
box-shadow: var(--shadow);
}
.tracking-input-row {
display: flex; gap: 12px; margin-top: 28px;
}
.tracking-input {
flex: 1; padding: 14px 18px; border-radius: 50px;
border: 2px solid var(--sand); background: var(--cream);
font-family: monospace; font-size: .95rem; color: var(--text);
outline: none; transition: border-color var(--transition);
}
.tracking-input:focus { border-color: var(--bark-dk); }
.btn-track-submit {
background: var(--bark-dk); color: var(--white);
padding: 14px 28px; border-radius: 50px;
font-size: .9rem; font-weight: 700;
transition: all var(--transition);
}
.btn-track-submit:hover { background: var(--bark); }
.status-stepper { margin-top: 36px; display: none; }
.status-stepper.visible { display: block; }
.step {
display: flex; gap: 16px; align-items: flex-start; padding: 14px 0;
position: relative;
}
.step:not(:last-child)::after {
content: ''; position: absolute;
left: 15px; top: 44px; bottom: -14px; width: 2px;
background: var(--sand);
}
.step.done::after { background: var(--leaf); }
.step-dot {
width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0;
border: 2px solid var(--sand); background: var(--white);
display: flex; align-items: center; justify-content: center;
font-size: .9rem;
transition: all var(--transition);
}
.step.done .step-dot { background: var(--leaf); border-color: var(--leaf); }
.step.active .step-dot {
background: var(--bark-dk); border-color: var(--bark-dk);
animation: pulse 1.8s ease-in-out infinite;
}
@keyframes pulse {
0%,100% { box-shadow: 0 0 0 0 rgba(139,111,71,.4); }
50% { box-shadow: 0 0 0 8px rgba(139,111,71,0); }
}
.step-label { font-weight: 600; color: var(--bark-dk); }
.step-sub { font-size: .82rem; color: var(--muted); margin-top: 2px; }
/* ─────────────────────────────────────────
STORE VISIT SECTION
───────────────────────────────────────── */
#visit {
padding: 100px 5vw;
background: var(--bark-dk);
text-align: center;
}
#visit .section-tag { color: var(--blush); }
#visit h2 {
font-family: 'Playfair Display', serif;
font-size: clamp(2rem, 4vw, 3rem); color: var(--white);
margin-bottom: 16px;
}
#visit p { color: rgba(255,255,255,.7); font-size: 1rem; max-width: 480px; margin: 0 auto 36px; line-height: 1.7; }
.btn-visit {
background: var(--white); color: var(--bark-dk);
padding: 16px 40px; border-radius: 50px;
font-size: .95rem; font-weight: 700;
transition: all var(--transition);
display: inline-flex; align-items: center; gap: 8px;
}
.btn-visit:hover { background: var(--cream); transform: translateY(-2px); }
/* ─────────────────────────────────────────
STORE MODAL
───────────────────────────────────────── */
#storeOverlay .modal { max-width: 500px; }
.store-body { padding: 40px 36px; }
.store-body h2 {
font-family: 'Playfair Display', serif;
font-size: 1.7rem; color: var(--bark-dk); margin-bottom: 24px;
}
.store-info-rows { display: flex; flex-direction: column; gap: 18px; }
.store-info-row { display: flex; gap: 14px; align-items: flex-start; }
.store-icon {
width: 40px; height: 40px; border-radius: 10px;
background: var(--warm); display: flex; align-items: center; justify-content: center;
font-size: 1.1rem; flex-shrink: 0;
}
.store-info-label { font-size: .78rem; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); margin-bottom: 3px; }
.store-info-val { font-size: .95rem; color: var(--text); line-height: 1.5; }
.store-map-placeholder {
margin-top: 24px; border-radius: var(--r);
overflow: hidden; background: var(--warm);
height: 160px; display: flex; align-items: center; justify-content: center;
color: var(--muted); font-size: .9rem; border: 2px dashed var(--sand);
gap: 8px;
}
/* ─────────────────────────────────────────
FOOTER
───────────────────────────────────────── */
footer {
background: #1C1612; color: rgba(255,255,255,.5);
padding: 40px 5vw; text-align: center;
font-size: .85rem;
}
footer strong { color: rgba(255,255,255,.9); }
/* ─────────────────────────────────────────
CONFETTI CANVAS
───────────────────────────────────────── */
#confettiCanvas {
position: fixed; inset: 0; z-index: 9999;
pointer-events: none;
}
/* ─────────────────────────────────────────
TOAST
───────────────────────────────────────── */
.toast-wrap {
position: fixed; bottom: 32px; left: 50%; transform: translateX(-50%);
z-index: 2000; display: flex; flex-direction: column; align-items: center; gap: 10px;
}
.toast {
background: var(--bark-dk); color: var(--white);
padding: 12px 24px; border-radius: 50px;
font-size: .88rem; font-weight: 600;
box-shadow: var(--shadow-lg);
opacity: 0; transform: translateY(12px);
transition: all .3s;
pointer-events: none;
white-space: nowrap;
}
.toast.show { opacity: 1; transform: translateY(0); }
/* ─────────────────────────────────────────
RESPONSIVE
───────────────────────────────────────── */
@media (max-width: 900px) {
.hero { padding-top: 90px; padding-bottom: 60px; }
.hero-visual { display: none; }
.hero-content { max-width: 100%; }
.modal-inner { grid-template-columns: 1fr; }
.modal-img { border-radius: 24px 24px 0 0; aspect-ratio: 16/9; }
.modal-img img { height: 100%; }
}
@media (max-width: 640px) {
.nav-links { display: none; }
.nav-hamburger { display: flex; }
.pets-grid { grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 16px; }
.pay-methods { flex-direction: column; }
.tracking-card { padding: 32px 20px; }
.tracking-input-row { flex-direction: column; }
.checkout-body { padding: 28px 20px; }
.store-body { padding: 28px 20px; }
}
/* animations on scroll (class toggled by JS) */
.fade-up {
opacity: 0; transform: translateY(30px);
transition: opacity .7s ease, transform .7s ease;
}
.fade-up.visible { opacity: 1; transform: translateY(0); }
</style>
</head>
<body>
<!-- ═══════════════════════════
NAVIGATION
═══════════════════════════ -->
<nav class="nav" id="mainNav">
<div class="nav-logo">KayPaw<span>House</span></div>
<ul class="nav-links">
<li><a href="#shop">Shop</a></li>
<li><a href="#tracking">Track Order</a></li>
<li><a href="#visit">Visit Us</a></li>
</ul>
<div class="nav-actions">
<button class="cart-btn" id="cartBtn" aria-label="Open cart">
🛒 Cart
<span class="cart-badge" id="cartBadge">0</span>
</button>
</div>
<button class="nav-hamburger" id="navHamburger" aria-label="Menu">
<span></span><span></span><span></span>
</button>
</nav>
<!-- ═══════════════════════════
HERO
═══════════════════════════ -->
<section class="hero" id="home">
<div class="hero-content">
<div class="hero-eyebrow">Premium Pet Companions</div>
<h1>Find Your <em>Perfect</em><br>Furry Friend</h1>
<p class="hero-desc">Ethically sourced, lovingly raised pets waiting for their forever homes. Cats, dogs, monkeys — each one unique, all of them wonderful.</p>
<div class="hero-cta">
<a href="#shop" class="btn-primary">Shop Pets</a>
<a href="#tracking" class="btn-outline">Track Order</a>
</div>
<div class="hero-stats">
<div>
<div class="hero-stat-num">200+</div>
<div class="hero-stat-lbl">Happy Owners</div>
</div>
<div>
<div class="hero-stat-num">3</div>
<div class="hero-stat-lbl">Pet Categories</div>
</div>
<div>
<div class="hero-stat-num">100%</div>
<div class="hero-stat-lbl">Ethical Sourcing</div>
</div>
</div>
</div>
<div class="hero-visual">
<div class="hero-visual-inner">
<img src="https://images.unsplash.com/photo-1587300003388-59208cc962cb?w=600&q=80" alt="Happy golden retriever" />
</div>
</div>
</section>
<!-- ═══════════════════════════
SHOP
═══════════════════════════ -->
<section id="shop">
<div class="section-header fade-up">
<div class="section-tag">Our Collection</div>
<h2>Meet Your New Best Friend</h2>
<p>Browse our curated selection of healthy, well-socialized pets ready for adoption.</p>
</div>
<div class="category-tabs">
<button class="tab-btn active" data-cat="all">All Pets</button>
<button class="tab-btn" data-cat="cats">🐱 Cats</button>
<button class="tab-btn" data-cat="dogs">🐶 Dogs</button>
<button class="tab-btn" data-cat="monkeys">🐒 Monkeys</button>
</div>
<!-- CATS -->
<div class="category-section fade-up" id="cat-section">
<div class="category-title">🐱 Cats</div>
<div class="pets-grid" id="cats-grid"></div>
</div>
<!-- DOGS -->
<div class="category-section fade-up" id="dog-section">
<div class="category-title">🐶 Dogs</div>
<div class="pets-grid" id="dogs-grid"></div>
</div>
<!-- MONKEYS -->
<div class="category-section fade-up" id="monkey-section">
<div class="category-title">🐒 Monkeys</div>
<div class="pets-grid" id="monkeys-grid"></div>
</div>
</section>
<!-- ═══════════════════════════
TRACKING SECTION
═══════════════════════════ -->
<section id="tracking">
<div class="section-header fade-up">
<div class="section-tag">Order Tracking</div>
<h2>Where Is My Pet?</h2>
<p>Enter your tracking code to see your order status in real time.</p>
</div>
<div class="tracking-card fade-up">
<div class="tracking-input-row">
<input class="tracking-input" id="trackingInput" type="text" placeholder="e.g. PAW-A1B2C3D4" />
<button class="btn-track-submit" id="trackBtn">Track Now</button>
</div>
<div class="status-stepper" id="statusStepper">
<div class="step done" id="step1">
<div class="step-dot">✓</div>
<div>
<div class="step-label">Order Placed</div>
<div class="step-sub">We've received your order</div>
</div>
</div>
<div class="step done" id="step2">
<div class="step-dot">✓</div>
<div>
<div class="step-label">Processing</div>
<div class="step-sub">Your pet is being prepared for travel</div>
</div>
</div>
<div class="step active" id="step3">
<div class="step-dot">🚚</div>
<div>
<div class="step-label">Shipped</div>
<div class="step-sub">On the way to your location</div>
</div>
</div>
<div class="step" id="step4">
<div class="step-dot">🏠</div>
<div>
<div class="step-label">Out for Delivery</div>
<div class="step-sub">Arriving at your doorstep today!</div>
</div>
</div>
</div>
</div>
</section>
<!-- ═══════════════════════════
STORE VISIT SECTION
═══════════════════════════ -->
<section id="visit">
<div class="section-tag">Physical Store</div>
<h2>Prefer to Visit Us In Person?</h2>
<p>Come meet our pets face to face. Our friendly staff will help you find the perfect companion.</p>
<button class="btn-visit" id="visitBtn">📍 Visit Store</button>
</section>
<!-- ═══════════════════════════
FOOTER
═══════════════════════════ -->
<footer>
<p>© 2026 <strong>PawHouse</strong> — Premium Pet Store. All rights reserved. | Made with 🐾 and love.</p>
</footer>
<!-- ═══════════════════════════
PET DETAIL MODAL
═══════════════════════════ -->
<div class="overlay" id="petOverlay" role="dialog" aria-modal="true">
<div class="modal" id="petModal">
<button class="modal-close" id="petModalClose" aria-label="Close">✕</button>
<div class="modal-inner">
<div class="modal-img">
<img id="modalImg" src="https://images.unsplash.com/photo-1514888286974-6c03e2ca1dba?w=600&q=80" alt="Pet image" />
</div>
<div class="modal-info">
<div class="modal-category" id="modalCategory"></div>
<div class="modal-name" id="modalName"></div>
<p class="modal-desc" id="modalDesc"></p>
<div>
<div class="modal-label">Color Options</div>
<div class="color-chips" id="modalColors"></div>
</div>
<div class="modal-price" id="modalPrice"></div>
<button class="btn-add-cart" id="modalAddCart">🛒 Add to Cart</button>
</div>
</div>
</div>
</div>
<!-- ═══════════════════════════
CART SIDEBAR
═══════════════════════════ -->
<div class="cart-overlay" id="cartOverlay">
<div class="cart-sidebar" id="cartSidebar">
<div class="cart-head">
<h3>Your Cart</h3>
<button class="cart-close" id="cartClose">✕</button>
</div>
<div class="cart-items" id="cartItemsList"></div>
<div class="cart-foot" id="cartFoot" style="display:none;">
<div class="cart-total">
<span>Total</span>
<span class="cart-total-price" id="cartTotalPrice">$0</span>
</div>
<button class="btn-checkout" id="checkoutBtn">Checkout →</button>
</div>
</div>
</div>
<!-- ═══════════════════════════
CHECKOUT MODAL
═══════════════════════════ -->
<div class="overlay" id="checkoutOverlay" role="dialog" aria-modal="true">
<div class="modal">
<button class="modal-close" id="checkoutClose">✕</button>
<div class="checkout-body">
<h2>Checkout</h2>
<p class="checkout-sub">Review your order and choose a payment method</p>
<div class="order-summary-items" id="checkoutSummaryItems"></div>
<div class="order-total-row">
<span>Total</span>
<span class="amount" id="checkoutTotal">$0</span>
</div>
<div class="payment-options">
<div class="payment-label">Payment Method</div>
<div class="pay-methods" id="payMethods">
<div class="pay-method selected" data-method="card">
<span class="pay-icon">💳</span>Credit Card
</div>
<div class="pay-method" data-method="apple">
<span class="pay-icon"> </span>Apple Pay
</div>
<div class="pay-method" data-method="paypal">
<span class="pay-icon">🅿️</span>PayPal
</div>
</div>
</div>
<button class="btn-confirm" id="confirmPayBtn">Confirm Payment 🎉</button>
</div>
</div>
</div>
<!-- ═══════════════════════════
SUCCESS MODAL
═══════════════════════════ -->
<div class="overlay" id="successOverlay" role="dialog" aria-modal="true">
<div class="modal">
<div class="success-body">
<div class="success-icon">🎉</div>
<h2>Order Successful!</h2>
<p>Thank you for choosing PawHouse. Your new companion is being prepared with love and care.</p>
<div class="tracking-code-box">
<div class="tracking-code-label">Your Tracking Code</div>
<div class="tracking-code-value" id="generatedCode">PAW-XXXXXXXX</div>
</div>
<p style="font-size:.85rem; color:var(--muted);">Save this code — use it in the <strong>Track Order</strong> section to follow your delivery.</p>
<br/>
<button class="btn-primary btn-track" id="goTrackBtn">Track My Order</button>
</div>
</div>
</div>
<!-- ═══════════════════════════
STORE MODAL
═══════════════════════════ -->
<div class="overlay" id="storeOverlay" role="dialog" aria-modal="true">
<div class="modal">
<button class="modal-close" id="storeModalClose">✕</button>
<div class="store-body">
<h2>Visit PawHouse</h2>
<div class="store-info-rows">
<div class="store-info-row">
<div class="store-icon">📍</div>
<div>
<div class="store-info-label">Address</div>
<div class="store-info-val">14 Harmony Lane, Lekki Phase 1<br/>Lagos, Nigeria</div>
</div>
</div>
<div class="store-info-row">
<div class="store-icon">🕐</div>
<div>
<div class="store-info-label">Opening Hours</div>
<div class="store-info-val">Mon – Sat: 9:00am – 7:00pm<br/>Sunday: 11:00am – 5:00pm</div>
</div>
</div>
<div class="store-info-row">
<div class="store-icon">📞</div>
<div>
<div class="store-info-label">Phone</div>
<div class="store-info-val">+234 801 234 5678</div>
</div>
</div>
<div class="store-info-row">
<div class="store-icon">✉️</div>
<div>
<div class="store-info-label">Email</div>
<div class="store-info-val">hello@pawhouse.ng</div>
</div>
</div>
</div>
<div class="store-map-placeholder">
🗺️ Interactive map coming soon
</div>
</div>
</div>
</div>
<!-- ═══════════════════════════
CONFETTI CANVAS
═══════════════════════════ -->
<canvas id="confettiCanvas"></canvas>
<!-- ═══════════════════════════
TOAST WRAPPER
═══════════════════════════ -->
<div class="toast-wrap" id="toastWrap"></div>
<!-- ═══════════════════════════
JAVASCRIPT
═══════════════════════════ -->
<script>
/* ─────────────────────────────────────────
DATA — Pet catalogue
All images are stable Unsplash photo IDs
───────────────────────────────────────── */
const PETS = {
cats: [
{
id: 'c1', name: 'Luna', category: 'Cat',
colors: ['White', 'Grey', 'Mixed'],
price: 280,
image: 'https://images.unsplash.com/photo-1514888286974-6c03e2ca1dba?w=600&q=80',
desc: 'Luna is a gentle, curious tabby who loves warm laps and morning sunbeams. She gets along with everyone and purrs like a little engine.'
},
{
id: 'c2', name: 'Oliver', category: 'Cat',
colors: ['Orange', 'Mixed'],
price: 320,
image: 'https://images.unsplash.com/photo-1478098711619-5ab0b478d6e6?w=600&q=80',
desc: 'Oliver is a playful ginger tom with an adventurous spirit. He loves toy mice, high shelves, and long afternoon naps in the sun.'
},
{
id: 'c3', name: 'Nala', category: 'Cat',
colors: ['Black', 'White'],
price: 260,
image: 'https://images.unsplash.com/photo-1548802673-380ab8ebc7b7?w=600&q=80',
desc: 'Nala is a sophisticated tuxedo cat with soulful eyes. She is calm, affectionate, and absolutely loves being brushed.'
},
{
id: 'c4', name: 'Simba', category: 'Cat',
colors: ['Brown', 'Golden'],
price: 350,
image: 'https://images.unsplash.com/photo-1561948955-570b270e7c36?w=600&q=80',
desc: 'Simba lives up to his name — regal, curious, and brave. A Maine Coon mix with a gorgeous mane and a playful heart.'
},
],
dogs: [
{
id: 'd1', name: 'Buddy', category: 'Dog',
colors: ['Golden', 'Cream'],
price: 480,
image: 'https://images.unsplash.com/photo-1587300003388-59208cc962cb?w=600&q=80',
desc: 'Buddy is the ultimate family dog — loyal, gentle, and endlessly enthusiastic. This golden retriever mix is fully vaccinated and house-trained.'
},
{
id: 'd2', name: 'Max', category: 'Dog',
colors: ['Black', 'Brown', 'Mixed'],
price: 520,
image: 'https://images.unsplash.com/photo-1534361960057-19f4434a4f10?w=600&q=80',
desc: 'Max is a handsome German Shepherd with a calm, protective temperament. Excellent with children and highly intelligent.'
},
{
id: 'd3', name: 'Bella', category: 'Dog',
colors: ['White', 'Cream'],
price: 440,
image: 'https://images.unsplash.com/photo-1558788353-f76d92427f16?w=600&q=80',
desc: 'Bella is a fluffy Samoyed who loves the cold and cuddles in equal measure. Her smile is contagious and her energy is boundless.'
},
{
id: 'd4', name: 'Rocky', category: 'Dog',
colors: ['Brown', 'Tan'],
price: 460,
image: 'https://images.unsplash.com/photo-1517849845537-4d257902454a?w=600&q=80',
desc: 'Rocky is a cheerful Beagle with an excellent nose and an even better personality. Perfect for active families who love outdoor adventures.'
},
],
monkeys: [
{
id: 'm1', name: 'Koko', category: 'Monkey',
colors: ['Brown', 'Tan'],
price: 1200,
image: 'https://images.unsplash.com/photo-1540573133985-87b6da6d54a9?w=600&q=80',
desc: 'Koko is a sociable capuchin monkey with remarkable intelligence. She loves puzzles, fruit, and interacting with people she trusts.'
},
{
id: 'm2', name: 'Zara', category: 'Monkey',
colors: ['Black', 'Brown'],
price: 1450,
image: 'https://images.unsplash.com/photo-1497206365907-f5e630693df0?w=600&q=80',
desc: 'Zara is a curious spider monkey with acrobatic talents. She thrives in enriched environments and bonds deeply with her owner.'
},
{
id: 'm3', name: 'Milo', category: 'Monkey',
colors: ['Golden', 'Brown'],
price: 980,
image: 'https://images.unsplash.com/photo-1425082661705-1834bfd09dca?w=600&q=80',
desc: 'Milo is a golden tamarin with a warm personality. Small, gentle, and absolutely captivating — perfect for experienced primate owners.'
},
]
};
/* ─────────────────────────────────────────
COLOUR MAP — used for dots on cards
───────────────────────────────────────── */
const COLOR_HEX = {
White: '#f5f5f5', Grey: '#9e9e9e', Mixed: 'linear-gradient(135deg,#f5f5f5,#8d6e63)',
Orange: '#FF7043', Black: '#212121', Brown: '#6D4C41', Golden: '#FFB300',
Cream: '#FFF9C4', Tan: '#D7CCC8', Blue: '#42A5F5', Red: '#EF5350'
};
function colorDot(color) {
const bg = COLOR_HEX[color] || '#ccc';
const style = bg.startsWith('linear') ? `background:${bg}` : `background:${bg}`;
return `<span class="color-dot" style="${style}" title="${color}"></span>`;
}
/* ─────────────────────────────────────────
RENDER PET CARDS
───────────────────────────────────────── */
function renderPets() {
['cats', 'dogs', 'monkeys'].forEach(cat => {
const grid = document.getElementById(`${cat}-grid`);
grid.innerHTML = PETS[cat].map(pet => `
<div class="pet-card" data-id="${pet.id}" data-cat="${cat}">
<div class="pet-card-img">
<img src="${pet.image}" alt="${pet.name}" loading="lazy" />
</div>
<div class="pet-card-body">
<div class="pet-card-name">${pet.name}</div>
<div class="pet-card-color">
${pet.colors.map(c => colorDot(c)).join('')}
${pet.colors.join(' / ')}
</div>
<div class="pet-card-footer">
<span class="pet-price">$${pet.price.toLocaleString()}</span>
<button class="btn-view">View Details</button>
</div>
</div>
</div>
`).join('');
});
// Attach click events
document.querySelectorAll('.pet-card').forEach(card => {
card.addEventListener('click', () => {
const cat = card.dataset.cat;
const pet = PETS[cat].find(p => p.id === card.dataset.id);
openPetModal(pet);
});
});
}
/* ─────────────────────────────────────────
CATEGORY TABS
───────────────────────────────────────── */
document.querySelectorAll('.tab-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
const cat = btn.dataset.cat;
['cat-section', 'dog-section', 'monkey-section'].forEach(id => {
const el = document.getElementById(id);
if (cat === 'all') { el.classList.remove('hidden'); return; }
const match = id.startsWith(cat.slice(0,-1)); // cats→cat, dogs→dog, monkeys→monkey
if (cat === 'cats' && id === 'cat-section') el.classList.remove('hidden');
else if (cat === 'dogs' && id === 'dog-section') el.classList.remove('hidden');
else if (cat === 'monkeys' && id === 'monkey-section') el.classList.remove('hidden');
else el.classList.add('hidden');
});
});
});
/* ─────────────────────────────────────────
PET DETAIL MODAL
───────────────────────────────────────── */
let currentPet = null;
function openPetModal(pet) {
currentPet = pet;
document.getElementById('modalImg').src = pet.image;
document.getElementById('modalImg').alt = pet.name;
document.getElementById('modalCategory').textContent = pet.category;
document.getElementById('modalName').textContent = pet.name;
document.getElementById('modalDesc').textContent = pet.desc;
document.getElementById('modalPrice').textContent = `$${pet.price.toLocaleString()}`;
// Render color chips
const chips = pet.colors.map(c =>
`<div class="color-chip" data-color="${c}">${c}</div>`
).join('');
const chipsEl = document.getElementById('modalColors');
chipsEl.innerHTML = chips;
chipsEl.querySelectorAll('.color-chip')[0]?.classList.add('selected');
chipsEl.querySelectorAll('.color-chip').forEach(chip => {
chip.addEventListener('click', () => {
chipsEl.querySelectorAll('.color-chip').forEach(c => c.classList.remove('selected'));
chip.classList.add('selected');
});
});
openOverlay('petOverlay');
}
function closeModal(overlayId) {
document.getElementById(overlayId).classList.remove('open');
}
function openOverlay(id) {
document.getElementById(id).classList.add('open');
}
document.getElementById('petModalClose').addEventListener('click', () => closeModal('petOverlay'));
document.getElementById('petOverlay').addEventListener('click', e => {
if (e.target.id === 'petOverlay') closeModal('petOverlay');
});
/* ─────────────────────────────────────────
CART STATE
───────────────────────────────────────── */
let cart = [];
function cartTotal() {
return cart.reduce((sum, item) => sum + item.price * item.qty, 0);
}
function cartCount() {
return cart.reduce((sum, item) => sum + item.qty, 0);
}
function updateCartBadge() {
const badge = document.getElementById('cartBadge');
const count = cartCount();
badge.textContent = count;
badge.classList.toggle('active', count > 0);
}
function renderCart() {
const list = document.getElementById('cartItemsList');
const foot = document.getElementById('cartFoot');
if (cart.length === 0) {
list.innerHTML = `
<div class="cart-empty">
<div class="cart-empty-icon">🐾</div>
<p>Your cart is empty.<br/>Add a pet to get started!</p>
</div>`;
foot.style.display = 'none';
return;
}
foot.style.display = 'block';
list.innerHTML = cart.map(item => `
<div class="cart-item" data-id="${item.id}">
<div class="cart-item-img">
<img src="${item.image}" alt="${item.name}" />
</div>
<div class="cart-item-info">
<div class="cart-item-name">${item.name}</div>
<div class="cart-item-price">$${item.price.toLocaleString()} each</div>
<div class="qty-ctrl">
<button class="qty-btn" data-action="dec" data-id="${item.id}">−</button>
<span class="qty-num">${item.qty}</span>
<button class="qty-btn" data-action="inc" data-id="${item.id}">+</button>
</div>
</div>
<button class="cart-item-remove" data-id="${item.id}" title="Remove">🗑</button>
</div>
`).join('');
document.getElementById('cartTotalPrice').textContent = `$${cartTotal().toLocaleString()}`;
// Qty buttons
list.querySelectorAll('.qty-btn').forEach(btn => {
btn.addEventListener('click', () => {
const id = btn.dataset.id;
const action = btn.dataset.action;
const item = cart.find(i => i.id === id);
if (!item) return;
if (action === 'inc') item.qty++;
if (action === 'dec') { item.qty--; if (item.qty < 1) cart = cart.filter(i => i.id !== id); }
updateCartBadge(); renderCart();
});
});
// Remove buttons
list.querySelectorAll('.cart-item-remove').forEach(btn => {
btn.addEventListener('click', () => {
cart = cart.filter(i => i.id !== btn.dataset.id);
updateCartBadge(); renderCart();
});
});
}
/* ─────────────────────────────────────────
ADD TO CART
───────────────────────────────────────── */
document.getElementById('modalAddCart').addEventListener('click', () => {
if (!currentPet) return;
const existing = cart.find(i => i.id === currentPet.id);
if (existing) {
existing.qty++;
showToast(`Added another ${currentPet.name} 🐾`);
} else {
cart.push({ ...currentPet, qty: 1 });
showToast(`${currentPet.name} added to cart! 🛒`);
}
updateCartBadge();
closeModal('petOverlay');
renderCart();
});
/* ─────────────────────────────────────────
CART SIDEBAR OPEN / CLOSE
───────────────────────────────────────── */
document.getElementById('cartBtn').addEventListener('click', () => {
renderCart();
openOverlay('cartOverlay');
});
document.getElementById('cartClose').addEventListener('click', () => closeModal('cartOverlay'));
document.getElementById('cartOverlay').addEventListener('click', e => {
if (e.target.id === 'cartOverlay') closeModal('cartOverlay');
});
/* ─────────────────────────────────────────
CHECKOUT
───────────────────────────────────────── */
document.getElementById('checkoutBtn').addEventListener('click', () => {
closeModal('cartOverlay');
// Render order summary
const summaryEl = document.getElementById('checkoutSummaryItems');
summaryEl.innerHTML = cart.map(item => `
<div class="order-row">
<span>${item.name} × ${item.qty}</span>
<span>$${(item.price * item.qty).toLocaleString()}</span>
</div>
`).join('');
document.getElementById('checkoutTotal').textContent = `$${cartTotal().toLocaleString()}`;
openOverlay('checkoutOverlay');
});
document.getElementById('checkoutClose').addEventListener('click', () => closeModal('checkoutOverlay'));
document.getElementById('checkoutOverlay').addEventListener('click', e => {
if (e.target.id === 'checkoutOverlay') closeModal('checkoutOverlay');
});
// Payment method selection
document.querySelectorAll('.pay-method').forEach(m => {
m.addEventListener('click', () => {
document.querySelectorAll('.pay-method').forEach(x => x.classList.remove('selected'));
m.classList.add('selected');
});
});
/* ─────────────────────────────────────────
CONFIRM PAYMENT → SUCCESS
───────────────────────────────────────── */
function generateTrackingCode() {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let code = 'PAW-';
for (let i = 0; i < 8; i++) code += chars[Math.floor(Math.random() * chars.length)];
return code;
}
let lastTrackingCode = '';
document.getElementById('confirmPayBtn').addEventListener('click', () => {
closeModal('checkoutOverlay');
lastTrackingCode = generateTrackingCode();
document.getElementById('generatedCode').textContent = lastTrackingCode;
cart = [];
updateCartBadge();
renderCart();
openOverlay('successOverlay');
launchConfetti();
});
/* ─────────────────────────────────────────
SUCCESS MODAL CLOSE & GO TRACK
───────────────────────────────────────── */
document.getElementById('successOverlay').addEventListener('click', e => {
if (e.target.id === 'successOverlay') closeModal('successOverlay');
});
document.getElementById('goTrackBtn').addEventListener('click', () => {
closeModal('successOverlay');
document.getElementById('trackingInput').value = lastTrackingCode;
document.getElementById('tracking').scrollIntoView({ behavior: 'smooth' });
setTimeout(() => triggerTracking(lastTrackingCode), 700);
});
/* ─────────────────────────────────────────
ORDER TRACKING
───────────────────────────────────────── */
function triggerTracking(code) {
// Simulate random status based on code
const hash = [...code].reduce((a, c) => a + c.charCodeAt(0), 0);
const phase = hash % 3; // 0 = processing, 1 = shipped, 2 = out for delivery
const steps = document.querySelectorAll('.status-stepper .step');
steps.forEach((step, i) => {
step.classList.remove('done', 'active');
if (i < phase + 2) step.classList.add('done');
});
if (steps[phase + 1]) {
steps[phase + 1].classList.remove('done');
steps[phase + 1].classList.add('active');
}
document.getElementById('statusStepper').classList.add('visible');
document.getElementById('statusStepper').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
document.getElementById('trackBtn').addEventListener('click', () => {
const val = document.getElementById('trackingInput').value.trim().toUpperCase();
if (!val.startsWith('PAW-') || val.length < 8) {
showToast('Please enter a valid tracking code (e.g. PAW-A1B2C3D4)');
return;
}
triggerTracking(val);
});
/* ─────────────────────────────────────────
STORE MODAL
───────────────────────────────────────── */
document.getElementById('visitBtn').addEventListener('click', () => openOverlay('storeOverlay'));
document.getElementById('storeModalClose').addEventListener('click', () => closeModal('storeOverlay'));
document.getElementById('storeOverlay').addEventListener('click', e => {
if (e.target.id === 'storeOverlay') closeModal('storeOverlay');
});
/* ─────────────────────────────────────────
TOAST NOTIFICATION
───────────────────────────────────────── */
function showToast(msg) {
const wrap = document.getElementById('toastWrap');
const toast = document.createElement('div');
toast.className = 'toast';
toast.textContent = msg;
wrap.appendChild(toast);
requestAnimationFrame(() => {
requestAnimationFrame(() => toast.classList.add('show'));
});
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => toast.remove(), 400);
}, 2800);
}
/* ─────────────────────────────────────────
CONFETTI
───────────────────────────────────────── */
function launchConfetti() {
const canvas = document.getElementById('confettiCanvas');
const ctx = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const colors = ['#8B6F47','#4A7C59','#E8A598','#FFB300','#42A5F5','#EF5350','#AB47BC'];
const particles = Array.from({ length: 120 }, () => ({
x: Math.random() * canvas.width,
y: Math.random() * -canvas.height,
r: Math.random() * 8 + 4,
d: Math.random() * 120 + 40,
color: colors[Math.floor(Math.random() * colors.length)],
tilt: Math.random() * 10 - 10,
tiltAngle: 0,
tiltIncrementFactor: Math.random() * 0.07 + 0.05,
shape: Math.random() > 0.5 ? 'rect' : 'circle'
}));
let angle = 0;
let frameCount = 0;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
angle += 0.01;
frameCount++;
particles.forEach(p => {
p.tiltAngle += p.tiltIncrementFactor;
p.y += (Math.cos(angle + p.d) + 2.5) * 1.4;
p.x += Math.sin(angle) * 1.5;
p.tilt = Math.sin(p.tiltAngle) * 12;
ctx.fillStyle = p.color;
ctx.beginPath();
if (p.shape === 'circle') {
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
} else {
ctx.rect(p.x + p.tilt, p.y, p.r, p.r * 1.6);
}
ctx.fill();
});
if (frameCount < 200) requestAnimationFrame(draw);
else { ctx.clearRect(0, 0, canvas.width, canvas.height); }
}
draw();
}
/* ─────────────────────────────────────────
SCROLL ANIMATIONS
───────────────────────────────────────── */
const observer = new IntersectionObserver((entries) => {
entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('visible'); });
}, { threshold: 0.12 });
document.querySelectorAll('.fade-up').forEach(el => observer.observe(el));
/* ─────────────────────────────────────────
NAV SCROLL SHADOW
───────────────────────────────────────── */
window.addEventListener('scroll', () => {
document.getElementById('mainNav').classList.toggle('scrolled', window.scrollY > 60);
});
/* ─────────────────────────────────────────
KEYBOARD CLOSE (ESC)
───────────────────────────────────────── */
document.addEventListener('keydown', e => {
if (e.key === 'Escape') {
['petOverlay','cartOverlay','checkoutOverlay','successOverlay','storeOverlay'].forEach(closeModal);
}
});
/* ─────────────────────────────────────────
INIT
───────────────────────────────────────── */
renderPets();
/* ─────────────────────────────────────────
IMAGE FALLBACKS — ensure no <img> is empty or broken
───────────────────────────────────────── */
const FALLBACK = {
cats: [
'https://images.unsplash.com/photo-1518791841217-8f162f1e1131?w=1200&q=80',
'https://images.unsplash.com/photo-1518976024611-4886f6d5d6e2?w=1200&q=80',
'https://images.unsplash.com/photo-1517423440428-a5a00ad493e8?w=1200&q=80'
],
dogs: [
'https://images.unsplash.com/photo-1507149833265-60c372daea22?w=1200&q=80',
'https://images.unsplash.com/photo-1517427671955-22b3b2e7fb11?w=1200&q=80',
'https://images.unsplash.com/photo-1508672019048-805c876b67e2?w=1200&q=80'
],
monkeys: [
'https://images.unsplash.com/photo-1540573133985-87b6da6d54a9?w=1200&q=80',
'https://images.unsplash.com/photo-1497206365907-f5e630693df0?w=1200&q=80',
'https://images.unsplash.com/photo-1425082661705-1834bfd09dca?w=1200&q=80'
],
generic: [
'https://images.unsplash.com/photo-1525253086316-d0c936c814f8?w=1200&q=80',
'https://images.unsplash.com/photo-1508739773434-c26b3d09e071?w=1200&q=80'
]
};
function randomFrom(arr){ return arr[Math.floor(Math.random()*arr.length)]; }
function chooseFallback(img){
const card = img.closest('.pet-card');
if(card && card.dataset.cat) {
const cat = card.dataset.cat; // 'cats' etc
if(FALLBACK[cat]) return randomFrom(FALLBACK[cat]);
if(cat.endsWith('s') && FALLBACK[cat.slice(0,-1)]) return randomFrom(FALLBACK[cat.slice(0,-1)]);
}
const alt = (img.alt||'').toLowerCase();
if(alt.includes('cat')||alt.includes('kitten')) return randomFrom(FALLBACK.cats);
if(alt.includes('dog')||alt.includes('puppy')) return randomFrom(FALLBACK.dogs);
if(alt.includes('monkey')||alt.includes('capuchin')||alt.includes('tamarin')) return randomFrom(FALLBACK.monkeys);
return randomFrom(FALLBACK.generic);
}
function ensureImages(){
document.querySelectorAll('img').forEach(img => {
// replace empty src
if(!img.getAttribute('src') || img.getAttribute('src').trim()===''){
img.src = chooseFallback(img);
}
// on error, replace once with fallback to avoid infinite loop
img.addEventListener('error', function handler(){
img.removeEventListener('error', handler);
img.src = chooseFallback(img);
});
});
}
// run after a tick to ensure dynamic content is rendered
requestAnimationFrame(() => setTimeout(ensureImages, 120));
</script>
</body>
</html>