Index: petify-backend/pom.xml
===================================================================
--- petify-backend/pom.xml	(revision fa32d0f462d925f109df1a21025f904e56606f8b)
+++ petify-backend/pom.xml	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
@@ -46,4 +46,8 @@
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-webmvc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-mail</artifactId>
         </dependency>
 
Index: petify-backend/src/main/java/com/petify/petify/api/AdminClinicApplicationsController.java
===================================================================
--- petify-backend/src/main/java/com/petify/petify/api/AdminClinicApplicationsController.java	(revision fa32d0f462d925f109df1a21025f904e56606f8b)
+++ petify-backend/src/main/java/com/petify/petify/api/AdminClinicApplicationsController.java	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
@@ -1,10 +1,9 @@
 package com.petify.petify.api;
 
-import com.petify.petify.domain.VetClinic;
 import com.petify.petify.domain.VetClinicApplication;
 import com.petify.petify.dto.VetClinicApplicationDTO;
 import com.petify.petify.repo.AdminRepository;
 import com.petify.petify.repo.VetClinicApplicationRepository;
-import com.petify.petify.repo.VetClinicRepository;
+import com.petify.petify.service.ClinicApprovalService;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
@@ -20,13 +19,13 @@
 
     private final VetClinicApplicationRepository applicationRepository;
-    private final VetClinicRepository clinicRepository;
     private final AdminRepository adminRepository;
+    private final ClinicApprovalService clinicApprovalService;
 
     public AdminClinicApplicationsController(VetClinicApplicationRepository applicationRepository,
-                                             VetClinicRepository clinicRepository,
-                                             AdminRepository adminRepository) {
+                                             AdminRepository adminRepository,
+                                             ClinicApprovalService clinicApprovalService) {
         this.applicationRepository = applicationRepository;
-        this.clinicRepository = clinicRepository;
         this.adminRepository = adminRepository;
+        this.clinicApprovalService = clinicApprovalService;
     }
 
@@ -56,25 +55,5 @@
                         .body(Map.of("error", "Admin access required"));
             }
-            VetClinicApplication application = applicationRepository.findById(applicationId)
-                .orElseThrow(() -> new RuntimeException("Application not found"));
-
-            application.setStatus("APPROVED");
-            application.setReviewedAt(LocalDateTime.now());
-            application.setReviewedBy(adminUserId);
-            application.setDenialReason(null);
-            VetClinicApplication saved = applicationRepository.save(application);
-
-            boolean clinicExists = clinicRepository.findAll().stream()
-                .anyMatch(clinic -> applicationId.equals(clinic.getApplicationId()));
-            if (!clinicExists) {
-                VetClinic clinic = new VetClinic();
-                clinic.setApplicationId(application.getApplicationId());
-                clinic.setName(application.getName());
-                clinic.setEmail(application.getEmail());
-                clinic.setPhone(application.getPhone());
-                clinic.setCity(application.getCity());
-                clinic.setAddress(application.getAddress());
-                clinicRepository.save(clinic);
-            }
+            VetClinicApplication saved = clinicApprovalService.approveApplication(applicationId, adminUserId);
 
             return ResponseEntity.ok(new VetClinicApplicationDTO(saved));
Index: petify-backend/src/main/java/com/petify/petify/api/AuthController.java
===================================================================
--- petify-backend/src/main/java/com/petify/petify/api/AuthController.java	(revision fa32d0f462d925f109df1a21025f904e56606f8b)
+++ petify-backend/src/main/java/com/petify/petify/api/AuthController.java	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
@@ -2,4 +2,6 @@
 
 import com.petify.petify.dto.AuthResponse;
+import com.petify.petify.dto.ChangePasswordRequest;
+import com.petify.petify.dto.ForgotPasswordRequest;
 import com.petify.petify.dto.LoginRequest;
 import com.petify.petify.dto.SignUpRequest;
@@ -47,4 +49,26 @@
     }
 
