Index: petify-frontend/src/views/LoginView.vue
===================================================================
--- petify-frontend/src/views/LoginView.vue	(revision 92e7c7aeb21d4b882e2f8107b1676468ceb14aa7)
+++ petify-frontend/src/views/LoginView.vue	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
@@ -29,4 +29,7 @@
         <div v-if="error" class="alert alert-danger auth-alert" role="alert">
           <strong class="me-1">Oops.</strong>{{ error }}
+        </div>
+        <div v-if="forgotSuccess" class="alert alert-success auth-alert" role="alert">
+          {{ forgotSuccess }}
         </div>
 
@@ -113,8 +116,8 @@
           <!-- Remember me -->
           <div class="d-flex justify-content-between align-items-center mt-2 mb-3">
-
-
-            <!-- enable later if you implement route -->
-            <!-- <RouterLink class="link small accent" to="/forgot">Forgot password?</RouterLink> -->
+            <span></span>
+            <button class="link-button small accent" type="button" @click="toggleForgotPassword">
+              Forgot password?
+            </button>
           </div>
 
@@ -123,4 +126,33 @@
             <span v-if="loading" class="spinner-border spinner-border-sm me-2" aria-hidden="true"></span>
             {{ loading ? 'Logging in…' : 'Log in' }}
+          </button>
+        </form>
+
+        <form v-if="showForgotPassword" class="forgot-panel mt-4" @submit.prevent="submitForgotPassword">
+          <label class="form-label" for="forgot-identifier">Username or email</label>
+          <div class="input-group auth-input">
+            <span class="input-group-text">
+              <svg width="18" height="18" viewBox="0 0 24 24" fill="none">
+                <path
+                  d="M4 6.5A2.5 2.5 0 0 1 6.5 4h11A2.5 2.5 0 0 1 20 6.5v11a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 4 17.5v-11Zm2.5-.5a.5.5 0 0 0-.5.5v.8l6 3.7 6-3.7v-.8a.5.5 0 0 0-.5-.5h-11Zm11.5 3.6-5.5 3.4a1 1 0 0 1-1 0L6 9.6v7.9a.5.5 0 0 0 .5.5h11a.5.5 0 0 0 .5-.5V9.6Z"
+                  fill="currentColor"
+                  opacity=".85"
+                />
+              </svg>
+            </span>
+            <input
+              id="forgot-identifier"
+              v-model.trim="forgotIdentifier"
+              class="form-control"
+              type="text"
+              autocomplete="username"
+              required
+              placeholder="username or email"
+            />
+          </div>
+          <div v-if="forgotError" class="text-danger small mt-2">{{ forgotError }}</div>
+          <button class="btn btn-outline-primary w-100 mt-3" type="submit" :disabled="forgotLoading">
+            <span v-if="forgotLoading" class="spinner-border spinner-border-sm me-2" aria-hidden="true"></span>
+            {{ forgotLoading ? 'Sending...' : 'Send temporary password' }}
           </button>
         </form>
@@ -144,4 +176,5 @@
 import { useRoute, useRouter } from 'vue-router'
 import { useAuthStore } from '../stores/auth'
+import { forgotPassword } from '../api/auth'
 
 const auth = useAuthStore()
@@ -153,4 +186,9 @@
 const loading = ref(false)
 const error = ref<string | null>(null)
+const forgotLoading = ref(false)
+const forgotError = ref<string | null>(null)
+const forgotSuccess = ref<string | null>(null)
+const showForgotPassword = ref(false)
+const forgotIdentifier = ref('')
 
 const showPassword = ref(false)
@@ -159,4 +197,13 @@
 function togglePassword() {
   showPassword.value = !showPassword.value
+}
+
+function toggleForgotPassword() {
+  showForgotPassword.value = !showForgotPassword.value
+  forgotError.value = null
+  forgotSuccess.value = null
+  if (showForgotPassword.value && !forgotIdentifier.value) {
+    forgotIdentifier.value = username.value
+  }
 }
 
@@ -175,4 +222,18 @@
   } finally {
     loading.value = false
+  }
+}
+
+async function submitForgotPassword() {
+  forgotError.value = null
+  forgotSuccess.value = null
+  forgotLoading.value = true
+  try {
+    forgotSuccess.value = await forgotPassword({ identifier: forgotIdentifier.value })
+    showForgotPassword.value = false
+  } catch (e) {
+    forgotError.value = e instanceof Error ? e.message : String(e)
+  } finally {
+    forgotLoading.value = false
   }
 }
@@ -276,4 +337,9 @@
 }
 
+.forgot-panel {
+  border-top: 1px solid rgba(31, 41, 55, 0.1);
+  padding-top: 1rem;
+}
+
 /* Input styling */
 .auth-input .input-group-text {
@@ -371,4 +437,20 @@
 }
 
+.link-button {
+  border: 0;
+  background: transparent;
+  padding: 0;
+  text-decoration: none;
+}
+
+.link-button.accent {
+  color: #ff7a18;
+  font-weight: 600;
+}
+
+.link-button.accent:hover {
+  color: #e76610;
+}
+
 /* Respect reduced motion */
 @media (prefers-reduced-motion: reduce) {
