diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 0000000000000000000000000000000000000000..accf79e327469c30c9457a0b38cc1c9df1046c48
Binary files /dev/null and b/.DS_Store differ
diff --git a/movie-group-8/.env b/movie-group-8/.env
new file mode 100644
index 0000000000000000000000000000000000000000..4ec34fde83f0116c4ff586e6f695eb0a151ad951
--- /dev/null
+++ b/movie-group-8/.env
@@ -0,0 +1,3 @@
+VITE_TMDB_API_KEY=42259df77843511296d8096fa29e08a8
+
+VITE_TMDB_BASE=https://api.themoviedb.org/3
diff --git a/movie-group-8/package-lock.json b/movie-group-8/package-lock.json
index c6e9575c2f7e1aca5b9ab83b9e720cd18bd6e052..5502da0ec210ec42ef3ac199421a2d8977f55e32 100644
--- a/movie-group-8/package-lock.json
+++ b/movie-group-8/package-lock.json
@@ -11,6 +11,7 @@
         "@headlessui/vue": "^1.7.23",
         "@heroicons/vue": "^2.2.0",
         "@tailwindcss/vite": "^4.0.14",
+        "axios": "^1.9.0",
         "firebase": "^11.4.0",
         "movie-group-8": "file:",
         "tailwindcss": "^4.0.14",
@@ -1719,6 +1720,36 @@
         "url": "https://github.com/chalk/ansi-styles?sponsor=1"
       }
     },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT"
+    },
+    "node_modules/axios": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
+      "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/call-bind-apply-helpers": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/cliui": {
       "version": "8.0.1",
       "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
@@ -1748,11 +1779,32 @@
       "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
       "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
     },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/csstype": {
       "version": "3.1.3",
       "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
       "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
     },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/detect-libc": {
       "version": "2.0.3",
       "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
@@ -1761,6 +1813,20 @@
         "node": ">=8"
       }
     },
+    "node_modules/dunder-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.2.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/emoji-regex": {
       "version": "8.0.0",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -1789,6 +1855,51 @@
         "url": "https://github.com/fb55/entities?sponsor=1"
       }
     },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-object-atoms": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+      "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-set-tostringtag": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+      "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+      "license": "MIT",
+      "dependencies": {
+        "es-errors": "^1.3.0",
+        "get-intrinsic": "^1.2.6",
+        "has-tostringtag": "^1.0.2",
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/esbuild": {
       "version": "0.25.1",
       "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz",
@@ -1852,6 +1963,20 @@
         "node": ">=0.8.0"
       }
     },
+    "node_modules/fdir": {
+      "version": "6.4.4",
+      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
+      "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
+      "license": "MIT",
+      "peerDependencies": {
+        "picomatch": "^3 || ^4"
+      },
+      "peerDependenciesMeta": {
+        "picomatch": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/firebase": {
       "version": "11.4.0",
       "resolved": "https://registry.npmjs.org/firebase/-/firebase-11.4.0.tgz",
@@ -1887,6 +2012,41 @@
         "@firebase/vertexai": "1.1.0"
       }
     },
+    "node_modules/follow-redirects": {
+      "version": "1.15.9",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+      "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
+      "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "es-set-tostringtag": "^2.1.0",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/fsevents": {
       "version": "2.3.3",
       "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -1900,6 +2060,15 @@
         "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
       }
     },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/get-caller-file": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@@ -1908,11 +2077,99 @@
         "node": "6.* || 8.* || >= 10.*"
       }
     },
+    "node_modules/get-intrinsic": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+      "license": "MIT",
+      "dependencies": {
+        "call-bind-apply-helpers": "^1.0.2",
+        "es-define-property": "^1.0.1",
+        "es-errors": "^1.3.0",
+        "es-object-atoms": "^1.1.1",
+        "function-bind": "^1.1.2",
+        "get-proto": "^1.0.1",
+        "gopd": "^1.2.0",
+        "has-symbols": "^1.1.0",
+        "hasown": "^2.0.2",
+        "math-intrinsics": "^1.1.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-proto": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+      "license": "MIT",
+      "dependencies": {
+        "dunder-proto": "^1.0.1",
+        "es-object-atoms": "^1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
     "node_modules/graceful-fs": {
       "version": "4.2.11",
       "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
       "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
     },
+    "node_modules/has-symbols": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/has-tostringtag": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+      "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+      "license": "MIT",
+      "dependencies": {
+        "has-symbols": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
     "node_modules/http-parser-js": {
       "version": "0.5.9",
       "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.9.tgz",
@@ -2174,6 +2431,36 @@
         "@jridgewell/sourcemap-codec": "^1.5.0"
       }
     },
+    "node_modules/math-intrinsics": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
     "node_modules/movie-group-8": {
       "resolved": "",
       "link": true
@@ -2200,6 +2487,18 @@
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
       "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
     },
+    "node_modules/picomatch": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+      "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
     "node_modules/postcss": {
       "version": "8.5.3",
       "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
@@ -2250,6 +2549,12 @@
         "node": ">=12.0.0"
       }
     },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+      "license": "MIT"
+    },
     "node_modules/require-directory": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -2359,6 +2664,22 @@
         "node": ">=6"
       }
     },