+    @PostMapping("/change-password")
+    public ResponseEntity<?> changePassword(@RequestBody ChangePasswordRequest request) {
+        try {
+            authService.changePassword(request.getUserId(), request.getCurrentPassword(), request.getNewPassword());
+            return ResponseEntity.ok(Map.of("message", "Password changed successfully"));
+        } catch (RuntimeException e) {
+            return ResponseEntity.badRequest()
+                    .body(Map.of("message", e.getMessage()));
+        }
+    }
+
+    @PostMapping("/forgot-password")
+    public ResponseEntity<?> forgotPassword(@RequestBody ForgotPasswordRequest request) {
+        try {
+            authService.sendForgotPasswordEmail(request.getIdentifier());
+            return ResponseEntity.ok(Map.of("message", "If an account matches that username or email, a temporary password has been sent."));
+        } catch (RuntimeException e) {
+            return ResponseEntity.badRequest()
+                    .body(Map.of("message", e.getMessage()));
+        }
+    }
+
 
     @GetMapping("/users")
Index: petify-backend/src/main/java/com/petify/petify/dto/ChangePasswordRequest.java
===================================================================
--- petify-backend/src/main/java/com/petify/petify/dto/ChangePasswordRequest.java	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
+++ petify-backend/src/main/java/com/petify/petify/dto/ChangePasswordRequest.java	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
@@ -0,0 +1,21 @@
+package com.petify.petify.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class ChangePasswordRequest {
+    private Long userId;
+    private String currentPassword;
+    private String newPassword;
+
+    public ChangePasswordRequest() {
+    }
+
+    public ChangePasswordRequest(Long userId, String currentPassword, String newPassword) {
+        this.userId = userId;
+        this.currentPassword = currentPassword;
+        this.newPassword = newPassword;
+    }
+}
Index: petify-backend/src/main/java/com/petify/petify/dto/ForgotPasswordRequest.java
===================================================================
--- petify-backend/src/main/java/com/petify/petify/dto/ForgotPasswordRequest.java	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
+++ petify-backend/src/main/java/com/petify/petify/dto/ForgotPasswordRequest.java	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
@@ -0,0 +1,17 @@
+package com.petify.petify.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class ForgotPasswordRequest {
+    private String identifier;
+
+    public ForgotPasswordRequest() {
+    }
+
+    public ForgotPasswordRequest(String identifier) {
+        this.identifier = identifier;
+    }
+}
Index: petify-backend/src/main/java/com/petify/petify/repo/VetClinicRepository.java
===================================================================
--- petify-backend/src/main/java/com/petify/petify/repo/VetClinicRepository.java	(revision fa32d0f462d925f109df1a21025f904e56606f8b)
+++ petify-backend/src/main/java/com/petify/petify/repo/VetClinicRepository.java	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
@@ -10,4 +10,5 @@
 public interface VetClinicRepository extends JpaRepository<VetClinic, Long> {
     java.util.List<VetClinic> findAllByOrderByNameAsc();
+    Optional<VetClinic> findByApplicationId(Long applicationId);
     Optional<VetClinic> findByUserId(Long userId);
     boolean existsByUserId(Long userId);
Index: petify-backend/src/main/java/com/petify/petify/service/AuthService.java
===================================================================
--- petify-backend/src/main/java/com/petify/petify/service/AuthService.java	(revision fa32d0f462d925f109df1a21025f904e56606f8b)
+++ petify-backend/src/main/java/com/petify/petify/service/AuthService.java	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
@@ -22,4 +22,5 @@
 import org.springframework.transaction.annotation.Transactional;
 
+import java.security.SecureRandom;
 import java.time.LocalDateTime;
 import java.util.List;
@@ -31,4 +32,6 @@
 
     private static final Logger logger = LoggerFactory.getLogger(AuthService.class);
+    private static final String PASSWORD_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789!@#$%";
+    private static final SecureRandom SECURE_RANDOM = new SecureRandom();
 
     private final UserRepository userRepository;
@@ -39,9 +42,11 @@
     private final AnalyticsRepository analyticsRepository;
     private final VetClinicRepository vetClinicRepository;
+    private final ClinicCredentialsEmailService credentialsEmailService;
 
     public AuthService(UserRepository userRepository, ClientRepository clientRepository,
                       OwnerRepository ownerRepository, AdminRepository adminRepository,
                       PasswordEncoder passwordEncoder, AnalyticsRepository analyticsRepository,
-                      VetClinicRepository vetClinicRepository) {
+                      VetClinicRepository vetClinicRepository,
+                      ClinicCredentialsEmailService credentialsEmailService) {
         this.userRepository = userRepository;
         this.clientRepository = clientRepository;
@@ -51,4 +56,5 @@
         this.analyticsRepository = analyticsRepository;
         this.vetClinicRepository = vetClinicRepository;
+        this.credentialsEmailService = credentialsEmailService;
     }
 
@@ -177,4 +183,69 @@
             isVerified
         );
