Add client outreach photos and Instagram profile link

Five optimized community images and outreach video on home hero, gallery, and events page; footer and SEO link to @miraclesinmotionfoundation on Instagram.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
defiQUG
2026-05-26 21:09:12 -07:00
co-authored by Cursor
parent c9948dff4a
commit 3a9d8082ac
14 changed files with 187 additions and 85 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 356 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.
+20 -67
View File
@@ -1,5 +1,5 @@
import React, { useState, useEffect, useRef, useContext, createContext } from 'react'
import { motion, useMotionValue, useSpring, useTransform, useScroll, AnimatePresence } from 'framer-motion'
import { motion, useTransform, useScroll, AnimatePresence } from 'framer-motion'
import {
ArrowRight,
Backpack,
@@ -75,8 +75,9 @@ import {
StoriesIntroBlock,
WhatWeDoPage,
} from './pages/foundation'
import { CommunityGallery } from './components/home/CommunityGallery'
import { WhoWeServe } from './components/home/WhoWeServe'
import { SITE, WHAT_WE_DO } from './content/siteContent'
import { COMMUNITY_PHOTOS, SITE, WHAT_WE_DO } from './content/siteContent'
/**
* Miracles in Motion — Complete Non-Profit Website
@@ -588,12 +589,6 @@ interface Translations {
}
}
interface TiltCardProps {
icon: React.ComponentType<IconProps>
title: string
desc: string
}
interface FeatureCardProps {
icon: React.ComponentType<IconProps>
title: string
@@ -796,6 +791,7 @@ function HomePage() {
<WhoWeServe />
<PartnerMarquee />
<Programs />
<CommunityGallery />
<Impact />
<HowItWorks />
<Testimonial />
@@ -1032,74 +1028,31 @@ function Hero() {
}
function HeroShowcase() {
const featured = COMMUNITY_PHOTOS.slice(0, 4)
return (
<div className="relative mx-auto max-w-sm lg:max-w-md xl:max-w-lg">
<div className="absolute -inset-6 -z-10 rounded-3xl bg-gradient-to-tr from-primary-500/15 via-secondary-500/15 to-secondary-600/15 blur-2xl" />
<div className="grid grid-cols-2 gap-3 sm:gap-4">
<TiltCard icon={Users} title="Community Outreach" desc="Direct support for families in need" />
<TiltCard icon={Heart} title="Crisis & Relief" desc="Fire, disaster, and emergency assistance" />
<TiltCard icon={Shield} title="Advocacy & Navigation" desc="Resources, referrals, and compassionate care" />
<TiltCard icon={Sparkles} title="Hope & Restoration" desc="Faith-rooted support when it matters most" />
{featured.map((photo, index) => (
<img
key={photo.src}
src={photo.src}
alt={photo.alt}
width={600}
height={800}
fetchPriority={index === 0 ? 'high' : 'auto'}
loading={index === 0 ? 'eager' : 'lazy'}
decoding="async"
className={`rounded-2xl border border-white/60 object-cover shadow-lg dark:border-white/10 ${
index === 0 ? 'col-span-2 aspect-[16/10] w-full' : 'aspect-[4/5] w-full'
}`}
/>
))}
</div>
</div>
)
}
function TiltCard({ icon: Icon, title, desc }: TiltCardProps) {
const x = useMotionValue(0)
const y = useMotionValue(0)
const rx = useTransform(y, [-50, 50], [12, -12])
const ry = useTransform(x, [-50, 50], [-12, 12])
const springX = useSpring(rx, { stiffness: 150, damping: 15 })
const springY = useSpring(ry, { stiffness: 150, damping: 15 })
const scale = useSpring(1, { stiffness: 300, damping: 25 })
return (
<motion.div
onMouseMove={(e) => {
const rect = e.currentTarget.getBoundingClientRect()
x.set(e.clientX - rect.left - rect.width / 2)
y.set(e.clientY - rect.top - rect.height / 2)
scale.set(1.05)
}}
onMouseLeave={() => {
x.set(0)
y.set(0)
scale.set(1)
}}
style={{
rotateX: springX,
rotateY: springY,
scale: scale,
transformStyle: "preserve-3d",
boxShadow: useTransform(scale, [1, 1.05], [
"0 4px 6px -1px rgba(0, 0, 0, 0.1)",
"0 20px 25px -5px rgba(0, 0, 0, 0.15), 0 10px 10px -5px rgba(0, 0, 0, 0.04)"
])
}}
className="group card card-hover will-change-transform cursor-pointer"
whileHover={{
z: 20
}}
transition={{ duration: 0.2 }}
>
<div className="pointer-events-none absolute inset-0 -z-10 bg-[radial-gradient(150px_80px_at_var(--x)_var(--y),_rgba(255,255,255,0.35),_transparent)] opacity-0 transition-opacity duration-500 group-hover:opacity-100" />
<div className="flex items-start gap-4" style={{ transform: "translateZ(40px)" }}>
<div className="grid h-12 w-12 place-items-center rounded-xl bg-gradient-to-br from-primary-500 to-secondary-600 text-white shadow-lg">
<Icon className="h-6 w-6" />
</div>
<div>
<div className="font-semibold tracking-tight">{title}</div>
<div className="text-sm text-neutral-600 dark:text-neutral-300">{desc}</div>
</div>
</div>
<div className="mt-4 flex items-center gap-2 text-sm text-primary-600 transition group-hover:translate-x-1 dark:text-primary-300">
Learn more <ArrowRight className="h-4 w-4" />
</div>
</motion.div>
)
}
function PartnerMarquee() {
return (
<section aria-label="Community partners" className="relative">
+12 -11
View File
@@ -1,5 +1,5 @@
import { Facebook, Globe, Instagram, Mail } from 'lucide-react'
import { SITE } from '../content/siteContent'
import { Instagram, Mail } from 'lucide-react'
import { SITE, SOCIAL } from '../content/siteContent'
import { LogoMark } from './ui/LogoMark'
export function Footer() {
@@ -16,15 +16,16 @@ export function Footer() {
{SITE.brandMessage}
</p>
<p className="mt-3 text-sm text-neutral-600 dark:text-neutral-400">{SITE.serviceArea}</p>
<div className="mt-4 flex gap-4">
<a href="#" className="text-neutral-600 hover:text-primary-600 dark:text-neutral-400" aria-label="Facebook">
<Facebook className="h-5 w-5" />
</a>
<a href="#" className="text-neutral-600 hover:text-primary-600 dark:text-neutral-400" aria-label="Instagram">
<Instagram className="h-5 w-5" />
</a>
<a href="#" className="text-neutral-600 hover:text-primary-600 dark:text-neutral-400" aria-label="Website">
<Globe className="h-5 w-5" />
<div className="mt-4">
<a
href={SOCIAL.instagram}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 text-neutral-700 transition hover:text-primary-600 dark:text-neutral-300 dark:hover:text-secondary-400"
aria-label="Follow Miracles in Motion Foundation on Instagram"
>
<Instagram className="h-5 w-5" aria-hidden />
@miraclesinmotionfoundation
</a>
</div>
</div>
+1 -3
View File
@@ -93,9 +93,7 @@ export function SEOHead({
"foundingDate": "2020",
"areaServed": "United States",
"sameAs": [
"https://facebook.com/miraclesinmotion",
"https://twitter.com/miraclesinmotion",
"https://instagram.com/miraclesinmotion"
"https://www.instagram.com/miraclesinmotionfoundation"
],
"contactPoint": {
"@type": "ContactPoint",
+5 -1
View File
@@ -18,9 +18,13 @@ describe('Footer Component', () => {
expect(screen.getByText(SITE.brandMessage)).toBeInTheDocument()
})
it('renders service area', () => {
it('renders service area and Instagram', () => {
render(<Footer />)
expect(screen.getByText(/Los Angeles County/)).toBeInTheDocument()
expect(screen.getByLabelText(/Instagram/i)).toHaveAttribute(
'href',
'https://www.instagram.com/miraclesinmotionfoundation'
)
})
it('renders Get Involved section with correct links', () => {
+73
View File
@@ -0,0 +1,73 @@
import { Instagram } from 'lucide-react'
import { COMMUNITY_PHOTOS, SOCIAL, SITE } from '../../content/siteContent'
type CommunityGalleryProps = {
/** Show section heading and Instagram CTA */
showHeader?: boolean
/** Max photos to display (default: all) */
limit?: number
className?: string
}
export function CommunityGallery({
showHeader = true,
limit,
className = '',
}: CommunityGalleryProps) {
const photos = limit ? COMMUNITY_PHOTOS.slice(0, limit) : COMMUNITY_PHOTOS
return (
<section
className={`relative py-20 mim-warm-section ${className}`}
aria-labelledby={showHeader ? 'community-gallery-heading' : undefined}
>
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
{showHeader && (
<div className="section-header mx-auto mb-12 max-w-3xl text-center">
<div className="section-eyebrow">Real moments, real hope</div>
<h2 id="community-gallery-heading" className="section-title">
Community in action
</h2>
<p className="section-subtitle">
Authentic photos from our outreach events and gatherings throughout {SITE.serviceArea.split('.')[0].replace('Serving ', '')}.
</p>
<a
href={SOCIAL.instagram}
target="_blank"
rel="noopener noreferrer"
className="btn-secondary mt-6 inline-flex"
>
<Instagram className="mr-2 h-4 w-4" aria-hidden />
Follow @{SOCIAL.instagram.split('/').pop()}
</a>
</div>
)}
<ul className="grid grid-cols-2 gap-3 sm:gap-4 md:grid-cols-3 lg:grid-cols-5">
{photos.map((photo, index) => (
<li
key={photo.src}
className={
index === 0
? 'col-span-2 row-span-2 md:col-span-2 md:row-span-2'
: ''
}
>
<figure className="group relative h-full overflow-hidden rounded-2xl border border-secondary-200/40 bg-white shadow-md dark:border-white/10">
<img
src={photo.src}
alt={photo.alt}
loading="lazy"
decoding="async"
className={`h-full w-full object-cover transition duration-500 group-hover:scale-[1.03] ${
index === 0 ? 'min-h-[280px] sm:min-h-[360px]' : 'aspect-[3/4] min-h-[140px]'
}`}
/>
</figure>
</li>
))}
</ul>
</div>
</section>
)
}
+22
View File
@@ -0,0 +1,22 @@
import { COMMUNITY_VIDEO } from '../../content/siteContent'
export function CommunityVideo() {
return (
<div className="card overflow-hidden p-0">
<video
className="w-full rounded-xl"
controls
playsInline
preload="metadata"
poster={COMMUNITY_VIDEO.poster}
aria-label={COMMUNITY_VIDEO.alt}
>
<source src={COMMUNITY_VIDEO.src} type="video/mp4" />
Your browser does not support embedded video.
</video>
<p className="px-4 py-3 text-center text-xs text-neutral-600 dark:text-neutral-400">
{COMMUNITY_VIDEO.alt}
</p>
</div>
)
}
+36
View File
@@ -13,6 +13,42 @@ export const SITE = {
closingLine: 'Because miracles still happen every day.',
} as const
/** Official social profiles */
export const SOCIAL = {
instagram: 'https://www.instagram.com/miraclesinmotionfoundation',
instagramQr: 'https://www.instagram.com/miraclesinmotionfoundation?utm_source=qr',
} as const
/** Client-provided community outreach photos (public/photos/community/) */
export const COMMUNITY_PHOTOS = [
{
src: '/photos/community/outreach-01.jpg',
alt: 'Miracles in Motion Foundation community outreach — volunteers serving families with compassion',
},
{
src: '/photos/community/outreach-02.jpg',
alt: 'Community event supporting families in Los Angeles County',
},
{
src: '/photos/community/outreach-03.jpg',
alt: 'Faith-centered outreach and fellowship at a Miracles in Motion Foundation gathering',
},
{
src: '/photos/community/outreach-04.jpg',
alt: 'Families and volunteers connected through hope and community support',
},
{
src: '/photos/community/outreach-05.jpg',
alt: 'Miracles in Motion Foundation outreach — restoring dignity and hope in the community',
},
] as const
export const COMMUNITY_VIDEO = {
src: '/photos/community/outreach-video.mp4',
poster: '/photos/community/outreach-01.jpg',
alt: 'Video from a Miracles in Motion Foundation community outreach event',
} as const
export const NAV_LINKS = [
{ href: '#/about', label: 'About', ariaLabel: 'About us' },
{ href: '#/what-we-do', label: 'What We Do', ariaLabel: 'Our programs and services' },
+18 -3
View File
@@ -1,11 +1,14 @@
import type { ReactNode } from 'react'
import { ArrowRight, Calendar, Heart, Mail, MapPin, Users } from 'lucide-react'
import { ArrowRight, Calendar, Heart, Instagram, Mail, MapPin, Users } from 'lucide-react'
import { CommunityGallery } from '../../components/home/CommunityGallery'
import { CommunityVideo } from '../../components/home/CommunityVideo'
import {
EVENTS,
INTRO,
MISSION,
POPULATIONS_SERVED,
SITE,
SOCIAL,
STORIES_OF_HOPE,
VISION,
WHAT_WE_DO,
@@ -151,8 +154,9 @@ export function EventsPage() {
<p>{EVENTS.body}</p>
</PageIntro>
<div className="mx-auto max-w-4xl px-4 sm:px-6 lg:px-8">
<div className="card flex flex-col items-center gap-4 py-12 text-center">
<div className="mx-auto max-w-4xl space-y-8 px-4 sm:px-6 lg:px-8">
<CommunityVideo />
<div className="card flex flex-col items-center gap-4 py-8 text-center">
<Calendar className="h-12 w-12 text-secondary-500" aria-hidden />
<p className="max-w-md text-sm text-neutral-600 dark:text-neutral-400">
Event listings will be posted here as dates are confirmed. We welcome volunteers and
@@ -165,9 +169,20 @@ export function EventsPage() {
<a href={EVENTS.ctaContact} className="btn-secondary">
Contact us
</a>
<a
href={SOCIAL.instagram}
target="_blank"
rel="noopener noreferrer"
className="btn-secondary inline-flex"
>
<Instagram className="mr-2 h-4 w-4" aria-hidden />
Instagram
</a>
</div>
</div>
</div>
<CommunityGallery showHeader={false} className="pt-8" />
</div>
)
}