+    "node_modules/tinyglobby": {
+      "version": "0.2.13",
+      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
+      "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
+      "license": "MIT",
+      "dependencies": {
+        "fdir": "^6.4.4",
+        "picomatch": "^4.0.2"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/SuperchupuDev"
+      }
+    },
     "node_modules/tslib": {
       "version": "2.8.1",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -2370,13 +2691,17 @@
       "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
     },
     "node_modules/vite": {
-      "version": "6.2.2",
-      "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz",
-      "integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==",
+      "version": "6.3.5",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
+      "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
+      "license": "MIT",
       "dependencies": {
         "esbuild": "^0.25.0",
+        "fdir": "^6.4.4",
+        "picomatch": "^4.0.2",
         "postcss": "^8.5.3",
-        "rollup": "^4.30.1"
+        "rollup": "^4.34.9",
+        "tinyglobby": "^0.2.13"
       },
       "bin": {
         "vite": "bin/vite.js"
diff --git a/movie-group-8/package.json b/movie-group-8/package.json
index 490d97713be8921292573244ccacae56fe3c8d84..22af383cd1cebd8a1c2023b2f9e7938c34cbac5d 100644
--- a/movie-group-8/package.json
+++ b/movie-group-8/package.json
@@ -12,6 +12,7 @@
     "@headlessui/vue": "^1.7.23",
     "@heroicons/vue": "^2.2.0",
     "@tailwindcss/vite": "^4.0.14",
+    "axios": "^1.9.0",
     "firebase": "^11.4.0",
     "movie-group-8": "file:",
     "tailwindcss": "^4.0.14",
diff --git a/movie-group-8/src/App.vue b/movie-group-8/src/App.vue
index cc3a63d6cb3cd5e2c6fb51c3e4617a07cf48e9bf..06a35b97d34a73ade52d72d75981b01ddec76847 100644
--- a/movie-group-8/src/App.vue
+++ b/movie-group-8/src/App.vue
@@ -12,3 +12,5 @@ import Navbar from './components/Navbar.vue';
 
 <style scoped>
 </style>
+
+
diff --git a/movie-group-8/src/components/MovieCard.vue b/movie-group-8/src/components/MovieCard.vue
new file mode 100644
index 0000000000000000000000000000000000000000..15e6322b2e79283be5c16313a8484ba56dd24151
--- /dev/null
+++ b/movie-group-8/src/components/MovieCard.vue
@@ -0,0 +1,57 @@
+<template>
+  <div class="bg-white dark:bg-neutral-800 border dark:border-neutral-700 rounded-lg p-4 flex gap-4 items-start">
+    <!-- Clickable poster -->
+    <router-link
+      :to="`/films/${movie.id}`"
+      class="shrink-0 hover:opacity-90 transition"
+    >
+      <img
+        v-if="movie.poster_path"
+        :src="'https://image.tmdb.org/t/p/w154' + movie.poster_path"
+        alt="Poster"
+        class="w-24 h-36 object-cover rounded"
+      />
+    </router-link>
+
+    <div class="flex-1">
+      <!-- Clickable title -->
+      <router-link
+        :to="`/films/${movie.id}`"
+        class="text-lg font-semibold text-gray-800 dark:text-white hover:underline block"
+      >
+        {{ movie.title }}
+      </router-link>
+
+      <p class="text-sm text-gray-600 dark:text-neutral-400">⭐ {{ movie.vote_average }}</p>
+
+      <!-- Interactive status dropdown -->
+      <label class="text-xs text-gray-500 dark:text-gray-400 block mt-2">
+        Status:
+        <select
+          v-model="selectedStatus"
+          @change="emitChange"
+          class="ml-2 bg-white dark:bg-neutral-700 border border-gray-300 dark:border-neutral-600 rounded px-2 py-1 text-sm"
+        >
+          <option value="planned">Planned</option>
+          <option value="watched">Watched</option>
+        </select>
+      </label>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, watch } from 'vue'
+
+const props = defineProps({
+  movie: Object,
+})
+
+const emit = defineEmits(['status-changed'])
+
+const selectedStatus = ref(props.movie.status)
+
+const emitChange = () => {
+  emit('status-changed', props.movie.id, selectedStatus.value)
+}
+</script>
\ No newline at end of file
diff --git a/movie-group-8/src/components/MovieList.vue b/movie-group-8/src/components/MovieList.vue
new file mode 100644
index 0000000000000000000000000000000000000000..aff845ac398f5703e6e5caf3eb56c0ffabfcb08b
--- /dev/null
+++ b/movie-group-8/src/components/MovieList.vue
@@ -0,0 +1,120 @@
+<template>
+  <div class="movie-list">
+    <div v-if="!movies.length" class="no-results">
+      <slot name="empty">No movies found.</slot>
+    </div>
+    <ul v-else class="movies">
+      <li v-for="movie in movies" :key="movie.id" class="movie-item">
+        <router-link
+            :to="{ name: 'FilmDetails', params: { id: movie.id } }"
+            class="movie-link"
+        >
+            <img
+            v-if="movie.poster_path"
+            :src="getPosterUrl(movie.poster_path)"
+            :alt="`${movie.title} poster`"
+            class="poster"
+            />
+            <div class="details">
+            <h2 class="title">{{ movie.title }}</h2>
+            <p class="release-date">{{ formatDate(movie.release_date) }}</p>
+            <p class="overview">{{ movie.overview || 'No overview available.' }}</p>
+            </div>
+        </router-link>
+
+      </li>
+    </ul>
+  </div>
+</template>
+
+<script setup>
+import { defineProps } from 'vue'
+
+const props = defineProps({
+  movies: {
+    type: Array,
+    required: true
+  }
+})
+
+// TMDB image base URL from Vite env or fallback
+const IMAGE_BASE = import.meta.env.VITE_TMDB_IMAGE_BASE || 'https://image.tmdb.org/t/p/w200'
+
+/**
+ * Build full poster URL
+ */
+function getPosterUrl(path) {
+  return `${IMAGE_BASE}${path}`
+}
+
+/**
+ * Format a date string to a more readable form
+ */
+function formatDate(dateString) {
+  if (!dateString) return 'Unknown'
+  const options = { year: 'numeric', month: 'long', day: 'numeric' }
+  return new Date(dateString).toLocaleDateString(undefined, options)
+}
+</script>
+
+<style scoped>
+.movie-list {
+  display: flex;
+  flex-direction: column;
+}
+
+.no-results {
+  text-align: center;
+  color: #666;
+  font-style: italic;
+}
+
+.movies {
+  list-style: none;
+  padding: 0;
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+  gap: 1rem;
+}
+
+.movie-item {
+  background: #fff;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+}
+
+.poster {
+  width: 100%;
+  object-fit: cover;
+  aspect-ratio: 2 / 3;
+}
+
+.details {
+  padding: 0.75rem;
+  display: flex;
+  flex-direction: column;
+  flex-grow: 1;
+}
+
+.title {
+  font-size: 1.1rem;
+  margin: 0 0 0.5rem;
+}
+
+.release-date {
+  font-size: 0.875rem;
+  color: #888;
+  margin: 0 0 0.5rem;
+}
+
+.overview {
+  font-size: 0.9rem;
+  color: #444;
+  flex-grow: 1;
+}
+
+
+</style>
diff --git a/movie-group-8/src/components/Navbar.vue b/movie-group-8/src/components/Navbar.vue
index 5ae04011c21832e5f613c42d75b1f64557a642b5..7abd7a88907732e7e5cb61faa5c7cbbd8d27570d 100644
--- a/movie-group-8/src/components/Navbar.vue
+++ b/movie-group-8/src/components/Navbar.vue
@@ -86,6 +86,8 @@ const userPhotoURL = ref('');
 const navigation = ref([
   { name: 'Films', href: '/films', authRequired: true },
   { name: 'Watchlist', href: '/watchlist', authRequired: true },
+  { name: 'Top Rated', href: '/top-rated', authRequired: true },
+
 ]);
 
 // Compute the current route to match against the navigation links
diff --git a/movie-group-8/src/components/TopRatedMovies.vue b/movie-group-8/src/components/TopRatedMovies.vue
new file mode 100644
index 0000000000000000000000000000000000000000..5df1ea21187c2f57bfa5dd31e65a7fe185cdc3a0
--- /dev/null
+++ b/movie-group-8/src/components/TopRatedMovies.vue
@@ -0,0 +1,142 @@
+<script setup>
+import { ref, onMounted, watch } from 'vue'
+import { addToWatchlist } from '@/composables/useWatchlist.js'
+
+const year = ref(2000)
+const genre = ref('')
+const genres = ref([])
+const movies = ref([])
+
+const TMDB_API_KEY = import.meta.env.VITE_TMDB_API_KEY
+
+// Fetch genres on component mount
+onMounted(async () => {
+  const genreRes = await fetch(`https://api.themoviedb.org/3/genre/movie/list?api_key=${TMDB_API_KEY}`)
+  const genreData = await genreRes.json()
+  genres.value = genreData.genres
+})
+
+// Watch for changes in year or genre and fetch movies
+watch([year, genre], async () => {
+  const url = `https://api.themoviedb.org/3/discover/movie?api_key=${TMDB_API_KEY}&sort_by=vote_average.desc&vote_count.gte=50&primary_release_year=${year.value}` +
+              (genre.value ? `&with_genres=${genre.value}` : '')
+
+  const res = await fetch(url)
+  const data = await res.json()
+  movies.value = data.results
+}, { immediate: true })
+</script>
+
+<template>
+    <div class="nyt-header">
+      <h1>The Movies We've Loved Since 2000</h1>
+      <p>
+        Explore top-rated films using the filters below — by genre and year — and rediscover hidden gems.
+      </p>
+    </div>
+  
+    <div class="filters">
+      <select v-model="genre">
+        <option value="">Pick a genre ...</option>
+        <option v-for="g in genres" :key="g.id" :value="g.id">{{ g.name }}</option>
+      </select>
+  
+      <select v-model="year">
+        <option v-for="y in Array.from({length: 25}, (_, i) => 2000 + i)" :key="y" :value="y">
+          {{ y }}
+        </option>
+      </select>
+    </div>
+  
+    <h2 class="section-title">Our favorite movies from {{ year }}</h2>
+  
+    <div class="movie-grid">
+      <div v-for="movie in movies" :key="movie.id" class="movie-card">
+        <img
+          v-if="movie.poster_path"
+          :src="'https://image.tmdb.org/t/p/w342' + movie.poster_path"
+          alt="poster"
+        />
+        <p class="title">{{ movie.title }}</p>
+        <p class="rating">⭐ {{ movie.vote_average }}</p>
+        <button @click="addToWatchlist(movie)">Add to Watchlist</button>
+      </div>
+    </div>
+  </template>
+  
+<style scoped>
+.nyt-header {
+  text-align: center;
+  max-width: 700px;
+  margin: 3rem auto 2rem auto;
+  padding: 0 1rem;
+}
+.nyt-header h1 {
+  font-size: 2.4rem;
+  font-weight: 700;
+  line-height: 1.2;
+}
+.nyt-header p {
+  font-size: 1.1rem;
+  margin-top: 0.75rem;
+  color: #555;
+}
+
+.filters {
+  display: flex;
+  justify-content: center;
+  flex-wrap: wrap;
+  gap: 1rem;
+  margin-bottom: 2rem;
+}
+
+.filters select {
+  padding: 0.5rem 1.2rem;
+  background-color: #cce8ff;
+  border: none;
+  border-radius: 8px;
+  font-weight: bold;
+  font-size: 1rem;
+  color: #003366;
+  cursor: pointer;
+  transition: background-color 0.2s ease;
+}
+.filters select:hover {
+  background-color: #b2dcff;
+}
+
+.section-title {
+  text-align: center;
+  font-size: 1.6rem;
+  font-weight: bold;
+  margin-bottom: 1.5rem;
+}
+
+.movie-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
+  gap: 1.2rem;
+  padding: 0 1rem;
+}
+
+.movie-card {
+  text-align: center;
+}
+
+.movie-card img {
+  width: 100%;
+  border-radius: 12px;
+  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
+}
+
+.title {
+  font-weight: 600;
+  margin-top: 0.5rem;
+}
+
+.rating {
+  color: #ffa500;
+  font-size: 0.9rem;
+}
+
+</style>
diff --git a/movie-group-8/src/components/Watchlist.vue b/movie-group-8/src/components/Watchlist.vue
new file mode 100644
index 0000000000000000000000000000000000000000..968bb92b4ce92aa74b7431459b883eb435ee6006
--- /dev/null
+++ b/movie-group-8/src/components/Watchlist.vue
@@ -0,0 +1,73 @@
+<template>
+    <div class="watchlist">
+        <h1>Your Watchlist</h1>
+  
+        <div class="filters">
+            <select v-model="filter">
+                <option value="">All</option>
+                <option value="planned">Plan To Watch</option>
+                <option value="watched">Watched</option>
+            </select>
+        </div>
+  
+        <div class="movie-grid">
+            <div 
+                v-for="movie in filteredWatchlist" 
+                :key="movie.id" 
+                class="movie-card"
+            >
+            <img :src="'https://image.tmdb.org/t/p/w342' + movie.poster_path" alt="poster" />
+            <p class="title">{{ movie.title }}</p>
+            <p class="rating">⭐ {{ movie.vote_average }}</p>
+            <p class="status">📌 {{ movie.status }}</p>
+            </div>
+        </div>
+    </div>
+</template>
+  
+<script setup>
+    import { ref, computed, onMounted } from 'vue'
+    import { getFirestore, collection, getDocs } from 'firebase/firestore'
+    import { getAuth } from 'firebase/auth'
+  
+    const db = getFirestore()
+    const auth = getAuth()
+  
+    const watchlist = ref([])
+    const filter = ref('')
+  
+    const fetchWatchlist = async () => {
+        const user = auth.currentUser
+        if (!user) return
+  
+        const snapshot = await getDocs(collection(db, 'users', user.uid, 'watchlist'))
+        watchlist.value = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }))
+    }
+  
+    const filteredWatchlist = computed(() => {
+        if (!filter.value) return watchlist.value
+        return watchlist.value.filter(movie => movie.status === filter.value)
+    })
+  
+    onMounted(() => {
+        fetchWatchlist()
+    })
+</script>
+  
+<style scoped>
+    .watchlist {
+        padding: 2rem;
+        text-align: center;
+    }
+  
+    .filters {
+        margin-bottom: 1rem;
+    }
+  
+    .movie-grid {
+        display: grid;
+        grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
+        gap: 1.2rem;
+    }
+</style>
+  
\ No newline at end of file
diff --git a/movie-group-8/src/composables/useMovies,js b/movie-group-8/src/composables/useMovies,js
new file mode 100644
index 0000000000000000000000000000000000000000..673d1e96b8d09008d2879a81c74758a1d19ac4a8
--- /dev/null
+++ b/movie-group-8/src/composables/useMovies,js
@@ -0,0 +1,29 @@
+import axios from 'axios'
+import { ref } from 'vue'
+
+const API_KEY = process.env.VITE_TMDB_API_KEY
+const BASE   = process.env.VUE_APP_TMDB_BASE
+
+export function useMovies() {
+  const results = ref([])
+  const loading = ref(false)
+  const error   = ref(null)
+
+  async function search(query) {
+    loading.value = true
+    error.value   = null
+    try {
+      const url = `${BASE}/search/movie`
+      const { data } = await axios.get(url, {
+        params: { api_key: API_KEY, query }
+      })
+      results.value = data.results
+    } catch (e) {
+      error.value = e
+    } finally {
+      loading.value = false
+    }
+  }
+
+  return { results, loading, error, search }
+}
\ No newline at end of file
diff --git a/movie-group-8/src/composables/useWatchlist.js b/movie-group-8/src/composables/useWatchlist.js
new file mode 100644
index 0000000000000000000000000000000000000000..7de03593946235c36f67c8966ec81774b616c01e
--- /dev/null
+++ b/movie-group-8/src/composables/useWatchlist.js
@@ -0,0 +1,18 @@
+import { getFirestore, doc, setDoc } from 'firebase/firestore'
+import { getAuth } from 'firebase/auth'
+
+export const addToWatchlist = async (movie) => {
+  const user = getAuth().currentUser
+  if (!user) return alert('You need to log in.')
+
+  const db = getFirestore()
+  const movieRef = doc(db, 'users', user.uid, 'watchlist', String(movie.id))
+  await setDoc(movieRef, {
+    title: movie.title,
+    poster_path: movie.poster_path,
+    vote_average: movie.vote_average,
+    status: 'planned'
+  })
+
+  alert('Movie added to watchlist!')
+}
\ No newline at end of file
diff --git a/movie-group-8/src/router/index.js b/movie-group-8/src/router/index.js
index 21bc9eb618bc3e8241d221a76a4e894b022d6ca4..f4763b4c1ce260ab69a24d77e4ab2956d5b63c49 100644
--- a/movie-group-8/src/router/index.js
+++ b/movie-group-8/src/router/index.js
@@ -1,21 +1,41 @@
 import { getAuth, onAuthStateChanged } from 'firebase/auth';
 import { createRouter, createWebHistory } from 'vue-router';