+    }
+
+    @Transactional
+    public void changePassword(Long userId, String currentPassword, String newPassword) {
+        if (userId == null) {
+            throw new RuntimeException("User is required");
+        }
+        if (currentPassword == null || currentPassword.isBlank()) {
+            throw new RuntimeException("Current password is required");
+        }
+        if (newPassword == null || newPassword.isBlank()) {
+            throw new RuntimeException("New password is required");
+        }
+        if (newPassword.length() < 8) {
+            throw new RuntimeException("New password must be at least 8 characters long");
+        }
+        if (newPassword.equals(currentPassword)) {
+            throw new RuntimeException("New password must be different from the current password");
+        }
+
+        User user = userRepository.findById(userId)
+            .orElseThrow(() -> new RuntimeException("User not found"));
+
+        boolean passwordMatches = passwordEncoder.matches(currentPassword, user.getPassword());
+        if (!passwordMatches && !currentPassword.equals(user.getPassword())) {
+            throw new RuntimeException("Current password is incorrect");
+        }
+
+        user.setPassword(passwordEncoder.encode(newPassword));
+        userRepository.save(user);
+        logger.info("Password changed successfully for user: {}", user.getUsername());
+    }
+
+    @Transactional
+    public void sendForgotPasswordEmail(String identifier) {
+        if (identifier == null || identifier.isBlank()) {
+            throw new RuntimeException("Username or email is required");
+        }
+
+        Optional<User> user = userRepository.findByUsernameOrEmail(identifier.trim(), identifier.trim());
+        if (user.isEmpty()) {
+            logger.info("Password reset requested for unknown identifier: {}", identifier);
+            return;
+        }
+
+        User foundUser = user.get();
+        String temporaryPassword = generateTemporaryPassword();
+        foundUser.setPassword(passwordEncoder.encode(temporaryPassword));
+        userRepository.save(foundUser);
+
+        credentialsEmailService.sendTemporaryPassword(
+            foundUser.getEmail(),
+            foundUser.getFirstName(),
+            foundUser.getUsername(),
+            temporaryPassword
+        );
+        logger.info("Temporary password sent for user: {}", foundUser.getUsername());
+    }
+
+    private String generateTemporaryPassword() {
+        StringBuilder password = new StringBuilder();
+        for (int i = 0; i < 14; i++) {
+            password.append(PASSWORD_CHARS.charAt(SECURE_RANDOM.nextInt(PASSWORD_CHARS.length())));
+        }
+        return password.toString();
     }
 
