'); paymentWindow.document.close(); } } catch (e) { console.warn('Failed to open payment window:', e); }bbShowLoading('Reserving slots with ranger...');const tourTypeMap = { 'private-town': 'private_town_tour', 'living-lake': 'living_at_the_lake', 'twilight': 'twilight_tour', 'large-group': 'large_group_tour', 'mines': 'mines_mills_tour' };const payload = { date: bbState.selectedDate, time: bbState.selectedTime, party_size: partySize, tour_type: tourTypeMap[tourKey] || 'large_group_tour', vehicle_acknowledgment: tourKey === 'mines' ? (document.getElementById('mines-vehicle-ack') ? document.getElementById('mines-vehicle-ack').checked : false) : false, guest: { name: name, email: email, phone: phone } };try { let csrfToken = ''; let recaptchaSiteKey = '';try { const csrfResponse = await fetchWithTimeout(BOOKING_API, { method: 'GET', credentials: 'include', timeout: 10000 });if (csrfResponse.ok) { const csrfData = await csrfResponse.json(); csrfToken = csrfData.csrf_token; recaptchaSiteKey = csrfData.recaptcha_site_key; } } catch (csrfErr) { console.warn('Could not retrieve CSRF token or site key:', csrfErr); }let recaptchaToken = '';if (recaptchaSiteKey) { try { await Promise.race([ loadRecaptchaScript(recaptchaSiteKey), new Promise(function(_, reject) { setTimeout(function() { reject(new Error('reCAPTCHA script load timeout')); }, 5000); }) ]);if (typeof grecaptcha !== 'undefined') { recaptchaToken = await Promise.race([ grecaptcha.execute(recaptchaSiteKey, { action: 'submit_booking' }), new Promise(function(_, reject) { setTimeout(function() { reject(new Error('reCAPTCHA execution timeout')); }, 4000); }) ]); } } catch (err) { console.warn('reCAPTCHA execution failed, proceeding without token:', err); } }payload.recaptcha_token = recaptchaToken;const response = await fetchWithTimeout(BOOKING_API, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, credentials: 'include', body: JSON.stringify(payload), timeout: 10000 });let result = {};try { result = await response.json(); } catch (jsonErr) { console.warn('Could not parse response JSON:', jsonErr); }if (response.ok && result.status === 'success') { const totalPrice = tour ? tour.calcPrice(partySize) : partySize * 25.00;document.getElementById('bb-amount-display').textContent = '$' + totalPrice.toFixed(2) + ' DUE'; document.getElementById('bb-confirm-id').textContent = result.booking_id || '--'; document.getElementById('bb-confirm-party').textContent = partySize + ' Guest(s)';const displayDate = new Date(bbState.selectedDate + 'T12:00:00').toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });document.getElementById('bb-confirm-datetime').textContent = tourName + ' on ' + displayDate + ' @ ' + bbState.selectedTime;const invoiceLink = document.getElementById('bb-invoice-link'); const countdownEl = document.getElementById('bb-redirect-countdown');if (invoiceLink && result.payment_link && isTrustedRedirectUrl(result.payment_link)) { invoiceLink.href = result.payment_link; invoiceLink.style.display = 'inline-flex'; } else if (invoiceLink) { invoiceLink.style.display = 'none'; }const cancelLink = document.getElementById('bb-cancel-link');if (cancelLink && result.booking_id && result.token) { cancelLink.href = 'https://us-west2-bodie-tours-prod.cloudfunctions.net/cancel-tour?booking_id=' + encodeURIComponent(result.booking_id) + '&token=' + encodeURIComponent(result.token); }let openedInNewTab = false;if (paymentWindow) { try { if (!paymentWindow.closed) { if (result.payment_link && isTrustedRedirectUrl(result.payment_link)) { paymentWindow.location.href = result.payment_link; openedInNewTab = true; } else { paymentWindow.close(); } } } catch (e) { console.warn('Failed to set payment window location:', e); } }if (countdownEl) { if (openedInNewTab) { countdownEl.innerHTML = 'Your invoice has been opened in a new tab. If it did not open, click Pay Your Invoice Now above.'; } else { countdownEl.innerHTML = 'Please click the Pay Your Invoice Now button above to complete your secure payment.'; } }bbGoTo(4); } else { if (paymentWindow) { try { paymentWindow.close(); } catch (e) {} }if (response.status === 400) { errMsg.textContent = result.message || 'Validation error. Please check your inputs.'; } else if (response.status === 409) { errMsg.textContent = result.message || 'The requested slot is already booked. Please choose another slot.'; } else if (response.status === 429) { errMsg.textContent = 'Too many booking attempts. Please wait a moment and try again.'; } else if (response.status === 500) { errMsg.textContent = 'A temporary server error occurred. Please try again shortly.'; } else { errMsg.textContent = result.message || 'The slot is no longer available. Please select another time.'; }errBox.style.display = 'flex'; } } catch (err) { if (paymentWindow) { try { paymentWindow.close(); } catch (e) {} }console.error('Booking failed:', err); errMsg.textContent = 'Network or connection error. Please try again.'; errBox.style.display = 'flex'; } finally { bbState.isSubmitting = false;if (confirmBtn) { confirmBtn.disabled = false; }bbHideLoading(); } }function bbAnnounce(message) { const announcer = document.getElementById('bb-announcer');if (announcer) { announcer.textContent = '';setTimeout(function() { announcer.textContent = message; }, 50); } }function bbGoTo(step) { if (step < 4) { bbClearTimers(); }document.querySelectorAll('.bb-step').forEach(function(pane) { pane.classList.remove('active'); });document.getElementById('step-pane-' + step).classList.add('active');document.getElementById('bb-progress-bar').style.width = (step / 4) * 100 + '%';const progressContainer = document.getElementById('bb-progress-container');if (progressContainer) { progressContainer.setAttribute('aria-valuenow', step + 1); }document.querySelectorAll('.bb-progress-step').forEach(function(dot, index) { if (index < step) { dot.className = 'bb-progress-step completed'; dot.removeAttribute('aria-current'); } else if (index === step) { dot.className = 'bb-progress-step active'; dot.setAttribute('aria-current', 'step'); } else { dot.className = 'bb-progress-step'; dot.removeAttribute('aria-current'); } });if (step === 1) { bbFetchMonthAvailability(bbState.currentYear, bbState.currentMonth).then(function() { bbRenderCalendar(); }); }if (step === 2) { bbPopulateSlots(); }if (step === 3) { bbUpdateStep3Fields(); }const heading = document.getElementById('bb-step-heading-' + step);if (heading) { heading.focus({ preventScroll: true }); bbAnnounce('Step ' + (step + 1) + ' of 5: ' + heading.textContent.trim()); }const widget = document.getElementById('bodie-booking-widget');if (widget) { window.scrollTo({ top: widget.offsetTop - 30, behavior: 'smooth' }); } }function bbSelectTour(tourKey) { bbState.selectedTour = tourKey;document.querySelectorAll('.bb-tour-card').forEach(function(card) { const isSelected = card.getAttribute('data-tour') === tourKey;if (isSelected) { card.classList.add('selected'); card.setAttribute('aria-checked', 'true'); card.setAttribute('aria-pressed', 'true'); card.setAttribute('tabindex', '0'); } else { card.classList.remove('selected'); card.setAttribute('aria-checked', 'false'); card.setAttribute('aria-pressed', 'false'); card.setAttribute('tabindex', '-1'); } });const tourName = TOURS[tourKey] ? TOURS[tourKey].name : 'Tour';bbAnnounce('Selected tour: ' + tourName);document.getElementById('bb-to-step-1').disabled = false;bbState.selectedDate = null; bbState.selectedTime = null;document.getElementById('bb-to-step-2').disabled = true; document.getElementById('bb-to-step-3').disabled = true; }function bbUpdateStep3Fields() { const tourKey = bbState.selectedTour; const tour = TOURS[tourKey];const ackGroup = document.getElementById('mines-vehicle-ack-group'); const ackInput = document.getElementById('mines-vehicle-ack'); const partyInput = document.getElementById('guest-party'); const partyLabel = document.querySelector('label[for="guest-party"]');if (tourKey === 'mines') { ackGroup.style.display = 'block'; ackInput.required = true; } else { ackGroup.style.display = 'none'; ackInput.required = false; ackInput.checked = false; }const maxParty = tour ? tour.maxParty : 20;partyInput.max = maxParty; partyInput.placeholder = 'e.g., 4 (maximum ' + maxParty + ')'; partyLabel.textContent = 'Number of Guests (Required, max ' + maxParty + ')'; }async function bbRetryCalendarFetch() { await bbFetchMonthAvailability(bbState.currentYear, bbState.currentMonth); bbRenderCalendar(); }function bbStartOver() { bbClearTimers();bbState = { selectedTour: null, currentYear: new Date().getFullYear(), currentMonth: new Date().getMonth(), selectedDate: null, selectedTime: null, availableDates: {}, redirectTimeout: null, redirectInterval: null, calendarFetchFailed: false, isSubmitting: false };const form = document.getElementById('bb-booking-form');if (form) { form.reset(); }const errBox = document.getElementById('bb-error-box');if (errBox) { errBox.style.display = 'none'; }document.getElementById('bb-to-step-1').disabled = true; document.getElementById('bb-to-step-2').disabled = true; document.getElementById('bb-to-step-3').disabled = true;document.querySelectorAll('.bb-tour-card').forEach(function(card) { card.classList.remove('selected'); card.setAttribute('aria-checked', 'false'); card.setAttribute('aria-pressed', 'false'); });bbGoTo(0); }function bbShowLoading(text) { const loading = document.getElementById('bb-loading'); const loadingText = document.getElementById('bb-loading-text');if (loadingText) { loadingText.textContent = text; }if (loading) { loading.setAttribute('aria-hidden', 'false'); loading.classList.add('active'); } }function bbHideLoading() { const loading = document.getElementById('bb-loading');if (loading) { loading.setAttribute('aria-hidden', 'true'); loading.classList.remove('active'); } }window.bbSelectTour = bbSelectTour; window.bbRetryCalendarFetch = bbRetryCalendarFetch; window.bbStartOver = bbStartOver; window.bbChangeMonth = bbChangeMonth; window.bbGoTo = bbGoTo; window.bbSubmitBooking = bbSubmitBooking; window.bbInit = bbInit;function bbSafeInit() { if (!document.getElementById('bodie-booking-widget')) { return; }bbInit(); }if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', bbSafeInit); } else { bbSafeInit(); } })();

