{"id":1055,"date":"2026-05-28T12:40:37","date_gmt":"2026-05-28T02:40:37","guid":{"rendered":"https:\/\/airliebeachpartybus.com.au\/?page_id=1055"},"modified":"2026-11-08T21:38:14","modified_gmt":"2026-11-08T11:38:14","slug":"need-a-ride-2","status":"publish","type":"page","link":"https:\/\/airliebeachpartybus.com.au\/?page_id=1055","title":{"rendered":"Book a Ride"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">We\u2019ve Moved the Party!<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">After an incredible journey, Need A Ride Whitsundays has officially wrapped up\u2014but your rides around Airlie Beach and the Whitsundays aren\u2019t stopping! <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">To keep you moving seamlessly, all future bookings and charters are now right here!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here is what you need to know:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Familiar, Easy Booking: You\u2019ll feel right at home. The online booking system works almost exactly the same way, so locking in your ride takes just a couple of clicks. <\/li>\n\n\n\n<li>New Loyalty Rewards: We adding a brand-new loyalty program! Now, the more you ride, the more perks and discounts you stack up.<\/li>\n\n\n\n<li>The Same Great Local Energy: Whether you need a hassle-free transfer, a crew pickup, or a celebration on wheels, you\u2019re in great hands. Head on below  and secure your seats for your next adventure!<\/li>\n<\/ul>\n\n\n    <div id=\"tb-ride-wrap\" class=\"tb-uber-wrap\">\n\n        <!-- \u2500\u2500 Step 1: Where to? \u2500\u2500 -->\n        <div class=\"tb-uber-step\" id=\"tb-ride-step-1\">\n            <h3 class=\"tb-uber-heading\">\ud83d\ude97 Request a Ride<\/h3>\n\n            <div class=\"tb-input-group\">\n                <span class=\"tb-input-icon\">\ud83d\udccd<\/span>\n                <input type=\"text\" id=\"tb-ride-pickup\" placeholder=\"Pickup location \u2014 start typing\u2026\" autocomplete=\"off\" required>\n            <\/div>\n            <div id=\"tb-ride-waypoints\"><\/div>\n            <button type=\"button\" id=\"tb-ride-add-stop\" class=\"tb-ride-add-stop\">+ Add a stop (up to 6)<\/button>\n            <p class=\"tb-ride-stop-help\">Stops are automatically ordered to minimise estimated driving distance.<\/p>\n            <div class=\"tb-input-group\">\n                <span class=\"tb-input-icon\">\ud83c\udfc1<\/span>\n                <input type=\"text\" id=\"tb-ride-dropoff\" placeholder=\"Dropoff location \u2014 start typing\u2026\" autocomplete=\"off\" required>\n            <\/div>\n            <!-- Hidden coord fields populated by autocomplete -->\n            <input type=\"hidden\" id=\"tb-ride-pickup-lat\">\n            <input type=\"hidden\" id=\"tb-ride-pickup-lng\">\n            <input type=\"hidden\" id=\"tb-ride-dropoff-lat\">\n            <input type=\"hidden\" id=\"tb-ride-dropoff-lng\">\n\n            <!-- Route preview map (shown once both addresses are resolved) -->\n            <div id=\"tb-ride-preview-map\" class=\"tb-preview-map\" style=\"display:none\"><\/div>\n            <div id=\"tb-ride-price-preview\" aria-live=\"polite\"><\/div>\n\n            <button id=\"tb-ride-get-quote\" class=\"tb-uber-btn tb-uber-btn-dark\" style=\"margin-top:12px\">\n                See prices\n            <\/button>\n            <div id=\"tb-ride-quote-error\" class=\"tb-error\" style=\"display:none\"><\/div>\n        <\/div>\n\n        <!-- \u2500\u2500 Step 2: Choose service \u2500\u2500 -->\n        <div class=\"tb-uber-step\" id=\"tb-ride-step-2\" style=\"display:none\">\n            <div class=\"tb-uber-back\" id=\"tb-ride-back-1\">\u2190 Back<\/div>\n            <h3 class=\"tb-uber-heading\">Choose a ride<\/h3>\n            <div id=\"tb-ride-surge-badge\" class=\"tb-surge-badge\" style=\"display:none\">\u26a1 Surge pricing active<\/div>\n            <div id=\"tb-ride-options\" class=\"tb-service-options\"><\/div>\n            <button id=\"tb-ride-continue\" class=\"tb-uber-btn tb-uber-btn-dark\" disabled>\n                Continue\n            <\/button>\n        <\/div>\n\n        <!-- \u2500\u2500 Step 3: Rider details \u2500\u2500 -->\n        <div class=\"tb-uber-step\" id=\"tb-ride-step-3\" style=\"display:none\">\n            <div class=\"tb-uber-back\" id=\"tb-ride-back-2\">\u2190 Back<\/div>\n            <h3 class=\"tb-uber-heading\">Your details<\/h3>\n                        <div class=\"tb-input-group tb-input-plain\"><input type=\"text\"     id=\"tb-ride-name\"       placeholder=\"Full name\"          required value=\"\" data-prefill=\"\"><\/div>\n            <div class=\"tb-input-group tb-input-plain\"><input type=\"email\"    id=\"tb-ride-email\"      placeholder=\"Email address\"      required value=\"\" data-prefill=\"\"><\/div>\n            <div class=\"tb-input-group tb-input-plain\"><input type=\"tel\"      id=\"tb-ride-phone\"      placeholder=\"Mobile for ride updates (optional), e.g. 0412 345 678\" autocomplete=\"tel\" inputmode=\"tel\" value=\"\" data-prefill=\"\"><\/div>\n            <div class=\"tb-input-group tb-input-plain\"><input type=\"number\"   id=\"tb-ride-passengers\" placeholder=\"Passengers\" value=\"1\" min=\"1\" max=\"6\"><\/div>\n            <div class=\"tb-input-group tb-input-plain\">\n                <label for=\"tb-ride-scheduled\" class=\"tb-ride-sched-label\">Schedule for later (optional)<\/label>\n                <input type=\"datetime-local\" id=\"tb-ride-scheduled\"\n                       min=\"2026-11-12T11:12\"\n                       max=\"2026-12-12T10:57\">\n            <\/div>\n            <div class=\"tb-input-group tb-input-plain\"><textarea id=\"tb-ride-notes\" placeholder=\"Notes for driver (optional)\" rows=\"2\"><\/textarea><\/div>\n            <div class=\"tb-card-field\"><label class=\"tb-label\">Card details <span class=\"tb-required\">*<\/span><\/label><div id=\"tb-ride-card\" class=\"tb-square-card\"><\/div><p class=\"tb-card-secure\">\ud83d\udd12 Secure one-time payment processed by Square.<\/p><\/div>\n            <div id=\"tb-ride-summary\" class=\"tb-fare-summary\"><\/div>\n            <button id=\"tb-ride-confirm\" class=\"tb-uber-btn tb-uber-btn-dark\">\n                Confirm &amp; Pay\n            <\/button>\n            <p class=\"tb-ride-cancel-note\">Free cancellation with a full refund until a driver accepts your ride. After that, a A$10.00 cancellation fee applies until pickup; the rest of your fare is refunded.<\/p>\n            <div id=\"tb-ride-result\"><\/div>\n        <\/div>\n\n        <!-- \u2500\u2500 Waiting for a driver to accept \u2500\u2500 -->\n        <div class=\"tb-uber-step\" id=\"tb-ride-step-searching\" style=\"display:none\">\n            <div class=\"tb-confirmed-icon\">\ud83d\udd0e<\/div>\n            <h3 class=\"tb-uber-heading\">Finding your driver\u2026<\/h3>\n            <div id=\"tb-ride-searching-detail\" class=\"tb-confirmed-detail\"><\/div>\n            <p id=\"tb-ride-searching-status\" class=\"tb-ride-searching-status\">Payment received. We'll confirm your ride as soon as a driver accepts.<\/p>\n            <button type=\"button\" class=\"tb-uber-btn tb-uber-btn-outline tb-ride-cancel-btn\" style=\"margin-top:16px\">Cancel ride<\/button>\n            <p class=\"tb-ride-cancel-note\">Free cancellation with a full refund until a driver accepts your ride. After that, a A$10.00 cancellation fee applies until pickup; the rest of your fare is refunded.<\/p>\n        <\/div>\n\n        <!-- \u2500\u2500 Booked for later (held until shortly before pickup) \u2500\u2500 -->\n        <div class=\"tb-uber-step\" id=\"tb-ride-step-scheduled\" style=\"display:none\">\n            <div class=\"tb-confirmed-icon\">\ud83d\udcc5<\/div>\n            <h3 class=\"tb-uber-heading\">Ride scheduled<\/h3>\n            <div id=\"tb-ride-scheduled-detail\" class=\"tb-confirmed-detail\"><\/div>\n            <p id=\"tb-ride-scheduled-status\" class=\"tb-ride-searching-status\"><\/p>\n            <a id=\"tb-ride-account-link\" class=\"tb-uber-btn tb-uber-btn-dark\" style=\"display:none;margin-top:12px;text-align:center;text-decoration:none\">View or change in My bookings<\/a>\n            <button type=\"button\" class=\"tb-uber-btn tb-uber-btn-outline tb-ride-cancel-btn\" style=\"margin-top:12px\">Cancel ride<\/button>\n            <p class=\"tb-ride-cancel-note\">Free cancellation with a full refund until a driver accepts your ride. After that, a A$10.00 cancellation fee applies until pickup; the rest of your fare is refunded.<\/p>\n            <button type=\"button\" class=\"tb-uber-btn tb-uber-btn-outline tb-ride-restart\" style=\"margin-top:12px\">Book another ride<\/button>\n        <\/div>\n\n        <!-- \u2500\u2500 Cancelled \u2500\u2500 -->\n        <div class=\"tb-uber-step\" id=\"tb-ride-step-cancelled\" style=\"display:none\">\n            <div class=\"tb-confirmed-icon\">\ud83d\udeab<\/div>\n            <h3 class=\"tb-uber-heading\">Ride cancelled<\/h3>\n            <p id=\"tb-ride-cancelled-msg\" class=\"tb-ride-searching-status\"><\/p>\n            <button type=\"button\" class=\"tb-uber-btn tb-uber-btn-outline tb-ride-restart\" style=\"margin-top:16px\">Book another ride<\/button>\n        <\/div>\n\n        <!-- \u2500\u2500 Step 4: Confirmed \u2500\u2500 -->\n        <div class=\"tb-uber-step\" id=\"tb-ride-step-4\" style=\"display:none\">\n            <div class=\"tb-confirmed-icon\">\u2705<\/div>\n            <h3 class=\"tb-uber-heading\">Ride confirmed!<\/h3>\n            <div id=\"tb-ride-confirmed-detail\" class=\"tb-confirmed-detail\"><\/div>\n\n            <!-- Live tracking map -->\n            <div id=\"tb-ride-map-wrap\" class=\"tb-map-wrap\" style=\"display:none\">\n                <div class=\"tb-map-header\">\n                    <span class=\"tb-map-label\">\ud83d\ude97 Driver is on the way<\/span>\n                    <span id=\"tb-ride-eta-badge\" class=\"tb-eta-badge\"><\/span>\n                <\/div>\n                <div id=\"tb-ride-map\" class=\"tb-map\"><\/div>\n                <div id=\"tb-ride-status-bar\" class=\"tb-status-bar\">\n                    <span id=\"tb-ride-status-text\">Driver assigned \u2014 heading to you<\/span>\n                <\/div>\n            <\/div>\n\n            <div id=\"tb-ride-driver-card\" class=\"tb-driver-card\" style=\"display:none\"><\/div>\n            <!-- Star rating -->\n            <div id=\"tb-ride-rating-wrap\" class=\"tb-rating-wrap\" style=\"display:none\">\n                <p>Rate your ride:<\/p>\n                <div class=\"tb-stars\">\n                    <span class=\"tb-star\" data-v=\"1\">\u2605<\/span>\n                    <span class=\"tb-star\" data-v=\"2\">\u2605<\/span>\n                    <span class=\"tb-star\" data-v=\"3\">\u2605<\/span>\n                    <span class=\"tb-star\" data-v=\"4\">\u2605<\/span>\n                    <span class=\"tb-star\" data-v=\"5\">\u2605<\/span>\n                <\/div>\n                <p id=\"tb-ride-rating-msg\"><\/p>\n            <\/div>\n            <button type=\"button\" class=\"tb-uber-btn tb-uber-btn-outline tb-ride-cancel-btn\" style=\"margin-top:16px\">Cancel ride<\/button>\n            <p class=\"tb-ride-cancel-note\">Free cancellation with a full refund until a driver accepts your ride. After that, a A$10.00 cancellation fee applies until pickup; the rest of your fare is refunded.<\/p>\n            <button id=\"tb-ride-new\" class=\"tb-uber-btn tb-uber-btn-outline\" style=\"margin-top:16px\">Book another ride<\/button>\n        <\/div>\n    <\/div>\n\n    <script>\n    \/\/ Load Leaflet lazily only when the map is needed\n    function tbLoadLeaflet(cb) {\n        if (window.L) { cb(); return; }\n        var css = document.createElement('link');\n        css.rel = 'stylesheet';\n        css.href = 'https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.css';\n        document.head.appendChild(css);\n        var js = document.createElement('script');\n        js.src = 'https:\/\/unpkg.com\/leaflet@1.9.4\/dist\/leaflet.js';\n        js.onload = cb;\n        document.head.appendChild(js);\n    }\n\n    if (typeof jQuery !== 'undefined') {\n        jQuery(function ($) {\n            var rideQuote       = null;\n            var selectedType    = null;\n            var confirmedRideId = null;\n            var rideRequestKey  = null;\n            var submittingRide  = false;\n            var rideToken       = null;   \/\/ tracking token: lets this rider track\/cancel their own ride\n            var tbMap           = null;\n            var driverMarker    = null;\n            var driverPos       = null;   \/\/ {lat, lng}\n            var pickupCoords    = null;   \/\/ {lat, lng}\n            var dropoffCoords   = null;   \/\/ {lat, lng}\n            var etaSeconds      = 0;\n            var etaInterval     = null;\n            var moveInterval    = null;\n            var tripPhase       = 'en_route_pickup';\n            var quoteVersion    = 0;\n            var quoteTimer      = null;\n            var maxRideStops    = 6;\n\n            \/\/ \u2500\u2500 Address autocomplete + route-preview map \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            var rideAC = null;\n            if (window.tbMaps) {\n                rideAC = tbMaps.attach({\n                    pickupInput   : '#tb-ride-pickup',\n                    dropoffInput  : '#tb-ride-dropoff',\n                    pickupLat     : '#tb-ride-pickup-lat',\n                    pickupLng     : '#tb-ride-pickup-lng',\n                    dropoffLat    : '#tb-ride-dropoff-lat',\n                    dropoffLng    : '#tb-ride-dropoff-lng',\n                    mapContainer  : 'tb-ride-preview-map',\n                    onBothResolved: function () {\n                        $('#tb-ride-preview-map').show();\n                        routeChanged();\n                    },\n                    onRouteChange: routeChanged\n                });\n            }\n\n            function routeChanged() {\n                quoteVersion++;\n                rideQuote = null;\n                selectedType = null;\n                $('#tb-ride-continue').prop('disabled', true);\n                $('#tb-ride-price-preview').empty();\n                $('#tb-ride-quote-error').hide();\n                clearTimeout(quoteTimer);\n                if (!rideAC) return;\n                var c = rideAC.getCoords();\n                if (c.pickup_lat === null || c.pickup_lng === null || c.dropoff_lat === null || c.dropoff_lng === null || rideAC.getWaypoints() === null) return;\n                quoteTimer = setTimeout(function () { getRideQuote(false); }, 400);\n            }\n\n            function labelRideStops() {\n                $('#tb-ride-waypoints .tb-ride-stop').each(function (index) {\n                    $(this).find('.tb-ride-stop-label').text('Stop ' + (index + 1));\n                    $(this).find('input').attr('aria-label', 'Stop ' + (index + 1) + ' address');\n                });\n            }\n\n            $('#tb-ride-add-stop').on('click', function () {\n                if (!rideAC) {\n                    $('#tb-ride-quote-error').text('Address search is unavailable. Please reload the page.').show();\n                    return;\n                }\n                var count = $('#tb-ride-waypoints .tb-ride-stop').length;\n                if (count >= maxRideStops) return;\n                var $row = $('<div class=\"tb-input-group tb-ride-stop\"><span class=\"tb-input-icon tb-ride-stop-label\"><\/span><input type=\"text\" placeholder=\"Stop address \u2014 start typing\u2026\" aria-label=\"Stop address\" autocomplete=\"off\"><button type=\"button\" class=\"tb-ride-remove-stop\" aria-label=\"Remove stop\">Remove<\/button><\/div>');\n                $('#tb-ride-waypoints').append($row);\n                rideAC.addWaypoint($row.find('input'));\n                labelRideStops();\n                $('#tb-ride-add-stop').prop('disabled', count + 1 >= maxRideStops);\n                $row.find('input').trigger('focus');\n            });\n            $('#tb-ride-waypoints').on('click', '.tb-ride-remove-stop', function () {\n                var $row = $(this).closest('.tb-ride-stop');\n                rideAC.removeWaypoint($row.find('input'));\n                $row.remove();\n                labelRideStops();\n                $('#tb-ride-add-stop').prop('disabled', false);\n            });\n\n            function getRideQuote(advance) {\n                var pickup  = $('#tb-ride-pickup').val().trim();\n                var dropoff = $('#tb-ride-dropoff').val().trim();\n                if (!pickup || !dropoff) {\n                    if (advance) $('#tb-ride-quote-error').text('Please enter pickup and dropoff.').show();\n                    return;\n                }\n                var waypoints = rideAC ? rideAC.getWaypoints() : [];\n                if (waypoints === null) {\n                    if (advance) $('#tb-ride-quote-error').text('Select an address from the search results for each stop, or remove it.').show();\n                    return;\n                }\n                $('#tb-ride-quote-error').hide();\n                var version = ++quoteVersion;\n                var $btn = $('#tb-ride-get-quote');\n                if (advance) $btn.prop('disabled', true).text('Loading\u2026');\n                else $('#tb-ride-price-preview').text(waypoints.length > 1 ? 'Optimising stops and updating prices\u2026' : 'Updating prices\u2026');\n                var payload = { pickup_address: pickup, dropoff_address: dropoff, waypoints: waypoints, optimize_waypoints: true };\n                if (rideAC) {\n                    var c = rideAC.getCoords();\n                    if (c.pickup_lat !== null && c.pickup_lng !== null && c.dropoff_lat !== null && c.dropoff_lng !== null) {\n                        payload.from_lat = c.pickup_lat; payload.from_lng = c.pickup_lng;\n                        payload.to_lat = c.dropoff_lat; payload.to_lng = c.dropoff_lng;\n                    }\n                }\n                $.ajax({\n                    url: tb_vars.ride_quote_url,\n                    method: 'POST', contentType: 'application\/json',\n                    data: JSON.stringify(payload),\n                    beforeSend: function (xhr) { xhr.setRequestHeader('X-WP-Nonce', tb_vars.nonce); },\n                    success: function (r) {\n                        if (version !== quoteVersion) return;\n                        if (rideAC) {\n                            var inputs = rideAC.reorderWaypoints(r.waypoint_order);\n                            if (!inputs) {\n                                $('#tb-ride-price-preview').empty();\n                                $('#tb-ride-quote-error').text('Could not apply the optimised stop order. Please try again.').show();\n                                return;\n                            }\n                            inputs.forEach(function (input) { $('#tb-ride-waypoints').append($(input).closest('.tb-ride-stop')); });\n                            labelRideStops();\n                            rideAC.setResolvedCoords(r.coords);\n                            $('#tb-ride-preview-map').show();\n                        }\n                        rideQuote    = r;\n                        selectedType = null;\n                        var prices = [];\n                        $.each(r.options, function (key, opt) { prices.push(opt.label + ': ' + opt.fare_display); });\n                        $('#tb-ride-price-preview').text(prices.join(' \u00b7 ') + ' (' + r.distance_km + ' km)');\n                        if (!advance) return;\n                        var html = '';\n                        $.each(r.options, function (key, opt) {\n                            html += '<div class=\"tb-service-card\" data-type=\"' + key + '\">' +\n                                '<span class=\"tb-svc-icon\">' + opt.icon + '<\/span>' +\n                                '<div class=\"tb-svc-info\"><strong>' + opt.label + '<\/strong>' +\n                                '<small>Up to ' + opt.max_seats + ' seats &bull; ~' + opt.eta_min + ' min away<\/small><\/div>' +\n                                '<div class=\"tb-svc-fare\">' + opt.fare_display + '<\/div>' +\n                                '<\/div>';\n                        });\n                        $('#tb-ride-options').html(html);\n                        if (parseFloat(r.surge) > 1) {\n                            $('#tb-ride-surge-badge').show();\n                        } else $('#tb-ride-surge-badge').hide();\n                        $('#tb-ride-step-1').hide();\n                        $('#tb-ride-step-2').show();\n                    },\n                    error: function (xhr) {\n                        if (version !== quoteVersion) return;\n                        var msg = 'Could not get prices.';\n                        try { msg = JSON.parse(xhr.responseText).error || msg; } catch(e){}\n                        $('#tb-ride-price-preview').empty();\n                        $('#tb-ride-quote-error').text(msg).show();\n                    },\n                    complete: function () { if (advance) $btn.prop('disabled', false).text('See prices'); }\n                });\n            }\n            \/\/ \u2500\u2500 Step 1 \u2192 2: get quotes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            $('#tb-ride-get-quote').on('click', function () {\n                clearTimeout(quoteTimer);\n                getRideQuote(true);\n            });\n\n            \/\/ Select service card\n            $(document).on('click', '.tb-service-card', function () {\n                $('.tb-service-card').removeClass('tb-selected');\n                $(this).addClass('tb-selected');\n                selectedType = $(this).data('type');\n                $('#tb-ride-continue').prop('disabled', false);\n            });\n\n            \/\/ Step 2 \u2192 3\n            $('#tb-ride-continue').on('click', function () {\n                if (!selectedType) return;\n                var opt = rideQuote.options[selectedType];\n                $('#tb-ride-summary').html(\n                    '<strong>' + opt.icon + ' ' + opt.label + '<\/strong>' +\n                    ' &mdash; ' + opt.fare_display +\n                    (parseFloat(rideQuote.surge) > 1 ? ' <span class=\"tb-surge-tag\">\u26a1 ' + rideQuote.surge + '\u00d7 surge<\/span>' : '') +\n                    '<br><small>' + rideQuote.distance_km + ' km \u00b7 ~' + rideQuote.duration_min + ' min<\/small>' +\n                    (rideQuote.waypoints || []).map(function (w, i) {\n                        return '<br><small>Stop ' + (i + 1) + ': ' + escHtml(w.address) + '<\/small>';\n                    }).join('')\n                );\n                \/\/ Passenger limit follows the chosen vehicle; the server enforces the same limit.\n                var maxSeats = Math.max(1, parseInt(opt.max_seats, 10) || 1);\n                var $pax = $('#tb-ride-passengers').attr('max', maxSeats)\n                    .attr('placeholder', 'Passengers (up to ' + maxSeats + ')');\n                if ((parseInt($pax.val(), 10) || 1) > maxSeats) $pax.val(maxSeats);\n                $('#tb-ride-step-2').hide();\n                $('#tb-ride-step-3').show();\n            });\n\n            \/\/ Back buttons\n            $('#tb-ride-back-1').on('click', function () { $('#tb-ride-step-2').hide(); $('#tb-ride-step-1').show(); });\n            $('#tb-ride-back-2').on('click', function () { $('#tb-ride-step-3').hide(); $('#tb-ride-step-2').show(); });\n\n            \/\/ Step 3: confirm & pay\n            $('#tb-ride-confirm').on('click', function () {\n                if (submittingRide) return;\n                var name      = $('#tb-ride-name').val().trim();\n                var email     = $('#tb-ride-email').val().trim();\n                var scheduled = $('#tb-ride-scheduled').val();\n                if (!name || !email) {\n                    $('#tb-ride-result').html('<div class=\"tb-error\">Please fill in all required fields.<\/div>');\n                    return;\n                }\n                if (!rideRequestKey) {\n                    try {\n                        if (window.crypto && window.crypto.randomUUID) {\n                            rideRequestKey = window.crypto.randomUUID();\n                        } else if (window.crypto && window.crypto.getRandomValues) {\n                            var bytes = new Uint8Array(16);\n                            window.crypto.getRandomValues(bytes);\n                            bytes[6] = (bytes[6] & 0x0f) | 0x40;\n                            bytes[8] = (bytes[8] & 0x3f) | 0x80;\n                            var hex = Array.prototype.map.call(bytes, function (byte) {\n                                return ('0' + byte.toString(16)).slice(-2);\n                            }).join('');\n                            rideRequestKey = hex.slice(0, 8) + '-' + hex.slice(8, 12) + '-'\n                                + hex.slice(12, 16) + '-' + hex.slice(16, 20) + '-' + hex.slice(20);\n                        } else {\n                            throw new Error('Your browser cannot securely start a booking. Please update your browser and try again.');\n                        }\n                    } catch (error) {\n                        $('#tb-ride-result').html($('<div class=\"tb-error\">').text(error.message));\n                        return;\n                    }\n                }\n                submittingRide = true;\n                var $confirm = $('#tb-ride-confirm').prop('disabled', true).text('Processing\u2026');\n                $('#tb-ride-result').html('<em>Requesting your ride\u2026<\/em>');\n                var payload = {\n                    idempotency_key: rideRequestKey,\n                    service_type:    selectedType,\n                    pickup_address:  $('#tb-ride-pickup').val().trim(),\n                    dropoff_address: $('#tb-ride-dropoff').val().trim(),\n                    name:            name,\n                    email:           email,\n                    phone:           $('#tb-ride-phone').val(),\n                    passengers:      parseInt($('#tb-ride-passengers').val()) || 1,\n                    notes:           $('#tb-ride-notes').val(),\n                    from_lat:        rideQuote.coords.from_lat,\n                    from_lng:        rideQuote.coords.from_lng,\n                    to_lat:          rideQuote.coords.to_lat,\n                    to_lng:          rideQuote.coords.to_lng,\n                    waypoints:       rideQuote.waypoints,\n                    quoted_fare_cents: rideQuote.options[selectedType].fare_cents,\n                };\n                if (scheduled) payload.scheduled_at = scheduled;\n                window.tbSquareCard.tokenize('tb-ride-card').then(function (token) {\n                payload.sq_nonce = token;\n                $.ajax({\n                    url: tb_vars.ride_request_url,\n                    method: 'POST', contentType: 'application\/json',\n                    data: JSON.stringify(payload),\n                    beforeSend: function (xhr) { xhr.setRequestHeader('X-WP-Nonce', tb_vars.nonce); },\n                    success: function (r) {\n                        rideRequestKey = null;\n                        confirmedRideId = r.ride_id;\n                        rideToken       = r.tracking_token;\n                        $('#tb-ride-result').html('');\n\n                        \/\/ Booked well ahead: held until shortly before pickup, so don't poll for a driver.\n                        if (r.status === 'scheduled') {\n                            $('#tb-ride-scheduled-detail').html(\n                                '<p><strong>Pickup:<\/strong> ' + escHtml(r.scheduled_display) + '<\/p>' +\n                                '<p><strong>Fare:<\/strong> ' + escHtml(r.fare_display) + '<\/p>' +\n                                '<p><strong>Distance:<\/strong> ' + r.distance_km + ' km &bull; ~' + r.duration_min + ' min<\/p>'\n                            );\n                            $('#tb-ride-scheduled-status').text(r.message || '');\n                            if (r.account_url) {\n                                $('#tb-ride-account-link').attr('href', r.account_url).show();\n                            }\n                            $('#tb-ride-step-3').hide();\n                            $('#tb-ride-step-scheduled').show();\n                            return;\n                        }\n\n                        $('#tb-ride-searching-detail').html(\n                            '<p><strong>Fare:<\/strong> ' + escHtml(r.fare_display) +\n                            (r.surge_multiplier > 1 ? ' <span class=\"tb-surge-tag\">\u26a1 ' + r.surge_multiplier + '\u00d7 surge<\/span>' : '') + '<\/p>' +\n                            '<p><strong>Distance:<\/strong> ' + r.distance_km + ' km &bull; ~' + r.duration_min + ' min<\/p>'\n                        );\n                        $('#tb-ride-searching-status').text(r.message || \"Payment received. We'll confirm your ride as soon as a driver accepts.\");\n                        $('#tb-ride-result').html('');\n                        $('#tb-ride-step-3').hide();\n                        $('#tb-ride-step-searching').show();\n                        waitForDriver(r);\n                    },\n                    error: function (xhr) {\n                        submittingRide = false;\n                        $confirm.prop('disabled', false).text('Confirm & Pay');\n                        var msg = 'Request failed.';\n                        try { msg = JSON.parse(xhr.responseText).error || msg; } catch(e){}\n                        $('#tb-ride-result').html('<div class=\"tb-error\">\u2717 ' + msg + '<\/div>');\n                    }\n                });\n                }, function (err) {\n                    submittingRide = false;\n                    $confirm.prop('disabled', false).text('Confirm & Pay');\n                    $('#tb-ride-result').html($('<div class=\"tb-error\">').text('\u2717 ' + err.message));\n                });\n            });\n\n            function escHtml(str) {\n                return $('<span>').text(str == null ? '' : String(str)).html();\n            }\n\n            \/\/ \u2500\u2500 Wait for a driver to accept, then confirm \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            var POLL_MS = 5000;\n            var pollTimer = null;\n\n            function waitForDriver(requestResp) {\n                rideToken = requestResp.tracking_token;\n                clearTimeout(pollTimer);\n                var rideId = requestResp.ride_id;\n\n                function poll() {\n                    if (confirmedRideId !== rideId) return;\n                    $.getJSON(tb_vars.ride_tracking_url, { ride_id: rideId, token: requestResp.tracking_token }).done(function (t) {\n                        if (confirmedRideId !== rideId) return;\n                        if ((t.status === 'driver_assigned' || t.status === 'in_progress') && t.driver) {\n                            showRideConfirmed(requestResp, t.driver, t.eta_minutes);\n                            return;\n                        }\n                        if (t.status === 'cancelled') {\n                            $('#tb-ride-searching-status').text('This ride request was cancelled. Please contact us about your payment.');\n                            return;\n                        }\n                        pollTimer = setTimeout(poll, POLL_MS);\n                    }).fail(function () {\n                        pollTimer = setTimeout(poll, POLL_MS);\n                    });\n                }\n                pollTimer = setTimeout(poll, POLL_MS);\n            }\n\n            function showRideConfirmed(r, driver, etaMin) {\n                $('#tb-ride-confirmed-detail').html(\n                    '<p><strong>Fare:<\/strong> ' + escHtml(r.fare_display) +\n                    (r.surge_multiplier > 1 ? ' <span class=\"tb-surge-tag\">\u26a1 ' + r.surge_multiplier + '\u00d7 surge<\/span>' : '') + '<\/p>' +\n                    '<p><strong>Distance:<\/strong> ' + r.distance_km + ' km &bull; ~' + r.duration_min + ' min<\/p>' +\n                    (etaMin ? '<p><strong>Driver ETA:<\/strong> <span id=\"tb-ride-eta-text\">' + etaMin + ' min<\/span><\/p>' : '')\n                );\n                $('#tb-ride-driver-card').html(\n                    '<div class=\"tb-driver-avatar\">\ud83e\uddd1\u200d\u2708\ufe0f<\/div>' +\n                    '<div><strong>' + escHtml(driver.name) + '<\/strong><br>' +\n                    '<small>' + escHtml([driver.vehicle_color, driver.vehicle_make, driver.vehicle_model].join(' ').trim()) + ' \u00b7 ' + escHtml(driver.vehicle_plate) + '<\/small><br>' +\n                    '<span class=\"tb-stars-small\">\u2b50 ' + escHtml(driver.rating) + '<\/span><\/div>'\n                ).show();\n                $('#tb-ride-step-searching').hide();\n                $('#tb-ride-step-4').show();\n\n                var rideResp = $.extend({}, r, { driver: driver, eta_min: etaMin });\n                if (rideQuote && rideQuote.coords) {\n                    pickupCoords  = { lat: rideQuote.coords.from_lat, lng: rideQuote.coords.from_lng };\n                    dropoffCoords = { lat: rideQuote.coords.to_lat,   lng: rideQuote.coords.to_lng  };\n                    etaSeconds    = (etaMin || 5) * 60;\n                    tbLoadLeaflet(function () {\n                        tbInitMap(pickupCoords, dropoffCoords, etaSeconds, rideResp);\n                    });\n                }\n\n                \/\/ Show rating after the simulated trip duration\n                var tripMs = ((r.duration_min || 10) * 60 + (etaMin || 5) * 60) * 1000;\n                setTimeout(function () { $('#tb-ride-rating-wrap').show(); }, tripMs);\n            }\n\n            \/\/ \u2500\u2500 Map initialisation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n            function tbInitMap(pickup, dropoff, etaSec, rideResp) {\n                $('#tb-ride-map-wrap').show();\n\n                \/\/ Destroy any previous map instance\n                if (tbMap) { tbMap.remove(); tbMap = null; }\n                clearInterval(etaInterval);\n                clearInterval(moveInterval);\n                tripPhase = 'en_route_pickup';\n\n                tbMap = L.map('tb-ride-map', { zoomControl: true, attributionControl: false });\n                L.tileLayer('https:\/\/{s}.tile.openstreetmap.org\/{z}\/{x}\/{y}.png', {\n                    maxZoom: 18,\n                    attribution: '\u00a9 OpenStreetMap'\n                }).addTo(tbMap);\n                L.control.attribution({ prefix: false }).addTo(tbMap);\n\n                \/\/ Custom icons\n                var pickupIcon = L.divIcon({ className: '', html: '<div class=\"tb-map-pin tb-pin-pickup\">\ud83d\udccd<\/div>', iconSize:[32,32], iconAnchor:[16,32] });\n                var dropoffIcon = L.divIcon({ className: '', html: '<div class=\"tb-map-pin tb-pin-dropoff\">\ud83c\udfc1<\/div>', iconSize:[32,32], iconAnchor:[16,32] });\n                var carIcon = L.divIcon({ className: '', html: '<div class=\"tb-map-pin tb-pin-car\">\ud83d\ude97<\/div>', iconSize:[32,32], iconAnchor:[16,16] });\n\n                L.marker([pickup.lat, pickup.lng], { icon: pickupIcon })\n                    .addTo(tbMap)\n                    .bindPopup('<strong>Pickup<\/strong><br>' + escHtml($('#tb-ride-pickup').val()));\n                L.marker([dropoff.lat, dropoff.lng], { icon: dropoffIcon })\n                    .addTo(tbMap)\n                    .bindPopup('<strong>Dropoff<\/strong><br>' + escHtml($('#tb-ride-dropoff').val()));\n                var routePoints = [pickup].concat(rideResp.waypoints || [], [dropoff]);\n                (rideResp.waypoints || []).forEach(function (stop, index) {\n                    L.marker([stop.lat, stop.lng]).addTo(tbMap)\n                        .bindPopup('<strong>Stop ' + (index + 1) + '<\/strong><br>' + escHtml(stop.address));\n                });\n\n                var d = rideResp.driver;\n                if (d && d.latitude != null && d.longitude != null) {\n                    driverPos = { lat: parseFloat(d.latitude), lng: parseFloat(d.longitude) };\n                } else {\n                    \/\/ No location from the driver: place the car ~0.8 km from pickup in a random direction\n                    var angle = Math.random() * Math.PI * 2;\n                    var offsetDeg = 0.008;\n                    driverPos = {\n                        lat: pickup.lat + Math.cos(angle) * offsetDeg,\n                        lng: pickup.lng + Math.sin(angle) * offsetDeg\n                    };\n                }\n                driverMarker = L.marker([driverPos.lat, driverPos.lng], { icon: carIcon })\n                    .addTo(tbMap)\n                    .bindPopup(rideResp.driver ? '<strong>' + escHtml(rideResp.driver.name) + '<\/strong><br>\u2b50 ' + escHtml(rideResp.driver.rating) : 'Your driver');\n\n                \/\/ Draw route polyline (straight line fallback; OSRM polyline drawn if available)\n                var routeLine = L.polyline(routePoints.map(function (p) { return [p.lat, p.lng]; }), {\n                    color: '#000', weight: 3, dashArray: '6,8', opacity: 0.5\n                }).addTo(tbMap);\n\n                \/\/ Try to draw real OSRM route\n                var osrm = 'https:\/\/router.project-osrm.org\/route\/v1\/driving\/' +\n                    routePoints.map(function (p) { return p.lng + ',' + p.lat; }).join(';') +\n                    '?overview=full&geometries=geojson';\n                $.getJSON(osrm).done(function (data) {\n                    if (data.routes && data.routes[0]) {\n                        tbMap.removeLayer(routeLine);\n                        L.geoJSON(data.routes[0].geometry, {\n                            style: { color: '#1a1a1a', weight: 4, opacity: 0.7 }\n                        }).addTo(tbMap);\n                    }\n                });\n\n                \/\/ Fit map to show all points\n                tbMap.fitBounds(routePoints.map(function (p) { return [p.lat, p.lng]; })\n                    .concat([[driverPos.lat, driverPos.lng]]), { padding: [40, 40] });\n\n                \/\/ \u2500\u2500 ETA countdown \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n                var remaining = etaSec;\n                var knownEta  = rideResp.eta_min != null;\n                function fmtEta(s) {\n                    if (!knownEta && tripPhase === 'en_route_pickup') return 'On the way';\n                    if (s <= 0) return 'Arriving now';\n                    var m = Math.floor(s \/ 60), sec = s % 60;\n                    return m + ':' + (sec < 10 ? '0' : '') + sec + ' away';\n                }\n                $('#tb-ride-eta-badge').text(fmtEta(remaining));\n                etaInterval = setInterval(function () {\n                    remaining--;\n                    if (remaining < 0) remaining = 0;\n                    $('#tb-ride-eta-badge').text(fmtEta(remaining));\n                    if (remaining <= 0) {\n                        clearInterval(etaInterval);\n                        tripPhase = 'arrived';\n                        $('#tb-ride-status-text').text('Driver arrived \u2014 boarding');\n                        $('#tb-ride-map-wrap .tb-map-label').text('\ud83d\ude97 Driver arrived');\n                        \/\/ Switch to trip phase after 5 s\n                        setTimeout(function () {\n                            tripPhase = 'en_route_dropoff';\n                            var tripEta = (rideResp.duration_min || 10) * 60;\n                            remaining  = tripEta;\n                            $('#tb-ride-map-wrap .tb-map-label').text('\ud83c\udfc1 En route to dropoff');\n                            $('#tb-ride-status-text').text('En route to destination');\n                            etaInterval = setInterval(function () {\n                                remaining--;\n                                if (remaining < 0) remaining = 0;\n                                $('#tb-ride-eta-badge').text(fmtEta(remaining));\n                                if (remaining <= 0) {\n                                    clearInterval(etaInterval);\n                                    clearInterval(moveInterval);\n                                    tripPhase = 'completed';\n                                    $('#tb-ride-map-wrap .tb-map-label').text('\u2705 Arrived at destination');\n                                    $('#tb-ride-eta-badge').text('Completed');\n                                    $('#tb-ride-status-text').text('You have arrived!');\n                                    driverMarker.setLatLng([dropoff.lat, dropoff.lng]);\n                                    $('#tb-ride-rating-wrap').show();\n                                }\n                            }, 1000);\n                        }, 5000);\n                    }\n                }, 1000);\n\n                \/\/ \u2500\u2500 Animate driver marker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n                var MOVE_INTERVAL_MS = 1500; \/\/ update every 1.5 s\n                moveInterval = setInterval(function () {\n                    var target = (tripPhase === 'en_route_dropoff') ? dropoff : pickup;\n                    var dlat = target.lat - driverPos.lat;\n                    var dlng = target.lng - driverPos.lng;\n                    var dist = Math.sqrt(dlat * dlat + dlng * dlng);\n                    if (dist < 0.0001) {\n                        driverPos = { lat: target.lat, lng: target.lng };\n                    } else {\n                        \/\/ Move ~10% of remaining distance each tick\n                        driverPos.lat += dlat * 0.1;\n                        driverPos.lng += dlng * 0.1;\n                    }\n                    driverMarker.setLatLng([driverPos.lat, driverPos.lng]);\n                }, MOVE_INTERVAL_MS);\n            }\n\n            \/\/ Star rating\n            $(document).on('click', '.tb-star', function () {\n                var stars = parseInt($(this).data('v'));\n                $('.tb-star').each(function (i) { $(this).toggleClass('tb-star-on', i < stars); });\n                if (!confirmedRideId) return;\n                $.ajax({\n                    url: tb_vars.ride_rate_url,\n                    method: 'POST', contentType: 'application\/json',\n                    data: JSON.stringify({ ride_id: confirmedRideId, rating: stars }),\n                    beforeSend: function (xhr) { xhr.setRequestHeader('X-WP-Nonce', tb_vars.nonce); },\n                    success: function () { $('#tb-ride-rating-msg').text('Thanks for your rating!'); }\n                });\n            });\n\n            \/\/ Book another\n            $('#tb-ride-new').on('click', function () {\n                clearTimeout(pollTimer);\n                clearInterval(etaInterval);\n                clearInterval(moveInterval);\n                if (tbMap) { tbMap.remove(); tbMap = null; }\n                rideQuote = null; selectedType = null; confirmedRideId = null;\n                rideRequestKey = null; submittingRide = false;\n                $('#tb-ride-confirm').prop('disabled', false).text('Confirm & Pay');\n                quoteVersion++; clearTimeout(quoteTimer);\n                if (rideAC) rideAC.reset();\n                $('#tb-ride-waypoints, #tb-ride-price-preview').empty();\n                $('#tb-ride-add-stop').prop('disabled', false);\n                $('#tb-ride-pickup, #tb-ride-dropoff, #tb-ride-name, #tb-ride-email, #tb-ride-phone, #tb-ride-notes, #tb-ride-scheduled').val('');\n                $('#tb-ride-step-3 [data-prefill]').each(function () { $(this).val($(this).attr('data-prefill')); });\n                $('#tb-ride-pickup-lat, #tb-ride-pickup-lng, #tb-ride-dropoff-lat, #tb-ride-dropoff-lng').val('');\n                $('#tb-ride-preview-map').hide();\n                $('#tb-ride-passengers').val(1);\n                $('#tb-ride-surge-badge, #tb-ride-driver-card, #tb-ride-rating-wrap, #tb-ride-map-wrap').hide();\n                $('.tb-star').removeClass('tb-star-on');\n                $('#tb-ride-result, #tb-ride-rating-msg').html('');\n                $('.tb-service-card').removeClass('tb-selected');\n                $('#tb-ride-continue').prop('disabled', true);\n                $('#tb-ride-step-4, #tb-ride-step-searching, #tb-ride-step-scheduled, #tb-ride-step-cancelled, #tb-ride-step-3, #tb-ride-step-2').hide();\n                $('#tb-ride-step-1').show();\n                rideToken = null;\n                resetCancelButtons();\n            });\n            $('.tb-ride-restart').on('click', function () { $('#tb-ride-new').trigger('click'); });\n\n            \/\/ Cancel ride (two taps): allowed until pickup, refund less the fee; the server enforces both.\n            var cancelArmed = false, cancelDisarm = null;\n            function resetCancelButtons() {\n                cancelArmed = false;\n                clearTimeout(cancelDisarm);\n                $('.tb-ride-cancel-btn').prop('disabled', false).removeClass('tb-ride-cancel-armed').text('Cancel ride');\n            }\n            $('.tb-ride-cancel-btn').on('click', function () {\n                if (!confirmedRideId || !rideToken) return;\n                var $btns = $('.tb-ride-cancel-btn');\n                if (!cancelArmed) {\n                    cancelArmed = true;\n                    \/\/ The fee only applies once a driver has accepted (the \"Ride confirmed\" screen).\n                    var feeApplies = $('#tb-ride-step-4').is(':visible') && tb_vars.ride_cancel_fee_cents > 0;\n                    $btns.addClass('tb-ride-cancel-armed').text(feeApplies\n                        ? 'Tap again to cancel (' + tb_vars.ride_cancel_fee + ' fee)'\n                        : 'Tap again to cancel (full refund)');\n                    cancelDisarm = setTimeout(resetCancelButtons, 5000);\n                    return;\n                }\n                clearTimeout(cancelDisarm);\n                $btns.prop('disabled', true).text('Cancelling\u2026');\n                $.ajax({\n                    url: tb_vars.ride_cancel_url,\n                    method: 'POST', contentType: 'application\/json',\n                    data: JSON.stringify({ ride_id: confirmedRideId, token: rideToken }),\n                    success: function (r) {\n                        clearTimeout(pollTimer);\n                        clearInterval(etaInterval);\n                        clearInterval(moveInterval);\n                        confirmedRideId = null;\n                        $('#tb-ride-cancelled-msg').text(r.message || 'Your ride has been cancelled.');\n                        $('#tb-ride-step-searching, #tb-ride-step-scheduled, #tb-ride-step-4').hide();\n                        $('#tb-ride-step-cancelled').show();\n                        resetCancelButtons();\n                    },\n                    error: function (xhr) {\n                        var msg = 'Could not cancel. Please try again.';\n                        try { msg = JSON.parse(xhr.responseText).error || msg; } catch (e) {}\n                        resetCancelButtons();\n                        $('#tb-ride-step-searching .tb-ride-cancel-note, #tb-ride-step-scheduled .tb-ride-cancel-note, #tb-ride-step-4 .tb-ride-cancel-note').text(msg);\n                    }\n                });\n            });\n        });\n    }\n    <\/script>\n    \n","protected":false},"excerpt":{"rendered":"<p>We\u2019ve Moved the Party! After an incredible journey, Need A Ride Whitsundays has officially wrapped up\u2014but your rides around Airlie Beach and the Whitsundays aren\u2019t stopping! To keep you moving seamlessly, all future bookings and charters are now right here! Here is what you need to know:<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_monsterinsights_skip_tracking":false,"_jetpack_hide_featured_image":false,"_jetpack_featured_image_duplicate_dismissed":0,"footnotes":""},"class_list":["post-1055","page","type-page","status-publish","hentry"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.5 - aioseo.com -->\n\t<meta name=\"description\" content=\"We\u2019ve Moved the Party! After an incredible journey, Need A Ride Whitsundays has officially wrapped up\u2014but your rides around Airlie Beach and the Whitsundays aren\u2019t stopping! To keep you moving seamlessly, all future bookings and charters are now right here! Here is what you need to know: Familiar, Easy Booking: You\u2019ll feel right at home.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/airliebeachpartybus.com.au\/?page_id=1055\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.5\" \/>\n\t\t<meta property=\"og:locale\" content=\"en_US\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Airlie Beach Party Bus | Servicing the Whitsundays and more!\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Book a Ride | Airlie Beach Party Bus\" \/>\n\t\t<meta property=\"og:description\" content=\"We\u2019ve Moved the Party! After an incredible journey, Need A Ride Whitsundays has officially wrapped up\u2014but your rides around Airlie Beach and the Whitsundays aren\u2019t stopping! To keep you moving seamlessly, all future bookings and charters are now right here! Here is what you need to know: Familiar, Easy Booking: You\u2019ll feel right at home.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/airliebeachpartybus.com.au\/?page_id=1055\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/172-105-183-176.ip.linodeusercontent.com\/wp-content\/uploads\/2019\/12\/bat-n-ball.png\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/172-105-183-176.ip.linodeusercontent.com\/wp-content\/uploads\/2019\/12\/bat-n-ball.png\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-05-28T02:40:37+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-11-08T11:38:14+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/facebook.com\/airliebeachpartybus\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Book a Ride | Airlie Beach Party Bus\" \/>\n\t\t<meta name=\"twitter:description\" content=\"We\u2019ve Moved the Party! After an incredible journey, Need A Ride Whitsundays has officially wrapped up\u2014but your rides around Airlie Beach and the Whitsundays aren\u2019t stopping! To keep you moving seamlessly, all future bookings and charters are now right here! Here is what you need to know: Familiar, Easy Booking: You\u2019ll feel right at home.\" \/>\n\t\t<meta name=\"twitter:image\" content=\"https:\/\/172-105-183-176.ip.linodeusercontent.com\/wp-content\/uploads\/2019\/12\/bat-n-ball.png\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/?page_id=1055#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/?page_id=1055#listItem\",\"name\":\"Book a Ride\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/?page_id=1055#listItem\",\"position\":2,\"name\":\"Book a Ride\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/#listItem\",\"name\":\"Home\"}}]},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/#organization\",\"name\":\"Airlie Beach Party Bus\",\"description\":\"Servicing the Whitsundays and more!\",\"url\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/\",\"telephone\":\"+61484653767\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https:\\\/\\\/172-105-183-176.ip.linodeusercontent.com\\\/wp-content\\\/uploads\\\/2018\\\/12\\\/Airlie-Party-Bus_Logo-Black-FINAL.jpg\",\"@id\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/?page_id=1055\\\/#organizationLogo\"},\"image\":{\"@id\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/?page_id=1055\\\/#organizationLogo\"},\"sameAs\":[\"https:\\\/\\\/facebook.com\\\/airliebeachpartybus\",\"https:\\\/\\\/instagram.com\\\/airliebeachpartybus\"]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/?page_id=1055#webpage\",\"url\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/?page_id=1055\",\"name\":\"Book a Ride | Airlie Beach Party Bus\",\"description\":\"We\\u2019ve Moved the Party! After an incredible journey, Need A Ride Whitsundays has officially wrapped up\\u2014but your rides around Airlie Beach and the Whitsundays aren\\u2019t stopping! To keep you moving seamlessly, all future bookings and charters are now right here! Here is what you need to know: Familiar, Easy Booking: You\\u2019ll feel right at home.\",\"inLanguage\":\"en-AU\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/?page_id=1055#breadcrumblist\"},\"datePublished\":\"2026-05-28T12:40:37+10:00\",\"dateModified\":\"2026-11-08T21:38:14+10:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/#website\",\"url\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/\",\"name\":\"Airlie Beach Party Bus\",\"description\":\"Servicing the Whitsundays and more!\",\"inLanguage\":\"en-AU\",\"publisher\":{\"@id\":\"https:\\\/\\\/airliebeachpartybus.com.au\\\/#organization\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Book a Ride | Airlie Beach Party Bus","description":"We\u2019ve Moved the Party! After an incredible journey, Need A Ride Whitsundays has officially wrapped up\u2014but your rides around Airlie Beach and the Whitsundays aren\u2019t stopping! To keep you moving seamlessly, all future bookings and charters are now right here! Here is what you need to know: Familiar, Easy Booking: You\u2019ll feel right at home.","canonical_url":"https:\/\/airliebeachpartybus.com.au\/?page_id=1055","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"BreadcrumbList","@id":"https:\/\/airliebeachpartybus.com.au\/?page_id=1055#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/airliebeachpartybus.com.au\/#listItem","position":1,"name":"Home","item":"https:\/\/airliebeachpartybus.com.au\/","nextItem":{"@type":"ListItem","@id":"https:\/\/airliebeachpartybus.com.au\/?page_id=1055#listItem","name":"Book a Ride"}},{"@type":"ListItem","@id":"https:\/\/airliebeachpartybus.com.au\/?page_id=1055#listItem","position":2,"name":"Book a Ride","previousItem":{"@type":"ListItem","@id":"https:\/\/airliebeachpartybus.com.au\/#listItem","name":"Home"}}]},{"@type":"Organization","@id":"https:\/\/airliebeachpartybus.com.au\/#organization","name":"Airlie Beach Party Bus","description":"Servicing the Whitsundays and more!","url":"https:\/\/airliebeachpartybus.com.au\/","telephone":"+61484653767","logo":{"@type":"ImageObject","url":"https:\/\/172-105-183-176.ip.linodeusercontent.com\/wp-content\/uploads\/2018\/12\/Airlie-Party-Bus_Logo-Black-FINAL.jpg","@id":"https:\/\/airliebeachpartybus.com.au\/?page_id=1055\/#organizationLogo"},"image":{"@id":"https:\/\/airliebeachpartybus.com.au\/?page_id=1055\/#organizationLogo"},"sameAs":["https:\/\/facebook.com\/airliebeachpartybus","https:\/\/instagram.com\/airliebeachpartybus"]},{"@type":"WebPage","@id":"https:\/\/airliebeachpartybus.com.au\/?page_id=1055#webpage","url":"https:\/\/airliebeachpartybus.com.au\/?page_id=1055","name":"Book a Ride | Airlie Beach Party Bus","description":"We\u2019ve Moved the Party! After an incredible journey, Need A Ride Whitsundays has officially wrapped up\u2014but your rides around Airlie Beach and the Whitsundays aren\u2019t stopping! To keep you moving seamlessly, all future bookings and charters are now right here! Here is what you need to know: Familiar, Easy Booking: You\u2019ll feel right at home.","inLanguage":"en-AU","isPartOf":{"@id":"https:\/\/airliebeachpartybus.com.au\/#website"},"breadcrumb":{"@id":"https:\/\/airliebeachpartybus.com.au\/?page_id=1055#breadcrumblist"},"datePublished":"2026-05-28T12:40:37+10:00","dateModified":"2026-11-08T21:38:14+10:00"},{"@type":"WebSite","@id":"https:\/\/airliebeachpartybus.com.au\/#website","url":"https:\/\/airliebeachpartybus.com.au\/","name":"Airlie Beach Party Bus","description":"Servicing the Whitsundays and more!","inLanguage":"en-AU","publisher":{"@id":"https:\/\/airliebeachpartybus.com.au\/#organization"}}]},"og:locale":"en_US","og:site_name":"Airlie Beach Party Bus | Servicing the Whitsundays and more!","og:type":"article","og:title":"Book a Ride | Airlie Beach Party Bus","og:description":"We\u2019ve Moved the Party! After an incredible journey, Need A Ride Whitsundays has officially wrapped up\u2014but your rides around Airlie Beach and the Whitsundays aren\u2019t stopping! To keep you moving seamlessly, all future bookings and charters are now right here! Here is what you need to know: Familiar, Easy Booking: You\u2019ll feel right at home.","og:url":"https:\/\/airliebeachpartybus.com.au\/?page_id=1055","og:image":"https:\/\/172-105-183-176.ip.linodeusercontent.com\/wp-content\/uploads\/2019\/12\/bat-n-ball.png","og:image:secure_url":"https:\/\/172-105-183-176.ip.linodeusercontent.com\/wp-content\/uploads\/2019\/12\/bat-n-ball.png","article:published_time":"2026-05-28T02:40:37+00:00","article:modified_time":"2026-11-08T11:38:14+00:00","article:publisher":"https:\/\/facebook.com\/airliebeachpartybus","twitter:card":"summary","twitter:title":"Book a Ride | Airlie Beach Party Bus","twitter:description":"We\u2019ve Moved the Party! After an incredible journey, Need A Ride Whitsundays has officially wrapped up\u2014but your rides around Airlie Beach and the Whitsundays aren\u2019t stopping! To keep you moving seamlessly, all future bookings and charters are now right here! Here is what you need to know: Familiar, Easy Booking: You\u2019ll feel right at home.","twitter:image":"https:\/\/172-105-183-176.ip.linodeusercontent.com\/wp-content\/uploads\/2019\/12\/bat-n-ball.png"},"aioseo_meta_data":{"post_id":"1055","title":null,"description":null,"keywords":null,"keyphrases":{"focus":{"keyphrase":"","score":0,"analysis":{"keyphraseInTitle":{"score":0,"maxScore":9,"error":1}}},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"WebPage","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":{"faqs":[],"keyPoints":[],"schemas":[],"titles":[],"descriptions":[],"socialPosts":{"email":{"subject":"","preview":"","content":""},"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"created":"2026-05-28 02:32:46","updated":"2026-11-08 11:38:39","seo_analyzer_scan_date":null,"focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/Pawia8-h1","jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/airliebeachpartybus.com.au\/index.php?rest_route=\/wp\/v2\/pages\/1055","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/airliebeachpartybus.com.au\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/airliebeachpartybus.com.au\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/airliebeachpartybus.com.au\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/airliebeachpartybus.com.au\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1055"}],"version-history":[{"count":12,"href":"https:\/\/airliebeachpartybus.com.au\/index.php?rest_route=\/wp\/v2\/pages\/1055\/revisions"}],"predecessor-version":[{"id":1247,"href":"https:\/\/airliebeachpartybus.com.au\/index.php?rest_route=\/wp\/v2\/pages\/1055\/revisions\/1247"}],"wp:attachment":[{"href":"https:\/\/airliebeachpartybus.com.au\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1055"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}