Index: petify-backend/src/main/java/com/petify/petify/service/ClinicApprovalService.java
===================================================================
--- petify-backend/src/main/java/com/petify/petify/service/ClinicApprovalService.java	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
+++ petify-backend/src/main/java/com/petify/petify/service/ClinicApprovalService.java	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
@@ -0,0 +1,126 @@
+package com.petify.petify.service;
+
+import com.petify.petify.domain.User;
+import com.petify.petify.domain.VetClinic;
+import com.petify.petify.domain.VetClinicApplication;
+import com.petify.petify.repo.UserRepository;
+import com.petify.petify.repo.VetClinicApplicationRepository;
+import com.petify.petify.repo.VetClinicRepository;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.security.SecureRandom;
+import java.time.LocalDateTime;
+import java.util.Locale;
+
+@Service
+public class ClinicApprovalService {
+
+    private static final int MAX_USERNAME_LENGTH = 30;
+    private static final String PASSWORD_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789!@#$%";
+    private static final SecureRandom SECURE_RANDOM = new SecureRandom();
+
+    private final VetClinicApplicationRepository applicationRepository;
+    private final VetClinicRepository clinicRepository;
+    private final UserRepository userRepository;
+    private final PasswordEncoder passwordEncoder;
+    private final ClinicCredentialsEmailService credentialsEmailService;
+
+    public ClinicApprovalService(VetClinicApplicationRepository applicationRepository,
+                                 VetClinicRepository clinicRepository,
+                                 UserRepository userRepository,
+                                 PasswordEncoder passwordEncoder,
+                                 ClinicCredentialsEmailService credentialsEmailService) {
+        this.applicationRepository = applicationRepository;
+        this.clinicRepository = clinicRepository;
+        this.userRepository = userRepository;
+        this.passwordEncoder = passwordEncoder;
+        this.credentialsEmailService = credentialsEmailService;
+    }
+
+    @Transactional
+    public VetClinicApplication approveApplication(Long applicationId, Long adminUserId) {
+        VetClinicApplication application = applicationRepository.findById(applicationId)
+            .orElseThrow(() -> new RuntimeException("Application not found"));
+
+        application.setStatus("APPROVED");
+        application.setReviewedAt(LocalDateTime.now());
+        application.setReviewedBy(adminUserId);
+        application.setDenialReason(null);
+        VetClinicApplication savedApplication = applicationRepository.save(application);
+
+        VetClinic clinic = clinicRepository.findByApplicationId(applicationId)
+            .orElseGet(() -> createClinic(application));
+
+        if (clinic.getUserId() == null) {
+            if (!StringUtils.hasText(application.getEmail())) {
+                throw new IllegalArgumentException("Clinic email is required to create and send account credentials");
+            }
+            if (userRepository.findByEmail(application.getEmail()).isPresent()) {
+                throw new IllegalArgumentException("A user with this clinic email already exists");
+            }
+
+            String username = generateUniqueUsername(application.getName());
+            String password = generatePassword();
+            User user = new User(
+                username,
+                application.getEmail(),
+                passwordEncoder.encode(password),
+                truncate(application.getName(), 60),
+                "Clinic"
+            );
+
+            User savedUser = userRepository.save(user);
+            clinic.setUserId(savedUser.getUserId());
+            clinicRepository.save(clinic);
+
+            credentialsEmailService.sendCredentials(application.getEmail(), application.getName(), username, password);
+        }
+
+        return savedApplication;
+    }
+
+    private VetClinic createClinic(VetClinicApplication application) {
+        VetClinic clinic = new VetClinic();
+        clinic.setApplicationId(application.getApplicationId());
+        clinic.setName(application.getName());
+        clinic.setEmail(application.getEmail());
+        clinic.setPhone(application.getPhone());
+        clinic.setCity(application.getCity());
+        clinic.setAddress(application.getAddress());
+        return clinicRepository.save(clinic);
+    }
+
+    private String generateUniqueUsername(String clinicName) {
+        String base = "clinic." + clinicName.toLowerCase(Locale.ROOT).replaceAll("[^a-z0-9]+", "");
+        if (base.equals("clinic.")) {
+            base = "clinic.account";
+        }
+        base = truncate(base, MAX_USERNAME_LENGTH);
+
+        String username = base;
+        int suffix = 2;
+        while (userRepository.findByUsername(username).isPresent()) {
+            String suffixText = String.valueOf(suffix++);
+            username = truncate(base, MAX_USERNAME_LENGTH - suffixText.length()) + suffixText;
+        }
+        return username;
+    }
+
+    private String generatePassword() {
+        StringBuilder password = new StringBuilder();
+        for (int i = 0; i < 14; i++) {
+            password.append(PASSWORD_CHARS.charAt(SECURE_RANDOM.nextInt(PASSWORD_CHARS.length())));
+        }
+        return password.toString();
+    }
+
+    private String truncate(String value, int maxLength) {
+        if (value == null || value.length() <= maxLength) {
+            return value;
+        }
+        return value.substring(0, maxLength);
+    }
+}
Index: petify-backend/src/main/java/com/petify/petify/service/ClinicCredentialsEmailService.java
===================================================================
--- petify-backend/src/main/java/com/petify/petify/service/ClinicCredentialsEmailService.java	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
+++ petify-backend/src/main/java/com/petify/petify/service/ClinicCredentialsEmailService.java	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
@@ -0,0 +1,94 @@
+package com.petify.petify.service;
+
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.mail.SimpleMailMessage;
+import org.springframework.mail.javamail.JavaMailSender;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+@Service
+public class ClinicCredentialsEmailService {
+
+    private final ObjectProvider<JavaMailSender> mailSenderProvider;
+    private final String fromAddress;
+    private final String mailPassword;
+
+    public ClinicCredentialsEmailService(ObjectProvider<JavaMailSender> mailSenderProvider,
+                                         @Value("${spring.mail.username:}") String fromAddress,
+                                         @Value("${spring.mail.password:}") String mailPassword) {
+        this.mailSenderProvider = mailSenderProvider;
+        this.fromAddress = fromAddress;
+        this.mailPassword = mailPassword;
+    }
+
+    public void sendCredentials(String recipientEmail, String clinicName, String username, String password) {
+        if (!StringUtils.hasText(recipientEmail)) {
+            throw new IllegalArgumentException("Clinic email is required to send account credentials");
+        }
+
+        JavaMailSender mailSender = getConfiguredMailSender();
+
+        SimpleMailMessage message = new SimpleMailMessage();
+        message.setFrom(fromAddress);
+        message.setTo(recipientEmail);
+        message.setSubject("Your Petify clinic account has been approved");
+        message.setText("""
+                Hello %s,
+
+                Your clinic application has been approved.
+
+                You can now sign in to Petify with these credentials:
+                Username: %s
+                Password: %s
+
+                Please change this password after your first login.
+
+                Petify Team
+                """.formatted(clinicName, username, password));
+
+        mailSender.send(message);
+    }
+
+    public void sendTemporaryPassword(String recipientEmail, String firstName, String username, String temporaryPassword) {
+        if (!StringUtils.hasText(recipientEmail)) {
+            throw new IllegalArgumentException("User email is required to send password reset credentials");
+        }
+
+        JavaMailSender mailSender = getConfiguredMailSender();
+
+        SimpleMailMessage message = new SimpleMailMessage();
+        message.setFrom(fromAddress);
+        message.setTo(recipientEmail);
+        message.setSubject("Your Petify password reset");
+        message.setText("""
+                Hello %s,
+
+                We received a request to reset your Petify password.
+
+                You can sign in with these temporary credentials:
+                Username: %s
+                Temporary password: %s
+
+                After signing in, open your profile's Account tab and choose a new password.
+
+                If you did not request this reset, please change your password right away.
+
+                Petify Team
+                """.formatted(firstName, username, temporaryPassword));
+
+        mailSender.send(message);
+    }
+
+    private JavaMailSender getConfiguredMailSender() {
+        if (!StringUtils.hasText(fromAddress) || !StringUtils.hasText(mailPassword)) {
+            throw new IllegalStateException("Mail username and password are not configured. Set MAIL_USERNAME and MAIL_PASSWORD before sending account emails.");
+        }
+
+        JavaMailSender mailSender = mailSenderProvider.getIfAvailable();
+        if (mailSender == null) {
+            throw new IllegalStateException("Mail sender is not configured. Set spring.mail.* properties before sending account emails.");
+        }
+        return mailSender;
+    }
+}
Index: petify-backend/src/main/resources/application.properties
===================================================================
--- petify-backend/src/main/resources/application.properties	(revision fa32d0f462d925f109df1a21025f904e56606f8b)
+++ petify-backend/src/main/resources/application.properties	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
@@ -33,6 +33,16 @@
 spring.flyway.baseline-version=0
 
