Header Image

Isle of Man (GBP£)
Italy (EUR€) Mauritius (MUR₨)
Jersey (GBP£) 3cstrong\u003eIt's right for you if...\u003c\/strong\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003eYou’re looking for top-of-the-line performance, speed and comfort. You want road-smoothing suspension parts, the fastest Bosch motor you can get and the benefit of a low-maintenance belt drive system.\u003c\/p\u003e\n\u003cp\u003e\u003cstrong\u003eThe tech you get\u003c\/strong\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003eA sleek, high-performance hydroformed aluminium frame with a fully-integrated 625 Wh Bosch PowerTube battery – the longest-range Bosch offers – an ultra-powerful Bosch Performance Speed (250 W, 75 Nm) drive unit that helps you sustain speeds of up to 45 km\/h and a Kiox controller. Plus, a Gates CDX belt drive with a NuVinci Enviolo CVT hub, an SR Suntour suspension fork with lockout feature, powerful 4-piston hydraulic disc brakes for all-weather stopping power, a suspension seat post, an integrated front and rear lighting system, sleek integrated cable routing through the stem, m Poland (PLNzł)
u003cp\u003e\u003cstrong\u003eWhat is your inside leg?\u003c\/strong\u003e\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003eHow to measure: Stand barefoot with your back straight and against a wall. Tuck a ruler or something that extends to the floor between your legs. Using both hands, and keeping it level, pull the ruler up into your crotch as if you were sitting on the saddle. Measure the distance from the top edge of the ruler to the ground to find your inside leg measurement. Your inside leg is very important to your final bike size so please measure carefully.\u003c\/p\u003e","published_at":"2024-10-11T10:30:56+01:00","created_at":"2024-10-11T10:30:56+01:00","vendor":"Trek","type":"Bicycles","tags":["Allant"],"price":475000,"price_min":475000,"price_max":475000,"available":false,"price_varies":false,"compare_at_price":500000,"compare_at_price_min":500000,"compare_at_price_max":500000,"compare_at_price_varies":false,"variants":[{"id":46044421718242,"title":"Large","option1":"Large","option2":null,"option3":nul Malta (EUR€)
t_ratio":1.333,"height":1080,"media_type":"image","src":"\/\/projektride.co.uk\/cdn\/shop\/files\/AllantPlus9S_20_30425_A_Primary.webp?v=1728638618","width":1440},{"alt":null,"id":34798468956386,"position":2,"preview_image":{"aspect_ratio":1.333,"height":1080,"width":1440,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/AllantPlus9S_20_30425_A_Alt2.webp?v=1728638626"},"aspect_ratio":1.333,"height":1080,"media_type":"image","src":"\/\/projektride.co.uk\/cdn\/shop\/files\/AllantPlus9S_20_30425_A_Alt2.webp?v=1728638626","width":1440},{"alt":null,"id":34798469087458,"position":3,"preview_image":{"aspect_ratio":1.333,"height":1080,"width":1440,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/AllantPlus9S_20_30425_A_Alt7.webp?v=1728638634"},"aspect_ratio":1.333,"height":1080,"media_type":"image","src":"\/\/projektride.co.uk\/cdn\/shop\/files\/AllantPlus9S_20_30425_A_Alt7.webp?v=1728638634","width":1440}],"requires_selling_plan":false,"selling_plan_groups":[],"content":"\u003cp\u003eThe Allant+ 9S is a high-end e-bikeGuinea-Bissau (XOFFr) please measure carefully.\u003c\/p\u003e"}; window._RestockRocketConfig.variantsInventoryPolicy = {46044421718242 : "deny",}; window._RestockRocketConfig.variantsInventoryQuantity = {46044421718242 : parseInt("0"),}; window._RestockRocketConfig.variantsPreorderCount = {46044421718242 : parseInt(""),}; window._RestockRocketConfig.variantsPreorderCountForMarket = {46044421718242 : null,}; window._RestockRocketConfig.variantsPreorderMaxCount = {46044421718242 : parseInt(""),}; window._RestockRocketConfig.variantsPreorderMaxCountForMarket = {46044421718242 : null,}; window._RestockRocketConfig.variantsShippingText = {46044421718242 : "",}; window._RestockRocketConfig.variantsShippingTextForMarket = {46044421718242 : null,}; window._RestockRocketConfig.selected_variant_id = 46044421718242; window._RestockRocketConfig.selected_variant_available = window._RestockRocketConfig.product.variants.find(function(variant) { return variant.id == window._RestockRocketConfig.selected_var Portugal (EUR€) < < Réunion (EUR€) 0) { console.debug(`STOQ - Client clock appears ahead of server by ${Math.abs(Math.round(liquidCacheAge / 60))} minutes, assuming cache fresh`); window._RestockRocketConfig.isLiquidCacheFresh = true; } else if (liquidCacheAge = LIQUID_CACHE_MAX_AGE) { console.debug(`STOQ - Liquid cache is fresh (${Math.round(liquidCacheAge / 60)} minutes old)`); window._RestockRocketConfig.isLiquidCacheFresh = true; } else { console.debug(`STOQ - Liquid cache is stale (${Math.round(liquidCacheAge / 60)} minutes old, max ${Math.round(LIQUID_CACHE_MAX_AGE / 60)} minutes)`); window._RestockRocketConfig.isLiquidCacheFresh = false; } } function checkSettingsExpiry(settings) { try { if (!settings || !settings.updated_at) { console.debug('STOQ - Invalid settings data structure'); return null; } if (!settings.cache) { console.debug('STOQ - settings caching disabled'); return null; } // Check if translations are enabled but missing from cache // This handles the backfill period where DB has translations but metafield doesn't if (settings.multi_language_enabled) { if (!settings.translations) { // Translations enabled but no age = Date.now() - updatedAt.getTime(); if (age Mayotte (EUR€)
SETTINGS_CACHE_DURATION) { console.debug('STOQ - settings changed recently, skipping cache'); return null; } return settings; } catch (error) { console.debug('STOQ - Error checking settings cache:', error); return null; } } function createRestockRocketContainer() { const restockRocketContainer = document.createElement('div'); restockRocketContainer.id = 'restock-rocket'; document.body.appendChild(restockRocketContainer); } function createRestockRocketScript(scriptUrl) { const restockRocketScriptElement = document.createElement('script'); restockRocketScriptElement.setAttribute('defer', 'defer'); restockRocketScriptElement.src = scriptUrl; document.body.appendChild(restockRocketScriptElement); } createRestockRocketContainer() console.debug('STOQ - extension activated') // Fire stoq_initialized once per page load so the funnel pipeline has a definitive // "our code ran on this page" signal independent of any cusIreland (EUR€)
onStorage.getItem('_stoq_pls') || '0', 10) || 0) + 1; window.sessionStorage.setItem('_stoq_pls', String(_stoqPageSeq)); } catch (e) { /* sessionStorage unavailable (private mode) — leave 0 */ } Shopify?.analytics?.publish?.('stoq_initialized', { cart_token: _stoqInitConfig.cartToken || '', page_url: window.location.href, page_type: _stoqInitConfig.pageType || '', shop_domain: _stoqInitConfig.shop || '', market_id: _stoqInitConfig.marketId || '', // Buyer's storefront country (localization.country.iso_code). Seeds the // pixel's extension_details blob so it rides forward onto Shopify's native // events to op Pay installments) on ALL surfaces, before our // bundle loads, via one delegated capture-phase listener (catches late-rendered buttons). // Only on shops with preorders configured (shared `preordersConfigured` signal from the // derivation block above) — nothing to bypass otherwise. variant_id is read per-clicked-button // from its form so it's correct on collection/index/search too. block_accelerated_checkout // context (STOQ-1612) rides in `properties` (forwarded verbatim by the pixel, no schema // change): a buy_now click on a preorder variant while the gate was armed is a visible // bypass signal for order debug. Also sends page_url (top-level, like the pixel's native // events) and two preorder signals in properties (see below). try { var _sqTrack = window._RestockRocketConfig; if (_sqTrack.preordersConfigured) { document.addEventListener('click', function (e) { try { var t = e.target; if (!t || !t.closest) return; var host = t.cl
ant_ids) && pl.variant_ids.indexOf(_sqVidNum) !== -1) { _sqPlan = pl; } }); Shopify?.analytics?.publish?.('stoq_accelerated_checkout_clicked', { variant_id: variantId, product_id: (_sqTrack.product && _sqTrack.product.id) || '', channel: channel, market_id: _sqTrack.marketId || '', country_code: _sqTrack.countryIsoCode || '', page_type: _sqTrack.pageType || '', cart_token: _sqTrack.cartToken || '', page_url: (window.location && window.location.href) || '', properties: { // Mirror the bundle's own preorder check (preorder.js checkInventoryProvider- // WithCachedFallback): the live set config.preorderVariantIds (refreshed after // page load by loadVariantInventoryData) OR the Liquid-injected // cachedPreorderVariantIds — always present, so it covers clicks before the // bundle loads (why this trackeMoldova (MDLL)
Isle of Man (GBP£) currently IN STOCK (in the inStockVariantIds // set, not the OOS preorderVariantIds set). Same live-OR-cached pattern as // is_preorder_variant. is_preorder_variant OR is_instock_variant = "should sell via // the plan" regardless of stock, so an in-stock preorder bypass is detectable too. is_instock_variant: (function () { var has = function (a) { return Array.isArray(a) && a.indexOf(_sqVidNum) !== -1; }; return has(_sqTrack.inStockVariantIds) || has(_sqTrack.cachedInStockVariantIds && _sqTrack.cachedInStockVariantIds.in_stock_variant_ids); })(), // The enrolled plan's Shopify selling-plan id + inventory_provider (stoq = STOQ tracks // the OOS count; shopify = in-stock preorder). Tells order-debug what kind of preorder // the Buy Now bypassed, without a downstream lookup. enrolled_plan_id: (_sqPlan && (_sqPlan.shopif ockRocketConfig.normalizedLocale; const translations = settings.translations; if (!normalizedLocale) { // No matching locale has translations; drop payload to save memory console.debug('STOQ - No matching locale for translations. Available:', Object.keys(translations || {})); delete settings.translations; return settings; } console.debug(`STOQ - Applying translations for normalized locale: ${normalizedLocale} (original: ${window._RestockRocketConfig.locale})`); const translatedFields = translations[normalizedLocale]; if (translatedFields && typeof translatedFields === 'object') { Object.keys(translatedFields).forEach(function(key) { const value = translatedFields[key]; if (value !== null && value !== undefined && value !== '') { settings[key] = value; } }); } else { console.debug('STOQ - No translated fields found for locale:', normalizedLocale); } delete
} else { console.debug('STOQ - no cart selling plan updates needed'); } }) .catch(error => { console.error('STOQ - error updating cart selling plans:', error); }); } }); } // ---- Live settings via the Storefront API (additive, does not gate init) ---- // The inlined `cachedSettings` comes from the app-embed Liquid render, which // Shopify edge-caches and can serve stale. Reading the same `settings` metafield // back over the Storefront API is a POST to /api/
version>/graphql.json — never // CDN-cached — so it returns the live value on every page load. We fetch it // best-effort and stash it on `window._RestockRocketConfig.storefrontSettings` // so individual behaviours can migrate onto the fresh value over time. This is // PURELY ADDITIVE: it does NOT change the resolution flow below, never blocks // init, and silently no-ops on any failure / missing token. (function loadStorefrontSettings() { const cfg = window._RestockRocketConfig; if (!cfg.storefrontAccessToken || cfg.disableStorefrontApi === true) return; // Deferred to browser idle so this best-effort read runs strictly AFTER the // critical init flow and never competes with it for a connection — the call // is a live, uncached Storefront round-trip and can be slow (multi-second // TTFB observed). Nothing on the critical path waits for it. function run() { const query = 'query StoqSettings($namespace: String!) { shop { metafield(namespace: $namespace, key:Monaco (EUR€)
Italy (EUR€) refrontSettings = JSON.parse(value); console.debug('STOQ - stored live settings from Storefront API on config.storefrontSettings'); }) .catch(function(e) { console.debug('STOQ - Storefront settings fetch failed (non-fatal):', e && e.message); }); } if (typeof window.requestIdleCallback === 'function') { window.requestIdleCallback(run, { timeout: 5000 }); } else { setTimeout(run, 0); } })(); // First try to get settings from metafields with expiry check const cachedSettings = window._RestockRocketConfig.cachedSettings; const validCachedSettings = cachedSettings ? checkSettingsExpiry(cachedSettings) : null; if (validCachedSettings) { console.debug('STOQ - using cached settings'); initializeScripts(validCachedSettings); } else { console.debug('STOQ - fetching fresh settings'); const headers = { 'X-Shopify-Shop-Domain': window._RestockRocketConfig.shop || window.Shopify.shop, 'ngrok-skip-browser-warning': 'ski
TOQ - failed to load settings:', error); } }) .catch(function(e) { console.error(e) }) } function fetchEmbedConfig(endpoint, apply) { return fetch( `${window._RestockRocketConfig.host}/api/v1/embed/${endpoint}.json`, { headers: { 'X-Shopify-Shop-Domain': window._RestockRocketConfig.shop || window.Shopify.shop, 'ngrok-skip-browser-warning': 'skip' } } ) .then(function(response) { if (!response.ok) throw new Error(`Failed to fetch ${endpoint}`); return response.json(); }) .then(function(data) { try { apply(data); } catch (applyError) { // Apply failures are programming bugs (e.g. response shape changed // server-side and the assignment threw). Surface them as console.error // so they're visible in browser logs, then re-throw to fall through // to the same Liquid-cached fallback as a fetch failure. console.error('STOQ - apply failed for ' +
Mozambique (GBP£)
Kosovo (EUR€) Netherlands (EUR€) Lesotho (GBP£)
Niger (XOFFr)
Liberia (GBP£)
Liechtenstein (CHFCHF)

Lithuania (EUR€)

Luxembourg (EUR€)

Madagascar (GBP£)

Malawi (MWKMK)

Mali (XOFFr)

Malta (EUR€)

Mauritania (GBP£)
Switzerland (CHFCHF) Mauritius (MUR₨)
Tanzania (TZSSh)
Mayotte (EUR€)

Togo (XOFFr)

Moldova (MDLL)
  • Tristan da Cunha (GBP£) Monaco (EUR€)

    Tunisia (GBP£) Montenegro (EUR€) Türkiye (GBP£)

    Morocco (MADد.م.)
  • Uganda (UGXUSh) Mozambique (GBP£)

    Ukraine (UAH₴)

    Namibia (GBP£)

    United Kingdom (GBP£)

    Netherlands (EUR€)
United States (GBP£)