Add brand kit, #/brand page, and responsive logo marks

Export script generates PNG/WebP/favicons/ZIP and OG image; nav uses symbol on mobile and horizontal lockup on desktop; press kit at #/brand with download links and print-friendly guidelines.

Co-authored-by: Cursor <[email protected]>
This commit is contained in:
defiQUG
2026-05-26 20:53:37 -07:00
co-authored by Cursor
parent 2019f56f27
commit 33aab0d3de
48 changed files with 569 additions and 77 deletions
+29 -26
View File
@@ -58,14 +58,15 @@ import {
import AIAssistancePortal from './components/AIAssistancePortal'
// Phase 3B: Enterprise Components
import AdvancedAnalyticsDashboard from './components/AdvancedAnalyticsDashboard'
import MobileVolunteerApp from './components/MobileVolunteerApp'
import StaffTrainingDashboard from './components/StaffTrainingDashboard'
import MimChatAgent from './components/MimChatAgent'
import AdvancedAnalyticsDashboard from './components/AdvancedAnalyticsDashboard'
import MobileVolunteerApp from './components/MobileVolunteerApp'
import StaffTrainingDashboard from './components/StaffTrainingDashboard'
import MimChatAgent from './components/MimChatAgent'
// Phase 4: Extracted Components
import { Navigation } from './components/Navigation'
import { Footer } from './components/Footer'
import { BrandAssetsPage } from './pages/BrandPage'
/**
* Miracles in Motion — Complete Non-Profit Website
@@ -2184,7 +2185,7 @@ function SponsorsPage() {
]
return (
<PageShell title="Corporate Sponsorship" icon={Building2} eyebrow="Partner with purpose" cta={<a className="btn-secondary" href="#">Download prospectus</a>}>
<PageShell title="Corporate Sponsorship" icon={Building2} eyebrow="Partner with purpose" cta={<a className="btn-secondary" href="#/brand">Brand assets</a>}>
<div className="grid gap-6 md:grid-cols-2">
<div className="space-y-6">
{tiers.map((t) => (
@@ -2368,7 +2369,7 @@ function LegalPage() {
</PolicySection>
<PolicySection id="sponsorship-terms" title="Sponsorship Terms & Logo Use">
<ul className="list-disc pl-5 space-y-2">
<li>Logo usage requires prior written approval and must follow brand guidelines.</li>
<li>Logo usage requires prior written approval and must follow our <a className="underline" href="#/brand">brand assets and guidelines</a>.</li>
<li>Sponsorship does not imply endorsement of products or services.</li>
<li>Benefits may be adjusted for equivalency based on availability.</li>
</ul>
@@ -4576,6 +4577,8 @@ function AppContent() {
return <TestimoniesPage />
case '/legal':
return <LegalPage />
case '/brand':
return <BrandAssetsPage />
case '/impact':
return <ImpactReportPage />
case '/request-assistance':
@@ -4603,9 +4606,9 @@ function AppContent() {
}
}
return (
<div className={`min-h-screen transition-colors duration-300 bg-neutral-50 text-neutral-900 antialiased selection:bg-primary-200/60 dark:bg-neutral-950 dark:text-neutral-50 ${darkMode ? 'dark' : ''}`}>
<BackgroundDecor />
return (
<div className={`min-h-screen transition-colors duration-300 bg-neutral-50 text-neutral-900 antialiased selection:bg-primary-200/60 dark:bg-neutral-950 dark:text-neutral-50 ${darkMode ? 'dark' : ''}`}>
<BackgroundDecor />
<SkipToContent />
{/* Offline Indicator */}
@@ -4628,20 +4631,20 @@ function AppContent() {
<main id="content" className="relative flex-1">
{renderPage()}
</main>
<Footer />
{![
'/admin-portal',
'/volunteer-portal',
'/resource-portal',
'/analytics',
'/ai-portal',
'/advanced-analytics',
'/mobile-volunteer',
'/staff-training'
].includes(currentPath) && <MimChatAgent currentPath={currentPath} />}
<StickyDonate />
<CookieBanner />
</div>
)
}
<Footer />
{![
'/admin-portal',
'/volunteer-portal',
'/resource-portal',
'/analytics',
'/ai-portal',
'/advanced-analytics',
'/mobile-volunteer',
'/staff-training'
].includes(currentPath) && <MimChatAgent currentPath={currentPath} />}
<StickyDonate />
<CookieBanner />
</div>
)
}
+1
View File
@@ -43,6 +43,7 @@ export function Footer() {
<h3 className="font-semibold">Organization</h3>
<ul className="mt-4 space-y-2 text-sm">
<li><a href="#/testimonies" className="navlink">Testimonials</a></li>
<li><a href="#/brand" className="navlink">Brand assets</a></li>
<li><a href="#/legal" className="navlink">Legal & Policies</a></li>
<li><a href="mailto:[email protected]" className="navlink">Contact Us</a></li>
<li><a href="tel:+18184916884" className="navlink">(818) 491-6884</a></li>
+2 -2
View File
@@ -17,7 +17,7 @@ interface SEOHeadProps {
export function SEOHead({
title = 'Miracles in Motion - Empowering Students with Essential Support',
description = 'A 501(c)(3) nonprofit providing students with school supplies, clothing, and emergency support to help them succeed in school and life.',
image = '/og-image.jpg',
image = '/og-image.png',
url = 'https://mim4u.org',
type = 'website',
article
@@ -87,7 +87,7 @@ export function SEOHead({
"@type": "Organization",
"name": "Miracles in Motion",
"url": "https://mim4u.org",
"logo": "https://mim4u.org/logo.png",
"logo": "https://mim4u.org/brand/logo-horizontal.png",
"description": description,
"foundingDate": "2020",
"areaServed": "United States",
+55 -5
View File
@@ -1,29 +1,79 @@
export type LogoMarkVariant = 'nav' | 'footer' | 'square'
export interface LogoMarkProps {
/** nav/footer: full wordmark; square: compact icon tile */
/** nav: symbol on mobile, horizontal on md+; footer: horizontal; square: stacked lockup */
variant?: LogoMarkVariant
className?: string
}
const LOGO_SRC = '/logo.png'
const BRAND = {
symbolSvg: '/brand/logo-symbol.svg',
symbolPng: '/brand/logo-symbol-512.png',
horizontal: '/brand/logo-horizontal.png',
square: '/brand/logo-square.png',
} as const
export function LogoMark({ variant = 'nav', className = '' }: LogoMarkProps) {
const isDecorative = variant === 'nav' || variant === 'footer'
if (variant === 'nav') {
return (
<span
className={['logo-mark', 'logo-mark--nav', className].filter(Boolean).join(' ')}
aria-hidden={true}
>
<img
src={BRAND.symbolSvg}
alt=""
className="logo-mark__img logo-mark__img--symbol md:hidden"
width={100}
height={99}
decoding="async"
fetchPriority="high"
/>
<img
src={BRAND.horizontal}
alt=""
className="logo-mark__img logo-mark__img--horizontal hidden md:block"
width={1024}
height={354}
decoding="async"
fetchPriority="high"
/>
</span>
)
}
if (variant === 'footer') {
return (
<span
className={['logo-mark', 'logo-mark--footer', className].filter(Boolean).join(' ')}
aria-hidden={true}
>
<img
src={BRAND.horizontal}
alt=""
className="logo-mark__img logo-mark__img--horizontal"
width={1024}
height={354}
decoding="async"
/>
</span>
)
}
return (
<span
className={['logo-mark', `logo-mark--${variant}`, className].filter(Boolean).join(' ')}
className={['logo-mark', 'logo-mark--square', className].filter(Boolean).join(' ')}
aria-hidden={isDecorative || undefined}
>
<img
src={LOGO_SRC}
src={BRAND.square}
alt={isDecorative ? '' : 'Miracles in Motion Foundation'}
className="logo-mark__img"
width={1024}
height={1024}
decoding="async"
fetchPriority={variant === 'nav' ? 'high' : undefined}
/>
</span>
)
+35 -23
View File
@@ -71,7 +71,7 @@
}
@layer components {
/* Official logo (public/logo.png) */
/* Brand logos: /brand/logo-symbol.svg, logo-horizontal.png, logo-square.png */
.logo-mark {
display: inline-flex;
align-items: center;
@@ -83,72 +83,75 @@
.logo-mark__img {
display: block;
object-fit: contain;
object-position: left center;
}
/* Mobile-first; scale up on tablet/desktop (square asset: height drives width) */
/* Nav: symbol (square) on mobile, horizontal lockup on md+ */
.logo-mark--nav {
height: 2.25rem;
height: 2.5rem;
}
.logo-mark--nav .logo-mark__img {
.logo-mark--nav .logo-mark__img--symbol {
height: 100%;
width: auto;
max-width: 9rem;
object-position: center;
max-width: 2.75rem;
}
@media (min-width: 640px) {
@media (min-width: 768px) {
.logo-mark--nav {
height: 3rem;
height: 3.25rem;
}
.logo-mark--nav .logo-mark__img {
max-width: 11rem;
.logo-mark--nav .logo-mark__img--horizontal {
height: 100%;
width: auto;
max-width: min(14rem, 42vw);
}
}
@media (min-width: 1024px) {
.logo-mark--nav {
height: 4rem;
height: 3.5rem;
}
.logo-mark--nav .logo-mark__img {
max-width: 14rem;
.logo-mark--nav .logo-mark__img--horizontal {
max-width: 15rem;
}
}
@media (min-width: 1280px) {
.logo-mark--nav {
height: 4.25rem;
height: 3.75rem;
}
.logo-mark--nav .logo-mark__img {
max-width: 15.5rem;
.logo-mark--nav .logo-mark__img--horizontal {
max-width: 16.5rem;
}
}
/* Footer: always horizontal */
.logo-mark--footer {
height: 2.75rem;
}
.logo-mark--footer .logo-mark__img {
.logo-mark--footer .logo-mark__img--horizontal {
height: 100%;
width: auto;
max-width: 10rem;
max-width: min(12rem, 80vw);
}
@media (min-width: 640px) {
.logo-mark--footer {
height: 3.25rem;
}
.logo-mark--footer .logo-mark__img {
max-width: 12rem;
.logo-mark--footer .logo-mark__img--horizontal {
max-width: 14rem;
}
}
@media (min-width: 1024px) {
.logo-mark--footer {
height: 3.75rem;
height: 3.5rem;
}
.logo-mark--footer .logo-mark__img {
max-width: 14rem;
.logo-mark--footer .logo-mark__img--horizontal {
max-width: 16rem;
}
}
@@ -569,6 +572,15 @@
}
@media print {
.no-print,
header,
footer,
.sticky-donate,
[class*="MimChat"],
dialog {
display: none !important;
}
.no-print {
display: none !important;
}
+184
View File
@@ -0,0 +1,184 @@
import { Download, Palette, Printer, Type } from 'lucide-react'
import manifest from '../../../config/brand-assets.manifest.json'
type BrandManifest = typeof manifest
export function BrandAssetsPage() {
const data = manifest as BrandManifest
return (
<div className="mx-auto max-w-4xl px-4 py-12 sm:px-6 lg:px-8">
<div className="section-header mb-10">
<div className="section-eyebrow">Press & partners</div>
<h1 className="section-title">Brand assets</h1>
<p className="section-subtitle">
Official logos, colors, and favicons for Miracles in Motion Foundation. Use only these
files; do not recreate or recolor the mark.
</p>
</div>
<div className="mb-8 flex flex-wrap gap-3 print:hidden">
<a href="/brand/MIM4U-Brand-Kit.zip" className="btn-primary" download>
<Download className="h-4 w-4" aria-hidden />
Download complete kit (ZIP)
</a>
<button
type="button"
className="btn-secondary"
onClick={() => window.print()}
>
<Printer className="h-4 w-4" aria-hidden />
Print guidelines
</button>
</div>
<section className="card mb-8" id="usage">
<h2 className="font-semibold tracking-tight">Usage (summary)</h2>
<ul className="mt-3 list-disc space-y-2 pl-5 text-sm text-neutral-700 dark:text-neutral-300">
<li>Do not stretch, rotate, or change logo colors.</li>
<li>
Keep clear space around the lockup (at least the height of the capital M in MIRACLES).
</li>
<li>
<strong>Website header:</strong> horizontal lockup on tablet/desktop; symbol mark on
mobile.
</li>
<li>
<strong>Favicon / app icon:</strong> symbol on forest green (<code>#1a3c34</code>).
</li>
<li>
Sponsor and co-branding use requires approval — see{' '}
<a className="underline text-primary-600 dark:text-secondary-400" href="#/legal#sponsorship-terms">
Sponsorship Terms
</a>
.
</li>
<li>
Questions:{' '}
<a className="underline" href="mailto:[email protected]">
contact@mim4u.org
</a>
</li>
</ul>
</section>
<section className="card mb-8">
<div className="flex items-center gap-2">
<Palette className="h-5 w-5 text-primary-600 dark:text-secondary-400" aria-hidden />
<h2 className="font-semibold tracking-tight">Colors</h2>
</div>
<div className="mt-4 grid gap-3 sm:grid-cols-2">
{data.colors.map((c) => (
<div
key={c.hex}
className="flex items-center gap-3 rounded-xl border border-neutral-200/80 p-3 dark:border-white/10"
>
<span
className="h-12 w-12 shrink-0 rounded-lg border border-black/10 shadow-inner"
style={{ backgroundColor: c.hex }}
aria-hidden
/>
<div className="text-sm">
<div className="font-medium">{c.name}</div>
<div className="font-mono text-xs text-neutral-600 dark:text-neutral-400">{c.hex}</div>
<div className="text-xs text-neutral-500">{c.role}</div>
</div>
</div>
))}
</div>
</section>
<section className="card mb-8">
<div className="flex items-center gap-2">
<Type className="h-5 w-5 text-primary-600 dark:text-secondary-400" aria-hidden />
<h2 className="font-semibold tracking-tight">Typography (website)</h2>
</div>
<ul className="mt-3 space-y-2 text-sm text-neutral-700 dark:text-neutral-300">
{data.typography.map((t) => (
<li key={t.name}>
<strong>{t.name}</strong> — {t.use}
</li>
))}
</ul>
<p className="mt-3 text-xs text-neutral-500 dark:text-neutral-400">
Type in the logo artwork is part of the supplied files; do not set live text in a substitute
font to imitate the lockup.
</p>
</section>
{data.groups.map((group) => (
<section key={group.id} className="card mb-6" id={`assets-${group.id}`}>
<h2 className="font-semibold tracking-tight">{group.title}</h2>
<p className="mt-1 text-sm text-neutral-600 dark:text-neutral-400">{group.description}</p>
<div className="mt-4 overflow-x-auto">
<table className="w-full text-left text-sm">
<thead>
<tr className="border-b border-neutral-200 dark:border-white/10">
<th className="py-2 pr-4 font-medium">File</th>
<th className="py-2 pr-4 font-medium">Format</th>
<th className="py-2 font-medium">Download</th>
</tr>
</thead>
<tbody>
{group.assets.map((asset) => (
<tr
key={asset.path}
className="border-b border-neutral-100 dark:border-white/5"
>
<td className="py-3 pr-4">{asset.title}</td>
<td className="py-3 pr-4 font-mono text-xs">{asset.format}</td>
<td className="py-3">
<a
href={asset.path}
className="inline-flex items-center gap-1 text-primary-600 hover:underline dark:text-secondary-400"
download
>
<Download className="h-3.5 w-3.5" aria-hidden />
{asset.path.split('/').pop()}
</a>
</td>
</tr>
))}
</tbody>
</table>
</div>
{group.id === 'horizontal' && (
<div className="mt-4 rounded-xl bg-primary-600/10 p-4 dark:bg-primary-900/20">
<img
src="/brand/logo-horizontal.png"
alt=""
className="mx-auto h-16 w-auto max-w-full object-contain md:h-20"
width={1024}
height={354}
/>
</div>
)}
{group.id === 'symbol' && (
<div className="mt-4 flex flex-wrap items-center justify-center gap-6 rounded-xl bg-neutral-100 p-6 dark:bg-neutral-900">
<img
src="/brand/logo-symbol.svg"
alt=""
className="h-20 w-20 object-contain"
width={100}
height={99}
/>
<img
src="/brand/logo-symbol-512.png"
alt=""
className="h-20 w-20 object-contain"
width={512}
height={512}
/>
</div>
)}
</section>
))}
<p className="text-center text-xs text-neutral-500 print:mt-8">
© Miracles in Motion Foundation. Kit version {data.version} · Updated {data.updated}
</p>
</div>
)
}
export default BrandAssetsPage
+1
View File
@@ -0,0 +1 @@
export { BrandAssetsPage, default } from './BrandAssetsPage'