+# Mail
+spring.mail.host=${MAIL_HOST:smtp.gmail.com}
+spring.mail.port=${MAIL_PORT:587}
+spring.mail.username=${MAIL_USERNAME:}
+spring.mail.password=${MAIL_PASSWORD:}
+spring.mail.properties.mail.smtp.auth=true
+spring.mail.properties.mail.smtp.starttls.enable=true
+spring.mail.properties.mail.smtp.starttls.required=true
+management.health.mail.enabled=false
+
 spring.profiles.active=local
-spring.config.import=optional:file:.env.properties
+spring.config.import=optional:file:.env.properties,optional:file:petify-backend/.env.properties
 
 
Index: petify-frontend/src/api/auth.ts
===================================================================
--- petify-frontend/src/api/auth.ts	(revision fa32d0f462d925f109df1a21025f904e56606f8b)
+++ petify-frontend/src/api/auth.ts	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
@@ -24,4 +24,14 @@
   }
   message?: string
+}
+
+export interface ChangePasswordRequest {
+  userId: number
+  currentPassword: string
+  newPassword: string
+}
+
+export interface ForgotPasswordRequest {
+  identifier: string
 }
 
@@ -141,2 +151,11 @@
   return { message: data.message || "Registration successful" }
 }
+
+export async function changePassword(payload: ChangePasswordRequest, options?: { signal?: AbortSignal }): Promise<void> {
+  await postJson<{ message?: string }>('/api/auth/change-password', payload, options)
+}
+
+export async function forgotPassword(payload: ForgotPasswordRequest, options?: { signal?: AbortSignal }): Promise<string> {
+  const data = await postJson<{ message?: string }>('/api/auth/forgot-password', payload, options)
+  return data.message || 'If an account matches that username or email, a temporary password has been sent.'
+}
Index: petify-frontend/src/views/LoginView.vue
===================================================================
--- petify-frontend/src/views/LoginView.vue	(revision fa32d0f462d925f109df1a21025f904e56606f8b)
+++ 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) {
Index: petify-frontend/src/views/ProfileView.vue
===================================================================
--- petify-frontend/src/views/ProfileView.vue	(revision fa32d0f462d925f109df1a21025f904e56606f8b)
+++ petify-frontend/src/views/ProfileView.vue	(revision ae836471908c8a67e8fc2a25ad6c445b1c867e48)
@@ -94,5 +94,72 @@
               </button>
             </li>
+            <li class="nav-item" role="presentation">
+              <button
+                class="nav-link"
+                :class="{ active: activeTab === 'account' }"
+                @click="activeTab = 'account'"
+                type="button"
+                role="tab"
+              >
+                <i class="bi bi-shield-lock-fill"></i> Account
+              </button>
+            </li>
           </ul>
+
+          <!-- Account Tab -->
+          <div v-if="activeTab === 'account'" class="tab-content-section">
+            <h2 class="section-title">Account</h2>
+            <form class="password-form" @submit.prevent="submitPasswordChange">
+              <div v-if="passwordSuccess" class="alert alert-success" role="alert">
+                {{ passwordSuccess }}
+              </div>
+              <div v-if="passwordError" class="alert alert-danger" role="alert">
+                {{ passwordError }}
+              </div>
+
+              <div class="mb-3">
+                <label class="form-label" for="current-password">Current password</label>
+                <input
+                  id="current-password"
+                  v-model="passwordForm.currentPassword"
+                  class="form-control"
+                  type="password"
+                  autocomplete="current-password"
+                  required
+                />
+              </div>
+
+              <div class="mb-3">
+                <label class="form-label" for="new-password">New password</label>
+                <input
+                  id="new-password"
+                  v-model="passwordForm.newPassword"
+                  class="form-control"
+                  type="password"
+                  autocomplete="new-password"
+                  minlength="8"
+                  required
+                />
+              </div>
+
+              <div class="mb-4">
+                <label class="form-label" for="confirm-new-password">Confirm new password</label>
+                <input
+                  id="confirm-new-password"
+                  v-model="passwordForm.confirmPassword"
+                  class="form-control"
+                  type="password"
+                  autocomplete="new-password"
+                  minlength="8"
+                  required
+                />
+              </div>
+
+              <button class="btn btn-primary" type="submit" :disabled="isPasswordSubmitting">
+                <span v-if="isPasswordSubmitting" class="spinner-border spinner-border-sm me-2" aria-hidden="true"></span>
+                {{ isPasswordSubmitting ? 'Changing...' : 'Change password' }}
+              </button>
+            </form>
+          </div>
 
           <!-- Listings Tab -->
@@ -746,9 +813,10 @@
   type Review,
 } from '../api/reviews'
