diff --git a/apps/web/cypress/e2e/landing.feature b/apps/web/cypress/e2e/landing.feature
index 4567e7d..714fd8b 100644
--- a/apps/web/cypress/e2e/landing.feature
+++ b/apps/web/cypress/e2e/landing.feature
@@ -22,6 +22,11 @@ Feature: Public landing page at the root
When I click the link "Créer un compte"
Then the URL should include "/signup"
+ Scenario: The landing page offers the Android app download
+ Given I am not signed in
+ When I visit "/"
+ Then I should see the Android download link
+
Scenario: Another guarded route still redirects a logged-out visitor to sign in
Given I am not signed in
When I visit "/recettes"
diff --git a/apps/web/cypress/e2e/landing.ts b/apps/web/cypress/e2e/landing.ts
index 2c58911..6763477 100644
--- a/apps/web/cypress/e2e/landing.ts
+++ b/apps/web/cypress/e2e/landing.ts
@@ -14,3 +14,16 @@ Then("I should see the landing call to action", () => {
.should("be.visible")
.and("have.attr", "href", "/login");
});
+
+// The Android build is distributed as a Forgejo release asset with a stable
+// filename (issue #64 builds/attaches it). We only assert the link is there
+// and points at the right URL — the file itself is external to the suite.
+Then("I should see the Android download link", () => {
+ cy.get(".landing__hero .landing__mobile-link")
+ .should("be.visible")
+ .and(
+ "have.attr",
+ "href",
+ "https://git.dev.kyuno.fr/kyuno/batchCooking/releases/latest/download/batchcooking.apk",
+ );
+});
diff --git a/apps/web/src/locales/fr/translation.json b/apps/web/src/locales/fr/translation.json
index 70ee33b..e0d99c3 100644
--- a/apps/web/src/locales/fr/translation.json
+++ b/apps/web/src/locales/fr/translation.json
@@ -1327,7 +1327,9 @@
"title": "Cuisinez une fois, mangez toute la semaine",
"lede": "Planifiez vos repas, importez des recettes depuis vos sites préférés, et générez la liste de courses qui va avec — pour tout le foyer, sans y penser deux fois.",
"ctaSignup": "Créer un compte",
- "ctaLogin": "Se connecter"
+ "ctaLogin": "Se connecter",
+ "mobileDownload": "Télécharger pour Android",
+ "mobileHint": "Fichier .apk à installer soi-même : Android demandera d'autoriser l'installation depuis une source inconnue. Ce n'est pas une version du Play Store, et un compte reste nécessaire."
},
"features": {
"title": "Ce que batchCooking fait pour vous",
diff --git a/apps/web/src/pages/landing/LandingPage.tsx b/apps/web/src/pages/landing/LandingPage.tsx
index 48b800a..ed3e33f 100644
--- a/apps/web/src/pages/landing/LandingPage.tsx
+++ b/apps/web/src/pages/landing/LandingPage.tsx
@@ -1,4 +1,4 @@
-import { CalendarDays, HeartPulse, Sparkles, Users } from "lucide-react";
+import { CalendarDays, HeartPulse, Smartphone, Sparkles, Users } from "lucide-react";
import type { ReactNode } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
@@ -50,6 +50,22 @@ export function LandingPage() {
{t("landing.hero.ctaLogin")}
+ {/*
+ * Secondary action — an account is needed on any platform, so the
+ * Android build is a discreet link under the CTA row, not a third
+ * button. Points at the Forgejo "latest release" asset with a
+ * stable filename (see issue #64); 404s until that pipeline ships,
+ * accepted as a transient state. The hint is honest that this is a
+ * sideloaded .apk, not a Play Store listing.
+ */}
+
+
{t("landing.hero.mobileHint")}