Queensland Itch in Horses: What It Is, What Happens, and How to Support Your Horse's Skin

Queensland Itch in Horses: What It Is, What Happens, and How to Support Your Horse's Skin

Read More
Best Horse Hair Growth Products Australia (2026 Guide)

Best Horse Hair Growth Products Australia (2026 Guide)

Read More
How to Clean Your Horse in Winter Without Water - The Australian Horse Owner's Guide

How to Clean Your Horse in Winter Without Water - The Australian Horse Owner's Guide

Read More
Equine Hair Regrowth Serum for Mane and Tail — What Actually Works

Equine Hair Regrowth Serum for Mane and Tail — What Actually Works

Read More
Getting Your Horse Winter-Ready: Rugging, Coat Care & What Actually Works

Getting Your Horse Winter-Ready: Rugging, Coat Care & What Actually Works

Read More
Rug Rubs & Bald Patches in Horses: What's Actually Happening (And How to Help)

Rug Rubs & Bald Patches in Horses: What's Actually Happening (And How to Help)

Read More
Natural Horse Grooming Products Australia: What Actually Works (And What To Avoid) - Horse Queened

Natural Horse Grooming Products Australia: What Actually Works (And What To Avoid)

Read More
horse mane before and after using a conditioning grooming routine for fuller looking mane

Horse Hair Growth: Supporting Fuller Manes & Tails Naturally

Read More
How to Use Our Purple Brightening Range (And Where Each One Works Best) - Horse Queened

How to Use Our Purple Brightening Range (And Where Each One Works Best)

Read More
Skin First, Shine Second: The Role of pH in Equine Care - Horse Queened

Skin First, Shine Second: The Role of pH in Equine Care

Read More
Hopples 101: Quick Fit Guide for Harness Racing Horses 🐴🏁 - Horse Queened

Hopples 101: Quick Fit Guide for Harness Racing Horses 🐴🏁

Read More
🧼 The Easiest Grooming Care Routine for Real Aussie Horses - Horse Queened

🧼 The Easiest Grooming Care Routine for Real Aussie Horses

Read More
🧼 The Best Horse Grooming Kit for Real Aussie Horses (No Hose Needed) - Horse Queened

🧼 The Best Horse Grooming Kit for Real Aussie Horses (No Hose Needed)

Read More
Revolutionise Your Horse's Grooming Routine with Swash It - Horse Queened

Revolutionise Your Horse's Grooming Routine with Swash It

Read More
Insights from the Stable - Horse Queened

Insights from the Stable

Read More
The Horse Care Journal - Horse Queened

The Horse Care Journal

Read More
Beyond Grooming - Horse Queened

Beyond Grooming