+import { changePassword } from '../api/auth'
 
 const router = useRouter()
 const auth = useAuthStore()
 
-const activeTab = ref<'listings' | 'pets' | 'create-listing' | 'favorites' | 'appointments'>('listings')
+const activeTab = ref<'listings' | 'pets' | 'create-listing' | 'favorites' | 'appointments' | 'account'>('listings')
 const listings = ref<any[]>([])
 const pets = ref<any[]>([])
@@ -782,4 +850,13 @@
 const petPhotoFile = ref<File | null>(null)
 const petPhotoPreview = ref('')
+const isPasswordSubmitting = ref(false)
+const passwordError = ref('')
+const passwordSuccess = ref('')
+
+const passwordForm = ref({
+  currentPassword: '',
+  newPassword: '',
+  confirmPassword: '',
+})
 
 const newListing = ref({
@@ -859,4 +936,41 @@
   // Fall back to petNameMap (from pets list)
   return petNameMap.value[animalId] || 'Unknown Pet'
+}
+
+async function submitPasswordChange() {
+  passwordError.value = ''
+  passwordSuccess.value = ''
+
+  if (!auth.user?.userId) {
+    passwordError.value = 'You need to be logged in to change your password.'
+    return
+  }
+  if (passwordForm.value.newPassword.length < 8) {
+    passwordError.value = 'New password must be at least 8 characters long.'
+    return
+  }
+  if (passwordForm.value.newPassword !== passwordForm.value.confirmPassword) {
+    passwordError.value = 'New passwords do not match.'
+    return
+  }
+
+  isPasswordSubmitting.value = true
+  try {
+    await changePassword({
+      userId: auth.user.userId,
+      currentPassword: passwordForm.value.currentPassword,
+      newPassword: passwordForm.value.newPassword,
+    })
+    passwordForm.value = {
+      currentPassword: '',
+      newPassword: '',
+      confirmPassword: '',
+    }
+    passwordSuccess.value = 'Password changed successfully.'
+  } catch (error: any) {
+    passwordError.value = error?.message || 'Failed to change password.'
+  } finally {
+    isPasswordSubmitting.value = false
+  }
 }
 
@@ -1670,4 +1784,8 @@
 }
 
+.password-form {
+  max-width: 520px;
+}
+
 @keyframes fadeIn {
   from {