Donate

You can make a difference by donating or becoming a member. Bodie’s history is rich with events and the stories of the colorful people who once lived here. Help us preserve this unique town for future generations.

Donate

Donations come in many forms; individual, corporate, and planned giving. What they all have in common is that they help us achieve our mission, and are greatly appreciated!

Become a Member

Help preserve Bodie by choosing a membership option that works for you! Click below to learn more about our monthly and annual membership levels or renew your membership.

Pay by Check

Make checks payable to Bodie Foundation. Please include any desired special instructions for the donation and mail to:

Bodie Foundation
PO Box 278
Bridgeport, CA 93517

More Ways to Give

Wills, Bequests and Trusts

Including the Bodie Foundation in your will or a trust is an easy way to ensure Bodie keeps standing without affecting your income during your lifetime. You can choose to leave the Bodie Foundation a specific dollar amount, a percentage of your estate, or the balance remaining after distributions have been made to other beneficiaries.

Retirement Funds

Naming the Bodie Foundation as the beneficiary of a retirement plan such as a 401(k), 403(b), IRA or profit-sharing pension plan allow your hard-earned savings to avoid taxes and be put to their fullest use to benefit Bodie.

Follow these 3 steps to complete:

  1. Request a change of beneficiary from your plan administrator.
  2. List the Bodie Foundation as a non-profit organization beneficiary using our tax ID #26-3107902.
  3. Return the completed form to your plan administrator and send a copy to Shannon Boniface at the Bodie Foundation, PO Box 278 Bridgeport, CA 93517, or email us at shannon@bodiefoundation.org.

Membership

Whatever future generations may discover and appreciate about Bodie is our responsibility today. Bodie Foundation is a non-profit organization which helps to carry on the vital work of stabilization, protecting artifacts and interpreting the history of Bodie.

All annual memberships include:

  • Subscription to our semi-annual newsletter, The Bodie Times

  • 20% discount off most merchandise sold in the Bodie museum bookstore and the Bodie Mercantile located in Bridgeport

  • An invite to our annual summer special event

  • Pre-sale tickets for our event tickets

  • Memberships are annual (based on the date you join)

Join or renew online; or download a membership form and mail it, along with your check payable to “Bodie Foundation” to:

Bodie Foundation 
Membership
PO Box 278 
Bridgeport, CA 93517 
(760) 932-7574

Join our mailing list

Love Bodie State Historic Park? Stay connected for information on Bodie and the Foundation’s work.