Read More
/* Product zoom: mouse-follow + touch toggle Works with: product-zoom-element[data-magnify], .product-zoom--wrapper, .product-zoom--enlarged */ (() => { const ZOOM_ATTR = 'data-magnify'; const roots = Array.from(document.querySelectorAll(`product-zoom-element[${ZOOM_ATTR}]`)); if (!roots.length) return; roots.forEach(root => { // ensure root is focusable for keyboard access if (!root.hasAttribute('tabindex')) root.setAttribute('tabindex', '0'); const wrapper = root.querySelector('.product-zoom--wrapper'); const enlarged = root.querySelector('.product-zoom--enlarged'); if (!wrapper) return; const rawZoom = parseFloat(root.getAttribute('data-magnify')) || 1.7; let effectiveZoom = rawZoom; // may be increased if high-res image is larger let isZoomed = false; // compute effective zoom based on the natural size of the enlarged image (if available) function updateEffectiveZoom() { try { if (enlarged && enlarged.naturalWidth && root.offsetWidth) { const imageRatio = enlarged.naturalWidth / root.offsetWidth; // we want at least rawZoom but if enlarged image is bigger, allow larger zoom effectiveZoom = Math.max(rawZoom, imageRatio); } else { effectiveZoom = rawZoom; } } catch (err) { effectiveZoom = rawZoom; } } if (enlarged) { if (enlarged.complete) updateEffectiveZoom(); else enlarged.addEventListener('load', updateEffectiveZoom, { once: true }); // also update on window resize (image/container geometry changes) window.addEventListener('resize', () => { updateEffectiveZoom(); }); } // Utility: set transform-origin and scale for enlarged image function setOriginAndScale(clientX, clientY) { const rect = root.getBoundingClientRect(); // clamp values 0..100 const x = Math.min(100, Math.max(0, ((clientX - rect.left) / rect.width) * 100)); const y = Math.min(100, Math.max(0, ((clientY - rect.top) / rect.height) * 100)); if (enlarged) { enlarged.style.transformOrigin = `${x}% ${y}%`; enlarged.style.transform = `scale(${effectiveZoom})`; } // slight parallax: move wrapper transform-origin to follow cursor (subtle) wrapper.style.transformOrigin = `${x}% ${y}%`; } // Activate zoom (add class, set scale) function activateZoom(clientX, clientY) { updateEffectiveZoom(); root.classList.add('is-zoomed'); isZoomed = true; if (typeof clientX === 'number' && typeof clientY === 'number') { setOriginAndScale(clientX, clientY); } else { // center if no coordinates provided if (enlarged) { enlarged.style.transformOrigin = `50% 50%`; enlarged.style.transform = `scale(${effectiveZoom})`; } wrapper.style.transformOrigin = `50% 50%`; } } // Deactivate zoom (reset transforms) function deactivateZoom() { root.classList.remove('is-zoomed'); isZoomed = false; if (enlarged) { enlarged.style.transform = 'scale(1)'; enlarged.style.transformOrigin = '50% 50%'; } wrapper.style.transform = 'scale(1)'; wrapper.style.transformOrigin = '50% 50%'; } // Mouse handlers function onMouseMove(e) { if (!isZoomed) activateZoom(e.clientX, e.clientY); else setOriginAndScale(e.clientX, e.clientY); } function onMouseEnter(e) { // activate but don't force a jump — set origin from event activateZoom(e.clientX, e.clientY); } function onMouseLeave() { deactivateZoom(); } // Touch handlers (simple toggle on first tap; move origin while zoomed) let lastTouchEnd = 0; function onTouchStart(e) { if (e.touches.length > 1) { // ignore pinch for now — allow browser default return; } const t = e.touches[0]; // toggle on tap if (!isZoomed) { activateZoom(t.clientX, t.clientY); } else { // if already zoomed, just update origin (user may pan) setOriginAndScale(t.clientX, t.clientY); } } function onTouchMove(e) { if (!isZoomed || e.touches.length === 0) return; const t = e.touches[0]; setOriginAndScale(t.clientX, t.clientY); } function onTouchEnd(e) { // if touchend with no subsequent touches, keep zoom active; second tap will close. // implement a quick double-tap-to-close const now = Date.now(); if (now - lastTouchEnd < 300) { // double-tap detected -> close deactivateZoom(); } lastTouchEnd = now; } // Keyboard (Enter/Space toggles) function onKeyDown(e) { if (e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar') { e.preventDefault(); if (!isZoomed) activateZoom(root.getBoundingClientRect().left + root.offsetWidth / 2, root.getBoundingClientRect().top + root.offsetHeight / 2); else deactivateZoom(); } else if (e.key === 'Escape' && isZoomed) { deactivateZoom(); } } // Bind events root.addEventListener('mousemove', onMouseMove); root.addEventListener('mouseenter', onMouseEnter); root.addEventListener('mouseleave', onMouseLeave); // touch: use passive listeners where safe root.addEventListener('touchstart', onTouchStart, { passive: true }); root.addEventListener('touchmove', onTouchMove, { passive: true }); root.addEventListener('touchend', onTouchEnd, { passive: true }); root.addEventListener('keydown', onKeyDown); // Clean up in case element is removed (optional) // If you dynamically remove elements, consider removing listeners to avoid leaks. }); })();