diff --git a/frontend/src/components/plexCards.dom.test.tsx b/frontend/src/components/plexCards.dom.test.tsx
index 4f751674..90c2c98a 100644
--- a/frontend/src/components/plexCards.dom.test.tsx
+++ b/frontend/src/components/plexCards.dom.test.tsx
@@ -208,6 +208,19 @@ describe("a started Plex card", () => {
expect(container.querySelector(".flex-col.reveal-hover")).toBeNull();
});
+ // E28 review D4: the card read "started" off the rounded progress %, the menu off the position —
+ // 20 s into a 3 h film is 0 % here and started there, so the card offered no Restart the menu had.
+ it("a resume point under 1 % still offers the Restart, as the card's menu does", () => {
+ render(
+ ,
+ );
+ expect(screen.getByRole("button", { name: "card.restart" })).toBeTruthy();
+ });
+
it("a not-started or watched card keeps the one Play", () => {
render();
expect(screen.queryByRole("button", { name: "card.restart" })).toBeNull();
diff --git a/frontend/src/components/plexCards.tsx b/frontend/src/components/plexCards.tsx
index 6df9b1e7..2e86f879 100644
--- a/frontend/src/components/plexCards.tsx
+++ b/frontend/src/components/plexCards.tsx
@@ -7,7 +7,7 @@ import { Carousel } from "./ui/Carousel";
import type { PlexCard, PlexCastMember, PlexFilters, PlexSeasonDetail } from "../lib/api";
import { navItemAttrs } from "../lib/listNav";
import { endsTextSelection, movedPastSlop } from "../lib/textSelection";
-import { plexAddLabelKey } from "./usePlexCardActions";
+import { plexAddLabelKey, plexStarted } from "./usePlexCardActions";
import { IconButton } from "./ui/IconButton";
import { MoreButton } from "./ui/MoreButton";
import { Pressable } from "./ui/Pressable";
@@ -186,6 +186,7 @@ function PlexPicture({
plays,
small = false,
pct = 0,
+ started = false,
bar,
children,
}: {
@@ -201,23 +202,26 @@ function PlexPicture({
/** A shelf's short landscape picture: the smaller Play, clear of the hover row under it. */
small?: boolean;
pct?: number;
+ /** Has a resume point (`plexStarted`, the card menu's own rule): Continue + Restart, not Play. */
+ started?: boolean;
bar: ReactNode;
/** The badges printed on the picture. */
children?: ReactNode;
}) {
- // The pct is the resume point's: a watched or untouched item has none, and plays from its start.
- const started = plays && pct > 0 && !!onRestart;
+ // Not read off `pct`: that is rounded, so 20 s into a 3 h film was 0 % and "not started" here while
+ // the menu offered "from the start" (E28 review D4). One rule, `plexStarted`, for both.
+ const resumable = plays && started && !!onRestart;
return (
{children}
- {plays && !started && }
+ {plays && !resumable && }
- {started && (
+ {resumable && (