+import Films from '@/views/Films.vue'
+import FilmDetails from '@/views/FilmDetails.vue'
+import ReviewPage from '@/views/ReviewPage.vue'
 
 const router = createRouter({
     history: createWebHistory(),
     routes: [
+        
+        
         { path: '/', component: () => import('../views/Home.vue') },
+        {path: '/top-rated', component: () => import('../views/TopRated.vue'), meta: { requiresAuth: true }},
         { path: '/register', component: () => import('../views/Register.vue') },
         { path: '/login', component: () => import('../views/Login.vue') },
         { path: '/recover-account', component: () => import('../views/RecoverAccount.vue') },
-        { 
-          path: '/films', 
-          component: () => import('../views/Films.vue'), 
-          meta: { requiresAuth: true },
+        {
+            path: '/films',
+            name: 'Films',
+            component: Films,
+            meta: { requiresAuth: true }  
+        },
+        {
+            path: '/films/:id',
+            name: 'FilmDetails',
+            component: FilmDetails,
+        },
+
+        {
+            path: '/films/:id/review',
+            name: 'ReviewFilm',
+            component: ReviewPage,
+            props: true,
         },
+
         { 
             path: '/watchlist', 
-            component: () => import('../views/Watchlist.vue'), 
+            component: () => import('../views/WatchlistView.vue'), 
             meta: { requiresAuth: true },
         },
         { 
@@ -28,6 +48,7 @@ const router = createRouter({
             component: () => import('../views/Settings.vue'), 
             meta: { requiresAuth: true },
         },
+
     ],
 });
 
diff --git a/movie-group-8/src/views/FilmDetails.vue b/movie-group-8/src/views/FilmDetails.vue
new file mode 100644
index 0000000000000000000000000000000000000000..8194f5cef562fe74c64aab5212b4c26b4036843d
--- /dev/null
+++ b/movie-group-8/src/views/FilmDetails.vue
@@ -0,0 +1,183 @@
+<template>
+  <div class="film-details">
+    <button class="back-button" @click="$router.back()">← Back</button>
+
+    <div v-if="loading" class="loading">Loading movie...</div>
+    <div v-else-if="error" class="error">Error: {{ error }}</div>
+
+    <div v-else class="details-container">
+      <div class="poster-container">
+        <img
+          v-if="movie.poster_path"
+          :src="`https://image.tmdb.org/t/p/w500${movie.poster_path}`"
+          :alt="`${movie.title} poster`"
+          class="poster"
+        />
+      </div>
+
+      <div class="info-container">
+        <h1 class="title">{{ movie.title }}</h1>
+        <p class="release-date">{{ formatDate(movie.release_date) }}</p>
+        <p class="overview">{{ movie.overview }}</p>
+
+        <div v-if="trailerUrl" class="trailer">
+          <h2>Trailer</h2>
+          <iframe
+            :src="trailerUrl"
+            frameborder="0"
+            allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
+            allowfullscreen
+          ></iframe>
+        </div>
+
+        <button
+          @click="addToWatchlist(movie)"
+          class="mt-4 px-4 py-2 bg-green-600 rounded text-white hover:bg-green-700"
+        >
+          Add to Watchlist
+        </button>
+
+        <RouterLink
+          :to="{ name: 'ReviewFilm', params: { id: movie.id } }"
+          class="mt-2 inline-block px-4 py-2 bg-blue-600 rounded text-white hover:bg-blue-700 text-center"
+        >
+          Review this Film
+        </RouterLink>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { useRoute } from 'vue-router'
+import { addToWatchlist } from '@/composables/useWatchlist.js'
+
+const route = useRoute()
+const movie = ref(null)
+const trailerUrl = ref('')
+const loading = ref(true)
+const error = ref('')
+
+function formatDate(dateString) {
+  if (!dateString) return 'Unknown'
+  return new Date(dateString).toLocaleDateString(undefined, {
+    year: 'numeric',
+    month: 'long',
+    day: 'numeric'
+  })
+}
+
+async function fetchMovieDetails(id) {
+  try {
+    const res = await fetch(
+      `${import.meta.env.VITE_TMDB_BASE}/movie/${id}?api_key=${import.meta.env.VITE_TMDB_API_KEY}`
+    )
+    if (!res.ok) throw new Error('Failed to fetch movie details')
+    movie.value = await res.json()
+  } catch (err) {
+    error.value = err.message
+  }
+}
+
+async function fetchMovieVideos(id) {
+  try {
+    const res = await fetch(
+      `${import.meta.env.VITE_TMDB_BASE}/movie/${id}/videos?api_key=${import.meta.env.VITE_TMDB_API_KEY}`
+    )
+    if (!res.ok) throw new Error('Failed to fetch movie videos')
+    const data = await res.json()
+    const trailer = data.results.find(v => v.type === 'Trailer' && v.site === 'YouTube')
+    if (trailer) {
+      trailerUrl.value = `https://www.youtube.com/embed/${trailer.key}`
+    }
+  } catch {
+    // ignore trailer errors
+  }
+}
+
+onMounted(async () => {
+  const id = route.params.id
+  await fetchMovieDetails(id)
+  await fetchMovieVideos(id)
+  loading.value = false
+})
+</script>
+
+<style scoped>
+.film-details {
+  background-color: #121212;
+  color: #ffffff;
+  min-height: 100vh;
+  padding: 2rem;
+  box-sizing: border-box;
+}
+
+.back-button {
+  color: #ffffff;
+  background: transparent;
+  margin-bottom: 1rem;
+  font-size: 1rem;
+}
+
+.loading,
+.error {
+  color: #ffffff;
+  text-align: center;
+  margin: 2rem 0;
+}
+
+.details-container {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 2rem;
+  background-color: #1e1e1e;
+  padding: 2rem;
+  border-radius: 8px;
+  max-width: 900px;
+  margin: 2rem auto 0;
+}
+
+.poster-container {
+  flex: 0 0 300px;
+}
+
+.poster {
+  width: 100%;
+  border-radius: 4px;
+}
+
+.info-container {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+}
+
+.title {
+  font-size: 2rem;
+  margin: 0 0 0.5rem;
+}
+
+.release-date {
+  color: #bbbbbb;
+  margin-bottom: 1rem;
+}
+
+.overview {
+  color: #dddddd;
+  line-height: 1.6;
+  margin-bottom: 1.5rem;
+}
+
+.trailer h2 {
+  margin-bottom: 0.5rem;
+  color: #ffffff;
+}
+
+.trailer iframe {
+  width: 100%;
+  height: 300px;
+  border-radius: 8px;
+  border: none;
+}
+</style>
diff --git a/movie-group-8/src/views/Films.vue b/movie-group-8/src/views/Films.vue
index 072b643deb3df336d3dd52bd1cccf947486db309..31474cbb988dd844fe1c2a8548fef94c70db2bfa 100644
--- a/movie-group-8/src/views/Films.vue
+++ b/movie-group-8/src/views/Films.vue
@@ -1 +1,106 @@
-<template>Films</template>
\ No newline at end of file
+<template>
+  <div class="films-container">
+    <!-- Centered search bar pushed down slightly -->
+    <div class="search-wrapper">
+      <input
+        v-model="term"
+        @keyup.enter="onSearch"
+        placeholder="Search movies…"
+        class="search-input"
+      />
+    </div>
+
+    <div v-if="loading" class="loading">Loading…</div>
+    <div v-else-if="error" class="error">Error: {{ error.message }}</div>
+
+    <!-- Movie grid -->
+    <MovieList :movies="results" />
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import axios from 'axios'
+import MovieList from '../components/MovieList.vue'
+
+const API_KEY = import.meta.env.VITE_TMDB_API_KEY
+const BASE    = import.meta.env.VITE_TMDB_BASE
+
+const term    = ref('')
+const results = ref([])
+const loading = ref(false)
+const error   = ref(null)
+
+async function search(query) {
+  loading.value = true
+  error.value   = null
+  try {
+    const { data } = await axios.get(`${BASE}/search/movie`, {
+      params: { api_key: API_KEY, query }
+    })
+    results.value = data.results
+  } catch (e) {
+    error.value = e
+  } finally {
+    loading.value = false
+  }
+}
+
+async function fetchPopular() {
+  loading.value = true
+  error.value   = null
+  try {
+    const { data } = await axios.get(`${BASE}/movie/popular`, {
+      params: { api_key: API_KEY }
+    })
+    results.value = data.results
+  } catch (e) {
+    error.value = e
+  } finally {
+    loading.value = false
+  }
+}
+
+function onSearch() {
+  if (term.value.trim()) {
+    search(term.value)
+  } else {
+    fetchPopular()
+  }
+}
+
+onMounted(fetchPopular)
+</script>
+
+<style scoped>
+.films-container {
+  padding-top: 6rem; /* adjust to nav height + spacing */
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  width: 100%;
+}
+
+.search-wrapper {
+  width: 100%;
+  max-width: 500px;
+  margin-bottom: 1.5rem;
+  display: flex;
+  justify-content: center;
+}
+
+.search-input {
+  width: 100%;
+  padding: 0.75rem 1rem;
+  font-size: 1.1rem;
+  border: 1px solid #ccc;
+  border-radius: 4px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.loading,
+.error {
+  margin: 1rem 0;
+  font-weight: bold;
+}
+</style>
diff --git a/movie-group-8/src/views/Login.vue b/movie-group-8/src/views/Login.vue
index 103aadffb63ab10d38b6133399466015312d108a..eecffe4823239b2c0feac6450cd4b1938ed3d129 100644
--- a/movie-group-8/src/views/Login.vue
+++ b/movie-group-8/src/views/Login.vue
@@ -25,7 +25,7 @@
   
             <div class="py-3 flex items-center text-xs text-gray-400 uppercase before:flex-1 before:border-t before:border-gray-200 before:me-6 after:flex-1 after:border-t after:border-gray-200 after:ms-6 dark:text-neutral-500 dark:before:border-neutral-600 dark:after:border-neutral-600">Or</div>
   
-            <form @submit.prevent="register">
+            <form @submit.prevent="login">
               <div class="grid gap-y-4">
                 <div>
                   <label class="block text-sm mb-2 dark:text-white">Email Address</label>
@@ -44,7 +44,7 @@
 
                 <button 
                     type="submit" 
-                    @click="register"
+                    @click="login"
                     :disabled="loading"
                     class="w-full py-3 bg-blue-600 text-white rounded-lg flex justify-center items-center gap-2 hover:bg-blue-700 transition duration-300 disabled:opacity-50 disabled:pointer-events-none"
                     >
@@ -73,12 +73,12 @@
   const router = useRouter();
   const loading = ref(false);
   
-  const register = async () => {
+  const login = async () => {
     try {
       loading.value = true;
       const auth = getAuth();
       await signInWithEmailAndPassword(auth, email.value, password.value);
-      console.log('Successfully registered!');
+      console.log('Successfully logged in!');
       router.push('/films');
     } catch (error) {
       console.error(error);
diff --git a/movie-group-8/src/views/Profile.vue b/movie-group-8/src/views/Profile.vue
index 3314b81e266a166a519fa0ee6c5461b23a8be0c0..733b4eefd0e59443c462995e23715c1aa7fa71d1 100644
--- a/movie-group-8/src/views/Profile.vue
+++ b/movie-group-8/src/views/Profile.vue
@@ -1 +1,153 @@
-<template>Profile</template>
\ No newline at end of file
+<template>
+  <div class="min-h-screen bg-gray-100 dark:bg-neutral-900 text-center px-4 py-8">
+    <div class="text-center mb-10">
+      <img
+        v-if="userPhotoURL"
+        :src="userPhotoURL"
+        alt="Profile Picture"
+        class="w-28 h-28 mx-auto rounded-full object-cover mb-4"
+      />
+      <h1 class="text-3xl font-bold text-gray-800 dark:text-white">{{ displayName || 'Anonymous' }}</h1>
+      <p class="text-sm text-gray-600 dark:text-neutral-400">{{ userEmail }}</p>
+      <p class="mt-2 text-base text-gray-600 dark:text-neutral-400">Movie fan and scene collector.</p>
+
+      <button
+        @click="showForm = !showForm"
+        class="mt-4 px-5 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition"
+      >
+        {{ showForm ? 'Close Update Form' : 'Update Profile' }}
+      </button>
+    </div>
+
+    <div v-if="showForm" class="max-w-xl mx-auto mb-12 bg-white dark:bg-neutral-800 p-6 rounded-xl border dark:border-neutral-700">
+      <form @submit.prevent="handleUpdateProfile" class="grid gap-6">
+        <div>
+          <label class="block text-sm mb-2 dark:text-white">Display Name</label>
+          <input type="text" v-model="displayName" class="w-full border border-gray-400 rounded-lg p-2 dark:text-white" required />
+        </div>
+
+        <div>
+          <label class="block text-sm mb-2 dark:text-white">New Password</label>
+          <input type="password" v-model="newPassword" class="w-full border border-gray-400 rounded-lg p-2 dark:text-white" />
+          <p v-if="newPassword && passwordError" class="text-red-500 text-sm mt-1">{{ passwordError }}</p>
+        </div>
+
+        <button
+          type="submit"
+          :disabled="loading || !!passwordError"
+          class="w-full py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition disabled:opacity-50"
+        >
+          <span v-if="!loading">Save Changes</span>
+          <span v-else>Saving...</span>
+        </button>
+      </form>
+    </div>
+
+    <div>
+      <h2 class="text-2xl font-semibold text-gray-800 dark:text-white mb-6">Your Watchlist</h2>
+
+      <div v-if="watchlist.length === 0" class="text-gray-500 dark:text-gray-400 text-sm">
+        Your watchlist is empty.
+      </div>
+
+      <div class="grid gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
+        <div
+          v-for="movie in watchlist"
+          :key="movie.id"
+          class="bg-white dark:bg-neutral-800 border dark:border-neutral-700 rounded-lg p-3 text-left"
+        >
+          <img
+            v-if="movie.poster_path"
+            :src="'https://image.tmdb.org/t/p/w342' + movie.poster_path"
+            alt="Poster"
+            class="w-full h-60 object-cover rounded mb-3"
+          />
+          <p class="font-semibold text-gray-800 dark:text-white">{{ movie.title }}</p>
+          <p class="text-sm text-gray-600 dark:text-neutral-400">⭐ {{ movie.vote_average }} — {{ movie.status }}</p>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, onMounted } from 'vue'
+import {
+  getAuth,
+  updateProfile as firebaseUpdateProfile,
+  updatePassword,
+} from 'firebase/auth'
+import {
+  getFirestore,
+  collection,
+  getDocs
+} from 'firebase/firestore'
+
+const auth = getAuth()
+const db = getFirestore()
+
+const user = auth.currentUser
+
+const loading = ref(false)
+const userEmail = ref('')
+const displayName = ref('')
+const newPassword = ref('')
+const userPhotoURL = ref('')
+const showForm = ref(false)
+
+const watchlist = ref([])
+
+// Load profile info
+onMounted(async () => {
+  if (user) {
+    userEmail.value = user.email
+    displayName.value = user.displayName || ''
+    userPhotoURL.value = user.photoURL || ''
+
+    // Fetch watchlist
+    const snapshot = await getDocs(collection(db, 'users', user.uid, 'watchlist'))
+    watchlist.value = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }))
+  }
+})
+
+// Password validation
+const passwordError = computed(() => {
+  if (!newPassword.value) return ''
+  if (newPassword.value.length < 8) return 'Password must be at least 8 characters.'
+  if (!/[A-Z]/.test(newPassword.value)) return 'Must include at least 1 uppercase letter.'
+  if (!/[0-9]/.test(newPassword.value)) return 'Must include at least 1 number.'
+  if (!/[@$!%*?&]/.test(newPassword.value)) return 'Must include at least 1 special character (@$!%*?&).'
+  return ''
+})
+
+// Update profile function
+const handleUpdateProfile = async () => {
+  if (passwordError.value) return
+
+  loading.value = true
+  try {
+    if (user) {
+      await firebaseUpdateProfile(user, {
+        displayName: displayName.value
+      })
+
+      if (newPassword.value) {
+        await updatePassword(user, newPassword.value)
+      }
+
+      alert('Profile updated successfully!')
+      showForm.value = false
+    }
+  } catch (error) {
+    alert(error.message)
+  } finally {
+    loading.value = false
+  }
+}
+</script>
+
+<style scoped>
+input:disabled {
+  cursor: not-allowed;
+}
+</style>
\ No newline at end of file
diff --git a/movie-group-8/src/views/ReviewPage.vue b/movie-group-8/src/views/ReviewPage.vue
new file mode 100644
index 0000000000000000000000000000000000000000..3f130f965fbd2ad7c5d5d09b54f88f8e6889bcec
--- /dev/null
+++ b/movie-group-8/src/views/ReviewPage.vue
@@ -0,0 +1,230 @@
+<template>
+    <div class="review-page">
+      <!-- Back -->
+      <button class="back-button" @click="$router.back()">← Back</button>
+  
+      <!-- Loading / Error states -->
+      <div v-if="loading" class="loading">Loading movie...</div>
+      <div v-else-if="error" class="error">Error: {{ error }}</div>
+  
+      <!-- Main content -->
+      <div v-else class="content">
+        <!-- Poster & Title -->
+        <div class="header">
+          <img
+            v-if="movie.poster_path"
+            :src="`https://image.tmdb.org/t/p/w300${movie.poster_path}`"
+            :alt="movie.title + ' poster'"
+            class="poster"
+          />
+          <h1 class="title">{{ movie.title }}</h1>
+        </div>
+  
+        <!-- Star Rating -->
+        <div class="rating">
+          <span
+            v-for="star in 5"
+            :key="star"
+            class="star"
+            :class="{ filled: star <= userRating }"
+            @click="setRating(star)"
+          >
+            ★
+          </span>
+          <span class="rating-value">{{ userRating }} / 5</span>
+        </div>
+  
+        <!-- Favourite Toggle -->
+        <button
+          class="favourite-btn"
+          :class="{ fav: isFavourite }"
+          @click="toggleFavourite"
+        >
+          {{ isFavourite ? '★ Favourite' : '☆ Add to Favourites' }}
+        </button>
+  
+        <!-- Review Text -->
+        <textarea
+          v-model="reviewText"
+          placeholder="Write your review…"
+          class="review-text"
+          rows="6"
+        ></textarea>
+  
+        <!-- Submit -->
+        <button class="submit-btn" @click="submitReview">
+          Submit Review
+        </button>
+      </div>
+    </div>
+  </template>
+  
+  <script setup>
+  import { ref, onMounted } from 'vue'
+  import { useRoute, useRouter } from 'vue-router'
+  
+  const route = useRoute()
+  const router = useRouter()
+  
+  // State
+  const movie        = ref(null)
+  const loading      = ref(true)
+  const error        = ref('')
+  const userRating   = ref(0)
+  const isFavourite  = ref(false)
+  const reviewText   = ref('')
+  
+  // Fetch movie details
+  async function fetchMovie() {
+    try {
+      const id  = route.params.id
+      const res = await fetch(
+        `${import.meta.env.VITE_TMDB_BASE}/movie/${id}?api_key=${import.meta.env.VITE_TMDB_API_KEY}`
+      )
+      if (!res.ok) throw new Error('Failed to load movie')
+      movie.value = await res.json()
+    } catch (err) {
+      error.value = err.message
+    } finally {
+      loading.value = false
+    }
+  }
+  
+  // Rating handlers
+  function setRating(star) {
+    userRating.value = star
+  }
+  
+  // Favourite toggle
+  function toggleFavourite() {
+    isFavourite.value = !isFavourite.value
+  }
+  
+  // Submit handler (stub — wire up to Firestore or your backend)
+  function submitReview() {
+    console.log({
+      movieId: route.params.id,
+      rating: userRating.value,
+      favourite: isFavourite.value,
+      review: reviewText.value,
+    })
+    // e.g. use Firestore:
+    // const db = getFirestore(); 
+    // setDoc(doc(db, 'reviews', `${user.uid}_${movieId}`), { ... })
+    router.back()
+  }
+  
+  onMounted(fetchMovie)
+  </script>
+  
+  <style scoped>
+  .review-page {
+    background: #121212;
+    color: #fff;
+    min-height: 100vh;
+    padding: 2rem;
+    box-sizing: border-box;
+  }
+  
+  .back-button {
+    background: transparent;
+    border: none;
+    color: #fff;
+    font-size: 1rem;
+  }
+  
+  .loading,
+  .error {
+    text-align: center;
+    margin: 2rem 0;
+  }
+  
+  .content {
+    max-width: 600px;
+    margin: 2rem auto;
+    background: #1e1e1e;
+    padding: 2rem;
+    border-radius: 8px;
+  }
+  
+  .header {
+    display: flex;
+    align-items: center;
+    gap: 1rem;
+    margin-bottom: 1.5rem;
+  }
+  
+  .poster {
+    width: 100px;
+    border-radius: 4px;
+  }
+  
+  .title {
+    font-size: 1.75rem;
+    margin: 0;
+  }
+  
+  .rating {
+    display: flex;
+    align-items: center;
+    margin-bottom: 1rem;
+  }
+  
+  .star {
+    font-size: 2rem;
+    cursor: pointer;
+    transition: transform 0.1s;
+    margin-right: 0.25rem;
+    color: #555;
+  }
+  .star.filled {
+    color: #f5c518;
+  }
+  .star:hover {
+    transform: scale(1.2);
+  }
+  
+  .rating-value {
+    margin-left: 0.5rem;
+    color: #bbb;
+  }
+  
+  .favourite-btn {
+    background: #333;
+    border: none;
+    color: #bbb;
+    padding: 0.5rem 1rem;
+    border-radius: 4px;
+    cursor: pointer;
+    margin-bottom: 1rem;
+  }
+  .favourite-btn.fav {
+    background: #d32f2f;
+    color: #fff;
+  }
+  
+  .review-text {
+    width: 100%;
+    padding: 0.75rem;
+    border: none;
+    border-radius: 4px;
+    background: #2a2a2a;
+    color: #fff;
+    margin-bottom: 1rem;
+    resize: vertical;
+  }
+  
+  .submit-btn {
+    background: #1976d2;
+    border: none;
+    color: #fff;
+    padding: 0.75rem 1.5rem;
+    border-radius: 4px;
+    cursor: pointer;
+    width: 100%;
+  }
+  .submit-btn:hover {
+    background: #1565c0;
+  }
+  </style>
+  
\ No newline at end of file
diff --git a/movie-group-8/src/views/TopRated.vue b/movie-group-8/src/views/TopRated.vue
new file mode 100644
index 0000000000000000000000000000000000000000..a8e7c29534e976e832e311de90f8230cd613b57a
--- /dev/null
+++ b/movie-group-8/src/views/TopRated.vue
@@ -0,0 +1,44 @@
+<script setup>
+import TopRatedMovies from '@/components/TopRatedMovies.vue'
+
+</script>
+
+<template>
+  <TopRatedMovies />
+  <template>
+  <div class="nyt-header">
+    <h1>The Movies We've Loved Since 2000</h1>
+    <p>
+      Explore top-rated films using the filters below — by genre and year — and rediscover hidden gems.
+    </p>
+  </div>
+
+  <div class="filters">
+    <select v-model="genre">
+      <option value="">Pick a genre ...</option>
+      <option v-for="g in genres" :key="g.id" :value="g.id">{{ g.name }}</option>
+    </select>
+
+    <select v-model="year">
+      <option v-for="y in Array.from({length: 25}, (_, i) => 2000 + i)" :key="y" :value="y">
+        {{ y }}
+      </option>
+    </select>
+  </div>
+
+  <h2 class="section-title">Our favorite movies from {{ year }}</h2>
+
+  <div class="movie-grid">
+    <div v-for="movie in movies" :key="movie.id" class="movie-card">
+      <img
+        v-if="movie.poster_path"
+        :src="'https://image.tmdb.org/t/p/w342' + movie.poster_path"
+        alt="poster"
+      />
+      <p class="title">{{ movie.title }}</p>
+      <p class="rating">⭐ {{ movie.vote_average }}</p>
+    </div>
+  </div>
+</template>
+
+</template>
diff --git a/movie-group-8/src/views/Watchlist.vue b/movie-group-8/src/views/Watchlist.vue
deleted file mode 100644
index 881fa1732ebc44f101dee618914366c413913c24..0000000000000000000000000000000000000000
--- a/movie-group-8/src/views/Watchlist.vue
+++ /dev/null
@@ -1 +0,0 @@
-<template>Watchlist</template>
\ No newline at end of file
diff --git a/movie-group-8/src/views/WatchlistView.vue b/movie-group-8/src/views/WatchlistView.vue
new file mode 100644
index 0000000000000000000000000000000000000000..4ce8909b73b403ffdd4d855defd6b30761e23de9
--- /dev/null
+++ b/movie-group-8/src/views/WatchlistView.vue
@@ -0,0 +1,74 @@
+<template>
+  <div class="min-h-screen bg-gray-100 dark:bg-neutral-900 p-6">
+    <h1 class="text-3xl font-bold mb-6 text-gray-900 dark:text-white text-center">Your Watchlist</h1>
+
+    <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
+      <!-- Planned Movies -->
+      <div>
+        <h2 class="text-2xl font-semibold mb-4 text-gray-800 dark:text-white">📌 Planned to Watch</h2>
+        <div v-if="plannedMovies.length === 0" class="text-gray-500 dark:text-gray-400 text-sm">
+          No movies planned yet.
+        </div>
+        <div class="grid gap-4">
+          <MovieCard
+            v-for="movie in plannedMovies"
+            :key="movie.id"
+            :movie="movie"
+            @status-changed="updateMovieStatus"
+          />
+        </div>
+      </div>
+
+      <!-- Watched Movies -->
+      <div>
+        <h2 class="text-2xl font-semibold mb-4 text-gray-800 dark:text-white">✅ Watched</h2>
+        <div v-if="watchedMovies.length === 0" class="text-gray-500 dark:text-gray-400 text-sm">
+          No watched movies yet.
+        </div>
+        <div class="grid gap-4">
+          <MovieCard
+            v-for="movie in watchedMovies"
+            :key="movie.id"
+            :movie="movie"
+            @status-changed="updateMovieStatus"
+          />
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue'
+import { getAuth } from 'firebase/auth'
+import { getFirestore, collection, getDocs, doc, updateDoc } from 'firebase/firestore'
+import MovieCard from '@/components/MovieCard.vue'
+
+const auth = getAuth()
+const db = getFirestore()
+
+const plannedMovies = ref([])
+const watchedMovies = ref([])
+
+const fetchWatchlist = async () => {
+  const user = auth.currentUser
+  if (!user) return
+
+  const snapshot = await getDocs(collection(db, 'users', user.uid, 'watchlist'))
+  const all = snapshot.docs.map(doc => ({ id: doc.id, ...doc.data() }))
+  plannedMovies.value = all.filter(m => m.status === 'planned')
+  watchedMovies.value = all.filter(m => m.status === 'watched')
+}
+
+const updateMovieStatus = async (movieId, newStatus) => {
+  const user = auth.currentUser
+  if (!user) return
+
+  const movieRef = doc(db, 'users', user.uid, 'watchlist', movieId)
+  await updateDoc(movieRef, { status: newStatus })
+
+  await fetchWatchlist() // Refresh lists
+}
+
+onMounted(fetchWatchlist)
+</script>
diff --git a/movie-group-8/vite.config.js b/movie-group-8/vite.config.js
index daa46667b4af838f364c216285fc6f9beee013be..8bbf4703424d6582b10489e90bdad6599bfa8345 100644
--- a/movie-group-8/vite.config.js
+++ b/movie-group-8/vite.config.js
@@ -1,10 +1,18 @@
 import { defineConfig } from 'vite'
 import vue from '@vitejs/plugin-vue'
 import tailwindcss from '@tailwindcss/vite'
+import path from 'path'
+
 
 // https://vite.dev/config/
 export default defineConfig({
   plugins: [vue(),
     tailwindcss()
   ],
+  resolve: {
+    alias: {
+      '@': path.resolve(__dirname, './src'),
+    },
+  },
+
 })