// direction-obsidian.jsx — DIRECTION 01: Obsidian Cinema
// Cinematic, video-led, deep black, italic Cormorant serif, restrained gold.
// A refined evolution of the existing direction.
const obsidianStyles = {
bg: '#0A0908',
bgSoft: '#141312',
cream: '#E8E2D3',
gold: '#C9A24C',
goldSoft: '#A88B3A',
gray: '#6C6760',
graySoft: '#3B3833',
serif: '"Cormorant Garamond", Georgia, serif',
sans: '"Outfit", -apple-system, sans-serif',
mono: '"JetBrains Mono", ui-monospace, monospace'
};
function ObsidianHero({ onTickets, fullViewport } = {}) {
const s = obsidianStyles;
const { mobile, tablet } = useMobile();
const px = mobile ? 24 : 56;
return (
{/* hero bg */}
{/* vignette */}
{/* film grain */}
{/* top nav — only shown when standalone in canvas */}
{!fullViewport &&
Elevate Noir Gala
{!mobile &&
The Evening Experience Sponsors Contact
}
Reserve
}
{/* corner marks */}
{!mobile &&
<>
◇ EST 2026 · TORONTO
VOL · I · BLACK DREAM
>
}
{/* prominent date — bottom right, aligned with tagline */}
{/* main content */}
A Luxury Experience Advancing Black Excellence
{GALA_DATA.date}
{GALA_DATA.time}
{[
['Date', GALA_DATA.date],
['Venue', GALA_DATA.venue + ' · Toronto'],
['Dress Code', GALA_DATA.dress],
['Theme', 'Black Dream']].
map(([k, v]) =>
)}
Reserve a Table →
{/* right edge scroll mark — hide on mobile */}
{!mobile &&
}
);
}
// ----- sponsor flow -----
function ObsidianFlow({ initialTier }) {
const s = obsidianStyles;
const { mobile } = useMobile();
const isDonationMode = initialTier === 'donation';
const flow = useSponsorFlow(isDonationMode ? 'title' : initialTier);
React.useEffect(() => { if (isDonationMode) flow.setIsDonation(true); }, []);
// Stripe payment state
const [stripeSecret, setStripeSecret] = React.useState(null);
const [paymentStatus, setPaymentStatus] = React.useState('idle'); // idle | loading | ready | processing | error | success
const [paymentError, setPaymentError] = React.useState('');
const [stripeElements, setStripeElements] = React.useState(null);
const [stripeInstance, setStripeInstance] = React.useState(null);
const paymentRef = React.useRef(null);
const formRef = React.useRef({});
// Collect form data from step 3 inputs
const getFormData = () => {
const inputs = formRef.current;
if (flow.isDonation) {
return { name: inputs['Full Name'] || '', email: inputs['Email'] || '', phone: inputs['Phone'] || '' };
}
return { name: inputs['Primary Contact'] || '', email: inputs['Email'] || '', company: inputs['Brand · Company'] || '', phone: inputs['Phone'] || '' };
};
// Create PaymentIntent and mount Stripe Elements
const initPayment = async () => {
setPaymentStatus('loading');
setPaymentError('');
const form = getFormData();
if (!form.name || !form.email) {
setPaymentError('Please fill in all required fields.');
setPaymentStatus('idle');
return false;
}
try {
// Send application/inquiry email first (best-effort, don't block on failure)
fetch('sponsor-apply.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: form.name, email: form.email, phone: form.phone || '',
company: form.company || '', tier: flow.tier.name, amount: flow.total,
activations: flow.activations.map(id => ACTIVATIONS.find(a => a.id === id)?.name || id),
isDonation: flow.isDonation,
}),
}).catch(() => {});
const res = await fetch('create-payment-intent.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
amount: flow.total,
tier: flow.tier.name,
name: form.name,
email: form.email,
activations: flow.activations.map(id => ACTIVATIONS.find(a => a.id === id)?.name || id),
isDonation: flow.isDonation,
}),
});
const data = await res.json();
if (!res.ok || !data.clientSecret) {
setPaymentError(data.error || 'Could not initiate payment.');
setPaymentStatus('error');
return false;
}
setStripeSecret(data.clientSecret);
const stripe = window.Stripe(window.STRIPE_PK);
setStripeInstance(stripe);
const elements = stripe.elements({
clientSecret: data.clientSecret,
appearance: {
theme: 'night',
variables: {
colorPrimary: '#C9A24C',
colorBackground: '#141312',
colorText: '#E8E2D3',
colorDanger: '#c44',
fontFamily: 'Outfit, system-ui, sans-serif',
borderRadius: '0px',
spacingUnit: '4px',
},
rules: {
'.Input': { border: '1px solid #3B3833', backgroundColor: '#0A0908', color: '#E8E2D3', fontSize: '14px' },
'.Input:focus': { border: '1px solid #C9A24C', boxShadow: '0 0 0 1px #C9A24C33' },
'.Label': { color: '#6C6760', fontSize: '11px', letterSpacing: '0.18em', textTransform: 'uppercase' },
}
}
});
setStripeElements(elements);
setPaymentStatus('ready');
// Mount after render
setTimeout(() => {
if (paymentRef.current) {
const pe = elements.create('payment');
pe.mount(paymentRef.current);
}
}, 50);
return true;
} catch (err) {
setPaymentError('Network error. Please try again.');
setPaymentStatus('error');
return false;
}
};
// Confirm payment
const confirmPayment = async () => {
if (!stripeInstance || !stripeElements) return;
setPaymentStatus('processing');
setPaymentError('');
const form = getFormData();
const { error } = await stripeInstance.confirmPayment({
elements: stripeElements,
confirmParams: { return_url: window.location.href, payment_method_data: { billing_details: { name: form.name, email: form.email } } },
redirect: 'if_required',
});
if (error) {
setPaymentError(error.message);
setPaymentStatus('ready');
} else {
setPaymentStatus('success');
// Send confirmation emails
try {
await fetch('payment-success.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
paymentIntentId: stripeSecret?.split('_secret_')[0] || '',
name: form.name, email: form.email, tier: flow.tier.name,
amount: flow.total, activations: flow.activations.map(id => ACTIVATIONS.find(a => a.id === id)?.name || id),
isDonation: flow.isDonation,
}),
});
} catch (e) { /* emails are best-effort */ }
flow.setStep(5);
}
};
const StepDot = ({ n, label }) =>
= n ? s.gold : s.graySoft}`,
background: flow.step > n ? s.gold : 'transparent',
color: flow.step > n ? s.bg : flow.step === n ? s.gold : s.gray,
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontFamily: s.mono, fontSize: 10, fontWeight: 500
}}>{flow.step > n ? '✓' : n}
{!mobile &&
= n ? s.cream : s.gray
}}>{label}
}
;
const Btn = ({ children, primary, onClick, disabled }) =>
{children} ;
return (
{/* faint background ornament */}
{/* header */}
SPONSORSHIP · 2026
Partner with us.
{/* step content */}
{flow.step === 1 &&
{TIERS.map((t) => {
const sel = flow.tierId === t.id && !flow.isDonation;
return (
{ flow.setIsDonation(false); flow.setTierId(t.id); if (!flow.customAmount || flow.tierId !== t.id) flow.setCustomAmount(String(t.price)); }} style={{
background: sel ? `linear-gradient(180deg, ${s.gold}18, ${s.gold}04)` : s.bgSoft,
border: `1px solid ${sel ? s.gold : s.graySoft}`,
padding: mobile ? '20px 18px' : '28px 22px', textAlign: 'left', cursor: 'pointer',
fontFamily: s.sans, color: s.cream, display: 'flex', flexDirection: 'column',
transition: 'all 0.25s', position: 'relative'
}}>
{String(TIERS.indexOf(t) + 1).padStart(2, '0')} · {t.avail} AVAIL
{t.name}
{sel ? (
$
flow.setCustomAmount(e.target.value)}
onClick={(e) => e.stopPropagation()}
style={{
width: mobile ? 100 : 120, background: 'transparent', border: 'none',
borderBottom: `1px solid ${flow.customAmount && Number(flow.customAmount) < t.price ? '#c44' : s.gold + '66'}`,
padding: '2px 0', color: s.gold,
fontFamily: s.serif, fontSize: mobile ? 26 : 32, fontStyle: 'italic', outline: 'none'
}}
/>
) : (
{money(t.price)}+
)}
{sel && flow.customAmount && Number(flow.customAmount) < t.price &&
Minimum {money(t.price)}
}
{t.perks.map((p) =>
— {p}
)}
{sel &&
●
}
);
})}
{/* Donation option for non-sponsors */}
{ flow.setIsDonation(!flow.isDonation); }}
style={{
marginTop: 14,
background: flow.isDonation ? `linear-gradient(180deg, ${s.gold}18, ${s.gold}04)` : s.bgSoft,
border: `1px solid ${flow.isDonation ? s.gold : s.graySoft}`,
padding: mobile ? '16px 20px' : '20px 28px',
textAlign: 'left', cursor: 'pointer', fontFamily: s.sans, color: s.cream,
display: 'flex', alignItems: 'center', gap: mobile ? 14 : 20, width: '100%',
transition: 'all 0.25s', position: 'relative'
}}
>
{flow.isDonation && '✓'}
Community Donation
Not sponsoring? Still want to support the movement.
{flow.isDonation ? (
$
{ e.stopPropagation(); flow.setDonationAmount(e.target.value); }}
onClick={e => e.stopPropagation()}
placeholder="50"
style={{
width: 90, background: 'transparent', border: 'none',
borderBottom: `1px solid ${flow.donationAmount && Number(flow.donationAmount) < flow.DONATION_MIN ? '#c44' : s.gold + '66'}`,
padding: '2px 0', color: s.gold,
fontFamily: s.serif, fontSize: mobile ? 22 : 28, fontStyle: 'italic', outline: 'none'
}}
/>
) : (
{money(flow.DONATION_MIN)}+
)}
{flow.isDonation && flow.donationAmount && Number(flow.donationAmount) < flow.DONATION_MIN && (
Minimum ${flow.DONATION_MIN}
)}
}
{flow.step === 2 && !flow.isDonation &&
Add custom brand activations to amplify your visibility throughout the night. Optional — but each one is a moment.
{ACTIVATIONS.map((a) => {
const sel = flow.activations.includes(a.id);
return (
flow.toggleActivation(a.id)} style={{
background: sel ? `${s.gold}10` : s.bgSoft,
border: `1px solid ${sel ? s.gold : s.graySoft}`,
padding: mobile ? '16px 18px' : '22px 26px', textAlign: 'left', cursor: 'pointer',
fontFamily: s.sans, color: s.cream, display: 'flex', alignItems: 'center', gap: mobile ? 14 : 20
}}>
{sel && '✓'}
+{money(a.price)}
);
})}
}
{flow.step === 3 &&
{(flow.isDonation
? [['Full Name', 'Ama Boateng', true], ['Email', 'ama@example.com', true], ['Phone', '+1 416 555 0188', false]]
: [['Brand · Company', 'Acme Holdings Inc.', true], ['Primary Contact', 'Ama Boateng', true], ['Email', 'partnerships@acme.com', true], ['Phone', '+1 416 555 0188', true]]
).map(([label, ph, req], i) =>
)}
{flow.isDonation ? 'YOUR DONATION' : 'YOUR PARTNERSHIP'}
{flow.isDonation
? Community Donation
: {flow.tier.name} Sponsor }
{flow.isDonation ? 'Community donation' : `${flow.tier.name} tier${flow.customAmount && Number(flow.customAmount) > flow.tier.price ? ' (custom)' : ''}`}
{money(flow.basePrice)}
{!flow.isDonation && flow.activations.map((id) => {
const a = ACTIVATIONS.find((x) => x.id === id);
return (
{a.name}
+{money(a.price)}
);
})}
Total
{money(flow.total)}
}
{flow.step === 4 &&
◇ SECURE PAYMENT
Complete your {flow.isDonation ? 'donation' : 'sponsorship'}.
All payments are processed securely via Stripe. Your card details never touch our servers.
{paymentStatus === 'loading' && (
)}
{paymentError && (
)}
{(paymentStatus === 'ready' || paymentStatus === 'processing') && (
{paymentStatus === 'processing' ? 'Processing...' : `Pay ${money(flow.total)} CAD →`}
)}
ORDER SUMMARY
{flow.isDonation
? Community Donation
: {flow.tier.name} Sponsor }
{flow.isDonation ? 'Donation' : `${flow.tier.name} tier`}
{money(flow.basePrice)}
{!flow.isDonation && flow.activations.map((id) => {
const a = ACTIVATIONS.find((x) => x.id === id);
return (
{a.name}
+{money(a.price)}
);
})}
Total
{money(flow.total)} CAD
}
{flow.step === 5 &&
{flow.isDonation ? 'PAYMENT CONFIRMED · THANK YOU' : 'PAYMENT CONFIRMED · WELCOME TO THE TABLE'}
Thank you,{flow.isDonation ? 'supporter.' : 'partner.'}
{flow.isDonation
? <>Your {money(flow.total)} CAD donation supports the Elevate Noir movement. A receipt has been sent to your email.>
: <>We've received your {flow.tier.name.toLowerCase()} commitment of {money(flow.total)} CAD. A partnership lead will reach out within 48 hours with next steps. A receipt has been sent to your email.>}
{ flow.reset(); setStripeSecret(null); setPaymentStatus('idle'); setStripeElements(null); setStripeInstance(null); formRef.current = {}; }}>Start again
}
{/* footer */}
{flow.step < 4 &&
{flow.isDonation ? 'Donation' : 'Running Total'}
{money(flow.total)}
← Back
{flow.step === 3 ? (
{ const ok = await initPayment(); if (ok) flow.setStep(4); }} disabled={paymentStatus === 'loading'}>
{paymentStatus === 'loading' ? 'Loading...' : 'Proceed to Payment →'}
) : (
Continue →
)}
}
{flow.step === 4 &&
Total
{money(flow.total)} CAD
{ flow.setStep(3); setPaymentStatus('idle'); setStripeSecret(null); setStripeElements(null); }}>← Back
}
);
}
Object.assign(window, { ObsidianHero, ObsidianFlow });