// ============================================================================ // SHOP CONTACT INFO (single source of truth for the whole page) // Verified 2026-07-29 against parkercoinshop.com, Yelp, and the BBB profile // for Parker Coin and Bullion, LLC. // ============================================================================ const CONTACT = { name: 'Parker Coin and Bullion', phone: '(303) 578-8288', phoneHref: 'tel:+13035788288', // The shop's email deliberately does NOT appear here or anywhere the browser // can read (harvester bots scrape pages and public JSON for addresses). The // contact form's recipient lives server-side in data/settings.json, which // the web server blocks from direct fetches; edit it via /admin/. street: '11020 S Pikes Peak Dr, Ste 107', cityLine: 'Parker, CO 80138', mapsUrl: 'https://maps.google.com/?q=11020+S+Pikes+Peak+Dr+Ste+107+Parker+CO+80138', hoursLine: 'Mon-Fri · 11am to 4:30pm', closedLine: 'Closed Sat & Sun', }; // Defaults for the owner-editable bits. The live values come from // data/content.json (edited through /admin/) and are merged over these, so a // missing or broken file changes nothing. const DEFAULT_CHIPS = ['American Silver Eagles', 'American Gold Eagles', 'Gold Buffalos', 'Canadian Maple Leafs', 'Silver rounds & bars', '90% U.S. silver coins', 'Morgan & Peace dollars', 'Rare & collectible coins', 'Estate collections']; const DEFAULT_BUY_NOTE = 'Please note: we do not buy jewelry or scrap gold.'; // ============================================================================ // NAV SCROLLING // The browser's native behavior:'smooth' moves at a fixed clip that reads as // an abrupt jump over long distances (hero down to contact). glideTo animates // the scroll itself: ease-in-out, duration scaled by distance, cancelled the // moment the visitor scrolls on their own, instant under reduced motion. // ============================================================================ let glideFrame = 0; const cancelGlide = () => cancelAnimationFrame(glideFrame); function glideTo(top) { cancelGlide(); if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { window.scrollTo(0, top); return; } const start = window.scrollY; const dist = top - start; if (Math.abs(dist) < 2) return; const duration = Math.min(1800, 750 + Math.abs(dist) * 0.4); window.addEventListener('wheel', cancelGlide, { passive: true, once: true }); window.addEventListener('touchstart', cancelGlide, { passive: true, once: true }); let t0 = null; const step = (ts) => { if (t0 === null) t0 = ts; const p = Math.min(1, (ts - t0) / duration); const eased = p < 0.5 ? 4 * p * p * p : 1 - Math.pow(-2 * p + 2, 3) / 2; window.scrollTo(0, start + dist * eased); if (p < 1) glideFrame = requestAnimationFrame(step); }; glideFrame = requestAnimationFrame(step); } // ============================================================================ // LIVE SPOT PRICES // Provider: api.gold-api.com (free, no API key, CORS-open, 1 request per // metal). Fallback options if that service ever disappears: goldapi.io // (100 req/day free, key via x-access-token header) or metalpriceapi.com // (100 req/month free, key in the query string). // ============================================================================ const SPOT_CONFIG = { enabled: true, // Refresh cadence. The provider updates its numbers roughly once a minute, // so faster than 60000 buys nothing. One fetch per metal per refresh. pollMs: 60000, symbols: { gold: 'XAU', silver: 'XAG', platinum: 'XPT' }, url: (sym) => `https://api.gold-api.com/price/${sym}`, }; // Variation 1 v2: Refined Classic: fluid + atmospheric purple-blue fades + more photography // Performance-tuned: parallax uses refs + rAF (no React rerenders); reveals use one-shot IO + CSS class. const RefinedClassicV2 = () => { const t = window.PCB_TOKENS; const [hovered, setHovered] = React.useState(null); // spotPrices shape: { gold, silver, platinum, palladium, pctChange: {…} } | null const [spotPrices, setSpotPrices] = React.useState(null); const [spotLive, setSpotLive] = React.useState(false); // true once first successful fetch const [spotMisses, setSpotMisses] = React.useState(0); // consecutive failed polls before first success // Owner-editable content, fetched once and merged over the defaults above. const [siteContent, setSiteContent] = React.useState(null); React.useEffect(() => { fetch('data/content.json') .then(r => (r.ok ? r.json() : null)) .then(json => { if (json && typeof json === 'object') setSiteContent(json); }) .catch(() => {}); }, []); const content = siteContent || {}; const C = { ...CONTACT, hoursLine: content.hoursLine || CONTACT.hoursLine, closedLine: content.closedLine || CONTACT.closedLine, }; const CHIPS = (Array.isArray(content.chips) && content.chips.length) ? content.chips : DEFAULT_CHIPS; const BUY_NOTE = content.buyNote || DEFAULT_BUY_NOTE; const ANNOUNCEMENT = typeof content.announcement === 'string' ? content.announcement.trim() : ''; // Lead attribution: remember how this visitor arrived (a ?src= or utm link, // or an outside referrer), first touch wins for the tab. The value rides // along with the contact form so the shop can see which channel a lead // came from. const leadSource = React.useMemo(() => { try { const q = new URLSearchParams(window.location.search); const fromUrl = (q.get('src') || q.get('utm_source') || '').slice(0, 120); const stored = sessionStorage.getItem('pcb_src'); if (stored) return stored; const ref = document.referrer && document.referrer.indexOf(window.location.hostname) === -1 ? document.referrer : ''; const src = fromUrl || ref || 'direct'; sessionStorage.setItem('pcb_src', src); return src; } catch (e) { return 'direct'; } }, []); // ?item= arrives from a collectibles listing CTA: prefill the interest field // and land the visitor on the contact form. const itemParam = React.useMemo(() => { try { return (new URLSearchParams(window.location.search).get('item') || '').slice(0, 120); } catch (e) { return ''; } }, []); const heroParallaxRef = React.useRef(null); const goldBarsParallaxRef = React.useRef(null); const goldBarsSectionRef = React.useRef(null); // Spot price polling, driven by SPOT_CONFIG above. Each metal is fetched // independently so one failed symbol never blanks the others. React.useEffect(() => { if (!SPOT_CONFIG.enabled) return; const fetchSpot = async () => { const entries = Object.entries(SPOT_CONFIG.symbols); const results = await Promise.allSettled(entries.map(async ([key, sym]) => { const res = await fetch(SPOT_CONFIG.url(sym)); if (!res.ok) throw new Error(`HTTP ${res.status}`); const json = await res.json(); if (typeof json.price !== 'number') throw new Error('no price field'); return [key, +json.price.toFixed(2)]; })); const ok = results.filter(r => r.status === 'fulfilled').map(r => r.value); if (ok.length === 0) { console.warn('[Parker Coin] Spot price fetch failed for all metals'); // keep any previous data; count misses so the UI can stop saying // "Connecting" when the feed is truly unreachable setSpotMisses(m => m + 1); return; } setSpotPrices(prev => { const next = { gold: null, silver: null, platinum: null, pctChange: {}, ...(prev || {}) }; ok.forEach(([key, price]) => { next[key] = price; }); return next; }); setSpotLive(true); }; fetchSpot(); const id = setInterval(fetchSpot, SPOT_CONFIG.pollMs); return () => clearInterval(id); }, []); // Single rAF-driven scroll handler: writes transforms directly to DOM. Zero rerenders. React.useEffect(() => { let raf = 0; let pending = false; const update = () => { pending = false; const y = window.scrollY; if (heroParallaxRef.current) { heroParallaxRef.current.style.transform = `translate3d(0, ${y * 0.18}px, 0)`; } if (goldBarsParallaxRef.current && goldBarsSectionRef.current) { const rect = goldBarsSectionRef.current.getBoundingClientRect(); const offset = (window.innerHeight / 2 - (rect.top + rect.height / 2)) * 0.12; goldBarsParallaxRef.current.style.transform = `translate3d(0, ${offset}px, 0)`; } }; const onScroll = () => { if (pending) return; pending = true; raf = requestAnimationFrame(update); }; window.addEventListener('scroll', onScroll, { passive: true }); update(); return () => { window.removeEventListener('scroll', onScroll); cancelAnimationFrame(raf); }; }, []); // One-shot reveal: IO toggles a class once, then disconnects. No React state churn. const Reveal = React.useMemo(() => ({ children, delay = 0 }) => { const ref = React.useRef(null); React.useEffect(() => { const el = ref.current; if (!el) return; const r = el.getBoundingClientRect(); if (r.top < window.innerHeight * 0.95) { requestAnimationFrame(() => el.classList.add('is-revealed')); return; } const io = new IntersectionObserver(([e]) => { if (e.isIntersecting) { el.classList.add('is-revealed'); io.disconnect(); } }, { threshold: 0.05, rootMargin: '0px 0px -40px 0px' }); io.observe(el); return () => io.disconnect(); }, []); return (
Colorado’s trusted hand for gold, silver & rare coins.
We buy and sell gold and silver bullion, and coins of every era. Whether you’re settling an estate, building a position, or curious about a single piece passed down, we offer honest evaluations and competitive prices. No pressure, no theatrics.
Colorado exempts investment-grade bullion and numismatic coins from state sales tax, a meaningful saving on every transaction, whether you’re acquiring a single Eagle or a multi-ounce position.
We’ll give you a fair, current quote based on live spot prices the day you visit.
Every piece weighed, tested, and quoted in front of you.
We buy and sell gold, silver, coins, and bullion: best prices paid. Bring in a single piece, an inheritance, or a lifelong collection. We’ll quote on the spot and pay the same day.
{BUY_NOTE}
Coins and precious metals are often the hardest part of an estate to value. We go through the whole collection with you at the counter, weigh and test every piece, and quote it in front of you. Cash the same day, and no obligation to sell.
We typically reply within one business day.