// Shared design tokens: palette derived from "coin on crystal" hero photo:
// kyanite-blue crystals, frosty quartz whites, raspberry tourmaline, and pure gold buffalo coin
const PCB_TOKENS = {
navy: '#1a3a6e', // kyanite blue (deep)
navyDeep: '#0f2348', // shadow blue from crystal cracks
navySoft: '#2c5394', // mid kyanite
gold: '#d4a64a', // buffalo coin gold
goldBright: '#e8c977', // coin highlight
goldDeep: '#a87a2e', // coin shadow
cream: '#f2ecdf', // frosty quartz
paper: '#faf6ec', // off-white quartz highlight
ink: '#1a1a1a',
raspberry: '#8b3a4e', // tourmaline accent
rule: 'rgba(212, 166, 74, 0.35)',
ruleSoft: 'rgba(212, 166, 74, 0.18)',
};
// Inline SVG: shop building (Victorian): placeholder silhouette
const ShopBuilding = ({ style, tone = 'gold' }) => {
const c = tone === 'gold' ? PCB_TOKENS.gold : '#fff';
return (
);
};
// Coin SVG: used as decorative element
const Coin = ({ size = 60, label = '50', style }) => (
);
// Mountain ridge SVG
const MountainRidge = ({ style, color = '#0a1422' }) => (
);
// Stack imagery placeholders
const VaultInterior = ({ style }) => (
);
const CoinDisplay = ({ style }) => (
);
const RawSpecimen = ({ style }) => (
);
// Logo / wordmark: uses the bison mark from uploaded logo
const PCBLogo = ({ color = '#d4a64a', style, size = 'md' }) => {
const s = size === 'lg' ? { mark: 56, gap: 14, title: 28, sub: 11 } :
size === 'md' ? { mark: 44, gap: 12, title: 20, sub: 9 } :
{ mark: 32, gap: 8, title: 16, sub: 8 };
// The PNG has a white background; use mix-blend-mode multiply on light surfaces,
// and on dark surfaces wrap it in a light circle so the white doesn't clash.
const isLight = color === '#fff' || color === '#ffffff' || color.toLowerCase().startsWith('#f');
return (
);
};
// Real photos: local files (uploaded by user)
const PCB_PHOTOS = {
// Crystal+coin signature image, two tiers: 1280 native (sharpened) and a
// Lanczos-upscaled+sharpened 2560 for desktop/retina via srcset. The old
// crystal-coin.png (4.4MB) stays on disk as the master but is not shipped.
heroMountains: 'images/hero-crystal-1280.jpg',
heroMountains2x: 'images/hero-crystal-2560.jpg',
// Photos pulled from the client's live Google Site 2026-07-29 (see
// parkerbullion/site-images/): storefront w/ clock tower, interior w/ bison
// skull, graded gold tray, silver rounds tray. colorado-mountains.jpg is
// staged in images/ but not yet placed anywhere.
shopBuilding: 'images/storefront.jpg',
coinCases: 'images/coin-cases.jpg',
shopInterior: 'images/interior-bison.jpg',
goldBars: 'images/gold-bars.jpg',
gradedGold: 'images/graded-gold.jpg',
silverTray: 'images/silver-tray.jpg',
bisonLogo: 'images/bison-logo.png',
};
// Photo wrappers: TRY real photos, fall back to SVG placeholders if Google's CDN blocks the request.
// Google Sites' lh3.googleusercontent.com URLs are referrer-locked, so cross-origin loads from this
// preview will fail. We swap to the SVG placeholder on `onError`.
const PhotoWithFallback = ({ src, srcSet, sizes, Fallback, style, alt }) => {
const [failed, setFailed] = React.useState(false);
if (failed) return ;
return
setFailed(true)} style={{ width: '100%', display: 'block', objectFit: 'cover', ...style }} alt={alt}/>;
};
const PhotoShop = ({ style }) => (
);
const PhotoInterior = ({ style }) => (
);
const PhotoCases = ({ style }) => (
);
const PhotoSpread = ({ style }) => (
);
const PhotoCrystal = ({ style }) => (
);
const PhotoGoldBars = ({ style }) => (
);
// Real-photo mountain hero (uses backgroundImage so it can fill an arbitrary box)
const PhotoMountainBg = ({ style, children }) => (
{children}
);
// Mountain hero with fallback to SVG ridge
const PhotoMountainHero = ({ opacity = 1 }) => {
const [failed, setFailed] = React.useState(false);
if (failed) {
return (
<>
>
);
}
return (
<>
setFailed(true)} style={{ position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', opacity }} alt=""/>
>
);
};
Object.assign(window, {
PCB_TOKENS, PCB_PHOTOS, ShopBuilding, Coin, MountainRidge, VaultInterior, CoinDisplay, RawSpecimen, PCBLogo,
PhotoShop, PhotoInterior, PhotoCases, PhotoSpread, PhotoCrystal, PhotoGoldBars, PhotoMountainBg, PhotoMountainHero,
});