// ================================================================= // CONFIG // ================================================================= var WORKER = 'https://bizz-r-us-printshop.processwithus.workers.dev'; var STRIPE_PK = 'pk_test_51Tvn33EzKD8ajis8JS3CQkQYFKhekegoVptRdwOCHvFE9rOcfvCbGbmpmgn3zpdGFihAxzvboJ9nGkuG18fS0uV700rvNzPpE2'; var MARKUP = 3; // x3 = 200% markup — NEVER shown to customer var BULK = [ { min:2500, pct:25 }, { min:1000, pct:20 }, { min:500, pct:15 }, { min:250, pct:10 }, { min:50, pct:5 }, { min:0, pct:0 } ]; // ================================================================= // BASE PRICES (wholesale per-unit — fallback when Sinalite API // doesn't return a price; frontend applies x3 markup on top) // ================================================================= var BASE_PRICES = { 'Business Cards': [ {max:25, p:0.72}, {max:50, p:0.46}, {max:100, p:0.28}, {max:250, p:0.15}, {max:500, p:0.10}, {max:1000, p:0.068}, {max:2500, p:0.048},{max:5000, p:0.036},{max:99999, p:0.027} ], 'Flyers': [ {max:100, p:0.55}, {max:250, p:0.32}, {max:500, p:0.20}, {max:1000, p:0.14}, {max:2500, p:0.098},{max:5000, p:0.075}, {max:99999,p:0.058} ], 'Postcards': [ {max:100, p:0.46}, {max:250, p:0.28}, {max:500, p:0.18}, {max:1000, p:0.12}, {max:2500, p:0.086},{max:5000, p:0.065}, {max:99999,p:0.050} ], 'Rack Cards': [ {max:250, p:0.26}, {max:500, p:0.17}, {max:1000, p:0.11}, {max:2500, p:0.078},{max:5000, p:0.060},{max:99999,p:0.045} ], 'Brochures': [ {max:250, p:0.44}, {max:500, p:0.28}, {max:1000, p:0.19}, {max:2500, p:0.14}, {max:5000, p:0.11}, {max:99999,p:0.083} ], 'Door Hangers': [ {max:250, p:0.38}, {max:500, p:0.24}, {max:1000, p:0.16}, {max:2500, p:0.11}, {max:5000, p:0.086},{max:99999,p:0.065} ], 'Table Tents': [ {max:250, p:0.56}, {max:500, p:0.36}, {max:1000, p:0.24}, {max:2500, p:0.17}, {max:5000, p:0.13}, {max:99999,p:0.10} ], 'Bookmarks': [ {max:250, p:0.22}, {max:500, p:0.145},{max:1000, p:0.098}, {max:2500, p:0.070},{max:5000, p:0.054},{max:99999,p:0.040} ], 'Stickers': [ {max:250, p:0.28}, {max:500, p:0.18}, {max:1000, p:0.12}, {max:2500, p:0.086},{max:5000, p:0.065},{max:99999,p:0.050} ], 'Labels': [ {max:250, p:0.25}, {max:500, p:0.16}, {max:1000, p:0.11}, {max:2500, p:0.076},{max:5000, p:0.058},{max:99999,p:0.043} ], 'Magnets': [ {max:100, p:1.22}, {max:250, p:0.84}, {max:500, p:0.60}, {max:1000, p:0.42}, {max:2500, p:0.30}, {max:99999,p:0.22} ], 'Banners': [ {max:1, p:28}, {max:5, p:22}, {max:10, p:18}, {max:25, p:15}, {max:50, p:12}, {max:99999,p:10} ], 'Signs': [ {max:5, p:12}, {max:10, p:9.0}, {max:25, p:7.5}, {max:50, p:6.2}, {max:100, p:5.0}, {max:99999,p:4.0} ], 'Large Format': [ {max:1, p:45}, {max:5, p:38}, {max:10, p:32}, {max:25, p:27}, {max:50, p:22}, {max:99999,p:18} ], 'Posters': [ {max:25, p:8.5}, {max:50, p:6.8}, {max:100, p:5.5}, {max:250, p:4.2}, {max:500, p:3.5}, {max:99999,p:2.8} ], 'Stationery': [ {max:250, p:0.18},{max:500, p:0.12},{max:1000, p:0.082}, {max:2500, p:0.058},{max:5000,p:0.044},{max:99999,p:0.034} ], 'Folders': [ {max:100, p:2.20},{max:250, p:1.65},{max:500, p:1.25}, {max:1000, p:0.95},{max:2500, p:0.72},{max:99999,p:0.55} ], 'Booklets': [ {max:100, p:3.50},{max:250, p:2.60},{max:500, p:1.95}, {max:1000, p:1.45},{max:2500, p:1.10},{max:99999,p:0.85} ], 'NCR Forms': [ {max:250, p:0.55},{max:500, p:0.38},{max:1000, p:0.26}, {max:2500, p:0.19},{max:99999,p:0.14} ], 'Calendars': [ {max:100, p:4.50},{max:250, p:3.20},{max:500, p:2.40}, {max:1000, p:1.80},{max:99999,p:1.40} ], 'Specialty': [ {max:100, p:1.80},{max:250, p:1.20},{max:500, p:0.88}, {max:1000, p:0.65},{max:99999,p:0.50} ] }; function getBasePrice(p, qty) { if (!p) return 0.15; var tiers = BASE_PRICES[p.group]; if (!tiers) { // Fallback: try a partial group match var keys = Object.keys(BASE_PRICES); for (var k = 0; k < keys.length; k++) { if (p.group.toLowerCase().indexOf(keys[k].toLowerCase()) !== -1 || keys[k].toLowerCase().indexOf(p.group.toLowerCase()) !== -1) { tiers = BASE_PRICES[keys[k]]; break; } } } if (!tiers) return 0.12; for (var i = 0; i < tiers.length; i++) { if (qty <= tiers[i].max) return tiers[i].p; } return tiers[tiers.length - 1].p; } // ================================================================= // PRODUCT CATALOG — 46 products // ================================================================= var CATALOG = [ // — BUSINESS CARDS — { name:'Business Cards 14pt', group:'Business Cards', cat:'Business Cards', desc:'The industry-standard 14pt business card with rich CMYK printing. Available in gloss, matte, or uncoated finishes. Sharp edges, professional weight.', emoji:'💼', img:'https://images.unsplash.com/photo-1589829545856-d10d557cf95f?w=400&q=80', sizes:['3.5" x 2"','3.5" x 2" Vertical','2" x 3.5"'], rc:false }, { name:'Business Cards 14pt UV Gloss', group:'Business Cards', cat:'Business Cards', desc:'14pt cardstock with a high-gloss UV coating for a premium, eye-catching finish that protects against everyday wear.', emoji:'✨', img:'https://images.unsplash.com/photo-1540479859555-17af45c78602?w=400&q=80', sizes:['3.5" x 2"','3.5" x 2" Vertical'], rc:false }, { name:'Business Cards 16pt Silk', group:'Business Cards', cat:'Business Cards', desc:'16pt silk laminated cards with a soft, luxurious semi-matte feel. Stands out in any stack.', emoji:'🎴', img:'https://images.unsplash.com/photo-1611532736597-de2d4265fba3?w=400&q=80', sizes:['3.5" x 2"','3.5" x 2" Vertical'], rc:true }, { name:'Business Cards 18pt Soft Touch', group:'Business Cards', cat:'Business Cards', desc:'18pt ultra-thick cardstock with a velvety soft-touch matte lamination. The card clients remember.', emoji:'🤞', img:'https://images.unsplash.com/photo-1572635196237-14b3f281503f?w=400&q=80', sizes:['3.5" x 2"','3.5" x 2" Vertical'], rc:true }, { name:'Round Corner Business Cards', group:'Business Cards', cat:'Business Cards', desc:'Smooth rounded corners give your card a modern, polished look that stands out from the standard stack.', emoji:'🔵', img:'https://images.unsplash.com/photo-1558655146-d09347e92766?w=400&q=80', sizes:['3.5" x 2"'], rc:true }, { name:'Ultra Thick 32pt Business Cards',group:'Business Cards', cat:'Business Cards', desc:'Nearly credit-card thickness. Extremely durable and memorable — a bold statement for premium brands.', emoji:'💪', img:'https://images.unsplash.com/photo-1600880292203-757bb62b4baf?w=400&q=80', sizes:['3.5" x 2"'], rc:false }, // — FLYERS — { name:'Flyers 8.5x11', group:'Flyers', cat:'Flyers', desc:'Full letter-size flyers on 100lb gloss or matte text stock. Perfect for promos, events, and announcements.', emoji:'📢', img:'https://images.unsplash.com/photo-1568702846914-96b305d2aaeb?w=400&q=80', sizes:['8.5" x 11"'], rc:false }, { name:'Flyers 5.5x8.5 Half Sheet',group:'Flyers', cat:'Flyers', desc:'Half-page flyers — compact, cost-effective, easy to hand out. Great for menus, coupons, and promos.', emoji:'📄', img:'https://images.unsplash.com/photo-1588681664899-f142ff2dc9b1?w=400&q=80', sizes:['5.5" x 8.5"'], rc:false }, { name:'Flyers 11x17 Tabloid', group:'Flyers', cat:'Flyers', desc:'Oversized tabloid flyers for maximum impact. Perfect for event posters and in-store displays.', emoji:'📰', img:'https://images.unsplash.com/photo-1504711434969-e33886168f5c?w=400&q=80', sizes:['11" x 17"'], rc:false }, { name:'Sell Sheets', group:'Flyers', cat:'Flyers', desc:'Single-page sell sheets on heavy cardstock. The essential B2B sales tool for meetings and trade shows.', emoji:'💼', img:'https://images.unsplash.com/photo-1460925895917-afdab827c52f?w=400&q=80', sizes:['8.5" x 11"','11" x 17"'], rc:false }, // — POSTCARDS — { name:'Postcards 4x6', group:'Postcards', cat:'Postcards', desc:'The classic direct-mail postcard on 14pt or 16pt cardstock. Durable and bright enough to get noticed.', emoji:'📮', img:'https://images.unsplash.com/photo-1534236789892-b39a2d882b0a?w=400&q=80', sizes:['4" x 6"'], rc:false }, { name:'Postcards 5x7', group:'Postcards', cat:'Postcards', desc:'A versatile mid-size postcard that dominates the mailbox. Great for real estate and promo campaigns.', emoji:'📬', img:'https://images.unsplash.com/photo-1563013544-824ae1b704d3?w=400&q=80', sizes:['5" x 7"'], rc:false }, { name:'Postcards 6x9', group:'Postcards', cat:'Postcards', desc:'Large-format 6x9 postcards command attention. Top choice for real estate and service businesses.', emoji:'📭', img:'https://images.unsplash.com/photo-1579389083078-4e7018379f7e?w=400&q=80', sizes:['6" x 9"'], rc:false }, { name:'Postcards 6x11', group:'Postcards', cat:'Postcards', desc:'Extra-large 6x11 — the maximum USPS postcard size. Premium canvas for high-impact direct mail.', emoji:'📫', img:'https://images.unsplash.com/photo-1526948128573-703ee1aeb6fa?w=400&q=80', sizes:['6" x 11"'], rc:false }, { name:'EDDM Postcards', group:'Postcards', cat:'EDDM Postcards', desc:'Every Door Direct Mail sized to USPS EDDM specs. Reach every household in a zip code effortlessly.', emoji:'🏡', img:'https://images.unsplash.com/photo-1560518883-ce09059eeffa?w=400&q=80', sizes:['6.5" x 9"','6.5" x 12"','8" x 6.5"','8.5" x 11"','9" x 12"'], rc:false }, // — RACK CARDS — { name:'Rack Cards 4x9', group:'Rack Cards', cat:'Rack Cards', desc:'Standard rack card size. Fits brochure holders perfectly. Ideal for tourism, restaurants, and real estate.', emoji:'📁', img:'https://images.unsplash.com/photo-1497366216548-37526070297c?w=400&q=80', sizes:['4" x 9"'], rc:false }, // — BROCHURES — { name:'Tri-Fold Brochures', group:'Brochures', cat:'Brochures', desc:'Classic tri-fold with 6 panels for menus, service guides, and product info.', emoji:'📑', img:'https://images.unsplash.com/photo-1517245386807-bb43f82c33c4?w=400&q=80', sizes:['8.5" x 11" → 3.67" x 8.5"','8.5" x 14" → 3.67" x 11"'], rc:false }, { name:'Bi-Fold Brochures', group:'Brochures', cat:'Brochures', desc:'Clean bi-fold brochures for a 4-panel presentation. Classic for corporate materials.', emoji:'📋', img:'https://images.unsplash.com/photo-1541178735493-479c1a27ed24?w=400&q=80', sizes:['8.5" x 11" → 5.5" x 8.5"','11" x 17" → 8.5" x 11"'], rc:false }, // — DOOR HANGERS — { name:'Door Hangers', group:'Door Hangers', cat:'Door Hangers', desc:'Pre-cut door hangers with hook holes on 14pt or 16pt cardstock. Perfect for local service marketing.', emoji:'🚪', img:'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&q=80', sizes:['3.5" x 8.5"','4.25" x 11"'], rc:false }, // — TABLE TENTS — { name:'Table Tents', group:'Table Tents', cat:'Table Tents', desc:'Self-standing table tent cards on 14pt cardstock. Perfect for restaurants, hotels, and trade show tables.', emoji:'🍽', img:'https://images.unsplash.com/photo-1414235077428-338989a2e8c0?w=400&q=80', sizes:['3.5" x 8.5" (tent)','4" x 6" (tent)'], rc:false }, // — BOOKMARKS — { name:'Bookmarks', group:'Bookmarks', cat:'Bookmarks', desc:'Printed bookmarks on 14pt cardstock. Keep your brand visible every time customers open a book.', emoji:'🔖', img:'https://images.unsplash.com/photo-1476275466078-4007374efbbe?w=400&q=80', sizes:['2" x 7"','2" x 8"'], rc:false }, // — STICKERS — { name:'Circle Stickers', group:'Stickers', cat:'Stickers', desc:'Vibrant circle stickers on premium gloss or matte vinyl. Perfect for branding and packaging.', emoji:'🔵', img:'https://images.unsplash.com/photo-1558618047-f4e7b7c07f8d?w=400&q=80', sizes:['1.5" Circle','2" Circle','2.5" Circle','3" Circle','4" Circle'], rc:false }, { name:'Rectangle Stickers', group:'Stickers', cat:'Stickers', desc:'Versatile rectangle stickers for product labels, packaging, and brand promotion.', emoji:'🟫', img:'https://images.unsplash.com/photo-1536924430914-91f9e2041b83?w=400&q=80', sizes:['2" x 3"','3" x 4"','3" x 5"','4" x 6"'], rc:false }, { name:'Oval Stickers', group:'Stickers', cat:'Stickers', desc:'Eye-catching oval stickers. Popular for wine labels, cosmetics, candles, and handmade goods.', emoji:'🥚', img:'https://images.unsplash.com/photo-1585771724684-38269d6639fd?w=400&q=80', sizes:['2" x 3"','3" x 4"','3.5" x 5"'], rc:false }, { name:'Bumper Stickers', group:'Stickers', cat:'Bumper Stickers', desc:'Durable outdoor vinyl bumper stickers with UV-resistant inks. Weatherproof and long-lasting.', emoji:'🚗', img:'https://images.unsplash.com/photo-1558979158-65a1eaa08691?w=400&q=80', sizes:['3" x 11.5"','4" x 15"'], rc:false }, // — LABELS — { name:'Roll Labels', group:'Labels', cat:'Labels', desc:'Custom die-cut roll labels for product packaging, jars, bottles, and mailers.', emoji:'🏷', img:'https://images.unsplash.com/photo-1553062407-98eeb64c6a62?w=400&q=80', sizes:['2" x 2"','2" x 3"','3" x 3"','3" x 4"','4" x 4"','4" x 6"'], rc:false }, // — MAGNETS — { name:'Business Card Magnets', group:'Magnets', cat:'Magnets', desc:'Business card magnets that stick to fridges and filing cabinets — a daily brand reminder.', emoji:'🧲', img:'https://images.unsplash.com/photo-1597589827317-4c6d6e0a90bd?w=400&q=80', sizes:['3.5" x 2"'], rc:false }, { name:'Car Magnets', group:'Magnets', cat:'Car Magnets', desc:'Removable full-color car magnets. Turn any vehicle into a moving billboard. Reusable.', emoji:'🚙', img:'https://images.unsplash.com/photo-1600880292203-757bb62b4baf?w=400&q=80', sizes:['8" x 12"','12" x 18"','18" x 24"'], rc:false }, // — BANNERS — { name:'Vinyl Banners', group:'Banners', cat:'Vinyl Banners', desc:'Full-color vinyl banners on 13oz or 18oz scrim with hemmed edges and grommets. Outdoor-ready.', emoji:'🏳', img:'https://images.unsplash.com/photo-1505373877841-8d25f7d46678?w=400&q=80', sizes:["2' x 4'","2' x 6'","2' x 8'","3' x 6'","3' x 8'","4' x 8'","4' x 10'","5' x 10'"], rc:false }, { name:'Retractable Banners', group:'Banners', cat:'Retractable Banners', desc:'Portable retractable banner stands for trade shows and events. Sets up in seconds. Bag included.', emoji:'🎪', img:'https://images.unsplash.com/photo-1540575467063-178a50c2df87?w=400&q=80', sizes:['24" x 72"','33" x 78"','36" x 80"'], rc:false }, { name:'Step and Repeat Banners',group:'Banners', cat:'Step and Repeat Banners', desc:'Event step-and-repeat photo backdrops for press events, red carpets, and trade shows.', emoji:'🎬', img:'https://images.unsplash.com/photo-1492684223066-81342ee5ff30?w=400&q=80', sizes:["4' x 8'","6' x 8'","8' x 8'","8' x 10'","10' x 8'"], rc:false }, // — SIGNS — { name:'Yard Signs Coroplast', group:'Signs', cat:'Yard Signs', desc:'Durable 4mm corrugated plastic yard signs with H-wire stakes. Fully weatherproof outdoor signage.', emoji:'🪇', img:'https://images.unsplash.com/photo-1560518883-ce09059eeffa?w=400&q=80', sizes:['12" x 18"','18" x 24"','24" x 36"'], rc:false }, { name:'Foam Board Signs', group:'Signs', cat:'Foam Board', desc:'Lightweight foam board signs for indoor displays, point-of-sale, and trade show booths.', emoji:'📌', img:'https://images.unsplash.com/photo-1571019613454-1cb2f99b2d8b?w=400&q=80', sizes:['11" x 14"','16" x 20"','18" x 24"','24" x 36"'], rc:false }, // — LARGE FORMAT — { name:'Canvas Prints', group:'Large Format', cat:'Canvas', desc:'Gallery-quality canvas prints stretched over solid wood frames. Sharp detail, rich color.', emoji:'🖼', img:'https://images.unsplash.com/photo-1513519245088-0e12902e5a38?w=400&q=80', sizes:['8" x 10"','11" x 14"','16" x 20"','20" x 24"','24" x 36"'], rc:false }, { name:'Posters 18x24', group:'Posters', cat:'Posters', desc:'Classic 18x24 poster prints on satin or gloss paper. Vivid full-color at a great price.', emoji:'🖼️', img:'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&q=80', sizes:['18" x 24"'], rc:false }, { name:'Posters 24x36', group:'Posters', cat:'Posters', desc:'Large 24x36 poster prints — the standard for event and concert posters. Full-bleed detail.', emoji:'🗻', img:'https://images.unsplash.com/photo-1504711434969-e33886168f5c?w=400&q=80', sizes:['24" x 36"'], rc:false }, // — STATIONERY — { name:'Letterhead', group:'Stationery', cat:'Letterhead', desc:'Professional letterhead on 24lb bond or 70lb offset. Sets the tone for business correspondence.', emoji:'📃', img:'https://images.unsplash.com/photo-1586953208448-b95a79798f07?w=400&q=80', sizes:['8.5" x 11"'], rc:false }, { name:'Envelopes No.10', group:'Stationery', cat:'Envelopes', desc:'Standard No.10 business envelopes with full-color printing. Coordinate with your letterhead.', emoji:'✉️', img:'https://images.unsplash.com/photo-1526948128573-703ee1aeb6fa?w=400&q=80', sizes:['No.10 — 4.125" x 9.5"'], rc:false }, { name:'Notepads', group:'Stationery', cat:'Notepads', desc:'Custom branded notepads with 25, 50, or 100 sheets per pad, glued at the top.', emoji:'📓', img:'https://images.unsplash.com/photo-1455390582262-044cdead277a?w=400&q=80', sizes:['4.25" x 5.5"','5.5" x 8.5"','8.5" x 11"'], rc:false }, // — FOLDERS — { name:'Presentation Folders', group:'Folders', cat:'Presentation Folders', desc:'Premium 14pt presentation folders with two inside pockets. Perfect for proposals and sales kits.', emoji:'📁', img:'https://images.unsplash.com/photo-1568992687947-868a62a9f521?w=400&q=80', sizes:['9" x 12" Standard'], rc:false }, // — BOOKLETS — { name:'Saddle Stitch Booklets', group:'Booklets', cat:'Booklets', desc:'Wire-stitched booklets for catalogs, programs, and manuals. Up to 64 pages.', emoji:'📚', img:'https://images.unsplash.com/photo-1481627834876-b7833e8f5570?w=400&q=80', sizes:['5.5" x 8.5"','8.5" x 11"'], rc:false }, { name:'Perfect Bound Books', group:'Booklets', cat:'Perfect Bound', desc:'Flat-spine perfect-bound books for thick catalogs, annual reports, and trade manuals.', emoji:'📖', img:'https://images.unsplash.com/photo-1497633762265-9d179a990aa6?w=400&q=80', sizes:['5.5" x 8.5"','6" x 9"','8.5" x 11"'], rc:false }, // — NCR — { name:'NCR Carbonless Forms', group:'NCR Forms', cat:'NCR Forms', desc:'2-part, 3-part, or 4-part carbonless forms for invoices, work orders, and contracts.', emoji:'📝', img:'https://images.unsplash.com/photo-1554224155-8d04cb21cd6c?w=400&q=80', sizes:['5.5" x 8.5"','8.5" x 11"'], rc:false }, // — CALENDARS — { name:'Wall Calendars', group:'Calendars', cat:'Calendars', desc:'Full-color 12-month wall calendars — a year-long branded touchpoint for clients and partners.', emoji:'📅', img:'https://images.unsplash.com/photo-1506784983877-45594efa4cbe?w=400&q=80', sizes:['8.5" x 11"','11" x 17"'], rc:false }, // — SPECIALTY — { name:'Greeting Cards', group:'Specialty', cat:'Greeting Cards', desc:'Custom folded greeting cards on 14pt or 16pt cardstock. Perfect for holidays and thank-yous.', emoji:'💌', img:'https://images.unsplash.com/photo-1513201099705-a9746072a1ff?w=400&q=80', sizes:['4.25" x 5.5" A2','5" x 7" A7','4" x 9.25" Slim'], rc:false }, { name:'Hang Tags', group:'Specialty', cat:'Hang Tags', desc:'Custom hang tags for clothing, retail, candles, and handmade goods. Thick cardstock with hole punch.', emoji:'🏷️', img:'https://images.unsplash.com/photo-1558618666-fcd25c85cd64?w=400&q=80', sizes:['2" x 3.5"','2.5" x 4"','3" x 4"','3.5" x 4"'], rc:false }, { name:'Window Clings', group:'Specialty', cat:'Window Clings', desc:'Static-cling decals that apply and remove without residue. Great for retail windows.', emoji:'🪟', img:'https://images.unsplash.com/photo-1486325212027-8081e485255e?w=400&q=80', sizes:['4" x 4"','4" x 6"','6" x 6"','6" x 8"','8" x 8"'], rc:false } ]; // ================================================================= // STOCK FALLBACK — product-specific defaults (4/0 = 1-sided, 4/4 = 2-sided) // ================================================================= var STOCK_FALLBACK = { 'Business Cards': [ '14 PT Printed 1 Side (4/0)','14 PT Printed 2 Sides (4/4)', '16 PT Silk Lam 1 Side (4/0)','16 PT Silk Lam 2 Sides (4/4)', '18 PT Soft Touch 1 Side (4/0)','18 PT Soft Touch 2 Sides (4/4)' ], 'Flyers': ['100 lb Gloss Text (4/0)','100 lb Gloss Text (4/4)','80 lb Matte Text (4/0)','80 lb Matte Text (4/4)'], 'Postcards': ['14 PT Gloss 1 Side (4/0)','14 PT Gloss 2 Sides (4/4)','16 PT Silk Lam 1 Side (4/0)','16 PT Silk Lam 2 Sides (4/4)'], 'Rack Cards': ['14 PT Gloss 1 Side (4/0)','14 PT Gloss 2 Sides (4/4)'], 'Brochures': ['80 lb Gloss Text (4/4)','100 lb Gloss Text (4/4)','60 lb Uncoated (4/4)'], 'Door Hangers': ['14 PT Gloss 1 Side (4/0)','14 PT Gloss 2 Sides (4/4)'], 'Table Tents': ['14 PT Gloss (4/4)','16 PT Silk Lam (4/4)'], 'Bookmarks': ['14 PT Gloss 1 Side (4/0)','14 PT Gloss 2 Sides (4/4)'], 'Stickers': ['White Gloss Vinyl (4/0)','White Matte Vinyl (4/0)','Clear Vinyl (4/0)'], 'Labels': ['White Gloss Vinyl (4/0)','White Matte Vinyl (4/0)','Clear Vinyl (4/0)'], 'Magnets': ['17 mil Magnet (4/0)'], 'Banners': ['13 oz Scrim Vinyl (4/0)','18 oz Scrim Vinyl (4/0)'], 'Signs': ['4mm Coroplast (4/0)','4mm Coroplast (4/4)'], 'Booklets': ['60 lb Offset Text (4/4)','80 lb Gloss Text (4/4)','100 lb Gloss Text (4/4)'], 'NCR Forms': ['2-Part Carbonless (4/0)','3-Part Carbonless (4/0)','4-Part Carbonless (4/0)'], 'Large Format': ['Canvas (4/0)'], 'Posters': ['60 lb Satin (4/0)','80 lb Gloss (4/0)'], 'Stationery': ['24 lb Bond (4/0)','70 lb Offset (4/0)','70 lb Offset (4/4)'], 'Folders': ['14 PT Gloss (4/0)'], 'Calendars': ['80 lb Gloss Text (4/4)','100 lb Gloss Text (4/4)'], 'Specialty': ['14 PT Gloss (4/0)','14 PT Gloss (4/4)'], 'default': ['Standard Stock (4/0)','Standard Stock (4/4)'] }; function getFallbackStock(p) { if (!p) return STOCK_FALLBACK['default']; var keys = Object.keys(STOCK_FALLBACK); for (var k = 0; k < keys.length; k++) { if (p.group === keys[k] || p.group.toLowerCase().indexOf(keys[k].toLowerCase()) !== -1) { return STOCK_FALLBACK[keys[k]]; } } return STOCK_FALLBACK['default']; } // ================================================================= // STATE // ================================================================= var cart = []; var currentIdx = -1; var currentProdId = null; var _allStock = []; var _currentSides = '1'; // '1' = 1-sided (4/0), '2' = 2-sided (4/4) var stripeObj = null; var stripeCardEl = null; var toastTimer = null; // ================================================================= // INIT // ================================================================= document.addEventListener('DOMContentLoaded', function() { try { stripeObj = Stripe(STRIPE_PK); } catch(e) {} buildCatNav(); renderGrid(CATALOG); pingWorker(); ['detailModal','filePrepModal','checkoutModal','quoteModal'].forEach(function(id) { document.getElementById(id).addEventListener('click', function(e) { if (e.target === this) { if (id==='detailModal') closeDetail(); if (id==='filePrepModal') closeFilePrepModal(); if (id==='checkoutModal') closeCheckout(); if (id==='quoteModal') closeQuoteModal(); } }); }); document.addEventListener('keydown', function(e) { if (e.key!=='Escape') return; closeDetail(); closeFilePrepModal(); closeCheckout(); closeQuoteModal(); }); }); async function pingWorker() { try { var r = await fetch(WORKER + '/api/products'); document.getElementById('apiStatus').textContent = r.ok ? '' : '⚠️ Estimated pricing'; } catch(e) { document.getElementById('apiStatus').textContent = ''; } } // ================================================================= // CATEGORY NAV // ================================================================= function buildCatNav() { var groups = ['All']; CATALOG.forEach(function(p) { if (groups.indexOf(p.group)===-1) groups.push(p.group); }); var html = ''; groups.forEach(function(g, i) { html += ''; }); document.getElementById('catNav').innerHTML = html; } function filterGroup(btn, group) { document.querySelectorAll('.cnav-btn').forEach(function(b){ b.classList.remove('active'); }); btn.classList.add('active'); renderGrid(group==='All' ? CATALOG : CATALOG.filter(function(p){ return p.group===group; })); } // ================================================================= // PRODUCT GRID // ================================================================= function renderGrid(list) { if (!list.length) { document.getElementById('productsGrid').innerHTML = '
No products in this category.
'; return; } var html = ''; list.forEach(function(p) { var idx = CATALOG.indexOf(p); var imgHtml = p.img ? '' + p.desc + '
'; html += '