Mid Michigan Model Makers’

Saginaw, Michigan International Plastic Modelers’ Society

Contest Registration

2027 Show Registration
4-M Mayhem — Registration Portal :root { –bg: #0d0d0f; –surface: #141418; –card: #1a1a20; –border: #2a2a35; –accent: #e8a020; –accent2: #c45c1a; –text: #e8e8ea; –muted: #6b6b7a; –success: #2ab574; –error: #e05252; } * { margin: 0; padding: 0; box-sizing: border-box; } body { background: var(–bg); color: var(–text); font-family: ‘DM Sans’, sans-serif; min-height: 100vh; overflow-x: hidden; } /* Background texture */ body::before { content: ”; position: fixed; inset: 0; background: radial-gradient(ellipse 80% 60% at 20% 0%, rgba(232,160,32,0.07) 0%, transparent 60%), radial-gradient(ellipse 60% 40% at 80% 100%, rgba(196,92,26,0.06) 0%, transparent 60%); pointer-events: none; z-index: 0; } .container { max-width: 960px; margin: 0 auto; padding: 0 24px; position: relative; z-index: 1; } /* Header */ header { padding: 48px 0 32px; border-bottom: 1px solid var(–border); margin-bottom: 48px; } .header-inner { display: flex; align-items: flex-end; justify-content: space-between; gap: 24px; flex-wrap: wrap; } .show-badge { font-family: ‘DM Mono’, monospace; font-size: 11px; letter-spacing: 0.15em; text-transform: uppercase; color: var(–accent); background: rgba(232,160,32,0.1); border: 1px solid rgba(232,160,32,0.25); padding: 6px 14px; border-radius: 2px; display: inline-block; margin-bottom: 12px; } h1 { font-family: ‘Bebas Neue’, sans-serif; font-size: clamp(48px, 8vw, 82px); line-height: 0.92; letter-spacing: 0.02em; color: var(–text); } h1 span { color: var(–accent); } .show-meta { text-align: right; font-family: ‘DM Mono’, monospace; font-size: 13px; color: var(–muted); line-height: 1.8; } .show-meta strong { color: var(–text); display: block; font-size: 15px; } /* Steps nav */ .steps-nav { display: flex; gap: 0; margin-bottom: 48px; border: 1px solid var(–border); border-radius: 4px; overflow: hidden; } .step-tab { flex: 1; padding: 16px 12px; text-align: center; background: var(–surface); border-right: 1px solid var(–border); cursor: pointer; transition: all 0.2s; position: relative; } .step-tab:last-child { border-right: none; } .step-tab.active { background: var(–card); } .step-tab.active::after { content: ”; position: absolute; bottom: 0; left: 0; right: 0; height: 3px; background: var(–accent); } .step-tab.done { background: var(–surface); } .step-tab.done .step-num { background: var(–success); color: #fff; } .step-num { width: 28px; height: 28px; border-radius: 50%; background: var(–border); color: var(–muted); font-family: ‘DM Mono’, monospace; font-size: 12px; font-weight: 500; display: flex; align-items: center; justify-content: center; margin: 0 auto 6px; transition: all 0.2s; } .step-tab.active .step-num { background: var(–accent); color: #000; } .step-label { font-size: 12px; color: var(–muted); font-weight: 500; letter-spacing: 0.05em; text-transform: uppercase; } .step-tab.active .step-label { color: var(–text); } /* Form panels — light background so all browsers render inputs with black text */ .panel { display: none; } .panel.active { display: block; animation: fadeIn 0.3s ease; } @keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } /* Light form area */ .panel-inner { background: #f4f4f7; border-radius: 8px; padding: 28px; margin-top: 8px; } .section-title { font-family: ‘Bebas Neue’, sans-serif; font-size: 32px; letter-spacing: 0.05em; color: var(–accent); margin-bottom: 4px; } .section-sub { color: #555560; font-size: 14px; margin-bottom: 32px; } /* Form grid */ .form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } .form-grid.cols-3 { grid-template-columns: 1fr 1fr 1fr; } .form-grid.full { grid-template-columns: 1fr; } @media (max-width: 640px) { .form-grid, .form-grid.cols-3 { grid-template-columns: 1fr; } } .field { display: flex; flex-direction: column; gap: 8px; } .field.span2 { grid-column: span 2; } @media (max-width: 640px) { .field.span2 { grid-column: span 1; } } label { font-size: 11px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: #44444f; } label .req { color: var(–accent); margin-left: 2px; } input, select, textarea { background: #ffffff; border: 1px solid #d0d0d8; border-radius: 4px; color: #1a1a22; font-family: ‘DM Sans’, sans-serif; font-size: 15px; padding: 12px 16px; transition: border-color 0.2s, box-shadow 0.2s; width: 100%; -webkit-appearance: none; } input:focus, select:focus, textarea:focus { outline: none; border-color: var(–accent); box-shadow: 0 0 0 3px rgba(232,160,32,0.18); } input.error, select.error { border-color: var(–error); } select { background-color: #ffffff; background-image: url(“data:image/svg+xml,%3Csvg xmlns=’http://www.w3.org/2000/svg’ width=’12’ height=’8′ viewBox=’0 0 12 8’%3E%3Cpath d=’M1 1l5 5 5-5′ stroke=’%23555′ stroke-width=’1.5′ fill=’none’ stroke-linecap=’round’/%3E%3C/svg%3E”); background-repeat: no-repeat; background-position: right 14px center; padding-right: 40px; color: #1a1a22; } select option { background: #ffffff; color: #1a1a22; } input:-webkit-autofill, input:-webkit-autofill:focus { -webkit-box-shadow: 0 0 0 1000px #ffffff inset; -webkit-text-fill-color: #1a1a22; } textarea { resize: vertical; min-height: 90px; } .field-hint { font-size: 12px; color: var(–muted); } .error-msg { font-size: 12px; color: var(–error); display: none; } .field.has-error .error-msg { display: block; } .field.has-error input, .field.has-error select { border-color: var(–error); } /* Entry cards */ .entries-list { display: flex; flex-direction: column; gap: 16px; margin-bottom: 24px; } .entry-card { background: #ffffff; border: 1px solid #d0d0d8; border-radius: 6px; padding: 20px; position: relative; } .entry-card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; } .entry-card-title { font-family: ‘DM Mono’, monospace; font-size: 12px; text-transform: uppercase; letter-spacing: 0.1em; color: var(–accent); } .btn-remove { background: none; border: 1px solid var(–border); color: var(–muted); padding: 4px 10px; border-radius: 3px; font-size: 12px; cursor: pointer; transition: all 0.2s; } .btn-remove:hover { border-color: var(–error); color: var(–error); } .btn-add-entry { width: 100%; padding: 14px; background: transparent; border: 1px dashed var(–border); border-radius: 4px; color: var(–muted); font-size: 14px; cursor: pointer; transition: all 0.2s; font-family: ‘DM Sans’, sans-serif; } .btn-add-entry:hover { border-color: var(–accent); color: var(–accent); background: rgba(232,160,32,0.04); } /* Fee table */ .fee-table { background: var(–card); border: 1px solid var(–border); border-radius: 6px; overflow: hidden; margin-bottom: 24px; } .fee-row { display: flex; justify-content: space-between; align-items: center; padding: 14px 20px; border-bottom: 1px solid var(–border); font-size: 14px; } .fee-row:last-child { border-bottom: none; } .fee-row.total { background: rgba(232,160,32,0.06); font-weight: 600; font-size: 16px; } .fee-row.total .fee-amount { color: var(–accent); font-family: ‘DM Mono’, monospace; font-size: 20px; } .fee-label { color: var(–muted); } .fee-row.total .fee-label { color: var(–text); } .fee-amount { font-family: ‘DM Mono’, monospace; color: var(–text); } /* Payment */ .payment-box { background: var(–card); border: 1px solid var(–border); border-radius: 6px; padding: 24px; margin-bottom: 24px; } .payment-methods { display: flex; gap: 12px; margin-bottom: 24px; flex-wrap: wrap; } .pay-method { flex: 1; min-width: 120px; border: 1px solid var(–border); border-radius: 4px; padding: 14px; text-align: center; cursor: pointer; transition: all 0.2s; background: var(–surface); font-size: 13px; color: var(–muted); } .pay-method:hover { border-color: var(–accent); } .pay-method.selected { border-color: var(–accent); background: rgba(232,160,32,0.08); color: var(–text); } .pay-method .pay-icon { font-size: 22px; display: block; margin-bottom: 6px; } .card-fields { display: none; } .card-fields.visible { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; } .card-fields .field.full { grid-column: span 2; } /* Notice boxes */ .notice { border-radius: 4px; padding: 14px 18px; font-size: 13px; line-height: 1.6; margin-bottom: 20px; display: flex; gap: 12px; align-items: flex-start; } .notice.info { background: rgba(232,160,32,0.12); border: 1px solid rgba(232,160,32,0.35); color: #2a2a35; } .notice.success { background: rgba(42,181,116,0.12); border: 1px solid rgba(42,181,116,0.35); color: #2a2a35; } .notice-icon { font-size: 16px; flex-shrink: 0; margin-top: 1px; } /* Buttons */ .btn-row { display: flex; gap: 12px; justify-content: flex-end; margin-top: 32px; } .btn { padding: 14px 32px; border-radius: 3px; font-family: ‘DM Sans’, sans-serif; font-weight: 600; font-size: 14px; letter-spacing: 0.06em; text-transform: uppercase; cursor: pointer; border: none; transition: all 0.2s; } .btn-primary { background: var(–accent); color: #000; } .btn-primary:hover { background: #f0b030; transform: translateY(-1px); box-shadow: 0 4px 20px rgba(232,160,32,0.3); } .btn-secondary { background: transparent; color: var(–muted); border: 1px solid var(–border); } .btn-secondary:hover { border-color: var(–text); color: var(–text); } /* Confirmation screen */ .confirm-hero { text-align: center; padding: 60px 0 40px; } .confirm-check { width: 80px; height: 80px; border-radius: 50%; background: rgba(42,181,116,0.15); border: 2px solid var(–success); display: flex; align-items: center; justify-content: center; font-size: 36px; margin: 0 auto 24px; animation: popIn 0.4s cubic-bezier(0.34, 1.56, 0.64, 1); } @keyframes popIn { from { transform: scale(0); opacity: 0; } to { transform: scale(1); opacity: 1; } } .confirm-hero h2 { font-family: ‘Bebas Neue’, sans-serif; font-size: 48px; color: var(–success); letter-spacing: 0.05em; } .confirm-hero p { color: var(–muted); font-size: 15px; margin-top: 8px; } .summary-card { background: var(–card); border: 1px solid var(–border); border-radius: 6px; overflow: hidden; margin-bottom: 24px; } .summary-header { padding: 16px 20px; background: var(–surface); border-bottom: 1px solid var(–border); font-family: ‘DM Mono’, monospace; font-size: 12px; text-transform: uppercase; letter-spacing: 0.1em; color: var(–accent); display: flex; justify-content: space-between; align-items: center; } .summary-body { padding: 20px; } .summary-row { display: flex; justify-content: space-between; padding: 8px 0; font-size: 14px; border-bottom: 1px solid rgba(255,255,255,0.04); } .summary-row:last-child { border-bottom: none; } .summary-row .label { color: var(–muted); } .summary-row .value { color: var(–text); font-weight: 500; } .conf-num { text-align: center; font-family: ‘DM Mono’, monospace; font-size: 28px; color: var(–accent); letter-spacing: 0.2em; padding: 24px; border: 1px dashed var(–border); border-radius: 4px; margin-bottom: 24px; } .conf-num small { display: block; font-size: 11px; color: var(–muted); letter-spacing: 0.1em; margin-bottom: 8px; } .action-btns { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; } .btn-outline { padding: 12px 24px; border: 1px solid var(–border); border-radius: 3px; background: transparent; color: var(–text); font-family: ‘DM Sans’, sans-serif; font-size: 13px; font-weight: 500; cursor: pointer; transition: all 0.2s; } .btn-outline:hover { border-color: var(–accent); color: var(–accent); } /* Rules checkbox */ .checkbox-field { display: flex; gap: 12px; align-items: flex-start; margin-bottom: 20px; } .checkbox-field input[type=checkbox] { width: 18px; height: 18px; margin-top: 2px; flex-shrink: 0; accent-color: var(–accent); } .checkbox-field label { font-size: 13px; color: var(–muted); line-height: 1.6; text-transform: none; letter-spacing: 0; font-weight: 400; } .checkbox-field label a { color: var(–accent); } /* Category chips */ .category-select { display: flex; flex-wrap: wrap; gap: 8px; } .cat-chip { padding: 7px 14px; border: 1px solid var(–border); border-radius: 2px; font-size: 12px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; cursor: pointer; transition: all 0.2s; background: var(–surface); color: var(–muted); user-select: none; } .cat-chip:hover { border-color: var(–accent); color: var(–accent); } .cat-chip.selected { border-color: var(–accent); background: rgba(232,160,32,0.12); color: var(–accent); } footer { margin-top: 80px; padding: 32px 0; border-top: 1px solid var(–border); text-align: center; font-size: 12px; color: var(–muted); } /* Scale chip visible indicator */ .scale-badge { font-family: ‘DM Mono’, monospace; font-size: 10px; padding: 2px 7px; background: rgba(232,160,32,0.1); border: 1px solid rgba(232,160,32,0.2); border-radius: 2px; color: var(–accent); margin-left: 8px; vertical-align: middle; } /* Registration closed banner */ .reg-closed-banner { background: rgba(224,82,82,0.1); border: 1px solid rgba(224,82,82,0.35); border-radius: 6px; padding: 28px 32px; text-align: center; margin: 40px 0; } .reg-closed-banner h2 { font-family:’Bebas Neue’,sans-serif; font-size:36px; color:var(–error); letter-spacing:.05em; margin-bottom:8px; } .reg-closed-banner p { color:var(–muted); font-size:14px; line-height:1.6; } /* Printable tag sheet styles */ .tag-sheet-overlay { display: none; position: fixed; inset: 0; z-index: 1000; background: rgba(0,0,0,0.85); overflow-y: auto; padding: 40px 20px; } .tag-sheet-overlay.open { display: block; } .tag-sheet-container { background: #fff; color: #111; max-width: 820px; margin: 0 auto; border-radius: 6px; padding: 32px; font-family: Arial, Helvetica, sans-serif; } .tag-sheet-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px; padding-bottom: 16px; border-bottom: 2px solid #111; } .tag-sheet-header h2 { font-size: 18px; color: #111; margin: 0; } .tag-sheet-header p { font-size: 12px; color: #555; margin: 4px 0 0; } .tag-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; } .model-tag { border: 2px solid #222; border-radius: 4px; padding: 14px; page-break-inside: avoid; position: relative; min-height: 160px; } .model-tag .tag-show { font-size: 9px; text-transform: uppercase; letter-spacing: .1em; color: #888; margin-bottom: 6px; } .model-tag .tag-entry-num { font-family: ‘DM Mono’, monospace; font-size: 22px; font-weight: 700; color: #111; line-height: 1; margin-bottom: 6px; } .model-tag .tag-model-name { font-size: 14px; font-weight: 700; color: #111; margin-bottom: 4px; line-height: 1.3; } .model-tag .tag-cat { font-size: 11px; color: #444; margin-bottom: 2px; } .model-tag .tag-registrant { font-size: 11px; color: #444; } .model-tag .tag-qr { position: absolute; bottom: 10px; right: 10px; } .model-tag .tag-qr canvas, .model-tag .tag-qr img { width: 64px !important; height: 64px !important; } .model-tag .tag-conf { font-size: 9px; color: #888; margin-top: 6px; font-family: monospace; } .tag-sheet-actions { display: flex; gap: 12px; margin-top: 24px; justify-content: center; } .tag-sheet-actions button { padding: 11px 28px; border-radius: 3px; font-size: 14px; font-weight: 600; cursor: pointer; border: none; } .btn-print-tags { background: #e8a020; color: #000; } .btn-close-tags { background: transparent; border: 1px solid #ccc !important; color: #555; } /* Confirmation QR section */ .conf-qr-row { display: flex; gap: 20px; justify-content: center; flex-wrap: wrap; margin: 24px 0; } .conf-qr-card { background: var(–surface); border: 1px solid var(–border); border-radius: 6px; padding: 16px 20px; text-align: center; min-width: 160px; } .conf-qr-card .qr-label { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(–muted); margin-bottom: 10px; } .conf-qr-card .qr-sublabel { font-size: 11px; color: var(–muted); margin-top: 8px; font-family: ‘DM Mono’, monospace; } @media print { body > *:not(.tag-sheet-overlay) { display: none !important; } .tag-sheet-overlay { display: block !important; position: static !important; background: #fff !important; padding: 0 !important; } .tag-sheet-container { box-shadow: none !important; } .tag-sheet-actions, .tag-sheet-header .close-btn { display: none !important; } .tag-grid { grid-template-columns: 1fr 1fr; } }
Pre-Registration Now Open

4-M
Mayhem

4M MAYHEM Date: January 30 2027 Venue: Conference Center at Curtiss Hall — Saginaw Valley State University, 7400 Bay Road, University Center, MI 48710 Entry Deadline: January 22, 2027 Registration 8am–Noon · Judging Noon–3pm · Awards 4pm
Registration Fees: Adults (18+) $15.00 flat · Juniors (under 18) $5.00 flat · Both include unlimited entries.
Fees increase to $20.00 after January 15, 2027. General Admission (spectators): $5.00 at the door.
1
Your Info
2
Entries
3
Review
4
Payment
Registrant Information

Tell us about yourself. All fields marked * are required.

First name is required
Last name is required
Valid email is required
City/State is required
— Select your level — Novice (0–2 years) Intermediate (3–7 years) Advanced (8+ years) Master / Professional Please select your experience level
— Select registration type — Adult (age 18+) — $15.00 flat, unlimited entries Junior (age 17 and under) — $5.00 flat, unlimited entries Please select a registration type
Your email will receive a confirmation with your registration number, entry table location, and show-day instructions.
Model Entries

Add each model you wish to enter. Your flat registration fee covers unlimited entries. Add as many as you like!

Review Your Registration

Please confirm your entries and fees before proceeding to payment.

Registrant Edit
Model Entries Edit
Payment

Choose your preferred payment method to complete registration.

Select Payment Method
💳Credit / Debit Card
🅿️PayPal
📬Mail Check
🏷️Pay at Door
🔒
Card processing powered by Stripe. Your card details are encrypted and never stored on our servers. Connect your Stripe account in the admin settings to activate live payments.
✓

You’re Registered!

Your entry has been received. Print your tag sheet below and bring it on show day.

Your Confirmation Number —
📱 Your Check-In QR Code
Volunteers will scan this at the door — instant check-in, no waiting
Registration Summary
🏷️
Next step: Click “Print My Tag Sheet” below. Cut out the individual model tags and attach one to each model you bring on show day. Present the full sheet at the door for instant check-in.

Model Entry Tags

Print this sheet, cut along the dotted lines, and place each tag with your model on show day. Bring your full confirmation sheet to the door for check-in.
Scan at Door for Check-In
Print this sheet and cut out each model tag. Tape or place on your model’s display base. Keep the top section intact — present it at the door for instant check-in.
// ═══════════════════════════════════════════════════════════════════════════════ // SHOW CONFIG — Update these values each year. Everything else runs automatically. // ═══════════════════════════════════════════════════════════════════════════════ const SHOW_CONFIG = { name: “4-M Mayhem”, fullName: “4-M Mayhem — Mid-Michigan Model Makers”, date: “January 30, 2027”, location: “Conference Center at Curtiss Hall — Saginaw Valley State University, 7400 Bay Road, University Center, MI 48710”, scheduleNote: “Registration 8am–Noon · Judging Noon–3pm · Awards 4pm”, // ── Registration deadline ──────────────────────────────────────────────────── // Format: “Month DD, YYYY” e.g. “January 15, 2027” // After this date the form closes automatically and shows the closed banner. // Change to your actual cutoff date — committee recommended ~1 week before show. deadline: “January 22, 2027”, // ── Late fee cutoff ────────────────────────────────────────────────────────── // After this date (but before deadline), the late fee kicks in automatically. // Set to a few days before deadline, or same as deadline if no late period. lateDeadline: “January 15, 2027”, // ── Fees ──────────────────────────────────────────────────────────────────── generalAdmission: 5.00, // door admission (info display only — not charged here) adultFee: 15.00, // Adults 18+ — unlimited entries juniorFee: 5.00, // Juniors 17 and under — unlimited entries lateFee: 20.00, // Late registration fee (after lateDeadline) — adults lateJuniorFee: 5.00, // Juniors always $5 — change if needed maxEntries: 99, // Unlimited — set to 99 as a practical cap adminEmail: “midmichiganmodelmakers.ipms@gmail.com”, website: “www.midmichiganmodelmakers.org”, // ── Theme Awards ───────────────────────────────────────────────────────────── // Set enabled: true/false per theme. Rename freely each year. themes: [ { id: “theme1”, enabled: true, name: “\”A Galaxy Far, Far Away\” — Star Wars 50th Anniversary”, description: “Celebrating 50 years of Star Wars! Any model depicting a Star Wars spacecraft, vehicle, character, figure, or diorama from any film, series, or expanded universe. All scales and manufacturers welcome.”, icon: “⭐”, }, { id: “theme2”, enabled: true, name: “\”Spirit of St. Louis\” — Best Lindberg Kit”, description: “In memory of Charles Lindbergh’s historic 1927 transatlantic flight. Any model kit manufactured by Lindberg (any era). Best overall build of a Lindberg-brand kit wins — any subject, any scale.”, icon: “✈️”, }, ], }; // ═══════════════════════════════════════════════════════════════════════════════ // CATEGORIES — Your official 2027 category list with codes. // Format: “Class Name (CODE)”: [“Sub-category 1”, “Sub-category 2”, …] // The code prints on entry tags so judges can sort quickly. // ═══════════════════════════════════════════════════════════════════════════════ const CATEGORIES = { “Aircraft”: [ “AC1A — 1/72 scale and smaller, all types”, “AC2A — 1/71 to 1/33 scale, all types (primarily 1/48)”, “AC3A — 1/32 scale and larger, all types”, ], “Space”: [ “SP1A — Real spacecraft, missiles, and vehicles, all scales”, “SP2A — Sci-Fi TV, movies, and print subjects, all scales”, “SP3A — Mecha, Gunpla, and Anime subjects, all scales”, ], “Military Vehicles”: [ “MV1A — 1/48 and smaller”, “MV2A — 1/47 and larger, 1945 and earlier”, “MV3A — 1/47 and larger, 1946 and later”, “MV4A — Non-armored with or without weapons, all types and scales”, “MV5A — Artillery, weaponized rockets, missiles, rocket artillery”, ], “Figures”: [ “FG1A — Busts, all types and scales”, “FG2A — 54mm and smaller”, “FG3A — 55mm and larger”, “FG4A — Fantasy and gaming figures”, “FG5A — Animals, past and present”, ], “Diorama”: [ “DR1A — 1/72 and smaller”, “DR2A — 1/48 (1/71 to 1/34)”, “DR3A — 1/35 and larger”, ], “Ships”: [ “SH1A — 1/500 and smaller, all types”, “SH2A — 1/499 and larger, all types”, ], “Automotive”: [ “AT1A — Replica or factory stock, all types”, “AT2A — Curbside and slammers”, “AT3A — Automotive to 1960 (hot rods, customs, stock, non-commercial pickups)”, “AT4A — Automotive 1961 and newer (street machines, stock, non-commercial pickups)”, “AT5A — Competition, all”, “AT6A — Light commercial (family car/truck in commercial use)”, “AT7A — Heavy commercial and farm implements”, ], “Box Build”: [ “BB1A — Box build, all types and scales”, ], “Juniors”: [ “JR1A — Age 12 and under, all scales and types”, “JR2A — Age 13 to 17, all scales and types”, ], “4ME — Special Needs”: [ “4MEA — Special needs modelers, all types”, ], “Miscellaneous”: [ “MS1A — Any model that does not fit into any other category”, ], }; // ─── Pricing helpers ────────────────────────────────────────────────────────── function isLateRegistration() { if (!SHOW_CONFIG.lateDeadline) return false; const late = new Date(SHOW_CONFIG.lateDeadline); if (isNaN(late)) return false; late.setHours(23, 59, 59, 999); return new Date() > late; } function getFeeForLevel(level) { const late = isLateRegistration(); if (level === ‘Junior (age 17 and under)’) return late ? SHOW_CONFIG.lateJuniorFee : SHOW_CONFIG.juniorFee; return late ? SHOW_CONFIG.lateFee : SHOW_CONFIG.adultFee; } function getFeeLabel() { return isLateRegistration() ? ‘ (late registration)’ : ”; } // ─── State ──────────────────────────────────────────────────────────────────── let entries = []; let currentStep = 1; let payMethod = ‘card’; let registrations = JSON.parse(localStorage.getItem(‘hsm_registrations’) || ‘[]’); // ─── Init ───────────────────────────────────────────────────────────────────── document.getElementById(‘show-name’).textContent = SHOW_CONFIG.name; document.getElementById(‘show-date’).textContent = “📅 ” + SHOW_CONFIG.date; document.getElementById(‘show-location’).textContent = “📍 ” + SHOW_CONFIG.location; document.getElementById(‘show-deadline’).textContent = “⏰ Pre-registration Deadline: ” + SHOW_CONFIG.deadline; if (SHOW_CONFIG.scheduleNote) { const sched = document.createElement(‘span’); sched.textContent = ‘🕗 ‘ + SHOW_CONFIG.scheduleNote; document.getElementById(‘show-deadline’).after(sched); } // Fee info line (function showFeeInfo() { const late = isLateRegistration(); const feeEl = document.getElementById(‘show-fee-info’); if (!feeEl) return; if (late) { feeEl.innerHTML = `⚠️ Late Registration Fees Apply: Adults $${SHOW_CONFIG.lateFee.toFixed(2)} · Juniors $${SHOW_CONFIG.lateJuniorFee.toFixed(2)} · Unlimited entries. General Admission (spectators): $${SHOW_CONFIG.generalAdmission.toFixed(2)} at the door.`; } else { feeEl.innerHTML = `Registration Fees: Adults (18+) $${SHOW_CONFIG.adultFee.toFixed(2)} flat · Juniors (under 18) $${SHOW_CONFIG.juniorFee.toFixed(2)} flat · Both include unlimited entries.
Fees increase to $${SHOW_CONFIG.lateFee.toFixed(2)} after ${SHOW_CONFIG.lateDeadline}. General Admission (spectators): $${SHOW_CONFIG.generalAdmission.toFixed(2)} at the door.`; } })(); // ── Check if registration is closed ────────────────────────────────────────── (function checkDeadline() { if (!SHOW_CONFIG.deadline || SHOW_CONFIG.deadline === ‘TBD’) return; const deadlineDate = new Date(SHOW_CONFIG.deadline); if (isNaN(deadlineDate)) return; // Close at end of deadline day deadlineDate.setHours(23, 59, 59, 999); if (new Date() > deadlineDate) { document.getElementById(‘reg-closed-banner’).style.display = ‘block’; document.getElementById(‘reg-form-wrapper’).style.display = ‘none’; document.getElementById(‘closed-show-name’).textContent = SHOW_CONFIG.name; document.getElementById(‘closed-deadline’).textContent = SHOW_CONFIG.deadline; const emailEl = document.getElementById(‘closed-email’); emailEl.href = ‘mailto:’ + SHOW_CONFIG.adminEmail; emailEl.textContent = SHOW_CONFIG.adminEmail; } })(); // Add first entry automatically addEntry(); // ─── Navigation ─────────────────────────────────────────────────────────────── function goToStep(n) { if (n > currentStep) { if (!validateStep(currentStep)) return; } if (n === 3) buildReview(); if (n === 4) buildPaymentSummary(); document.getElementById(‘panel-‘ + currentStep).classList.remove(‘active’); document.getElementById(‘tab-‘ + currentStep).classList.remove(‘active’); if (n > currentStep) document.getElementById(‘tab-‘ + currentStep).classList.add(‘done’); currentStep = n; document.getElementById(‘panel-‘ + n).classList.add(‘active’); document.getElementById(‘tab-‘ + n).classList.add(‘active’); window.scrollTo({ top: 0, behavior: ‘smooth’ }); } // ─── Validation ─────────────────────────────────────────────────────────────── function validateStep(step) { if (step === 1) { let ok = true; const checks = [ { id: ‘f-fname’, el: ‘fname’, test: v => v.trim().length > 0 }, { id: ‘f-lname’, el: ‘lname’, test: v => v.trim().length > 0 }, { id: ‘f-email’, el: ’email’, test: v => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v) }, { id: ‘f-city’, el: ‘city’, test: v => v.trim().length > 0 }, { id: ‘f-experience’, el: ‘experience’, test: v => v !== ” }, { id: ‘f-reglevel’, el: ‘reglevel’, test: v => v !== ” }, ]; checks.forEach(c => { const el = document.getElementById(c.el); const fld = document.getElementById(c.id); if (!c.test(el.value)) { fld.classList.add(‘has-error’); ok = false; } else fld.classList.remove(‘has-error’); }); return ok; } if (step === 2) { if (entries.length === 0) { alert(‘Please add at least one model entry.’); return false; } let ok = true; entries.forEach((e, i) => { if (!e.name || !e.category || !e.scale) ok = false; }); if (!ok) { alert(‘Please fill in the Name, Category, and Scale for all entries.’); } return ok; } if (step === 3) { if (!document.getElementById(‘agree-rules’).checked) { alert(‘Please read and agree to the Show Rules before continuing.’); return false; } return true; } return true; } // ─── Entries ────────────────────────────────────────────────────────────────── function addEntry() { if (entries.length >= SHOW_CONFIG.maxEntries) { alert(`Maximum ${SHOW_CONFIG.maxEntries} entries allowed.`); return; } const id = Date.now(); const themeDefaults = {}; SHOW_CONFIG.themes.filter(t => t.enabled).forEach(t => { themeDefaults[t.id] = false; }); entries.push({ id, name: ”, category: ”, subcategory: ”, scale: ”, notes: ”, …themeDefaults }); renderEntries(); } function removeEntry(id) { if (entries.length e.id !== id); renderEntries(); } function renderEntries() { const list = document.getElementById(‘entries-list’); list.innerHTML = ”; entries.forEach((entry, idx) => { const catOpts = Object.keys(CATEGORIES).map(c => `${c}`).join(”); const subCats = entry.category ? CATEGORIES[entry.category] : []; const subOpts = subCats.map(s => `${s}`).join(”); list.innerHTML += `
Entry #${idx + 1} included
— Select —${catOpts}
— Select —${subOpts}
— Select — ${[‘1/700′,’1/350′,’1/144′,’1/100′,’1/72′,’1/48′,’1/35′,’1/32′,’1/25′,’1/24′,’1/20′,’1/16′,’1/12′,’1/8′,’Other’].map(s=>`${s}`).join(”)}
${(() => { const activeThemes = SHOW_CONFIG.themes.filter(t => t.enabled); if (!activeThemes.length) return ”; return `
${activeThemes.map(t => ` `).join(”)}
`; })()}
`; }); } function updateEntry(id, key, val) { const e = entries.find(e => e.id === id); if (e) e[key] = val; } // ─── Review & Fees ──────────────────────────────────────────────────────────── function calcTotal() { const level = document.getElementById(‘reglevel’) ? document.getElementById(‘reglevel’).value : ”; return getFeeForLevel(level); } function updateFeeDisplay() { const level = document.getElementById(‘reglevel’).value; const noteEl = document.getElementById(‘fee-note’); if (!level) { noteEl.textContent = ”; return; } const fee = getFeeForLevel(level); const late = isLateRegistration(); if (late) { noteEl.innerHTML = `⚠️ Late registration fee applies. Your fee: $${fee.toFixed(2)}`; noteEl.style.color = ‘var(–warning, #c57400)’; } else { noteEl.innerHTML = `✅ Early registration rate. Your fee: $${fee.toFixed(2)} (unlimited entries)`; noteEl.style.color = ‘var(–success)’; } } function buildReview() { const r = getRegistrant(); document.getElementById(‘review-registrant’).innerHTML = `
Name${r.fname} ${r.lname}
Email${r.email}
Phone${r.phone||’—’}
Club${r.club||’—’}
Location${r.city}
Experience${r.experience}
Registration Type${r.reglevel}
`; let eHTML = ”; entries.forEach((e, i) => { const themeNames = SHOW_CONFIG.themes.filter(t => t.enabled && e[t.id]).map(t => t.icon + ‘ ‘ + t.name.split(‘—’)[0].trim()).join(‘, ‘); eHTML += `
#${i+1} — ${e.category}${e.name} ${e.scale}${themeNames ? `
Theme: ${themeNames}` : ”}
`; }); document.getElementById(‘review-entries’).innerHTML = eHTML; document.getElementById(‘fee-table’).innerHTML = buildFeeHTML(); } function buildFeeHTML() { const level = document.getElementById(‘reglevel’) ? document.getElementById(‘reglevel’).value : ”; const total = getFeeForLevel(level); const late = isLateRegistration(); const levelLabel = level || ‘Registration’; let rows = ”; rows += `
${levelLabel} — flat fee, unlimited entries${late ? ‘ (late)‘ : ”}$${total.toFixed(2)}
`; rows += `
Models entered${entries.length}
`; rows += `
Per-model feeNone — included
`; rows += `
Total Due$${total.toFixed(2)}
`; return rows; } function buildPaymentSummary() { document.getElementById(‘payment-fee-summary’).innerHTML = buildFeeHTML(); } // ─── Payment ────────────────────────────────────────────────────────────────── function selectPay(method) { payMethod = method; [‘card’,’paypal’,’check’,’door’].forEach(m => { document.getElementById(‘pay-‘ + m).classList.toggle(‘selected’, m === method); document.getElementById(m + ‘-fields’).style.display = m === method ? (m === ‘card’ ? ‘grid’ : ‘block’) : ‘none’; }); const cf = document.getElementById(‘card-fields’); cf.className = ‘card-fields’ + (method === ‘card’ ? ‘ visible’ : ”); document.getElementById(‘submit-btn’).textContent = method === ‘card’ ? ‘Complete Registration’ : method === ‘paypal’ ? ‘Continue to PayPal’ : ‘Submit Registration’; } function formatCard(el) { el.value = el.value.replace(/\D/g,”).replace(/(.{4})/g,’$1 ‘).trim().slice(0,19); } function formatExp(el) { let v = el.value.replace(/\D/g,”); if (v.length > 2) v = v.slice(0,2) + ‘ / ‘ + v.slice(2,4); el.value = v; } // ─── Submit ─────────────────────────────────────────────────────────────────── let lastReg = null; // store for tag sheet use function submitRegistration() { const confNum = ‘SM-‘ + new Date().getFullYear() + ‘-‘ + String(Math.floor(Math.random()*9000)+1000); const r = getRegistrant(); const reg = { confNum, timestamp: new Date().toISOString(), registrant: r, entries: JSON.parse(JSON.stringify(entries)), total: calcTotal(), payMethod }; registrations.push(reg); localStorage.setItem(‘hsm_registrations’, JSON.stringify(registrations)); lastReg = reg; // Show confirmation panel document.getElementById(‘panel-4’).classList.remove(‘active’); document.getElementById(‘tab-4’).classList.add(‘done’); document.getElementById(‘panel-5’).classList.add(‘active’); document.getElementById(‘conf-number’).textContent = confNum; let confHTML = `
Name${r.fname} ${r.lname}
Email${r.email}
Models Entered${entries.length}
Total Paid$${calcTotal().toFixed(2)}
Payment Method${payMethod}
Show${SHOW_CONFIG.name}
`; document.getElementById(‘conf-summary’).innerHTML = confHTML; // Generate check-in QR code const qrContainer = document.getElementById(‘conf-checkin-qr’); qrContainer.innerHTML = ”; new QRCode(qrContainer, { text: confNum, width: 120, height: 120, colorDark: ‘#000000’, colorLight: ‘#ffffff’, correctLevel: QRCode.CorrectLevel.H }); window.scrollTo({ top: 0, behavior: ‘smooth’ }); } // ─── Tag Sheet ──────────────────────────────────────────────────────────────── function openTagSheet() { if (!lastReg) return; const r = lastReg.registrant; const confNum = lastReg.confNum; // Set header info document.getElementById(‘tag-sheet-title’).textContent = SHOW_CONFIG.name + ‘ — Model Entry Tags’; document.getElementById(‘tag-sheet-subtitle’).textContent = r.fname + ‘ ‘ + r.lname + ‘ · ‘ + confNum + ‘ · ‘ + SHOW_CONFIG.date; document.getElementById(‘tag-sheet-conf-num’).textContent = confNum; document.getElementById(‘tag-sheet-name’).textContent = r.fname + ‘ ‘ + r.lname + (r.club ? ‘ · ‘ + r.club : ”); document.getElementById(‘tag-sheet-show’).textContent = SHOW_CONFIG.name + ‘ · ‘ + SHOW_CONFIG.date; // Check-in QR at top const ciQR = document.getElementById(‘tag-sheet-checkin-qr’); ciQR.innerHTML = ”; new QRCode(ciQR, { text: confNum, width: 80, height: 80, colorDark:’#000′, colorLight:’#fff’, correctLevel: QRCode.CorrectLevel.H }); // Build individual model tags const grid = document.getElementById(‘tag-grid’); grid.innerHTML = ”; lastReg.entries.forEach((e, idx) => { const tagId = `tag-qr-${idx}`; const entryCode = confNum + ‘-E’ + (idx + 1); const div = document.createElement(‘div’); div.className = ‘model-tag’; div.innerHTML = `
${SHOW_CONFIG.name} · ${SHOW_CONFIG.date}
Entry #${idx + 1}
${e.name || ‘(untitled)’}
${e.category || ”}${e.subcategory ? ‘ › ‘ + e.subcategory : ”} · ${e.scale || ”}
${r.fname} ${r.lname}${r.club ? ‘ · ‘ + r.club : ”}
${entryCode}
`; grid.appendChild(div); // Generate per-entry QR setTimeout(() => { new QRCode(document.getElementById(tagId), { text: entryCode, width: 64, height: 64, colorDark: ‘#000000’, colorLight: ‘#ffffff’, correctLevel: QRCode.CorrectLevel.M }); }, 50); }); document.getElementById(‘tag-sheet-overlay’).classList.add(‘open’); } function getRegistrant() { return { fname: document.getElementById(‘fname’).value, lname: document.getElementById(‘lname’).value, email: document.getElementById(’email’).value, phone: document.getElementById(‘phone’).value, club: document.getElementById(‘club’).value, city: document.getElementById(‘city’).value, experience: document.getElementById(‘experience’).value, reglevel: document.getElementById(‘reglevel’) ? document.getElementById(‘reglevel’).value : ”, }; } // ─── Download contestant data as CSV (Excel-compatible) ─────────────────────── function downloadMyData() { const r = getRegistrant(); const activeThemes = SHOW_CONFIG.themes.filter(t => t.enabled); const themeHeaders = activeThemes.map(t => `”${t.name}”`).join(‘,’); const regFee = calcTotal(); let csv = `Conf#,First,Last,Email,Phone,Club,City,Experience,RegType,Entry#,Model Name,Category,Sub-Category,Scale,Manufacturer,Notes,RegistrationFee${themeHeaders ? ‘,’ + themeHeaders : ”}\n`; entries.forEach((e, i) => { const feeCol = i === 0 ? `”$${regFee.toFixed(2)}”` : `”$0.00 (included)”`; const themeCols = activeThemes.map(t => `”${e[t.id] ? ‘Yes’ : ‘No’}”`).join(‘,’); csv += `”${document.getElementById(‘conf-number’).textContent}”,”${r.fname}”,”${r.lname}”,”${r.email}”,”${r.phone}”,”${r.club}”,”${r.city}”,”${r.experience}”,”${r.reglevel}”,”${i+1}”,”${e.name}”,”${e.category}”,”${e.subcategory||”}”,”${e.scale}”,”${e.manufacturer||”}”,”${e.notes}”,${feeCol}${themeCols ? ‘,’ + themeCols : ”}\n`; }); const blob = new Blob([csv], { type: ‘text/csv’ }); const a = document.createElement(‘a’); a.href = URL.createObjectURL(blob); a.download = `registration-${document.getElementById(‘conf-number’).textContent}.csv`; a.click(); }