diff --git a/frontend/src/components/PlexPlayer.tsx b/frontend/src/components/PlexPlayer.tsx
index 1409a85..9d840ff 100644
--- a/frontend/src/components/PlexPlayer.tsx
+++ b/frontend/src/components/PlexPlayer.tsx
@@ -860,8 +860,11 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [activeMarker?.type, activeMarker?.start_s, prefs.autoSkipIntro, prefs.autoSkipCredits, prefs.autoSkipDelay, doSkip]);
- // Seekbar hover: a timestamp tooltip that follows the cursor.
- const [hover, setHover] = useState<{ x: number; t: number } | null>(null);
+ // Seekbar hover: a timestamp tooltip that follows the cursor. Store the position as a FRACTION
+ // (0..1), not pixels: the bar sits inside the transform:scale(1.25) subtree, so a px `left` would be
+ // re-scaled and drift right of the cursor (only 0 stayed correct). A % `left` is relative to the
+ // bar's own width and scales cleanly.
+ const [hover, setHover] = useState<{ frac: number; t: number } | null>(null);
// Seekbar scrubbing: the absolute time under the dragging finger. While non-null the head/thumb
// preview this position; the real seek fires once on release (avoids restarting the session on
// every out-of-buffer step of the drag). A plain click is a down+up at one spot → seeks there.
@@ -1037,9 +1040,9 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) {
}}
onPointerMove={(e) => {
const r = (e.currentTarget as HTMLElement).getBoundingClientRect();
- const x = Math.max(0, Math.min(e.clientX - r.left, r.width));
- const tt = (x / r.width) * duration;
- setHover({ x, t: tt });
+ const frac = Math.max(0, Math.min((e.clientX - r.left) / r.width, 1));
+ const tt = frac * duration;
+ setHover({ frac, t: tt });
if (e.buttons & 1) setScrub(tt); // dragging with the primary button held
}}
onPointerUp={() => {
@@ -1054,7 +1057,7 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) {
{hover && duration > 0 && (