/* global bingocrData */
( function () {
	'use strict';

	var data        = window.bingocrData || {};
	var REST        = data.restUrl           || '';
	var NONCE       = data.nonce             || '';
	var I18N        = data.i18n              || {};
	var I18N_ALL    = data.i18nAll           || {};
	var SYM         = data.currencySymbol    || '€';
	var POS         = data.currencyPosition  || 'before';
	var FEATURES    = data.features          || { extras: false, discounts: false };
	var DEF_DEPOSIT = parseFloat( data.defaultDeposit || 0 );
	var OOH         = data.outOfHours        || { enabled: false, fee: 0, start: '09:00', end: '18:00' };
	var PAY         = data.payments          || { cash: true, card: false, bank: false };
	var PAY_NOTE    = data.paymentInstructions || '';

	function timeToMinutes( time ) {
		var p = String( time || '' ).split( ':' );
		return ( parseInt( p[0], 10 ) || 0 ) * 60 + ( parseInt( p[1], 10 ) || 0 );
	}

	// Out-of-hours fee from the current time inputs: per occurrence (pickup + return).
	function calcOohFee() {
		if ( ! OOH.enabled || ! ( OOH.fee > 0 ) ) {
			return { fee: 0, count: 0 };
		}
		var startM = timeToMinutes( OOH.start );
		var endM   = timeToMinutes( OOH.end );
		var count  = 0;
		[ 'bcr-pickup-time', 'bcr-return-time' ].forEach( function ( id ) {
			var input = el( id );
			if ( ! input || ! input.value ) { return; }
			var m = timeToMinutes( input.value );
			if ( m < startM || m > endM ) { count++; }
		} );
		return { fee: count * OOH.fee, count: count };
	}

	function t( key ) { return I18N[ key ] || key; }

	function price( amount ) {
		var f = parseFloat( amount || 0 ).toFixed( 2 );
		return POS === 'before' ? SYM + f : f + SYM;
	}

	function api( endpoint, method, body ) {
		var opts = {
			method:  method || 'GET',
			headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': NONCE },
		};
		if ( body ) { opts.body = JSON.stringify( body ); }
		return fetch( REST + endpoint, opts ).then( function ( r ) {
			return r.json().then( function ( json ) {
				if ( ! r.ok ) { throw new Error( json.message || t( 'error_required' ) ); }
				return json;
			} );
		} );
	}

	function esc( str ) {
		var d = document.createElement( 'div' );
		d.appendChild( document.createTextNode( String( str == null ? '' : str ) ) );
		return d.innerHTML;
	}

	function el( id )           { return document.getElementById( id ); }
	function setText( id, txt ) { var n = el( id ); if ( n ) { n.textContent = txt; } }
	function hide( id )         { var n = el( id ); if ( n ) { n.style.display = 'none'; } }

	// ── App ──────────────────────────────────────────────────────────────

	var App = {
		container:   null,
		pricing:     null,
		discount:    null,
		_escHandler: null,

		state: {
			step:        'search',
			pickupDate:  '',
			returnDate:  '',
			pickupLoc:   '',
			dropoffLoc:  '',
			sameDropoff: true,
			category:    '',
			cars:        [],
			categories:  [],
			extras:      [],
			locations:   [],
			reservation: null,
			email:       '',
			lang:        data.lang || 'en',
		},

		// ── Init ─────────────────────────────────────────────────────────

		init: function ( container ) {
			this.container   = container;
			this.showLocations  = container.dataset.showLocations  !== 'false';
			this.showCategories = container.dataset.showCategories !== 'false';
			this.showExtras     = container.dataset.showExtras     !== 'false';
			var self = this;

			if ( data.autoDetectLang ) {
				fetch( 'https://ipapi.co/json/' )
					.then( function ( r ) { return r.json(); } )
					.then( function ( geo ) {
						// RO visitors get Romanian, everyone else English —
						// and the UI strings switch too, not just the emails.
						var detected = ( geo && geo.country_code === 'RO' ) ? 'ro' : 'en';
						self.state.lang = detected;
						if ( I18N_ALL[ detected ] ) { I18N = I18N_ALL[ detected ]; }
					} )
					.catch( function () {} )
					.finally( function () { self.loadInitialData(); } );
			} else {
				this.loadInitialData();
			}
		},

		loadInitialData: function () {
			var self = this;
			// Each request falls back to [] on its own — one failing endpoint
			// must not blank out the others (extras would silently disappear).
			var reqs = [
				api( 'locations' ).catch( function () { return []; } ),
				api( 'cars/categories' ).catch( function () { return []; } ),
				api( 'extras' ).catch( function () { return []; } ),
			];
			Promise.all( reqs ).then( function ( results ) {
				self.state.locations  = results[0] || [];
				self.state.categories = ( results[1] || [] ).map( function ( c ) {
					return typeof c === 'object' ? ( c.category || '' ) : c;
				} ).filter( Boolean );
				self.state.extras = ( results[2] || [] ).filter( function ( x ) {
					return x && x.status === 'active';
				} );
				self.render();
			} );
		},

		render: function () {
			switch ( this.state.step ) {
				case 'search':  this.renderSearch();  break;
				case 'cars':    this.renderCars();    break;
				case 'confirm': this.renderConfirm(); break;
			}
		},

		// ── Step 1 : Search ──────────────────────────────────────────────

		renderSearch: function () {
			var self  = this;
			var s     = this.state;
			var today = new Date().toISOString().split( 'T' )[0];

			var html = '<div class="bcr-search-box">';
			html += '<h2 class="bcr-search-title">' + esc( t( 'search_title' ) ) + '</h2>';
			html += '<div class="bcr-search-grid">';

			// Pickup date
			html += '<div class="bcr-field">';
			html += '<label class="bcr-field-label" for="bcr-pickup-date">' + esc( t( 'pickup_date' ) ) + '</label>';
			html += '<input type="date" id="bcr-pickup-date" class="bcr-input" min="' + today + '" value="' + esc( s.pickupDate ) + '">';
			html += '</div>';

			// Return date
			html += '<div class="bcr-field">';
			html += '<label class="bcr-field-label" for="bcr-return-date">' + esc( t( 'return_date' ) ) + '</label>';
			html += '<input type="date" id="bcr-return-date" class="bcr-input" min="' + today + '" value="' + esc( s.returnDate ) + '">';
			html += '</div>';

			// Pickup location
			if ( this.showLocations && s.locations.length ) {
				html += '<div class="bcr-field">';
				html += '<label class="bcr-field-label" for="bcr-pickup-loc">' + esc( t( 'pickup_location' ) ) + '</label>';
				html += '<select id="bcr-pickup-loc" class="bcr-input">';
				html += '<option value="">' + esc( t( 'any_location' ) ) + '</option>';
				s.locations.forEach( function ( loc ) {
					var sel = s.pickupLoc == loc.id ? ' selected' : '';
					html += '<option value="' + esc( loc.id ) + '"' + sel + '>' + esc( loc.name ) + '</option>';
				} );
				html += '</select></div>';

				// Return location with same-as-pickup checkbox
				html += '<div class="bcr-field">';
				html += '<label class="bcr-field-label" for="bcr-dropoff-loc">' + esc( t( 'dropoff_location' ) ) + '</label>';
				html += '<select id="bcr-dropoff-loc" class="bcr-input"' + ( s.sameDropoff ? ' disabled' : '' ) + '>';
				html += '<option value="">' + esc( t( 'any_location' ) ) + '</option>';
				s.locations.forEach( function ( loc ) {
					var sel = ( ! s.sameDropoff && s.dropoffLoc == loc.id ) ? ' selected' : '';
					html += '<option value="' + esc( loc.id ) + '"' + sel + '>' + esc( loc.name ) + '</option>';
				} );
				html += '</select>';
				html += '<label class="bcr-same-pickup-label">';
				html += '<input type="checkbox" id="bcr-same-pickup"' + ( s.sameDropoff ? ' checked' : '' ) + '> ';
				html += esc( t( 'same_as_pickup' ) );
				html += '</label>';
				html += '</div>';
			}

			// Category filter
			if ( this.showCategories ) {
				html += '<div class="bcr-field">';
				html += '<label class="bcr-field-label" for="bcr-category">' + esc( t( 'category' ) ) + '</label>';
				html += '<select id="bcr-category" class="bcr-input">';
				html += '<option value="">' + esc( t( 'all_categories' ) ) + '</option>';
				s.categories.forEach( function ( cat ) {
					var sel = s.category === cat ? ' selected' : '';
					var label = cat.charAt( 0 ).toUpperCase() + cat.slice( 1 );
					html += '<option value="' + esc( cat ) + '"' + sel + '>' + esc( label ) + '</option>';
				} );
				html += '</select></div>';
			}

			html += '</div>'; // .bcr-search-grid

			html += '<div class="bcr-search-footer">';
			html += '<button class="bcr-btn bcr-btn-primary bcr-btn-search" id="bcr-search-btn">' + esc( t( 'search_btn' ) ) + '</button>';
			html += '<div class="bcr-error" id="bcr-search-error" style="display:none"></div>';
			html += '</div>';
			html += '</div>'; // .bcr-search-box

			this.container.innerHTML = html;

			document.getElementById( 'bcr-search-btn' ).addEventListener( 'click', function () {
				self.handleSearch();
			} );

			// "Same as pickup" logic
			var sameChk = document.getElementById( 'bcr-same-pickup' );
			if ( sameChk ) {
				sameChk.addEventListener( 'change', function () {
					var dropSel = document.getElementById( 'bcr-dropoff-loc' );
					dropSel.disabled = this.checked;
					if ( this.checked ) {
						dropSel.value = document.getElementById( 'bcr-pickup-loc' ).value;
					}
				} );
				var pickSel = document.getElementById( 'bcr-pickup-loc' );
				if ( pickSel ) {
					pickSel.addEventListener( 'change', function () {
						if ( sameChk.checked ) {
							document.getElementById( 'bcr-dropoff-loc' ).value = this.value;
						}
					} );
				}
			}
		},

		handleSearch: function () {
			var pickup = document.getElementById( 'bcr-pickup-date' ).value;
			var ret    = document.getElementById( 'bcr-return-date' ).value;
			var errEl  = document.getElementById( 'bcr-search-error' );
			var today  = new Date().toISOString().split( 'T' )[0];

			errEl.style.display = 'none';
			if ( ! pickup || ! ret ) {
				errEl.textContent = t( 'error_dates' ); errEl.style.display = 'block'; return;
			}
			if ( pickup >= ret ) {
				errEl.textContent = t( 'error_return_before' ); errEl.style.display = 'block'; return;
			}
			if ( pickup < today ) {
				errEl.textContent = t( 'error_past_date' ); errEl.style.display = 'block'; return;
			}

			this.state.pickupDate = pickup;
			this.state.returnDate = ret;

			var pickLoc = document.getElementById( 'bcr-pickup-loc' );
			if ( pickLoc ) {
				this.state.pickupLoc = pickLoc.value;
				var sameChk = document.getElementById( 'bcr-same-pickup' );
				this.state.sameDropoff = sameChk ? sameChk.checked : true;
				this.state.dropoffLoc = this.state.sameDropoff
					? this.state.pickupLoc
					: document.getElementById( 'bcr-dropoff-loc' ).value;
			}

			var catSel = document.getElementById( 'bcr-category' );
			if ( catSel ) { this.state.category = catSel.value; }

			this.state.step = 'cars';
			this.fetchCars();
		},

		// ── Step 2 : Cars ────────────────────────────────────────────────

		fetchCars: function () {
			var self = this;
			this.container.innerHTML = '<div class="bingocr-loading">' + esc( t( 'loading' ) ) + '</div>';

			var qs = 'pickup_date=' + encodeURIComponent( this.state.pickupDate )
			       + '&return_date=' + encodeURIComponent( this.state.returnDate );
			if ( this.state.category ) { qs += '&category=' + encodeURIComponent( this.state.category ); }

			api( 'cars/available?' + qs )
				.then( function ( cars ) { self.state.cars = cars; self.renderCars(); } )
				.catch( function ( err ) {
					self.container.innerHTML = '<div class="bcr-error">' + esc( err.message ) + '</div>';
				} );
		},

		renderCars: function () {
			var self = this;
			var s    = this.state;
			var days = Math.round(
				( new Date( s.returnDate ) - new Date( s.pickupDate ) ) / 86400000
			);

			var html = '<div class="bcr-cars-header">';
			html += '<button class="bcr-btn bcr-btn-ghost" id="bcr-back-search">&#8592; ' + esc( t( 'back_btn' ) ) + '</button>';
			html += '<div class="bcr-dates-badge">';
			html += '<strong>' + esc( s.pickupDate ) + '</strong> &rarr; <strong>' + esc( s.returnDate ) + '</strong>';
			html += ' &nbsp;·&nbsp; ' + esc( days ) + ' ' + esc( t( 'days' ) );
			if ( s.category ) { html += ' &nbsp;·&nbsp; ' + esc( s.category ); }
			html += '</div></div>';

			if ( ! s.cars.length ) {
				html += '<p class="bcr-empty">' + esc( t( 'no_cars' ) ) + '</p>';
				this.container.innerHTML = html;
				document.getElementById( 'bcr-back-search' ).addEventListener( 'click', function () {
					self.state.step = 'search'; self.render();
				} );
				return;
			}

			// ── Filter bar (shown when there is something to filter) ──────
			var cats = [], trans = [];
			s.cars.forEach( function ( c ) {
				if ( c.category && cats.indexOf( c.category ) === -1 )          { cats.push( c.category ); }
				if ( c.transmission && trans.indexOf( c.transmission ) === -1 ) { trans.push( c.transmission ); }
			} );

			if ( s.cars.length > 1 ) {
				html += '<div class="bcr-filter-bar">';
				html += '<input type="text" id="bcr-f-text" class="bcr-input bcr-f-text" placeholder="' + esc( t( 'filter_text' ) ) + '">';
				if ( cats.length > 1 ) {
					html += '<select id="bcr-f-cat" class="bcr-input bcr-f-select">';
					html += '<option value="">' + esc( t( 'all_categories' ) ) + '</option>';
					cats.forEach( function ( c ) {
						html += '<option value="' + esc( c ) + '">' + esc( c.charAt( 0 ).toUpperCase() + c.slice( 1 ) ) + '</option>';
					} );
					html += '</select>';
				}
				if ( trans.length > 1 ) {
					html += '<select id="bcr-f-trans" class="bcr-input bcr-f-select">';
					html += '<option value="">' + esc( t( 'all_transmissions' ) ) + '</option>';
					html += '<option value="manual">' + esc( t( 'manual' ) ) + '</option>';
					html += '<option value="automatic">' + esc( t( 'automatic' ) ) + '</option>';
					html += '</select>';
				}
				html += '<select id="bcr-f-sort" class="bcr-input bcr-f-select">';
				html += '<option value="">' + esc( t( 'sort_default' ) ) + '</option>';
				html += '<option value="asc">' + esc( t( 'sort_price_asc' ) ) + '</option>';
				html += '<option value="desc">' + esc( t( 'sort_price_desc' ) ) + '</option>';
				html += '</select>';
				html += '</div>';
			}

			html += '<div id="bcr-cars-grid-wrap"></div>';
			this.container.innerHTML = html;

			document.getElementById( 'bcr-back-search' ).addEventListener( 'click', function () {
				self.state.step = 'search'; self.render();
			} );

			// Filter listeners update only the grid, so the inputs keep focus.
			[ [ 'bcr-f-text', 'input' ], [ 'bcr-f-cat', 'change' ], [ 'bcr-f-trans', 'change' ], [ 'bcr-f-sort', 'change' ] ]
				.forEach( function ( pair ) {
					var node = el( pair[0] );
					if ( node ) { node.addEventListener( pair[1], function () { self.renderCarsGrid( days ); } ); }
				} );

			this.renderCarsGrid( days );
		},

		_filteredCars: function () {
			var cars  = this.state.cars.slice();
			var text  = ( el( 'bcr-f-text' )  && el( 'bcr-f-text' ).value.toLowerCase().trim() ) || '';
			var cat   = ( el( 'bcr-f-cat' )   && el( 'bcr-f-cat' ).value )   || '';
			var trans = ( el( 'bcr-f-trans' ) && el( 'bcr-f-trans' ).value ) || '';
			var sort  = ( el( 'bcr-f-sort' )  && el( 'bcr-f-sort' ).value )  || '';

			if ( text ) {
				cars = cars.filter( function ( c ) {
					return ( c.brand + ' ' + c.model ).toLowerCase().indexOf( text ) !== -1;
				} );
			}
			if ( cat )   { cars = cars.filter( function ( c ) { return c.category === cat; } ); }
			if ( trans ) { cars = cars.filter( function ( c ) { return c.transmission === trans; } ); }
			if ( sort === 'asc' )  { cars.sort( function ( a, b ) { return parseFloat( a.price_per_day ) - parseFloat( b.price_per_day ); } ); }
			if ( sort === 'desc' ) { cars.sort( function ( a, b ) { return parseFloat( b.price_per_day ) - parseFloat( a.price_per_day ); } ); }
			return cars;
		},

		renderCarsGrid: function ( days ) {
			var self = this;
			var s    = this.state;
			var wrap = el( 'bcr-cars-grid-wrap' );
			if ( ! wrap ) { return; }

			var cars = this._filteredCars();
			if ( ! cars.length ) {
				wrap.innerHTML = '<p class="bcr-empty">' + esc( t( 'no_match' ) ) + '</p>';
				return;
			}

			var html = '<div class="bcr-cars-grid">';
			cars.forEach( function ( car ) {
				var total = ( parseFloat( car.price_per_day ) * days ).toFixed( 2 );
				html += '<div class="bcr-car-card" data-id="' + esc( car.id ) + '">';
				if ( car.image_url ) {
					html += '<img class="bcr-car-img" src="' + esc( car.image_url ) + '" alt="' + esc( car.brand + ' ' + car.model ) + '">';
				} else {
					html += '<div class="bcr-car-img bcr-car-no-img"><span>&#128664;</span></div>';
				}
				html += '<div class="bcr-car-body">';
				html += '<h3 class="bcr-car-name">' + esc( car.brand ) + ' ' + esc( car.model ) + '</h3>';
				html += '<div class="bcr-car-specs">';
				if ( car.year )     { html += '<span>' + esc( car.year ) + '</span>'; }
				html += '<span>' + esc( car.seats ) + ' ' + esc( t( 'seats' ) ) + '</span>';
				html += '<span>' + esc( car.transmission === 'automatic' ? t( 'automatic' ) : t( 'manual' ) ) + '</span>';
				html += '<span>' + esc( car.fuel_type ) + '</span>';
				if ( car.category ) { html += '<span class="bcr-cat-badge">' + esc( car.category ) + '</span>'; }
				html += '</div>';
				html += '<div class="bcr-car-pricing">';
				html += '<div class="bcr-price-day">' + esc( price( car.price_per_day ) ) + '<small>' + esc( t( 'price_per_day' ) ) + '</small></div>';
				html += '<div class="bcr-price-total">' + esc( price( total ) ) + ' ' + esc( t( 'total' ) ) + '</div>';
				html += '</div>';
				html += '<button class="bcr-btn bcr-btn-primary bcr-book-btn">' + esc( t( 'book_btn' ) ) + '</button>';
				html += '</div></div>';
			} );
			html += '</div>';

			wrap.innerHTML = html;

			wrap.querySelectorAll( '.bcr-book-btn' ).forEach( function ( btn ) {
				btn.addEventListener( 'click', function () {
					var card  = btn.closest( '.bcr-car-card' );
					var carId = parseInt( card.dataset.id, 10 );
					var car   = s.cars.find( function ( c ) { return c.id == carId; } );
					if ( car ) { self.openModal( car ); }
				} );
			} );
		},

		// ── Modal template (fallback when PHP didn't render it) ──────────

		_modalTemplate: function () {
			var h = '<div class="bcr-modal">';

			// Header — car strip + close button.
			h += '<div class="bcr-modal-header">';
			h += '<div class="bcr-modal-car-strip">';
			h += '<img id="bcr-car-img" src="" alt="" class="bcr-modal-car-img" style="display:none">';
			h += '<div class="bcr-modal-car-info"><div class="bcr-modal-car-name" id="bcr-car-name"></div><div class="bcr-modal-car-dates" id="bcr-car-dates"></div></div>';
			h += '</div>';
			h += '<button type="button" class="bcr-modal-close" id="bcr-modal-close">&times;</button>';
			h += '</div>';

			// Order Summary — flex-shrink:0, always visible above the scrollable body.
			h += '<div class="bcr-modal-summary">';
			h += '<div class="bcr-summary-label">' + esc( t( 'order_summary' ) ) + '</div>';
			h += '<div class="bcr-bd">';
			h += '<div class="bcr-bd-row" id="bcr-bd-base-row"><span id="bcr-bd-base-label"></span><span id="bcr-bd-base-amt"></span></div>';
			h += '<div class="bcr-bd-row bcr-bd-rule" id="bcr-bd-period-row" style="display:none"><span id="bcr-bd-period-label"></span><span id="bcr-bd-period-amt"></span></div>';
			h += '<div id="bcr-bd-seasonal-rows"></div>';
			h += '<div id="bcr-bd-extras-rows"></div>';
			h += '<div class="bcr-bd-row bcr-bd-surcharge" id="bcr-bd-ooh-row" style="display:none"><span id="bcr-bd-ooh-label"></span><span id="bcr-bd-ooh-amt"></span></div>';
			h += '<div class="bcr-bd-row bcr-bd-saving" id="bcr-bd-discount-row" style="display:none"><span id="bcr-bd-discount-label"></span><span id="bcr-bd-discount-amt"></span></div>';
			h += '<div class="bcr-bd-total-row"><span>' + esc( t( 'total' ) ) + '</span><span class="bcr-total-amt" id="bcr-bd-total-amt">&hellip;</span></div>';
			h += '<div class="bcr-bd-row bcr-bd-deposit" id="bcr-bd-deposit-row" style="display:none"><span>' + esc( t( 'deposit' ) ) + ' (refundable)</span><span id="bcr-bd-deposit-amt"></span></div>';
			h += '</div>';
			h += '</div>';

			// Scrollable body — extras, discount, form.
			h += '<div class="bcr-modal-body">';
			h += '<div class="bcr-mcard" id="bcr-extras-card" style="display:none"><div class="bcr-mcard-header">' + esc( t( 'extras_title' ) ) + '</div><div class="bcr-mcard-body" id="bcr-extras-list"></div></div>';
			h += '<div class="bcr-mcard" id="bcr-discount-card" style="display:none"><div class="bcr-mcard-header">' + esc( t( 'discount_code' ) ) + '</div><div class="bcr-mcard-body"><div class="bcr-discount-wrap"><input type="text" id="bcr-discount-input" class="bcr-input" placeholder="CODE" style="text-transform:uppercase"><button type="button" id="bcr-apply-discount" class="bcr-btn bcr-btn-outline">' + esc( t( 'apply' ) ) + '</button></div><div id="bcr-discount-msg" class="bcr-discount-msg"></div></div></div>';
			h += '<div class="bcr-mcard" id="bcr-payment-card" style="display:none"><div class="bcr-mcard-header">' + esc( t( 'payment_method' ) ) + '</div><div class="bcr-mcard-body"><div id="bcr-payment-list"></div><div id="bcr-payment-instructions" class="bcr-payment-instructions" style="display:none"></div></div></div>';
			h += '<form id="bcr-booking-form" novalidate>';
			h += '<h5 class="bcr-form-section-title">' + esc( t( 'your_details' ) ) + '</h5>';
			h += '<div class="bcr-form-grid">';
			h += '<div class="bcr-field"><label class="bcr-field-label" for="bcr-pickup-time">' + esc( t( 'pickup_time' ) ) + '</label><input type="time" id="bcr-pickup-time" name="pickup_time" value="10:00" class="bcr-input"></div>';
			h += '<div class="bcr-field"><label class="bcr-field-label" for="bcr-return-time">' + esc( t( 'return_time' ) ) + '</label><input type="time" id="bcr-return-time" name="return_time" value="10:00" class="bcr-input"></div>';
			h += '<div class="bcr-field"><label class="bcr-field-label" for="bcr-cust-name">' + esc( t( 'your_name' ) ) + ' *</label><input type="text" id="bcr-cust-name" name="customer_name" required class="bcr-input" autocomplete="name"></div>';
			h += '<div class="bcr-field"><label class="bcr-field-label" for="bcr-cust-email">' + esc( t( 'email' ) ) + ' *</label><input type="email" id="bcr-cust-email" name="customer_email" required class="bcr-input" autocomplete="email"></div>';
			h += '<div class="bcr-field"><label class="bcr-field-label" for="bcr-cust-phone">' + esc( t( 'phone' ) ) + '</label><input type="tel" id="bcr-cust-phone" name="customer_phone" class="bcr-input" autocomplete="tel"></div>';
			h += '<div class="bcr-field bcr-field-full"><label class="bcr-field-label" for="bcr-cust-notes">' + esc( t( 'notes' ) ) + '</label><textarea id="bcr-cust-notes" name="notes" rows="2" class="bcr-input"></textarea></div>';
			h += '</div>';
			h += '<label class="bcr-terms-label" for="bcr-accept-terms"><input type="checkbox" id="bcr-accept-terms" required> <span>' + esc( t( 'accept_terms_pre' ) ) + ' <a href="#" id="bcr-terms-link" class="bcr-terms-link">' + esc( t( 'terms_link' ) ) + '</a></span></label>';
			h += '</form>';
			h += '<div id="bcr-paypal-buttons" class="bcr-paypal-buttons" style="display:none"></div>';
			h += '</div>';

			h += '<div class="bcr-modal-footer"><p class="bcr-error" id="bcr-book-error" style="display:none"></p><div class="bcr-modal-footer-btns"><button type="button" class="bcr-btn" id="bcr-modal-cancel">' + esc( t( 'cancel' ) ) + '</button><button type="submit" form="bcr-booking-form" class="bcr-btn bcr-btn-primary bcr-btn-confirm">' + esc( t( 'confirm_booking' ) ) + '</button></div></div>';
			h += '</div>';
			return h;
		},

		// ── Terms & Conditions viewer ────────────────────────────────────

		showTerms: function () {
			var self    = this;
			var overlay = el( 'bcr-terms-overlay' );
			if ( ! overlay ) {
				overlay = document.createElement( 'div' );
				overlay.id        = 'bcr-terms-overlay';
				overlay.className = 'bcr-overlay bcr-terms-overlay';
				overlay.innerHTML =
					'<div class="bcr-modal bcr-terms-modal">' +
					'<div class="bcr-modal-header"><div class="bcr-modal-car-name">' + esc( t( 'terms_title' ) ) + '</div>' +
					'<button type="button" class="bcr-modal-close" id="bcr-terms-close">&times;</button></div>' +
					'<div class="bcr-modal-body bcr-terms-body"></div>' +
					'</div>';
				( self.container || document.body ).appendChild( overlay );
				// Admin-provided content, sanitized server-side with wp_kses_post.
				overlay.querySelector( '.bcr-terms-body' ).innerHTML = data.termsText || '';
				el( 'bcr-terms-close' ).onclick = function () { overlay.style.display = 'none'; };
				overlay.onclick = function ( e ) {
					if ( e.target === overlay ) { overlay.style.display = 'none'; }
				};
			}
			overlay.style.display = 'flex';
		},

		// ── Modal ────────────────────────────────────────────────────────

		openModal: function ( car ) {
			var self = this;
			var s    = this.state;
			var days = Math.round(
				( new Date( s.returnDate ) - new Date( s.pickupDate ) ) / 86400000
			);

			// Reset state.
			this.pricing  = null;
			this.discount = null;

			// ── Ensure overlay is in the DOM FIRST ───────────────────────
			// renderCars() replaces container.innerHTML which destroys the PHP
			// modal template, so we must create it here before any el() calls.
			var overlay = el( 'bcr-overlay' );
			if ( ! overlay ) {
				overlay = document.createElement( 'div' );
				overlay.id        = 'bcr-overlay';
				overlay.className = 'bcr-overlay';
				overlay.innerHTML = self._modalTemplate();
				( self.container || document.body ).appendChild( overlay );
			}

			// ── Populate header ───────────────────────────────────────────
			var imgEl = el( 'bcr-car-img' );
			if ( imgEl ) {
				if ( car.image_url ) { imgEl.src = car.image_url; imgEl.style.display = ''; }
				else                 { imgEl.style.display = 'none'; }
			}
			setText( 'bcr-car-name',  car.brand + ' ' + car.model );
			setText( 'bcr-car-dates', s.pickupDate + ' → ' + s.returnDate + '  ·  ' + days + ' ' + t( 'days' ) );

			// ── Reset form ────────────────────────────────────────────────
			var form = el( 'bcr-booking-form' );
			if ( form ) { form.reset(); }
			hide( 'bcr-book-error' );
			setText( 'bcr-bd-total-amt', '…' );
			var confirmBtn = document.querySelector( 'button[form="bcr-booking-form"]' );
			if ( confirmBtn ) {
				confirmBtn.disabled    = false;
				confirmBtn.textContent = t( 'confirm_booking' );
			}

			// ── Reset discount UI ─────────────────────────────────────────
			var discInput = el( 'bcr-discount-input' );
			var discMsg   = el( 'bcr-discount-msg' );
			if ( discInput ) { discInput.value = ''; }
			if ( discMsg )   { discMsg.textContent = ''; discMsg.className = 'bcr-discount-msg'; }

			// ── Extras card ───────────────────────────────────────────────
			var extrasCard = el( 'bcr-extras-card' );
			var extrasList = el( 'bcr-extras-list' );
			var showExtras = s.extras.length > 0;
			if ( extrasCard ) { extrasCard.style.display = showExtras ? '' : 'none'; }
			if ( showExtras && extrasList ) {
				var extHtml = '';
				s.extras.forEach( function ( extra ) {
					var xp = extra.price_type === 'per_day'
						? price( extra.price ) + ' ' + t( 'per_day' )
						: price( extra.price ) + ' ' + t( 'flat' );
					extHtml += '<label class="bcr-extra-row" for="bcr-extra-' + extra.id + '">';
					extHtml += '<input type="checkbox" id="bcr-extra-' + extra.id + '" class="bcr-extra-check"';
					extHtml += ' value="' + extra.id + '"';
					extHtml += ' data-price="' + extra.price + '" data-type="' + ( extra.price_type || 'per_day' ) + '">';
					extHtml += '<span class="bcr-extra-box" aria-hidden="true"></span>';
					extHtml += '<span class="bcr-extra-info">';
					extHtml += '<span class="bcr-extra-name">' + esc( extra.name ) + '</span>';
					if ( extra.description ) {
						extHtml += '<span class="bcr-extra-desc">' + esc( extra.description ) + '</span>';
					}
					extHtml += '</span>';
					extHtml += '<span class="bcr-extra-badge">' + esc( xp ) + '</span>';
					extHtml += '</label>';
				} );
				extrasList.innerHTML = extHtml;
				extrasList.querySelectorAll( '.bcr-extra-check' ).forEach( function ( cb ) {
					cb.addEventListener( 'change', function () {
						cb.closest( '.bcr-extra-row' ).classList.toggle( 'bcr-extra-selected', cb.checked );
						self.updateBreakdown( car, days );
					} );
				} );
			}

			// ── Discount card ─────────────────────────────────────────────
			var discCard = el( 'bcr-discount-card' );
			if ( discCard ) { discCard.style.display = FEATURES.discounts ? '' : 'none'; }

			// ── Payment method card ───────────────────────────────────────
			var payCard = el( 'bcr-payment-card' );
			var payList = el( 'bcr-payment-list' );
			var payMethods = [];
			if ( PAY.cash )   { payMethods.push( [ 'cash', t( 'pay_cash' ) ] ); }
			if ( PAY.card )   { payMethods.push( [ 'card', t( 'pay_card' ) ] ); }
			if ( PAY.bank )   { payMethods.push( [ 'bank', t( 'pay_bank' ) ] ); }
			if ( PAY.paypal ) { payMethods.push( [ 'paypal', t( 'pay_paypal' ) ] ); }
			if ( payCard && payList ) {
				if ( payMethods.length ) {
					var pHtml = '';
					payMethods.forEach( function ( m, i ) {
						pHtml += '<label class="bcr-pay-row">';
						pHtml += '<input type="radio" name="bcr-pay" class="bcr-pay-radio" value="' + m[0] + '"' + ( i === 0 ? ' checked' : '' ) + '>';
						pHtml += '<span class="bcr-pay-name">' + esc( m[1] ) + '</span>';
						pHtml += '</label>';
					} );
					payList.innerHTML = pHtml;
					payList.querySelectorAll( '.bcr-pay-radio' ).forEach( function ( r ) {
						r.addEventListener( 'change', function () { self.updatePaymentUI( car, days ); } );
					} );
					var payNote = el( 'bcr-payment-instructions' );
					if ( payNote ) {
						if ( ( PAY_NOTE || '' ).replace( /<[^>]*>/g, '' ).trim() ) {
							payNote.innerHTML   = PAY_NOTE;   // admin text, sanitized server-side
							payNote.style.display = '';
						} else {
							payNote.style.display = 'none';
						}
					}
					payCard.style.display = '';
				} else {
					payCard.style.display = 'none';
				}
			}

			// ── Terms & conditions link ───────────────────────────────────
			var termsLink = el( 'bcr-terms-link' );
			if ( termsLink ) {
				if ( ( data.termsText || '' ).replace( /<[^>]*>/g, '' ).trim() ) {
					termsLink.onclick = function ( e ) {
						e.preventDefault(); // also stops the label from toggling the checkbox
						self.showTerms();
					};
				} else {
					// No terms configured — show plain text instead of a dead link.
					var termsPlain = document.createElement( 'span' );
					termsPlain.textContent = termsLink.textContent;
					termsLink.replaceWith( termsPlain );
				}
			}

			// ── Time inputs → live out-of-hours fee ───────────────────────
			[ 'bcr-pickup-time', 'bcr-return-time' ].forEach( function ( tid ) {
				var input = el( tid );
				if ( input ) {
					input.onchange = function () {
						// With a discount applied the server owns the total — refresh it.
						if ( self.discount ) { self.applyDiscount( car, days ); }
						else                 { self.updateBreakdown( car, days ); }
					};
				}
			} );

			// ── Show overlay ──────────────────────────────────────────────
			overlay.style.display        = 'flex';
			document.body.style.overflow = 'hidden';

			// Always start the modal body at the top so Order Summary is visible.
			var modalBody = overlay.querySelector( '.bcr-modal-body' );
			if ( modalBody ) { modalBody.scrollTop = 0; }

			// ── Wire events (use .onclick to auto-replace on each open) ──────
			el( 'bcr-modal-close' ).onclick  = function () { self.closeModal(); };
			el( 'bcr-modal-cancel' ).onclick = function () { self.closeModal(); };
			overlay.onclick = function ( e ) {
				if ( e.target === overlay ) { self.closeModal(); }
			};
			if ( this._escHandler ) {
				document.removeEventListener( 'keydown', this._escHandler );
			}
			this._escHandler = function ( e ) {
				if ( e.key === 'Escape' ) { self.closeModal(); }
			};
			document.addEventListener( 'keydown', this._escHandler );

			var applyBtn = el( 'bcr-apply-discount' );
			if ( applyBtn ) {
				applyBtn.onclick = function () { self.applyDiscount( car, days ); };
			}

			el( 'bcr-booking-form' ).onsubmit = function ( e ) {
				e.preventDefault();
				self.submitBooking( car, days );
			};

			// Toggle Confirm vs PayPal button for the initially-selected method.
			this.updatePaymentUI( car, days );

			// Fetch initial pricing (no extras, no discount).
			this.fetchPricing( car, days );
		},

		closeModal: function () {
			var overlay = el( 'bcr-overlay' );
			if ( overlay ) { overlay.style.display = 'none'; }
			document.body.style.overflow = '';
			if ( this._escHandler ) {
				document.removeEventListener( 'keydown', this._escHandler );
				this._escHandler = null;
			}
			this.pricing  = null;
			this.discount = null;
		},

		fetchPricing: function ( car, days ) {
			var self = this;
			setText( 'bcr-bd-total-amt', t( 'loading' ) );

			api( 'pricing/calculate', 'POST', {
				car_id:        parseInt( car.id, 10 ),
				pickup_date:   this.state.pickupDate,
				return_date:   this.state.returnDate,
				extras:        [],
				discount_code: '',
			} )
				.then( function ( result ) {
					self.pricing = result;
					self.updateBreakdown( car, days );
				} )
				.catch( function () {
					var base = parseFloat( car.price_per_day ) * days;
					self.pricing = { days: days, day_rate: parseFloat( car.price_per_day ), base_price: base, period_rule: null, period_type: null, seasonal_adj: 0, total: base };
					self.updateBreakdown( car, days );
				} );
		},

		applyDiscount: function ( car, days ) {
			var self    = this;
			var codeEl  = el( 'bcr-discount-input' );
			var msgEl   = el( 'bcr-discount-msg' );
			var code    = codeEl ? codeEl.value.trim().toUpperCase() : '';
			if ( ! code ) { return; }

			msgEl.textContent = t( 'loading' );
			msgEl.className   = 'bcr-discount-msg';

			var extras = [];
			document.querySelectorAll( '.bcr-extra-check:checked' ).forEach( function ( cb ) {
				extras.push( parseInt( cb.value, 10 ) );
			} );

			api( 'pricing/calculate', 'POST', {
				car_id:        parseInt( car.id, 10 ),
				pickup_date:   this.state.pickupDate,
				return_date:   this.state.returnDate,
				pickup_time:   ( el( 'bcr-pickup-time' ) && el( 'bcr-pickup-time' ).value ) || '',
				return_time:   ( el( 'bcr-return-time' ) && el( 'bcr-return-time' ).value ) || '',
				extras:        extras,
				discount_code: code,
			} )
				.then( function ( result ) {
					var discAmt = parseFloat( result.discount_amount || 0 );
					if ( discAmt > 0 ) {
						self.pricing  = result;
						self.discount = { code: result.discount_code, amount: discAmt };
						msgEl.className   = 'bcr-discount-msg bcr-success';
						msgEl.textContent = '✓  -' + price( discAmt );
					} else {
						msgEl.className   = 'bcr-discount-msg bcr-error';
						msgEl.textContent = t( 'error_required' );
					}
					self.updateBreakdown( car, days );
				} )
				.catch( function ( err ) {
					self.discount     = null;
					msgEl.className   = 'bcr-discount-msg bcr-error';
					msgEl.textContent = err.message;
				} );
		},

		updateBreakdown: function ( car, days ) {
			var r = this.pricing;
			if ( ! r ) { return; }

			var apiTotal  = parseFloat( r.total || 0 );
			var basePrice = parseFloat( r.base_price || 0 );
			var seasAdj   = parseFloat( r.seasonal_adj || 0 );

			// Extras calculated client-side (only when no server discount applied yet).
			var extrasTotal = 0;
			var extrasHtml  = '';
			document.querySelectorAll( '.bcr-extra-check:checked' ).forEach( function ( cb ) {
				var xprice = parseFloat( cb.dataset.price || 0 );
				var xtype  = cb.dataset.type || 'per_day';
				var xamt   = xtype === 'per_day' ? xprice * days : xprice;
				var xname  = cb.closest( '.bcr-extra-row' ).querySelector( '.bcr-extra-name' ).textContent;
				extrasTotal += xamt;
				extrasHtml += '<div class="bcr-bd-row bcr-bd-extra">';
				extrasHtml += '<span>+ ' + esc( xname ) + '</span>';
				extrasHtml += '<span>' + price( xamt ) + '</span>';
				extrasHtml += '</div>';
			} );

			// If a discount was applied via the API call, the extras+discount are already
			// in r.total; just show the server extras breakdown to avoid double-counting.
			var discAmt = 0;
			if ( this.discount ) {
				discAmt    = parseFloat( this.discount.amount || 0 );
				extrasHtml = '';  // already in r.total
				extrasTotal = 0;
				if ( r.extras && r.extras.length ) {
					r.extras.forEach( function ( x ) {
						extrasHtml += '<div class="bcr-bd-row bcr-bd-extra">';
						extrasHtml += '<span>+ ' + esc( x.name ) + '</span>';
						extrasHtml += '<span>' + price( x.amount ) + '</span>';
						extrasHtml += '</div>';
					} );
				}
			}

			// Out-of-hours fee. When a discount was applied via the API (times
			// included in that call), the fee is already inside r.total.
			var ooh = calcOohFee();
			var total = apiTotal + extrasTotal + ( this.discount ? 0 : ooh.fee );

			var oohRow = el( 'bcr-bd-ooh-row' );
			if ( oohRow ) {
				if ( ooh.fee > 0 ) {
					setText( 'bcr-bd-ooh-label', '🌙 ' + t( 'out_of_hours_fee' ) + ( ooh.count > 1 ? ' ×' + ooh.count : '' ) );
					setText( 'bcr-bd-ooh-amt', '+' + price( ooh.fee ) );
					oohRow.style.display = '';
				} else {
					oohRow.style.display = 'none';
				}
			}

			// Base row — reflects the effective nightly rate (a duration rule's rate
			// when one applies, otherwise the car's). base_price is pre-seasonal;
			// the seasonal row below carries the day-by-day adjustment.
			var dayRate = parseFloat( r.day_rate || car.price_per_day );
			if ( r.period_rule && r.period_type === 'fixed_total' ) {
				setText( 'bcr-bd-base-label', '📅 ' + r.period_rule );
			} else if ( r.period_rule ) {
				setText( 'bcr-bd-base-label', '📅 ' + r.period_rule + ' (' + days + ' ' + t( 'days' ) + ' × ' + price( dayRate ) + ')' );
			} else {
				setText( 'bcr-bd-base-label', t( 'base_rental' ) + ' (' + days + ' ' + t( 'days' ) + ' × ' + price( dayRate ) + ')' );
			}
			setText( 'bcr-bd-base-amt', price( basePrice ) );

			// The old standalone duration money row is folded into the base line above.
			var periodRow = el( 'bcr-bd-period-row' );
			if ( periodRow ) { periodRow.style.display = 'none'; }

			// Seasonal — one row per season (name · N nights, ±M%).
			var seasWrap = el( 'bcr-bd-seasonal-rows' );
			if ( seasWrap ) {
				var segs = r.seasonal_breakdown || [];
				var sh   = '';
				segs.forEach( function ( seg ) {
					var amt = parseFloat( seg.amount || 0 );
					if ( amt === 0 ) { return; }
					var mod    = parseFloat( seg.modifier || 0 );
					var modStr = ( mod > 0 ? '+' : '' ) + ( mod % 1 === 0 ? mod : mod.toFixed( 2 ) ) + '%';
					var nWord  = seg.nights === 1 ? t( 'day' ) : t( 'days' );
					var name   = seg.name || t( 'seasonal_adj' );
					var label  = t( 'seasonal_adj' ) + ' · ' + name + ' (' + seg.nights + ' ' + nWord + ', ' + modStr + ')';
					var cls    = amt >= 0 ? 'bcr-bd-surcharge' : 'bcr-bd-saving';
					sh += '<div class="bcr-bd-row ' + cls + '"><span>' + esc( label ) + '</span><span>' + ( amt >= 0 ? '+' : '' ) + price( amt ) + '</span></div>';
				} );
				// Fallback: adjustment exists but no per-season detail provided.
				if ( ! sh && seasAdj !== 0 ) {
					var fcls = seasAdj >= 0 ? 'bcr-bd-surcharge' : 'bcr-bd-saving';
					sh = '<div class="bcr-bd-row ' + fcls + '"><span>' + esc( t( 'seasonal_adj' ) ) + '</span><span>' + ( seasAdj >= 0 ? '+' : '' ) + price( seasAdj ) + '</span></div>';
				}
				seasWrap.innerHTML = sh;
			}

			// Extras rows
			var extrasRowsEl = el( 'bcr-bd-extras-rows' );
			if ( extrasRowsEl ) { extrasRowsEl.innerHTML = extrasHtml; }

			// Discount row
			var discRow = el( 'bcr-bd-discount-row' );
			if ( discAmt > 0 && discRow ) {
				setText( 'bcr-bd-discount-label', '🏷 ' + ( this.discount.code || '' ) );
				setText( 'bcr-bd-discount-amt',   '-' + price( discAmt ) );
				discRow.style.display = '';
			} else if ( discRow ) {
				discRow.style.display = 'none';
			}

			// Total
			setText( 'bcr-bd-total-amt', price( total ) );

			// Deposit
			var depRow = el( 'bcr-bd-deposit-row' );
			var dep    = this.calcDeposit( car, total );
			if ( dep > 0 && depRow ) {
				setText( 'bcr-bd-deposit-amt', price( dep ) );
				depRow.style.display = '';
			} else if ( depRow ) {
				depRow.style.display = 'none';
			}
		},

		calcDeposit: function ( car, total ) {
			var type  = car.deposit_type  || 'default';
			var value = parseFloat( car.deposit_value || 0 );
			if ( type === 'percentage' ) { return total * ( value / 100 ); }
			if ( type === 'flat' )       { return value; }
			return DEF_DEPOSIT;
		},

		// ── Submit ───────────────────────────────────────────────────────

		showBookError: function ( msg ) {
			var errEl = el( 'bcr-book-error' );
			if ( errEl ) {
				errEl.textContent   = msg || '';
				errEl.style.display = msg ? 'block' : 'none';
			}
		},

		// Returns an error message, or '' when the form is valid.
		validateBookingForm: function () {
			var form = el( 'bcr-booking-form' );
			if ( ! form ) { return t( 'error_required' ); }
			var name  = form.querySelector( '[name="customer_name"]' ).value.trim();
			var email = form.querySelector( '[name="customer_email"]' ).value.trim();
			if ( ! name || ! email ) { return t( 'error_required' ); }
			var termsEl = el( 'bcr-accept-terms' );
			if ( termsEl && ! termsEl.checked ) { return t( 'error_terms' ); }
			return '';
		},

		// Builds the reservation payload from the current modal state.
		buildBookingPayload: function ( car, days, method ) {
			var form = el( 'bcr-booking-form' );

			var extras = [];
			document.querySelectorAll( '.bcr-extra-check:checked' ).forEach( function ( cb ) {
				extras.push( parseInt( cb.value, 10 ) );
			} );

			var apiTotal    = this.pricing ? parseFloat( this.pricing.total ) : parseFloat( car.price_per_day ) * days;
			var extrasTotal = 0;
			if ( ! this.discount ) {
				document.querySelectorAll( '.bcr-extra-check:checked' ).forEach( function ( cb ) {
					var xprice = parseFloat( cb.dataset.price || 0 );
					var xtype  = cb.dataset.type || 'per_day';
					extrasTotal += xtype === 'per_day' ? xprice * days : xprice;
				} );
			}
			var total       = apiTotal + extrasTotal + ( this.discount ? 0 : calcOohFee().fee );
			var discountAmt = this.discount ? parseFloat( this.discount.amount || 0 ) : 0;

			return {
				car_id:              car.id,
				customer_name:       form.querySelector( '[name="customer_name"]' ).value.trim(),
				customer_email:      form.querySelector( '[name="customer_email"]' ).value.trim(),
				customer_phone:      form.querySelector( '[name="customer_phone"]' ).value.trim(),
				pickup_date:         this.state.pickupDate,
				return_date:         this.state.returnDate,
				pickup_time:         ( el( 'bcr-pickup-time' ) && el( 'bcr-pickup-time' ).value ) || '10:00',
				return_time:         ( el( 'bcr-return-time' ) && el( 'bcr-return-time' ).value ) || '10:00',
				pickup_location_id:  this.state.pickupLoc  || null,
				dropoff_location_id: this.state.dropoffLoc || null,
				notes:               form.querySelector( '[name="notes"]' ).value.trim(),
				extras:              extras,
				discount_code:       this.discount ? ( this.discount.code || '' ) : '',
				discount_amount:     discountAmt,
				deposit_amount:      this.calcDeposit( car, total ),
				total_price:         total,
				payment_method:      method,
				language:            this.state.lang,
			};
		},

		submitBooking: function ( car, days ) {
			var self = this;
			var err  = this.validateBookingForm();
			if ( err ) { this.showBookError( err ); return; }
			this.showBookError( '' );

			var method  = ( document.querySelector( '.bcr-pay-radio:checked' ) || {} ).value || 'cash';
			var payload = this.buildBookingPayload( car, days, method );
			var email   = payload.customer_email;

			var btn = document.querySelector( 'button[form="bcr-booking-form"]' );
			if ( btn ) { btn.disabled = true; btn.textContent = t( 'loading' ); }

			api( 'reservations', 'POST', payload )
				.then( function ( res ) {
					self.closeModal();
					self.state.reservation = res;
					self.state.email       = email;
					self.state.step        = 'confirm';
					self.render();
				} )
				.catch( function ( error ) {
					self.showBookError( error.message );
					if ( btn ) { btn.disabled = false; btn.textContent = t( 'confirm_booking' ); }
				} );
		},

		// ── PayPal Buttons ───────────────────────────────────────────────

		renderPayPalButtons: function ( car, days ) {
			var self      = this;
			var container = el( 'bcr-paypal-buttons' );
			if ( ! container || ! window.paypal ) { return; }
			container.innerHTML = '';

			window.paypal.Buttons( {
				style: { layout: 'vertical', color: 'gold', shape: 'rect', label: 'paypal' },

				createOrder: function () {
					var err = self.validateBookingForm();
					if ( err ) {
						self.showBookError( err );
						return Promise.reject( new Error( err ) );
					}
					self.showBookError( '' );
					// Create the pending reservation, then the PayPal order for it.
					var payload = self.buildBookingPayload( car, days, 'paypal' );
					self._payEmail = payload.customer_email;
					return api( 'reservations', 'POST', payload )
						.then( function ( res ) {
							return api( 'payments/paypal/create', 'POST', { reservation_id: res.id, customer_email: self._payEmail } );
						} )
						.then( function ( order ) { return order.id; } );
				},

				onApprove: function ( data ) {
					return api( 'payments/paypal/capture', 'POST', { order_id: data.orderID } )
						.then( function ( r ) {
							self.closeModal();
							self.state.reservation = { reservation_number: r.reservation_number };
							self.state.email       = self._payEmail || '';
							self.state.step        = 'confirm';
							self.render();
						} )
						.catch( function ( e ) { self.showBookError( e.message ); } );
				},

				onError: function () {
					self.showBookError( t( 'pay_error' ) );
				},
			} ).render( '#bcr-paypal-buttons' );
		},

		// Toggle between the Confirm button (offline) and PayPal Buttons.
		updatePaymentUI: function ( car, days ) {
			var method     = ( document.querySelector( '.bcr-pay-radio:checked' ) || {} ).value || 'cash';
			var confirmBtn = document.querySelector( 'button[form="bcr-booking-form"]' );
			var ppWrap     = el( 'bcr-paypal-buttons' );
			var isPaypal   = ( method === 'paypal' );
			if ( confirmBtn ) { confirmBtn.style.display = isPaypal ? 'none' : ''; }
			if ( ppWrap )     { ppWrap.style.display     = isPaypal ? '' : 'none'; }
			if ( isPaypal )   { this.renderPayPalButtons( car, days ); }
		},

		// ── Confirm ──────────────────────────────────────────────────────

		renderConfirm: function () {
			var self  = this;
			var res   = this.state.reservation;
			var email = this.state.email;

			var html = '<div class="bcr-confirmation">';
			html += '<div class="bcr-confirm-icon">&#10003;</div>';
			html += '<h2>' + esc( t( 'booking_confirmed' ) ) + '</h2>';
			html += '<div class="bcr-ref-box">';
			html += esc( t( 'booking_ref' ) ) + ' <strong>' + esc( res.reservation_number ) + '</strong>';
			html += '</div>';
			html += '<p>' + esc( t( 'confirmation_email' ) ) + ' <strong>' + esc( email ) + '</strong></p>';
			html += '<button class="bcr-btn bcr-btn-outline" id="bcr-new-search">' + esc( t( 'new_search' ) ) + '</button>';
			html += '</div>';

			this.container.innerHTML = html;
			document.getElementById( 'bcr-new-search' ).addEventListener( 'click', function () {
				self.state.step = 'search';
				self.render();
			} );
		},
	};

	// ── Boot ─────────────────────────────────────────────────────────────

	document.addEventListener( 'DOMContentLoaded', function () {
		document.querySelectorAll( '.bingocr-widget' ).forEach( function ( container ) {
			var instance    = Object.create( App );
			instance.state  = JSON.parse( JSON.stringify( App.state ) );
			instance.pricing     = null;
			instance.discount    = null;
			instance._escHandler = null;
			instance.init( container );
		} );
	} );

} )();
