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:
+29
-26
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
export { BrandAssetsPage, default } from './BrandAssetsPage'
|
||||
Reference in New Issue
Block a user