diff --git a/pwa/src/app/browse/[gid]/[token]/page.tsx b/pwa/src/app/browse/[gid]/[token]/page.tsx
index 5a3c54fd..98f2a805 100644
--- a/pwa/src/app/browse/[gid]/[token]/page.tsx
+++ b/pwa/src/app/browse/[gid]/[token]/page.tsx
@@ -39,11 +39,10 @@ function PreviewGrid({
return (
{thumbs.map((thumb) => {
- // Scale based on width only — backend normalizes sprite heights.
- const tw = thumb.width ?? 200
+ // Scale X offset using height-based ratio — auto 100% forces sprite
+ // height to match the container, so X offset must use the same ratio.
const th = thumb.height ?? 300
- const scale = cellSize.w ? cellSize.w / tw : 1
- const scaledH = th * scale
+ const hScale = cellSize.h ? cellSize.h / th : 1
return (
diff --git a/pwa/src/components/Reader/index.tsx b/pwa/src/components/Reader/index.tsx
index 3be5ae6d..c67db769 100644
--- a/pwa/src/components/Reader/index.tsx
+++ b/pwa/src/components/Reader/index.tsx
@@ -681,13 +681,14 @@ function ThumbnailStrip({ images, currentPage, onPageSelect, previews }: Thumbna
const ox = Number(parts[1])
const cellW = Number(parts[2]) || 200
const cellH = Number(parts[3]) || 300
- // Scale based on width only — backend normalizes sprite heights.
- const scale = 48 / cellW
- const scaledOx = ox * scale
+ // Scale based on container height (64px) — auto 100% forces sprite
+ // height to match the container, so X offset must use the same ratio.
+ const hScale = 64 / cellH
+ const scaledOx = ox * hScale
spriteStyle = {
backgroundImage: `url(/api/eh/thumb-proxy?url=${encodeURIComponent(spriteUrl)})`,
- backgroundPosition: `${scaledOx}px center`,
- backgroundSize: `auto ${cellH * scale}px`,
+ backgroundPosition: `${scaledOx}px 0px`,
+ backgroundSize: `auto 100%`,
backgroundRepeat: 'no-repeat',
width: '100%',
height: '100%',