tomer interaction.
// Detected variants: the variants present in this page's Liquid context (product page has them;
// collection/index/etc. don't expose variants from Liquid). Used to disambiguate "embed didn't
// load" vs "embed loaded but the variant wasn't a preorder/BIS candidate" in order debug.
try {
const _stoqInitConfig = window._RestockRocketConfig;
const _stoqDetectedVariantIds = (_stoqInitConfig.product && Array.isArray(_stoqInitConfig.product.variants))
? _stoqInitConfig.product.variants.map(function(v) { return v.id })
: [];
const _stoqSelectedVariantId = _stoqInitConfig.selected_variant_id;
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 || '',
detected_variant_ids: _stoqDetectedVariantIds,
selected_variant_id: _stoqSele
ctedVariantId || '',
liquid_rendered_at: _stoqInitConfig.liquidRenderedAt || 0,
app_version: _stoqInitConfig.appVersion || '',
liquid_cache_age: _stoqInitConfig.liquidCacheAge,
// Selected variant's stock posture as our app saw it at render — explains
// whether we *should* have treated it as a preorder candidate.
inventory_policy: (_stoqInitConfig.variantsInventoryPolicy || {})[_stoqSelectedVariantId] || '',
inventory_quantity: (_stoqInitConfig.variantsInventoryQuantity || {})[_stoqSelectedVariantId],
});
} catch (e) {
console.debug('STOQ - stoq_initialized publish failed:', e);
}
function applyTranslations(settings) {
try {
// Skip translation logic entirely if multi-language is not enabled
if (!settings || !settings.multi_language_enabled) {
return settings;
}
if (!settings.translations) {
console.debug('STOQ - No translations found, skipping translation');
return settings;
}
const n
ormalizedLocale = window._RestockRocketConfig.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:', normalizedL
ocale);
}
delete settings.translations;
return settings;
} catch (e) {
console.debug('STOQ - error applying translations:', e);
return settings;
}
}
// Setup event listener for cart selling plan updates
// This must be called before any scripts are loaded to avoid race conditions
function setupCartSellingPlanUpdater(settings) {
// Setup listener regardless - updateCartSellingPlans has its own guards
// This ensures cleanup happens even when preorders are disabled globally
// Listen for stoq:inventory-data-loaded event dispatched by api.js
window.addEventListener('stoq:inventory-data-loaded', function(event) {
console.debug('STOQ - Inventory data loaded, updating cart selling plans');
if (window._RestockRocket && window._RestockRocket.updateCartSellingPlans) {
window._RestockRocket.updateCartSellingPlans()
.then(hasUpdates => {
if (hasUpdates) {
console.debug('STOQ - cart selling plans update
ch(
`${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 ' + endpoint + ':', applyError);
throw applyError;
}
})
.catch(function(error) {
console.debug(`STOQ - using cached ${endpoint}:`, error.message);
}
Front Suspension);
}
function initializeScripts(settings) {
settings = applyTranslations(settings);
window._RestockRocketConfig.settings = settings;
console.debug(`STOQ - settings configured for ${window._RestockRocketConfig.pageType}`);
// Stale-Liquid resilience (default-on, per-shop opt-out via the
// `disable_refresh_on_stale_liquid` Toggle, surfaced as the negative
// `disable_refresh_on_stale_liquid` flag in settings.json so that
// `undefined` -- in CDN-cached metafield payloads that predate this
// key -- reads as `!undefined === true` and gets default-on behavior
// immediately, no metafield rewrite required).
// When the Liquid CDN cache is older than LIQUID_CACHE_MAX_AGE the in-page
// selling_plans / integrations metafields can be wrong; refresh both from
// the API before launching scripts. Race against a 1000ms timeout so a slow
// API can't block init indefinitely. If the timeout wins, the in-flight
// fetches still complete and update window._RestockRocketConfig — the
// bundle re-reads sellingPlans/integrations on every interaction, so the
// late-arriving values benefit subsequent renders even though the first
// paint may use the Liquid-cached values. On any failure the existing
// Liquid-loaded values stay in place via fetchEmbedConfig's catch.
if (!window._RestockRocketConfig.isLiquidCacheFresh && !settings.disable_refresh_on_stale_liquid) {
console.debug('STOQ - Liquid cache stale, refreshing selling_plans + integrations');
Promise.race([
Promise.all([
fetchEmbedConfig('selling_plans', function(data) {
if (data && Array.isArray(data.plans)) {
window._RestockRocketConfig.sellingPlans = data.plans;
window._RestockRocketConfig.disabledSellingPlanIds = data.disabled_plan_ids || [];
}
}),
fetchEmbedConfig('integrations', function(data) {
if (Array.isArray(data)) {
window._RestockRocketConfig.integrations
= data;
}
})
]),
new Promise(function(resolve) { setTimeout(resolve, 1000); })
]).then(function() { loadScripts(settings); });
return;
}
loadScripts(settings);
}
function loadScripts(settings) {
// Setup cart selling plan updater BEFORE loading any scripts to avoid race conditions
setupCartSellingPlanUpdater(settings);
if(settings.enable_app) {
const hijackIntegration = window._RestockRocketConfig.integrations.find(function(integration) {
return integration.type === 'hijack' && integration.enabled && integration.page_types.includes(window._RestockRocketConfig.pageType);
})
// STOQ-1520: serve the lean back-in-stock-only build (no preorder/hijack code)
// only to shops with NO preorder plans. Use the full build if preorder is on,
// an enabled offer exists, or a disabled-but-kept plan id remains (cart sweep
// must still strip those). Rationale in the PR.
const hasEnabledOffer = Arra
Contact Usge":{"aspect_ratio":1.152,"height":1942,"width":2238,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/Screenshot_2025-02-01_at_16.41.37.png?v=1738428106"}},"requires_selling_plan":false,"selling_plan_allocations":[],"quantity_rule":{"min":1,"max":null,"increment":1}},{"id":55070134501759,"title":"Silver \/ Silver \/ Both \/ Braided","option1":"Silver \/ Silver","option2":"Both","option3":"Braided","sku":null,"requires_shipping":true,"taxable":true,"featured_image":{"id":73221279711615,"product_id":14930892292479,"position":14,"created_at":"2025-02-01T16:41:42+00:00","updated_at":"2025-02-01T16:41:46+00:00","alt":null,"width":2238,"height":1942,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/Screenshot_2025-02-01_at_16.41.37.png?v=1738428106","variant_ids":[55070134370687,55070134403455,55070134436223,55070134468991,55070134501759,55070134534527]},"available":true,"name":"Hope Tech 4 E4 (dose not include rotors) - Silver \/ Silver \/ Both \/ Braided","public_title":"Silver \/ Silver \/ Both \/ Braided","options
3,55070134468991,55070134501759,55070134534527]},"available":true,"name":"Hope Tech 4 E4 (dose not include rotors) - Silver \/ Silver \/ Both \/ Non-Briaded","public_title":"Silver \/ Silver \/ Both \/ Non-Briaded","options":["Silver \/ Silver","Both","Non-Briaded"],"price":40000,"weight":0,"compare_at_price":null,"inventory_management":null,"barcode":null,"featured_media":{"alt":null,"id":64081594777983,"position":14,"preview_image":{"aspect_ratio":1.152,"height":1942,"width":2238,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/Screenshot_2025-02-01_at_16.41.37.png?v=1738428106"}},"requires_selling_plan":false,"selling_plan_allocations":[],"quantity_rule":{"min":1,"max":null,"increment":1}},{"id":55070134567295,"title":"Red \/ Silver \/ Left \/ Braided","option1":"Red \/ Silver","option2":"Left","option3":"Braided","sku":null,"requires_shipping":true,"taxable":true,"featured_image":{"id":73221014487423,"product_id":14930892292479,"position":6,"created_at":"2025-02-01T16:18:03+00:00","updated_at":"2025-02-04T
\/ Left \/ Non-Briaded","option1":"Red \/ Silver","option2":"Left","option3":"Non-Briaded","sku":null,"requires_shipping":true,"taxable":true,"featured_image":{"id":73221014487423,"product_id":14930892292479,"position":6,"created_at":"2025-02-01T16:18:03+00:00","updated_at":"2025-02-04T17:38:16+00:00","alt":null,"width":2238,"height":1942,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/Screenshot_2025-02-01_at_16.17.57.png?v=1738690696","variant_ids":[55070133387647,55070133420415,55070133453183,55070133485951,55070133518719,55070133551487,55070134567295,55070134600063,55070134632831,55070134665599,55070134698367,55070134731135]},"available":true,"name":"Hope Tech 4 E4 (dose not include rotors) - Red \/ Silver \/ Left \/ Non-Briaded","public_title":"Red \/ Silver \/ Left \/ Non-Briaded","options":["Red \/ Silver","Left","Non-Briaded"],"price":20500,"weight":0,"compare_at_price":null,"inventory_management":null,"barcode":null,"featured_media":{"alt":null,"id":64081427661183,"position":6,"preview_image":{"aspe
Blog\/ Braided","public_title":"Red \/ Silver \/ Right \/ Braided","options":["Red \/ Silver","Right","Braided"],"price":21500,"weight":0,"compare_at_price":null,"inventory_management":null,"barcode":null,"featured_media":{"alt":null,"id":64081427661183,"position":6,"preview_image":{"aspect_ratio":1.152,"height":1942,"width":2238,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/Screenshot_2025-02-01_at_16.17.57.png?v=1738690696"}},"requires_selling_plan":false,"selling_plan_allocations":[],"quantity_rule":{"min":1,"max":null,"increment":1}},{"id":55070134665599,"title":"Red \/ Silver \/ Right \/ Non-Briaded","option1":"Red \/ Silver","option2":"Right","option3":"Non-Briaded","sku":null,"requires_shipping":true,"taxable":true,"featured_image":{"id":73221014487423,"product_id":14930892292479,"position":6,"created_at":"2025-02-01T16:18:03+00:00","updated_at":"2025-02-04T17:38:16+00:00","alt":null,"width":2238,"height":1942,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/Screenshot_2025-02-01_at_16.17.57.png?v=1738690
are_at_price":null,"inventory_management":null,"barcode":null,"featured_media":{"alt":null,"id":64081427661183,"position":6,"preview_image":{"aspect_ratio":1.152,"height":1942,"width":2238,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/Screenshot_2025-02-01_at_16.17.57.png?v=1738690696"}},"requires_selling_plan":false,"selling_plan_allocations":[],"quantity_rule":{"min":1,"max":null,"increment":1}},{"id":55070134763903,"title":"Blue \/ Silver \/ Left \/ Braided","option1":"Blue \/ Silver","option2":"Left","option3":"Braided","sku":null,"requires_shipping":true,"taxable":true,"featured_image":{"id":73221019238783,"product_id":14930892292479,"position":12,"created_at":"2025-02-01T16:18:38+00:00","updated_at":"2025-02-01T16:18:42+00:00","alt":null,"width":2238,"height":1942,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/Screenshot_2025-02-01_at_16.18.33.png?v=1738426722","variant_ids":[55070134763903,55070134796671,55070134829439,55070134862207,55070134894975,55070134927743]},"available":true,"name":"Hope Tech
Track Service Progresshop\/files\/Screenshot_2025-02-01_at_16.18.33.png?v=1738426722","variant_ids":[55070134763903,55070134796671,55070134829439,55070134862207,55070134894975,55070134927743]},"available":true,"name":"Hope Tech 4 E4 (dose not include rotors) - Blue \/ Silver \/ Left \/ Non-Briaded","public_title":"Blue \/ Silver \/ Left \/ Non-Briaded","options":["Blue \/ Silver","Left","Non-Briaded"],"price":20500,"weight":0,"compare_at_price":null,"inventory_management":null,"barcode":null,"featured_media":{"alt":null,"id":64081431265663,"position":12,"preview_image":{"aspect_ratio":1.152,"height":1942,"width":2238,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/Screenshot_2025-02-01_at_16.18.33.png?v=1738426722"}},"requires_selling_plan":false,"selling_plan_allocations":[],"quantity_rule":{"min":1,"max":null,"increment":1}},{"id":55070134829439,"title":"Blue \/ Silver \/ Right \/ Braided","option1":"Blue \/ Silver","option2":"Right","option3":"Braided","sku":null,"requires_shipping":true,"taxable":true,"featured_image":{"id":73
Building Your Bike From the Box
Insure Your Bike
Privacy Policy
Cookie Policy
Terms of Service
Refund policy
Key Features
// Increased power versus Tech 3
// Lever shape updated to provide a more ergonomic shape orientated to provide the best mechanical advantage during braking.
// Hinged clamp reducing weight and to improve ergonomics
// Tool free bite point and reach adjustments// Hybrid piston design// Rigid CNC'd one piece caliper
// Compatible with a wider range of current shifter options
Brakes
See More
Complete Brakes
Brake Rotors
Brake Pads
Brake Spares
French Southern Territories (EUR€)
Western Sahara (MADد.م.)