diff --git a/src/components/settings/__tests__/one-piece-anatomy.test.tsx b/src/components/settings/__tests__/one-piece-anatomy.test.tsx
index 6b9c50846..d801cf1e7 100644
--- a/src/components/settings/__tests__/one-piece-anatomy.test.tsx
+++ b/src/components/settings/__tests__/one-piece-anatomy.test.tsx
@@ -67,6 +67,20 @@ describe("SettingsCard — the container owns the rhythm", () => {
const html = render(body);
expect(html).toContain("gap-2");
});
+
+ it("drops the gap as well as the padding on a flush ledger card", () => {
+ // Dropping only the padding leaves the card's own gap standing between
+ // the rows as dead space the first row does not have. Both breakpoints
+ // are named because the `` primitive carries a `md:` gap that a
+ // bare `gap-0` loses to.
+ const html = render(rows);
+ expect(html).toContain("gap-0");
+ expect(html).toContain("md:gap-0");
+ expect(html).toContain("p-0");
+ expect(html).toContain("md:p-0");
+ expect(html).not.toMatch(/class="[^"]*\bgap-4\b/);
+ expect(html).not.toMatch(/class="[^"]*\bp-4\b/);
+ });
});
describe("SettingsCardHeader — one description sentence, one text run", () => {
diff --git a/src/components/settings/cycle-tracking-card.tsx b/src/components/settings/cycle-tracking-card.tsx
deleted file mode 100644
index fb5d4bed4..000000000
--- a/src/components/settings/cycle-tracking-card.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-"use client";
-
-import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
-import Link from "next/link";
-import { CalendarHeart } from "lucide-react";
-
-import { Button } from "@/components/ui/button";
-import { Label } from "@/components/ui/label";
-import { Switch } from "@/components/ui/switch";
-import { SettingsCard } from "@/components/settings/settings-card";
-import { SettingsCardHeader } from "@/components/settings/_card-header";
-import { useTranslations } from "@/lib/i18n/context";
-import { queryKeys } from "@/lib/query-keys";
-import { apiGet, apiPatch } from "@/lib/api/api-fetch";
-
-/**
- * v1.15.0 — the cycle-tracking enable on-ramp.
- *
- * The cycle vertical auto-enables for accounts set to female (gender-derived
- * default), but everyone else needs a way IN: the gated `/cycle` page bounces
- * them home and the in-page settings (where the goal/prefs live) sit behind
- * that same gate. This card reads the UNGATED `/api/auth/me/cycle-prefs` so any
- * account can flip `cycleTrackingEnabled` on (or a female account can opt out)
- * before the gated page is reachable. PATCHes the same prefs route the iOS app
- * uses; invalidates the nav gate so the sidebar/bottom-nav entry appears.
- */
-
-interface CyclePrefsShape {
- cycleTrackingEnabled: boolean;
-}
-
-export function CycleTrackingCard({
- isAuthenticated,
-}: {
- isAuthenticated: boolean;
-}) {
- const { t } = useTranslations();
- const queryClient = useQueryClient();
-
- const { data } = useQuery({
- queryKey: queryKeys.cyclePrefs(),
- enabled: isAuthenticated,
- queryFn: async () => {
- return apiGet("/api/auth/me/cycle-prefs");
- },
- });
-
- const toggle = useMutation({
- mutationFn: async (enabled: boolean) => {
- return apiPatch("/api/auth/me/cycle-prefs", { enabled });
- },
- onSuccess: () => {
- void queryClient.invalidateQueries({ queryKey: queryKeys.cyclePrefs() });
- void queryClient.invalidateQueries({ queryKey: queryKeys.cycle() });
- // The nav gate reads the resolved enable state off /auth/me.
- void queryClient.invalidateQueries({ queryKey: queryKeys.authMe() });
- },
- });
-
- const enabled = data?.cycleTrackingEnabled ?? false;
-
- return (
-
-
-