// Wargames Nearby — mock data for prototype directory. // // Source: real warhammer Google Sheet (17 venues that passed QA). // Fields tagged MOCK below are hand-fabricated to demo UX — production pipeline // would need to extract these via deeper crawl + structured prompts. // - events[]: only ~12/17 venues had any event text in the sheet, all unstructured. // Hand-normalized into {day,time,system,name,real}. Each event tagged real:true/false. // - discountPct: discount_msrp column is EMPTY for all 17 venues in current pipeline run. // A few are inferable from factoids ("15% off" for Frontline) — those tagged real:true. // // All other fields (name, address, ratings, systems_stocked text, amenities, photos, factoids, // review insights, nearby landmarks) come directly from the sheet, unmodified. const PHOTO_CDN = 'https://pub-db9039c171d847a98075cd9b5ccebd4f.r2.dev'; const USER_LOCATION = { city: 'Salt Lake City', state: 'UT', lat: 40.7608, lng: -111.8910, }; const SYSTEMS = [ { id: '40k', name: 'Warhammer 40,000', short: '40K' }, { id: 'aos', name: 'Age of Sigmar', short: 'AoS' }, { id: 'old-world', name: 'The Old World', short: 'Old World' }, { id: 'horus-heresy', name: 'Horus Heresy', short: 'HH' }, { id: 'kill-team', name: 'Kill Team', short: 'Kill Team' }, { id: 'warcry', name: 'Warcry', short: 'Warcry' }, { id: 'necromunda', name: 'Necromunda', short: 'Necromunda' }, { id: 'underworlds', name: 'Underworlds', short: 'Underworlds' }, { id: 'blood-bowl', name: 'Blood Bowl', short: 'Blood Bowl' }, { id: 'mtg', name: 'Magic: The Gathering', short: 'MTG' }, { id: 'dnd', name: 'D&D', short: 'D&D' }, ]; // Helpers ──────────────────────────────────────────────────────── function milesBetween(a, b) { const R = 3958.8; const dLat = (b.lat - a.lat) * Math.PI / 180; const dLng = (b.lng - a.lng) * Math.PI / 180; const lat1 = a.lat * Math.PI / 180; const lat2 = b.lat * Math.PI / 180; const x = Math.sin(dLat/2) ** 2 + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLng/2) ** 2; return Math.round(R * 2 * Math.atan2(Math.sqrt(x), Math.sqrt(1 - x))); } function driveTime(miles) { const h = miles / 50; if (h < 1) return `${Math.round(h * 60)}m drive`; const hh = Math.floor(h); const mm = Math.round((h - hh) * 60); return `${hh}h ${mm}m drive`; } // Stores ───────────────────────────────────────────────────────── const STORES = [ { id: 'hastur-games', placeId: 'ChIJ_7SifrCJUocRUWHIT-LKWK8', name: "Hastur Games", city: 'Midvale', state: 'UT', zip: '84047', address: '6831 S State St, Midvale, UT 84047', phone: '(801) 352-2605', website: 'https://www.hasturgames.com/', lat: 40.627398, lng: -111.889475, rating: 4.5, reviewCount: 1158, storeType: 'hybrid', systems: ['40k', 'aos', 'horus-heresy', 'mtg', 'dnd'], systemsAll:['40k', 'aos', 'horus-heresy', 'mtg', 'dnd', 'pokemon', 'star-wars-unlimited', 'lorcana'], playSpace: true, paintStations: false, terrain: true, gwRetailer: true, commission: false, dropIn: true, discountPct: { value: 10, scope: 'loyalty members', real: false }, events: [ { day: 'thu', time: '18:30', system: '40k', name: '40K Crusade Night', real: false }, { day: 'sat', time: '12:00', system: 'aos', name: 'Age of Sigmar Open Play', real: false }, ], summary: 'Legendary regional destination since the late 90s. Best stock consistency in Utah Valley for 40K, AoS, Horus Heresy.', factoids: [ 'Pilgrimage destination. Customers drive from Nevada. Reputation maintained since the late 1990s.', 'Stocks 40K, Horus Heresy, and Age of Sigmar plus D&D miniatures, card sleeves, terrain. One-visit complete setup.', 'In-store kiosk for Magic singles; staff retrieves specific cards from customer want-lists.', ], insights: [ { type: 'pro_tip', text: "Staff is exceptionally knowledgeable. Ask for painting advice or model recommendations; they engage with both newcomers and competitive players." }, { type: 'what_to_expect', text: "Best bet for hard-to-locate 40K and AoS models. As an official GW retailer with decades of reputation, in-stock consistency beats most stores." }, { type: 'first_timer', text: "Multiple reviews call out helpful staff for hobbyists getting started." }, ], landmarks: ['Kids Empire Midvale', 'Wasatch Mountain State Park', 'The Crater at Homestead'], photos: 6, }, { id: 'gajo-games', placeId: 'ChIJJ0yfFQWXTYcR38I6rytEX9A', name: 'Gajo Games', city: 'Sandy', state: 'UT', zip: '84070', address: '9420 S Union Square, Sandy, UT 84070', phone: '(801) 563-5956', website: 'http://www.gajominis.com/', lat: 40.5802975, lng: -111.8740077, rating: 4.9, reviewCount: 311, storeType: 'FLGS', systems: ['40k', 'aos'], systemsAll:['40k', 'aos'], playSpace: true, paintStations: false, terrain: true, gwRetailer: false, commission: true, dropIn: true, discountPct: { value: 10, scope: 'minis to members', real: false }, events: [ { day: 'wed', time: '18:00', system: '40k', name: 'Paint & Play', real: false }, { day: 'sat', time: '10:00', system: '40k', name: '40K Tournament Saturday', real: false }, ], summary: 'Owns Wasatch Miniatures, making their own pewter line. Stock said to rival official Warhammer stores in the region.', factoids: [ 'Manufactures its own 28mm pewter figure line in-house (Wasatch Miniatures). Rare for an FLGS.', 'Stock rivals official Warhammer stores. Reviewers call it the largest miniatures + paint selection between Salt Lake and Provo.', 'Multi-level: upstairs and basement gaming floors for tournaments and casual play.', 'Commission painting service with a catalog of pre-painted figures.', ], insights: [ { type: 'money_saver', text: "Carries one of the largest paint selections in Utah. Stock up here rather than ordering online for hard-to-find shades." }, { type: 'what_to_expect', text: "Serious gaming hub with multi-level play space. Arrive ready to play or to watch, not just a retail floor." }, { type: 'first_timer', text: "Staff is knowledgeable and not intimidating. Good destination for new players asking beginner questions." }, { type: 'pro_tip', text: "Their in-house Wasatch line stocks terrain, bases, and gaming supplies. One-stop shop for complete army builds." }, ], landmarks: ['Loveland Living Planet Aquarium', 'Gardner Village', 'The Gale Museum'], photos: 6, }, { id: 'warhammer-south-jordan', placeId: 'ChIJB24g7JWFUocRG3U6GORA2f8', name: 'Warhammer South Jordan', city: 'South Jordan', state: 'UT', zip: '84095', address: '11516 District Main Dr, South Jordan, UT 84095', phone: '(801) 727-8285', website: 'https://www.warhammer.com/', lat: 40.5422651, lng: -111.980952, rating: 4.9, reviewCount: 328, storeType: 'dedicated', systems: ['40k'], systemsAll:['40k'], playSpace: 'limited', paintStations: true, terrain: false, gwRetailer: true, commission: false, dropIn: true, discountPct: null, events: [ { day: 'wed', time: '18:00', system: '40k', name: 'Painting Class', real: false }, { day: 'sat', time: '12:00', system: '40k', name: 'Battle Honors League', real: false }, ], summary: 'Dedicated GW store. Battle Honors program tracks customer painting progress over time. Free assembly and painting instruction.', factoids: [ 'Free shipping on items not in stock, unusual for a GW retailer.', 'Extensive paint selection; carries most Warhammer minis from multiple legions.', 'Battle Honors program tracks painting progress and encourages repeat visits.', 'Recently shortened hours have eliminated play availability for customers with 9-5 jobs.', ], insights: [ { type: 'first_timer', text: "Free assembly and painting instruction classes. Staff praised for time spent with beginners on rules, assembly, and technique." }, { type: 'money_saver', text: "Battle Honors program: bring completed minis each visit, staff reviews progress and provides guidance." }, { type: 'best_time', text: "Hours have been shortened. Call ahead or visit early to use the limited play tables." }, ], landmarks: ['Loveland Living Planet Aquarium', 'Gardner Village'], photos: 6, }, { id: 'dragons-keep-lehi', placeId: 'ChIJQ-ILmC2ATYcRu_L_nrwBdgE', name: "Dragon's Keep Lehi", city: 'Lehi', state: 'UT', zip: '84043', address: '189 E State St, Lehi, UT 84043', phone: '(385) 352-7544', website: 'http://www.dragonskeep.com/', lat: 40.3968352, lng: -111.8465066, rating: 4.5, reviewCount: 383, storeType: 'hybrid', systems: ['old-world', '40k', 'aos'], systemsAll:['old-world', '40k', 'aos', 'mtg', 'pokemon', 'lorcana'], playSpace: true, paintStations: true, terrain: true, gwRetailer: true, commission: false, dropIn: true, discountPct: { value: 5, scope: 'loyalty members', real: false }, events: [ { day: 'thu', time: '19:00', system: 'multi', name: 'Weekly Game Night', real: true }, { day: 'sat', time: '10:00', system: 'multi', name: 'Family Fun Day (1st Sat/mo)', real: true }, ], summary: '30+ years in business. One of four Dragon\'s Keep locations across Utah Valley. Active support for Warhammer The Old World.', factoids: [ 'Weekly Game Nights every Thursday at 7 PM. Old World, 40K, and AoS on tap.', 'Four locations across Utah Valley with terrain-equipped tables and good lighting for painting.', 'GW retailer offering loyalty discounts and early access to new releases.', '500+ active community members across 4 locations.', ], insights: [ { type: 'best_time', text: "Thursday 7 PM Weekly Game Night and first-Saturday Family Fun Day are the two anchors. Drop-in friendly with terrain-equipped tables." }, { type: 'first_timer', text: "Staff is patient with hobby and rules questions. Good place to start with Warhammer." }, { type: 'money_saver', text: "Booster pack pricing runs higher than big box. Ask about loyalty discounts to offset markup on hobby investments." }, ], landmarks: ['Thanksgiving Point', 'Museum of Natural Curiosity', 'Butterfly Biosphere'], photos: 6, }, { id: 'mana-vault', placeId: 'ChIJ95IqHGeDTYcRJKU32VM--zc', name: 'Mana Vault Game Shop', city: 'Pleasant Grove', state: 'UT', zip: '84062', address: '597 S Pleasant Grove Blvd Suite #3, Pleasant Grove, UT 84062', phone: '(801) 310-8188', website: '', lat: 40.3570478, lng: -111.7625798, rating: 4.9, reviewCount: 81, storeType: 'FLGS', systems: ['40k', 'mtg'], systemsAll:['40k', 'mtg', 'pokemon'], playSpace: true, paintStations: false, terrain: false, gwRetailer: true, commission: false, dropIn: true, discountPct: null, events: [ { day: 'fri', time: '18:00', system: '40k', name: '40K Casual Friday', real: false }, ], summary: 'Owner Jeff is highly knowledgeable about Warhammer. Beginner-friendly chill atmosphere.', factoids: [ 'Official Games Workshop retailer in Pleasant Grove, stocking 40K alongside Magic, Pokemon, and board games.', 'Dedicated card and gaming play area with gathering space for the local community.', ], insights: [ { type: 'pro_tip', text: "Jeff (owner) is highly knowledgeable about Warhammer. Ask about army building and rules." }, { type: 'first_timer', text: "Owner goes above and beyond for beginners. Multiple reviews mention extra product and personalized guidance for first-time customers." }, ], landmarks: ['Discovery Park', 'Battle Creek Falls Trail Head'], photos: 6, }, { id: 'blakfyre-games', placeId: 'ChIJpRHbUhSETYcR83LfkFQkS60', name: 'Blakfyre Games', city: 'Pleasant Grove', state: 'UT', zip: '84062', address: '391 S Main St, Pleasant Grove, UT 84062', phone: '(801) 785-9713', website: 'http://blakfyre.com/', lat: 40.359315, lng: -111.7400629, rating: 4.7, reviewCount: 421, storeType: 'FLGS', systems: ['40k', 'aos', 'mtg', 'dnd'], systemsAll:['40k', 'aos', 'mtg', 'dnd'], playSpace: true, paintStations: false, terrain: true, gwRetailer: false, commission: false, dropIn: true, discountPct: { value: 0, scope: 'matches online retail', real: true }, events: [ { day: 'fri', time: '19:00', system: 'mtg', name: 'MTG Friday Draft', real: true }, { day: 'sat', time: '13:00', system: '40k', name: '40K Crusade Saturday', real: false }, ], summary: 'Free-to-use tabletops with terrain. Pricing matches online retailers, so you can buy locally without losing the deal.', factoids: [ 'Pricing matches online retailers, so you can buy locally and play the same day.', 'Several free-to-use tabletops with terrain pieces and a board-game library to try before buying.', 'Family-friendly community supportive of all skill levels.', ], insights: [ { type: 'money_saver', text: "Competitive pricing matches online retailers. Support local without paying the premium." }, { type: 'pro_tip', text: "Multiple free tabletops with terrain. Walk in for 40K or AoS without reserving." }, { type: 'first_timer', text: "Knowledgeable, welcoming staff. Ask for help building your first Warhammer army." }, ], landmarks: ['Discovery Park', 'The Escape Date'], photos: 6, }, { id: 'dragons-keep-orem', placeId: 'ChIJ04_kbMyaTYcR5tos2xfmldE', name: "The Dragon's Keep · Orem", city: 'Orem', state: 'UT', zip: '84057', address: '48 W 300 N, Orem, UT 84057', phone: '(801) 607-1156', website: 'http://www.dragonskeep.com/', lat: 40.3031637, lng: -111.6965137, rating: 4.4, reviewCount: 536, storeType: 'hybrid', systems: ['old-world', '40k', 'aos'], systemsAll:['old-world', '40k', 'aos', 'mtg', 'lorcana', 'pokemon'], playSpace: true, paintStations: true, terrain: true, gwRetailer: true, commission: false, dropIn: true, discountPct: { value: 5, scope: 'loyalty members', real: false }, events: [ { day: 'thu', time: '19:00', system: 'multi', name: 'Weekly Game Night', real: true }, { day: 'sat', time: '10:00', system: 'multi', name: 'Family Fun Day (1st Sat/mo)', real: true }, ], summary: 'Flagship of the Dragon\'s Keep franchise. Basement gaming area with 4-6 tables plus separate paint room.', factoids: [ 'Hosts Warhammer The Old World events monthly across 25+ events.', 'Basement gaming area: 4-6 tables plus a separate room with excellent painting lighting.', 'Expert staff conducts hands-on hobby demos.', ], insights: [ { type: 'best_time', text: "Thursday 7 PM Weekly Game Night brings the welcoming regular community out." }, { type: 'what_to_expect', text: "Basement gaming area with 4-6 tables and a separate painting room with great lighting." }, { type: 'pro_tip', text: "Ask for Jeremy: knowledgeable on miniatures and gaming systems, and uses the products himself." }, ], landmarks: ['Dreamwalk Park', 'Timpanogos Cave', "Nielsen's Grove Park"], photos: 6, }, { id: 'dragons-keep-provo', placeId: 'ChIJ9Za2lKyQTYcRSIlef8GHyyk', name: "Dragons Keep · Provo", city: 'Provo', state: 'UT', zip: '84601', address: '260 N University Ave, Provo, UT 84601', phone: '(801) 373-3482', website: 'http://www.dragonskeep.com/', lat: 40.2372028, lng: -111.6582361, rating: 4.4, reviewCount: 916, storeType: 'hybrid', systems: ['old-world', '40k', 'aos'], systemsAll:['old-world', '40k', 'aos', 'mtg', 'pokemon', 'lorcana'], playSpace: true, paintStations: true, terrain: true, gwRetailer: true, commission: false, dropIn: true, discountPct: { value: 5, scope: 'loyalty members', real: false }, events: [ { day: 'thu', time: '19:00', system: 'multi', name: 'Weekly Game Night', real: true }, { day: 'sat', time: '10:00', system: 'multi', name: 'Family Fun Day (1st Sat/mo)', real: true }, ], summary: 'Dragon\'s Keep Provo location. Largest review count of the franchise (900+).', factoids: [ 'Large play area with great lighting for painting and plenty of space.', 'Four locations across Utah Valley; serves as a central gathering point for 500+ community members.', ], insights: [ { type: 'best_time', text: "Thursday 7 PM and first-Saturday Family Fun Day." }, { type: 'first_timer', text: "Welcoming community focused on helping new players improve, not crushing them." }, ], landmarks: ['The Rift', 'Provo Pioneer Village', 'Rock Canyon', 'Bean Life Science Museum'], photos: 6, }, { id: 'dragons-keep-spanish-fork', placeId: 'ChIJ2ad2ltC8TYcRTOU-U42ou3k', name: "Dragon's Keep · Spanish Fork", city: 'Spanish Fork', state: 'UT', zip: '84660', address: '239 N Main St, Spanish Fork, UT 84660', phone: '(801) 504-6522', website: 'https://www.dragonskeep.com/', lat: 40.1130811, lng: -111.6551758, rating: 4.5, reviewCount: 259, storeType: 'hybrid', systems: ['old-world', '40k', 'aos'], systemsAll:['old-world', '40k', 'aos', 'mtg', 'lorcana'], playSpace: true, paintStations: true, terrain: true, gwRetailer: true, commission: false, dropIn: true, discountPct: { value: 5, scope: 'loyalty members', real: false }, events: [ { day: 'thu', time: '19:00', system: 'multi', name: 'Weekly Game Night', real: true }, ], summary: 'Spanish Fork location. Good painting lighting noted in reviews.', factoids: [ 'Multiple gaming tables with terrain; drop-in friendly with purchase.', 'Good painting lighting and snacks available.', ], insights: [ { type: 'what_to_expect', text: "Casual, friendly environment. Space to relax and play." }, ], landmarks: ['Fifth Water Hot Springs Trailhead', 'The Rift'], photos: 6, }, { id: 'power-9-games', placeId: 'ChIJG_ks2VrdyIAR3lKb7Tns8jY', name: 'Power 9 Games', city: 'North Las Vegas', state: 'NV', zip: '89030', address: '2575 E Craig Rd suite c, North Las Vegas, NV 89030', phone: '(702) 643-8889', website: 'http://powerninegames.com/', lat: 36.2392701, lng: -115.1154988, rating: 4.4, reviewCount: 554, storeType: 'FLGS', systems: ['40k', 'mtg'], systemsAll:['40k', 'mtg', 'pokemon', 'digimon', 'dnd'], playSpace: true, paintStations: false, terrain: true, gwRetailer: false, commission: false, dropIn: true, discountPct: null, events: [ { day: 'sat', time: '12:00', system: '40k', name: '40K Open Play', real: false }, ], summary: 'Comprehensive hobby retailer with dedicated wargame terrain. Mixed service reputation.', factoids: [ 'Store terrain available for wargaming on pre-made gaming tables.', 'Stocks 40K alongside MTG, Pokemon, Digimon, board games, and RPGs.', ], insights: [ { type: 'pro_tip', text: "Ask for Kaitlin or Fran specifically. Service quality varies; these two get consistently positive mentions." }, { type: 'money_saver', text: "Pricing often runs above MSRP. Compare before larger purchases." }, ], landmarks: ['AREA15', 'Aliante Nature Discovery Park', 'Fremont Street Experience'], photos: 6, }, { id: 'voidheadz', placeId: 'ChIJw1BKqzDFyIAR3-VXJzF5B3w', name: 'VoidHeadz Gaming and Hobby', city: 'Las Vegas', state: 'NV', zip: '89103', address: '3560 Polaris Ave Unit 6, Las Vegas, NV 89103', phone: '(725) 205-4072', website: 'http://www.voidheadz.com/', lat: 36.1244787, lng: -115.1852663, rating: 5.0, reviewCount: 131, storeType: 'hybrid', systems: ['40k'], systemsAll:['40k'], playSpace: true, paintStations: false, terrain: true, gwRetailer: true, commission: true, dropIn: true, discountPct: { value: null, scope: 'exclusive player discount', real: true }, events: [ { day: 'wed', time: '18:00', system: '40k', name: 'Paint & Play', real: false }, { day: 'sat', time: '12:00', system: '40k', name: '40K Pickup Saturdays', real: false }, ], summary: 'Owner Thomas has 20+ years of mini painting. Best-looking gaming tables in Vegas per reviewers. No table fees.', factoids: [ "Owner Thomas has 20+ years painting miniatures. Commission painting available with gallery.", 'No table fees, so regular gaming carries no recurring cost.', 'Reviewers describe the best-looking gaming tables in Las Vegas for 40K.', 'Offers 3D custom prints and props alongside GW retail.', 'Exclusive player discount program for regulars.', ], insights: [ { type: 'pro_tip', text: "Owner Thomas has 20+ years painting experience. Ask for guidance on model selection, technique, and hobby advice." }, { type: 'best_time', text: "Game nights and weekends. Terrain-rich tables, no table fees." }, { type: 'first_timer', text: "Welcoming to newcomers. Community members teach hobby fundamentals on the floor." }, ], landmarks: ['Sphere', 'AREA15', 'Springs Preserve'], photos: 6, }, { id: 'war-room-games', placeId: 'ChIJQ_OfA_XPyIARMSghbw4cEJ0', name: 'War Room Games', city: 'Las Vegas', state: 'NV', zip: '89120', address: '2510 E Sunset Rd #3, Las Vegas, NV 89120', phone: '(702) 685-6445', website: 'http://www.warroomgameswrg.com/', lat: 36.0724304, lng: -115.1160656, rating: 4.8, reviewCount: 347, storeType: 'hybrid', systems: ['40k', 'aos', 'horus-heresy'], systemsAll:['40k', 'aos', 'horus-heresy', 'mtg', 'dnd', 'star-wars'], playSpace: true, paintStations: false, terrain: true, gwRetailer: false, commission: false, dropIn: true, discountPct: null, events: [ { day: 'sat', time: '10:00', system: '40k', name: '40K Saturday Open Play', real: true }, ], summary: 'Comprehensive Warhammer range including Horus Heresy. Owner is hobby expert; gives painting advice on demand.', factoids: [ 'Carries 40K, Age of Sigmar, and Horus Heresy alongside multiple paint brands and used gaming merchandise.', 'Saturday open play with established 40K community ("come early; bring lotza DAKA").', 'Owner provides painting advice; multiple large gaming tables with terrain.', ], insights: [ { type: 'best_time', text: "Saturday open play. Come early, bring your army. The established community welcomes drop-ins." }, { type: 'pro_tip', text: "Staff knowledgeable about painting. The owner is described as passionate and helpful." }, { type: 'first_timer', text: "Staff actively introduces newcomers to experienced local players and mentors." }, ], landmarks: ['Sphere', 'Museum of Illusions Las Vegas', 'AREA15'], photos: 6, }, { id: 'frontline-gaming', placeId: 'ChIJtaAqm-0ryYARYpA36gBu6zw', name: 'Frontline Gaming', city: 'Las Vegas', state: 'NV', zip: '89119', address: '1650 E Helm Ave Suite 200, Las Vegas, NV 89119', phone: '', website: 'https://www.store.frontlinegaming.org/', lat: 36.0685009, lng: -115.1306684, rating: 4.3, reviewCount: 104, storeType: 'FLGS', systems: ['40k'], systemsAll:['40k', 'mtg', 'pokemon'], playSpace: true, paintStations: false, terrain: true, gwRetailer: false, commission: true, dropIn: true, discountPct: { value: 15, scope: 'most Warhammer models', real: true }, events: [ { day: 'fri', time: '19:00', system: 'mtg', name: 'Friday Night Magic', real: true }, { day: 'sat', time: '14:00', system: '40k', name: '40K Learn-to-Play', real: true }, ], summary: 'Manufactures custom gaming mats in-house. 15% off most Warhammer models, one of the best pricing options in Vegas.', factoids: [ '15% off most Warhammer models vs GW retail. Best-in-class pricing.', 'In-house manufacturer of custom 6×4 gaming mats with storage bags.', 'Pro miniature painting commission service.', 'Multiple terrain-equipped tables; snacks and restrooms support extended play sessions.', ], insights: [ { type: 'money_saver', text: "Most Warhammer models at 15% off MSRP. One of the best pricing options for building an army." }, { type: 'first_timer', text: "Learn-to-play sessions for beginners. Staff member Jet is known for patient guidance with new players." }, { type: 'pro_tip', text: "Professional commission painting available. Longer timeline, but excellent quality and communication." }, ], landmarks: ['John Wick Experience', 'Sphere', 'AREA15'], photos: 6, }, { id: 'warhammer-las-vegas', placeId: 'ChIJ2WKm6bfIyIAR0JDcLtSbj3g', name: 'Warhammer Las Vegas', city: 'Las Vegas', state: 'NV', zip: '89139', address: '4270 Blue Diamond Rd #104, Las Vegas, NV 89139', phone: '(702) 432-3008', website: 'https://www.warhammer.com/', lat: 36.0392743, lng: -115.1983425, rating: 4.2, reviewCount: 181, storeType: 'dedicated', systems: ['40k', 'horus-heresy', 'aos', 'kill-team', 'blood-bowl'], systemsAll:['40k', 'horus-heresy', 'aos', 'kill-team', 'blood-bowl', 'underworlds'], playSpace: 'limited', paintStations: true, terrain: false, gwRetailer: true, commission: false, dropIn: true, discountPct: null, events: [ { day: 'sat', time: '12:00', system: '40k', name: '40K Crusade', real: false }, { day: 'sun', time: '13:00', system: 'kill-team', name: 'Kill Team Sundays', real: false }, ], summary: 'Full GW system range: 40K, Horus Heresy, AoS, Kill Team, Blood Bowl, Underworlds. Largest GW system stock in Vegas.', factoids: [ 'Full GW system range across 40K, Horus Heresy, AoS, Kill Team, and Blood Bowl.', 'Dedicated GW store with hobby supplies, terrain products, and gaming mats.', ], insights: [ { type: 'what_to_expect', text: "Full GW range across 40K, HH, AoS, Kill Team. Best system breadth in Vegas." }, ], landmarks: ['Museum of Illusions Las Vegas', 'Shark Reef Aquarium', 'Springs Preserve'], photos: 6, }, { id: 'warhammer-spokane', placeId: 'ChIJS4GZ55wjnlQRf5x5HPvubl8', name: 'Warhammer Spokane', city: 'Spokane', state: 'WA', zip: '99223', address: '2718 E 57th Ave Suite 104, Spokane, WA 99223', phone: '(509) 606-1621', website: '', lat: 47.6017016, lng: -117.3698802, rating: 4.8, reviewCount: 58, storeType: 'dedicated', systems: ['40k'], systemsAll:['40k'], playSpace: true, paintStations: true, terrain: false, gwRetailer: true, commission: false, dropIn: true, discountPct: null, events: [ { day: 'sat', time: '12:00', system: '40k', name: '40K Newcomer Event', real: true }, { day: 'wed', time: '18:00', system: '40k', name: 'Painting Lessons', real: true }, ], summary: 'Dedicated GW store with the strongest beginner program in the network: free practice models, painting lessons, model borrowing.', factoids: [ 'Free practice models and painting lessons. Community painters provide coaching at dedicated stations.', 'Lets customers borrow models for play, eliminating the entry barrier for new hobbyists.', 'Drop-in play tables: free with purchase or bring your own.', 'Manager remembers individual customer names and provides detailed 40K lore and rules coaching.', ], insights: [ { type: 'pro_tip', text: "Michael (manager) knows 40K lore and rules cold. Ask questions; he is patient with new players." }, { type: 'first_timer', text: "Dedicated newcomer events. Free practice models and painting lessons make this an ideal start." }, { type: 'what_to_expect', text: "Drop in free with purchase, or borrow store models. Painting stations with active community painters." }, ], landmarks: ['Manito Park', 'Riverfront Park', 'Spokane Falls'], photos: 6, }, { id: 'gamers-haven-spokane', placeId: 'ChIJCT4mlQUgnlQRYDRqvEOPQd8', name: 'The Gamers Haven', city: 'Spokane', state: 'WA', zip: '99201', address: '1403 W Broadway Ave, Spokane, WA 99201', phone: '(509) 443-5992', website: 'http://www.thegamershaven.net/', lat: 47.6642073, lng: -117.4331187, rating: 4.7, reviewCount: 528, storeType: 'FLGS', systems: ['40k', 'mtg', 'dnd'], systemsAll:['40k', 'mtg', 'dnd', 'pokemon', 'lorcana', 'yugioh'], playSpace: true, paintStations: false, terrain: false, gwRetailer: false, commission: false, dropIn: true, discountPct: { value: 5, scope: 'members', real: false }, events: [ { day: 'fri', time: '19:00', system: 'mtg', name: 'Friday Night Magic', real: true }, { day: 'sat', time: '14:00', system: '40k', name: '40K Saturdays', real: false }, ], summary: 'Welcoming community FLGS. Demo nights for trying games before purchase. Open tables for D&D campaigns.', factoids: [ 'Demo nights, so you can try a game before purchase.', 'Open tables for D&D campaigns and pickup play.', 'Welcoming to beginners; matches people with games they\'ll love.', ], insights: [ { type: 'first_timer', text: "Welcoming to beginners. Staff matches new players with games that fit their interests." }, { type: 'best_time', text: "Friday Night Magic + Saturday game days." }, ], landmarks: ['Riverfront Park', 'Spokane Falls', 'The Great Northern Clocktower'], photos: 6, }, { id: 'warhammer-south-portland', placeId: 'ChIJJRmVh2uZskwRM3dEMJJ7Cso', name: 'Warhammer South Portland', city: 'South Portland', state: 'ME', zip: '04106', address: '343 Gorham Rd, South Portland, ME 04106', phone: '(207) 253-0300', website: 'https://www.warhammer.com/', lat: 43.6368151, lng: -70.3299623, rating: 4.2, reviewCount: 111, storeType: 'dedicated', systems: ['40k', 'aos', 'kill-team'], systemsAll:['40k', 'aos', 'kill-team'], playSpace: 'limited', paintStations: true, terrain: false, gwRetailer: true, commission: false, dropIn: true, discountPct: null, events: [ { day: 'sat', time: '12:00', system: '40k', name: '40K Crusade', real: false }, ], summary: 'Dedicated GW store. Service quality varies by staff; Dan is the painting instruction standout.', factoids: [ 'Stocks 40K, Kill Team, and Age of Sigmar with paint supplies and rulebooks.', 'Staff member Dan praised for painting instruction and encouraging returning hobbyists.', 'Service quality reportedly inconsistent. Independent competitors (Crossroads, Greenhouse, Wizard Ridge) gain customers on pricing and service.', ], insights: [ { type: 'first_timer', text: "Staff quality inconsistent. If service is poor, try nearby independents (Crossroads, Greenhouse, Wizard Ridge)." }, { type: 'pro_tip', text: "Ask for Dan if returning to the hobby; strong painting instruction reputation." }, { type: 'money_saver', text: "Pricing higher than independent game stores. Expect $10+ markup on some sets. Reserve for pre-orders and specialty paints needed immediately." }, ], landmarks: ['Victoria Mansion', 'Fort Gorges', 'Portland Head Light'], photos: 6, }, ]; // Real photo URL hashes (from the R2 CDN, pulled from enriched_listings sheet column AB). const PHOTO_HASHES = { 'ChIJ_7SifrCJUocRUWHIT-LKWK8': ['0_532fb4a7','1_ac6fe9d9','2_3c110fdf','3_92a1963f','4_044a81c5','5_0f473bea'], 'ChIJJ0yfFQWXTYcR38I6rytEX9A': ['0_69e35c46','1_72af963c','2_e13d18ce','3_2bc20640','4_4635733a','5_53f7dfcf'], 'ChIJB24g7JWFUocRG3U6GORA2f8': ['0_35c99e7b','1_89ccaaf3','2_c5fc4f5e','3_78aafbd4','4_1738ffe5','5_6c03b635'], 'ChIJQ-ILmC2ATYcRu_L_nrwBdgE': ['0_6ad81f0b','1_f743d0ad','2_0bcb05c5','3_f8aa6b54','4_07a9a6b7','5_f2d1d339'], 'ChIJ95IqHGeDTYcRJKU32VM--zc': ['0_cb555402','1_f5d150f3','2_c6e22365','3_58cb5f47','4_24ef6067','5_8f902f53'], 'ChIJpRHbUhSETYcR83LfkFQkS60': ['0_e29559e3','1_19d0c242','2_5782cfa7','3_8a55208c','4_64d1cf93','5_65ebcb7c'], 'ChIJ04_kbMyaTYcR5tos2xfmldE': ['0_33a753ec','1_fcde8ec6','2_26ba3ea7','3_2328a0c9','4_3dede9e5','5_19d1fd15'], 'ChIJ9Za2lKyQTYcRSIlef8GHyyk': ['0_6cd43cf9','1_202cfb12','2_8b759da3','3_cd3a6304','4_ef57f33e','5_6f1efae1'], 'ChIJ2ad2ltC8TYcRTOU-U42ou3k': ['0_19cd0e9f','1_5cdaf55d','2_d5178a31','3_b1ea354f','4_870da057','5_221b66ea'], 'ChIJG_ks2VrdyIAR3lKb7Tns8jY': ['0_bea0b260','1_cf26d7fd','2_8165f03d','3_eae1be35','4_a8b91877','5_762cd451'], 'ChIJw1BKqzDFyIAR3-VXJzF5B3w': ['0_0d850e8f','1_947cca4e','2_c043336a','3_6852552c','4_7e642357','5_d81661e7'], 'ChIJQ_OfA_XPyIARMSghbw4cEJ0': ['0_507584b6','1_921410a9','2_ea5f002f','3_327032e9','4_9acee4a9','5_8a351b28'], 'ChIJtaAqm-0ryYARYpA36gBu6zw': ['0_535a4828','1_ad902f79','2_40b945cf','3_5e0c3da7','4_8e088dee','5_513de82f'], 'ChIJ2WKm6bfIyIAR0JDcLtSbj3g': ['0_66ecf0ee','1_da137cd6','2_fe70fbd2','3_cc73bf20','4_862a4f73','5_90b5f99a'], 'ChIJS4GZ55wjnlQRf5x5HPvubl8': ['0_1c00dd8b','1_899cc442','2_b630a540','3_dcd52fdf','4_874d2242','5_ee6659ef'], 'ChIJCT4mlQUgnlQRYDRqvEOPQd8': ['0_5dc83d99','1_94c7798d','2_400db3f5','3_358a6203','4_ebd04e20','5_5b8919d4'], 'ChIJJRmVh2uZskwRM3dEMJJ7Cso': ['0_54de0d4b','1_c7fd3f9b','2_f78acea6','3_86760e7f','4_e97a9cfa','5_59fdaaa2'], }; // Decorate with distance from user location and resolved photo URLs ─ STORES.forEach(s => { s.miles = milesBetween(USER_LOCATION, s); s.drive = driveTime(s.miles); const hashes = PHOTO_HASHES[s.placeId] || []; s.photos = hashes.map(h => `${PHOTO_CDN}/${s.placeId}/${h}.jpg`); }); // Day name helpers ────────────────────────────────────────────── const DAYS_FULL = { mon: 'Monday', tue: 'Tuesday', wed: 'Wednesday', thu: 'Thursday', fri: 'Friday', sat: 'Saturday', sun: 'Sunday' }; const DAYS_ORDER = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun']; // System lookup ───────────────────────────────────────────────── function sys(id) { return SYSTEMS.find(s => s.id === id) || { id, name: id, short: id }; } // Flatten events into a chronological-by-weekday feed ───────── function eventsFeed(stores, opts = {}) { const list = []; stores.forEach(s => { s.events.forEach(e => { list.push({ ...e, storeId: s.id, storeName: s.name, storeCity: s.city, storeMiles: s.miles, storeDrive: s.drive, }); }); }); // Sort by weekday order, then time list.sort((a, b) => { const dayDiff = DAYS_ORDER.indexOf(a.day) - DAYS_ORDER.indexOf(b.day); if (dayDiff !== 0) return dayDiff; return a.time.localeCompare(b.time); }); return list; } window.WGN = { SYSTEMS, STORES, USER_LOCATION, DAYS_FULL, DAYS_ORDER, sys, eventsFeed, milesBetween, driveTime, };