diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..581839a
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+# MIT License
+
+Copyright (c) 2025 Witchcraft Systems
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
index 6194e14..d9eb2ea 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,62 @@
 # pds-dash
 
-Frontend with stats for your ATProto PDS
\ No newline at end of file
+a frontend dashboard with stats for your ATProto PDS.
+
+## setup
+
+### prerequisites
+
+- [deno](https://deno.com/manual/getting_started/installation)
+
+### installing
+
+clone the repo, install dependencies using deno:
+
+```sh
+deno install
+```
+
+### development server
+
+local develompent server with hot reloading:
+
+```sh
+deno task dev
+```
+
+### building
+
+to build the optimized bundle run:
+
+```sh
+deno task build
+```
+
+the output will be in the `dist/` directory.
+
+## deploying
+
+we use our own CI/CD workflow at [`.forgejo/workflows/deploy.yaml`](.forgejo/workflows/deploy.yaml), but it boils down to building the project bundle and deploying it to a web server. it'll probably make more sense to host it on the same domain as your PDS, but it doesn't affect anything if you host it somewhere else.
+
+## configuring
+
+[`config.ts`](config.ts) is the main configuration file, you can find more information in the file itself.
+
+## theming
+
+the colors are designated in [`src/app.css`](src/app.css) as variables, go crazy with them
+
+the rest is done by editing the css files and style tags directly, good luck
+
+relevant files:
+
+- [`src/App.svelte`](src/App.svelte)
+- [`src/app.css`](src/app.css)
+- [`src/lib/AccountComponent.svelte`](src/lib/AccountComponent.svelte)
+- [`src/lib/PostComponent.svelte`](src/lib/PostComponent.svelte)
+
+the favicon is located at [`public/favicon.ico`](public/favicon.ico)
+
+## license
+
+MIT
diff --git a/config.ts b/config.ts
index fe28a5a..8d09cf6 100644
--- a/config.ts
+++ b/config.ts
@@ -2,27 +2,36 @@
  * Configuration module for the PDS Dashboard
  */
 export class Config {
-    /**
-     * The base URL of the PDS (Personal Data Server)
-     * @default "https://pds.witchcraft.systems"
-     */
-    static readonly PDS_URL: string = "https://pds.witchcraft.systems";
+  /**
+   * The base URL of the PDS (Personal Data Server)
+   * @default "https://pds.witchcraft.systems"
+   */
+  static readonly PDS_URL: string = "https://pds.witchcraft.systems";
 
-    /**
-     * The base URL of the frontend service for linking to replies
-     * @default "https://deer.social"
-     */
-    static readonly FRONTEND_URL: string = "https://deer.social";
+  /**
+   * The base URL of the frontend service for linking to replies/quotes/accounts etc.
+   * @default "https://deer.social"
+   */
+  static readonly FRONTEND_URL: string = "https://deer.social";
 
-    /**
-     * Maximum number of posts to show in the feed (across all users)
-     * @default 100
-     */
-    static readonly MAX_POSTS: number = 100;
+  /**
+   * Maximum number of posts to fetch from the PDS per request
+   * Should be around 20 for about 10 users on the pds
+   * The more users you have, the lower the number should be
+   * since sorting is slow and is done on the frontend
+   * @default 20
+   */
+  static readonly MAX_POSTS: number = 20;
 
-    /**
-     * Footer text for the dashboard
-     * @default "Astrally projected from witchcraft.systems"
-     */
-    static readonly FOOTER_TEXT: string = "Astrally projected from <a href='https://witchcraft.systems' target='_blank'>witchcraft.systems</a>";
-}
\ No newline at end of file
+  /**
+   * Footer text for the dashboard, you probably want to change this
+   */
+  static readonly FOOTER_TEXT: string =
+    "Astrally projected from <a href='https://witchcraft.systems' target='_blank'>witchcraft.systems</a><br><br><a href='https://git.witchcraft.systems/scientific-witchery/pds-dash' target='_blank'>Source</a> (<a href='https://github.com/witchcraft-systems/pds-dash/' target='_blank'>github mirror</a>)";
+
+  /**
+   * Whether to show the posts that are in the future
+   * @default false
+   */
+  static readonly SHOW_FUTURE_POSTS: boolean = false;
+}
diff --git a/deno.lock b/deno.lock
index 0616852..90a8393 100644
--- a/deno.lock
+++ b/deno.lock
@@ -1,5 +1,5 @@
 {
-  "version": "4",
+  "version": "5",
   "specifiers": {
     "npm:@atcute/bluesky@^2.0.2": "2.0.2_@atcute+client@3.0.1",
     "npm:@atcute/client@^3.0.1": "3.0.1",
@@ -7,7 +7,9 @@
     "npm:@sveltejs/vite-plugin-svelte@^5.0.3": "5.0.3_svelte@5.28.1__acorn@8.14.1_vite@6.3.2__picomatch@4.0.2",
     "npm:@tsconfig/svelte@^5.0.4": "5.0.4",
     "npm:moment@^2.30.1": "2.30.1",
+    "npm:mutex-ts@^1.2.1": "1.2.1",
     "npm:svelte-check@^4.1.5": "4.1.6_svelte@5.28.1__acorn@8.14.1_typescript@5.7.3",
+    "npm:svelte-infinite-loading@^1.4.0": "1.4.0",
     "npm:svelte@^5.23.1": "5.28.1_acorn@8.14.1",
     "npm:typescript@~5.7.2": "5.7.3",
     "npm:vite@^6.3.1": "6.3.2_picomatch@4.0.2"
@@ -53,79 +55,129 @@
       "integrity": "sha512-GEhUCk9c4XbNxi+0YZHZsV4fYNd6HejfWuN4Ti4c02DauX+LyX5WY1Y3WfyZ8Pxxl0zqhs+MLtW98cMh86vv6g=="
     },
     "@esbuild/aix-ppc64@0.25.2": {
-      "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag=="
+      "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==",
+      "os": ["aix"],
+      "cpu": ["ppc64"]
     },
     "@esbuild/android-arm64@0.25.2": {
-      "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w=="
+      "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==",
+      "os": ["android"],
+      "cpu": ["arm64"]
     },
     "@esbuild/android-arm@0.25.2": {
-      "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA=="
+      "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==",
+      "os": ["android"],
+      "cpu": ["arm"]
     },
     "@esbuild/android-x64@0.25.2": {
-      "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg=="
+      "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==",
+      "os": ["android"],
+      "cpu": ["x64"]
     },
     "@esbuild/darwin-arm64@0.25.2": {
-      "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA=="
+      "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==",
+      "os": ["darwin"],
+      "cpu": ["arm64"]
     },
     "@esbuild/darwin-x64@0.25.2": {
-      "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA=="
+      "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==",
+      "os": ["darwin"],
+      "cpu": ["x64"]
     },
     "@esbuild/freebsd-arm64@0.25.2": {
-      "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w=="
+      "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==",
+      "os": ["freebsd"],
+      "cpu": ["arm64"]
     },
     "@esbuild/freebsd-x64@0.25.2": {
-      "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ=="
+      "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==",
+      "os": ["freebsd"],
+      "cpu": ["x64"]
     },
     "@esbuild/linux-arm64@0.25.2": {
-      "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g=="
+      "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==",
+      "os": ["linux"],
+      "cpu": ["arm64"]
     },
     "@esbuild/linux-arm@0.25.2": {
-      "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g=="
+      "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==",
+      "os": ["linux"],
+      "cpu": ["arm"]
     },
     "@esbuild/linux-ia32@0.25.2": {
-      "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ=="
+      "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==",
+      "os": ["linux"],
+      "cpu": ["ia32"]
     },
     "@esbuild/linux-loong64@0.25.2": {
-      "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w=="
+      "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==",
+      "os": ["linux"],
+      "cpu": ["loong64"]
     },
     "@esbuild/linux-mips64el@0.25.2": {
-      "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q=="
+      "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==",
+      "os": ["linux"],
+      "cpu": ["mips64el"]
     },
     "@esbuild/linux-ppc64@0.25.2": {
-      "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g=="
+      "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==",
+      "os": ["linux"],
+      "cpu": ["ppc64"]
     },
     "@esbuild/linux-riscv64@0.25.2": {
-      "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw=="
+      "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==",
+      "os": ["linux"],
+      "cpu": ["riscv64"]
     },
     "@esbuild/linux-s390x@0.25.2": {
-      "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q=="
+      "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==",
+      "os": ["linux"],
+      "cpu": ["s390x"]
     },
     "@esbuild/linux-x64@0.25.2": {
-      "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg=="
+      "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==",
+      "os": ["linux"],
+      "cpu": ["x64"]
     },
     "@esbuild/netbsd-arm64@0.25.2": {
-      "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw=="
+      "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==",
+      "os": ["netbsd"],
+      "cpu": ["arm64"]
     },
     "@esbuild/netbsd-x64@0.25.2": {
-      "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg=="
+      "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==",
+      "os": ["netbsd"],
+      "cpu": ["x64"]
     },
     "@esbuild/openbsd-arm64@0.25.2": {
-      "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg=="
+      "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==",
+      "os": ["openbsd"],
+      "cpu": ["arm64"]
     },
     "@esbuild/openbsd-x64@0.25.2": {
-      "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw=="
+      "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==",
+      "os": ["openbsd"],
+      "cpu": ["x64"]
     },
     "@esbuild/sunos-x64@0.25.2": {
-      "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA=="
+      "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==",
+      "os": ["sunos"],
+      "cpu": ["x64"]
     },
     "@esbuild/win32-arm64@0.25.2": {
-      "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q=="
+      "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==",
+      "os": ["win32"],
+      "cpu": ["arm64"]
     },
     "@esbuild/win32-ia32@0.25.2": {
-      "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg=="
+      "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==",
+      "os": ["win32"],
+      "cpu": ["ia32"]
     },
     "@esbuild/win32-x64@0.25.2": {
-      "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA=="
+      "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==",
+      "os": ["win32"],
+      "cpu": ["x64"]
     },
     "@jridgewell/gen-mapping@0.3.8": {
       "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
@@ -152,64 +204,104 @@
       ]
     },
     "@rollup/rollup-android-arm-eabi@4.40.0": {
-      "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg=="
+      "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==",
+      "os": ["android"],
+      "cpu": ["arm"]
     },
     "@rollup/rollup-android-arm64@4.40.0": {
-      "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w=="
+      "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==",
+      "os": ["android"],
+      "cpu": ["arm64"]
     },
     "@rollup/rollup-darwin-arm64@4.40.0": {
-      "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ=="
+      "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==",
+      "os": ["darwin"],
+      "cpu": ["arm64"]
     },
     "@rollup/rollup-darwin-x64@4.40.0": {
-      "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA=="
+      "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==",
+      "os": ["darwin"],
+      "cpu": ["x64"]
     },
     "@rollup/rollup-freebsd-arm64@4.40.0": {
-      "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg=="
+      "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==",
+      "os": ["freebsd"],
+      "cpu": ["arm64"]
     },
     "@rollup/rollup-freebsd-x64@4.40.0": {
-      "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw=="
+      "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==",
+      "os": ["freebsd"],
+      "cpu": ["x64"]
     },
     "@rollup/rollup-linux-arm-gnueabihf@4.40.0": {
-      "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA=="
+      "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==",
+      "os": ["linux"],
+      "cpu": ["arm"]
     },
     "@rollup/rollup-linux-arm-musleabihf@4.40.0": {
-      "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg=="
+      "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==",
+      "os": ["linux"],
+      "cpu": ["arm"]
     },
     "@rollup/rollup-linux-arm64-gnu@4.40.0": {
-      "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg=="
+      "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==",
+      "os": ["linux"],
+      "cpu": ["arm64"]
     },
     "@rollup/rollup-linux-arm64-musl@4.40.0": {
-      "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ=="
+      "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==",
+      "os": ["linux"],
+      "cpu": ["arm64"]
     },
     "@rollup/rollup-linux-loongarch64-gnu@4.40.0": {
-      "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg=="
+      "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==",
+      "os": ["linux"],
+      "cpu": ["loong64"]
     },
     "@rollup/rollup-linux-powerpc64le-gnu@4.40.0": {
-      "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw=="
+      "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==",
+      "os": ["linux"],
+      "cpu": ["ppc64"]
     },
     "@rollup/rollup-linux-riscv64-gnu@4.40.0": {
-      "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA=="
+      "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==",
+      "os": ["linux"],
+      "cpu": ["riscv64"]
     },
     "@rollup/rollup-linux-riscv64-musl@4.40.0": {
-      "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ=="
+      "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==",
+      "os": ["linux"],
+      "cpu": ["riscv64"]
     },
     "@rollup/rollup-linux-s390x-gnu@4.40.0": {
-      "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw=="
+      "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==",
+      "os": ["linux"],
+      "cpu": ["s390x"]
     },
     "@rollup/rollup-linux-x64-gnu@4.40.0": {
-      "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ=="
+      "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==",
+      "os": ["linux"],
+      "cpu": ["x64"]
     },
     "@rollup/rollup-linux-x64-musl@4.40.0": {
-      "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw=="
+      "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==",
+      "os": ["linux"],
+      "cpu": ["x64"]
     },
     "@rollup/rollup-win32-arm64-msvc@4.40.0": {
-      "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ=="
+      "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==",
+      "os": ["win32"],
+      "cpu": ["arm64"]
     },
     "@rollup/rollup-win32-ia32-msvc@4.40.0": {
-      "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA=="
+      "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==",
+      "os": ["win32"],
+      "cpu": ["ia32"]
     },
     "@rollup/rollup-win32-x64-msvc@4.40.0": {
-      "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ=="
+      "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==",
+      "os": ["win32"],
+      "cpu": ["x64"]
     },
     "@sveltejs/acorn-typescript@1.0.5_acorn@8.14.1": {
       "integrity": "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==",
@@ -246,7 +338,8 @@
       "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="
     },
     "acorn@8.14.1": {
-      "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg=="
+      "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
+      "bin": true
     },
     "aria-query@5.3.2": {
       "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="
@@ -274,7 +367,7 @@
     },
     "esbuild@0.25.2": {
       "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==",
-      "dependencies": [
+      "optionalDependencies": [
         "@esbuild/aix-ppc64",
         "@esbuild/android-arm",
         "@esbuild/android-arm64",
@@ -300,7 +393,9 @@
         "@esbuild/win32-arm64",
         "@esbuild/win32-ia32",
         "@esbuild/win32-x64"
-      ]
+      ],
+      "scripts": true,
+      "bin": true
     },
     "esm-env@1.2.2": {
       "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="
@@ -315,10 +410,15 @@
       "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
       "dependencies": [
         "picomatch"
+      ],
+      "optionalPeers": [
+        "picomatch"
       ]
     },
     "fsevents@2.3.3": {
-      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "os": ["darwin"],
+      "scripts": true
     },
     "is-reference@3.0.3": {
       "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==",
@@ -347,8 +447,12 @@
     "ms@2.1.3": {
       "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
     },
+    "mutex-ts@1.2.1": {
+      "integrity": "sha512-OkcXgf0viuCgYdnm48kiNQ9PzC5OzISQ261svHr/Ybc2vBYC/5xfLXn44hQ+dYRX74v7MCSqV/LKPEbpYdDybw=="
+    },
     "nanoid@3.3.11": {
-      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="
+      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+      "bin": true
     },
     "picocolors@1.1.1": {
       "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="
@@ -370,6 +474,9 @@
     "rollup@4.40.0": {
       "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==",
       "dependencies": [
+        "@types/estree"
+      ],
+      "optionalDependencies": [
         "@rollup/rollup-android-arm-eabi",
         "@rollup/rollup-android-arm64",
         "@rollup/rollup-darwin-arm64",
@@ -390,9 +497,9 @@
         "@rollup/rollup-win32-arm64-msvc",
         "@rollup/rollup-win32-ia32-msvc",
         "@rollup/rollup-win32-x64-msvc",
-        "@types/estree",
         "fsevents"
-      ]
+      ],
+      "bin": true
     },
     "sade@1.8.1": {
       "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==",
@@ -413,7 +520,11 @@
         "sade",
         "svelte",
         "typescript"
-      ]
+      ],
+      "bin": true
+    },
+    "svelte-infinite-loading@1.4.0": {
+      "integrity": "sha512-Jo+f/yr/HmZQuIiiKKzAHVFXdAUWHW2RBbrcQTil8JVk1sCm/riy7KTJVzjBgQvHasrFQYKF84zvtc9/Y4lFYg=="
     },
     "svelte@5.28.1_acorn@8.14.1": {
       "integrity": "sha512-iOa9WmfNG95lSOSJdMhdjJ4Afok7IRAQYXpbnxhd5EINnXseG0GVa9j6WPght4eX78XfFez45Fi+uRglGKPV/Q==",
@@ -442,24 +553,31 @@
       ]
     },
     "typescript@5.7.3": {
-      "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="
+      "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
+      "bin": true
     },
     "vite@6.3.2_picomatch@4.0.2": {
       "integrity": "sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==",
       "dependencies": [
         "esbuild",
         "fdir",
-        "fsevents",
         "picomatch",
         "postcss",
         "rollup",
         "tinyglobby"
-      ]
+      ],
+      "optionalDependencies": [
+        "fsevents"
+      ],
+      "bin": true
     },
     "vitefu@1.0.6_vite@6.3.2__picomatch@4.0.2": {
       "integrity": "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA==",
       "dependencies": [
         "vite"
+      ],
+      "optionalPeers": [
+        "vite"
       ]
     },
     "zimmerframe@1.1.2": {
@@ -475,7 +593,9 @@
         "npm:@sveltejs/vite-plugin-svelte@^5.0.3",
         "npm:@tsconfig/svelte@^5.0.4",
         "npm:moment@^2.30.1",
+        "npm:mutex-ts@^1.2.1",
         "npm:svelte-check@^4.1.5",
+        "npm:svelte-infinite-loading@^1.4.0",
         "npm:svelte@^5.23.1",
         "npm:typescript@~5.7.2",
         "npm:vite@^6.3.1"
diff --git a/index.html b/index.html
index f71d006..adcfab3 100644
--- a/index.html
+++ b/index.html
@@ -1,4 +1,4 @@
-<!doctype html>
+<!DOCTYPE html>
 <html lang="en">
   <head>
     <meta charset="UTF-8" />
diff --git a/package.json b/package.json
index 9f84465..3b293c0 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,9 @@
     "@atcute/bluesky": "^2.0.2",
     "@atcute/client": "^3.0.1",
     "@atcute/identity-resolver": "^0.1.2",
-    "moment": "^2.30.1"
+    "moment": "^2.30.1",
+    "mutex-ts": "^1.2.1",
+    "svelte-infinite-loading": "^1.4.0"
   },
   "devDependencies": {
     "@sveltejs/vite-plugin-svelte": "^5.0.3",
diff --git a/src/App.svelte b/src/App.svelte
index 95912e2..c6e7534 100644
--- a/src/App.svelte
+++ b/src/App.svelte
@@ -1,47 +1,69 @@
 <script lang="ts">
   import PostComponent from "./lib/PostComponent.svelte";
   import AccountComponent from "./lib/AccountComponent.svelte";
-  import { fetchAllPosts, Post, getAllMetadataFromPds } from "./lib/pdsfetch";
+  import InfiniteLoading from "svelte-infinite-loading";
+  import { getNextPosts, Post, getAllMetadataFromPds } from "./lib/pdsfetch";
   import { Config } from "../config";
-  const postsPromise = fetchAllPosts();
   const accountsPromise = getAllMetadataFromPds();
+  import { onMount } from "svelte";
+
+  let posts: Post[] = [];
+
+  onMount(() => {
+    // Fetch initial posts
+    getNextPosts().then((initialPosts) => {
+      posts = initialPosts;
+    });
+  });
+  // Infinite loading function
+  const onInfinite = ({
+    detail: { loaded, complete },
+  }: {
+    detail: { loaded: () => void; complete: () => void };
+  }) => {
+    getNextPosts().then((newPosts) => {
+      console.log("Loading next posts...");
+      if (newPosts.length > 0) {
+        posts = [...posts, ...newPosts];
+        loaded();
+      } else {
+        complete();
+      }
+    });
+  };
 </script>
 
 <main>
   <div id="Content">
-  {#await accountsPromise}
-    <p>Loading...</p>
-  {:then accountsData}
-    <div id="Account">
-      <h1 id="Header">ATProto PDS</h1>
-      <p>Home to {accountsData.length} accounts</p>
-      <div id="accountsList">
-      {#each accountsData as accountObject}
-        <AccountComponent account={accountObject} />
-      {/each}
+    {#await accountsPromise}
+      <p>Loading...</p>
+    {:then accountsData}
+      <div id="Account">
+        <h1 id="Header">ATProto PDS</h1>
+        <p>Home to {accountsData.length} accounts</p>
+        <div id="accountsList">
+          {#each accountsData as accountObject}
+            <AccountComponent account={accountObject} />
+          {/each}
+        </div>
+        <p>{@html Config.FOOTER_TEXT}</p>
       </div>
-      <p>{@html Config.FOOTER_TEXT}</p>
-    </div>
-  {:catch error}
-    <p>Error: {error.message}</p>    
-  {/await}
+    {:catch error}
+      <p>Error: {error.message}</p>
+    {/await}
 
-  {#await postsPromise}
-    <p>Loading...</p>
-  {:then postsData}
     <div id="Feed">
       <div id="spacer"></div>
-      {#each postsData as postObject}
+      {#each posts as postObject}
         <PostComponent post={postObject as Post} />
       {/each}
+        <InfiniteLoading on:infinite={onInfinite} distance={3000} />
       <div id="spacer"></div>
     </div>
-  {/await}
   </div>
 </main>
 
 <style>
-
   /* desktop style */
 
   #Content {
@@ -56,9 +78,9 @@
     color: var(--text-color);
   }
   #Feed {
+    overflow-y: scroll;
     width: 65%;
     height: 100vh;
-    overflow-y: scroll;
     padding: 20px;
     padding-bottom: 0;
     padding-top: 0;
@@ -74,7 +96,7 @@
   #Account {
     width: 35%;
     display: flex;
-    flex-direction: column; 
+    flex-direction: column;
     border: 1px solid var(--border-color);
     background-color: var(--content-background-color);
     height: 80vh;
@@ -107,7 +129,7 @@
       margin-top: 5%;
     }
     #Account {
-      width: auto;
+      width: 85%;
       padding-left: 5%;
       padding-right: 5%;
       margin-bottom: 20px;
@@ -121,7 +143,7 @@
       margin-left: 10%;
       margin-right: 10%;
       padding: 0px;
-      height: auto;
+      overflow-y: visible;
     }
 
     #spacer {
diff --git a/src/app.css b/src/app.css
index b89ecff..50da734 100644
--- a/src/app.css
+++ b/src/app.css
@@ -1,5 +1,5 @@
 @font-face {
-  font-family: 'ProggyClean';
+  font-family: "ProggyClean";
   src: url(https://witchcraft.systems/ProggyCleanNerdFont-Regular.ttf);
 }
 
@@ -62,10 +62,14 @@ body {
   min-width: 320px;
   min-height: 100vh;
   background-color: var(--background-color);
-  font-family: 'ProggyClean', monospace;
+  font-family: "ProggyClean", monospace;
   font-size: 24px;
   color: var(--text-color);
   border-color: var(--border-color);
+  overflow-wrap: break-word;
+  word-wrap: normal;
+  word-break: break-word;
+  hyphens: none;
 }
 
 h1 {
@@ -75,9 +79,10 @@ h1 {
 
 #app {
   max-width: 1400px;
+  width: 100%;
   margin: 0;
   padding: 0;
   margin-left: auto;
   margin-right: auto;
   text-align: center;
-}
\ No newline at end of file
+}
diff --git a/src/lib/AccountComponent.svelte b/src/lib/AccountComponent.svelte
index e324393..880db3f 100644
--- a/src/lib/AccountComponent.svelte
+++ b/src/lib/AccountComponent.svelte
@@ -32,17 +32,18 @@
   #accountName {
     margin-left: 10px;
     font-size: 0.9em;
+    max-width: 80%;
 
     /* replace overflow with ellipsis */
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
-    max-width: 80%;
   }
   #avatar {
     width: 50px;
     height: 50px;
     margin: 0px;
+    object-fit: cover;
     border-right: var(--border-color) 1px solid;
   }
 </style>
diff --git a/src/lib/PostComponent.svelte b/src/lib/PostComponent.svelte
index 69ea88b..43ad667 100644
--- a/src/lib/PostComponent.svelte
+++ b/src/lib/PostComponent.svelte
@@ -113,7 +113,7 @@
           <div id="carouselControls">
             <button
               id="prevBtn"
-              on:click={prevImage}
+              onclick={prevImage}
               disabled={currentImageIndex === 0}>←</button
             >
             <div id="carouselIndicators">
@@ -125,7 +125,7 @@
             </div>
             <button
               id="nextBtn"
-              on:click={nextImage}
+              onclick={nextImage}
               disabled={currentImageIndex === post.imagesCid.length - 1}
               >→</button
             >
@@ -141,11 +141,17 @@
         controls
       ></video>
     {/if}
+    {#if post.gifLink}
+      <img
+        id="embedVideo"
+        src="{post.gifLink}"
+        alt="Post GIF"
+      />
+    {/if}
   </div>
 </div>
 
 <style>
-
   a:hover {
     text-decoration: underline;
   }
@@ -171,12 +177,21 @@
     height: 60px;
   }
   #displayName {
+    display: block;
     color: var(--text-color);
     font-size: 1.2em;
     padding: 0;
     margin: 0;
+    overflow-wrap:normal;
+    word-wrap: break-word;
+    word-break: break-word;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+    width: 100%;
   }
   #handle {
+    display: block;
     color: var(--border-color);
     font-size: 0.8em;
     padding: 0;
@@ -197,6 +212,7 @@
     background-color: var(--content-background-color);
     color: var(--text-color);
     overflow-wrap: break-word;
+    white-space: pre-line;
   }
   #replyingText {
     font-size: 0.7em;
@@ -213,18 +229,30 @@
   #postText {
     margin: 0;
     padding: 0;
+    overflow-wrap: break-word;
+    word-wrap: normal;
+    word-break: break-word;
+    hyphens: none;
   }
   #headerText {
     margin-left: 10px;
     font-size: 0.9em;
     text-align: start;
-    overflow-wrap: break-word;
+    word-break: break-word;
+    max-width: 80%;
+    max-height: 95%;
     overflow: hidden;
+    align-self: flex-start;
+    margin-top: auto;
+    margin-bottom: auto;
   }
   #avatar {
-    height: 100%;
+    height: 60px;
+    width: 60px;
     margin: 0px;
     margin-left: 0px;
+    overflow: hidden;
+    object-fit: cover;
     border-right: var(--border-color) 1px solid;
   }
   #carouselContainer {
diff --git a/src/lib/pdsfetch.ts b/src/lib/pdsfetch.ts
index aa3fc85..20120fb 100644
--- a/src/lib/pdsfetch.ts
+++ b/src/lib/pdsfetch.ts
@@ -13,16 +13,21 @@ import {
   WebDidDocumentResolver,
 } from "@atcute/identity-resolver";
 import { Config } from "../../config";
+import { Mutex } from "mutex-ts"
 // import { ComAtprotoRepoListRecords.Record } from "@atcute/client/lexicons";
 // import { AppBskyFeedPost } from "@atcute/client/lexicons";
 // import { AppBskyActorDefs } from "@atcute/client/lexicons";
 
 interface AccountMetadata {
-  did: string;
+  did: At.Did;
   displayName: string;
   handle: string;
   avatarCid: string | null;
+  currentCursor?: string;
 }
+
+let accountsMetadata: AccountMetadata[] = [];
+
 interface atUriObject {
   repo: string;
   collection: string;
@@ -42,6 +47,7 @@ class Post {
   replyingUri: atUriObject | null;
   imagesCid: string[] | null;
   videosLinkCid: string | null;
+  gifLink: string | null;
 
   constructor(
     record: ComAtprotoRepoListRecords.Record,
@@ -65,10 +71,11 @@ class Post {
     this.quotingUri = null;
     this.imagesCid = null;
     this.videosLinkCid = null;
+    this.gifLink = null;
     switch (post.embed?.$type) {
       case "app.bsky.embed.images":
-        this.imagesCid = post.embed.images.map((imageRecord: any) =>
-          imageRecord.image.ref.$link
+        this.imagesCid = post.embed.images.map(
+          (imageRecord: any) => imageRecord.image.ref.$link,
         );
         break;
       case "app.bsky.embed.video":
@@ -81,8 +88,8 @@ class Post {
         this.quotingUri = processAtUri(post.embed.record.record.uri);
         switch (post.embed.media.$type) {
           case "app.bsky.embed.images":
-            this.imagesCid = post.embed.media.images.map((imageRecord) =>
-              imageRecord.image.ref.$link
+            this.imagesCid = post.embed.media.images.map(
+              (imageRecord) => imageRecord.image.ref.$link,
             );
 
             break;
@@ -92,6 +99,11 @@ class Post {
             break;
         }
         break;
+      case "app.bsky.embed.external": // assuming that external embeds are gifs for now
+        if (post.embed.external.uri.includes(".gif")) {
+          this.gifLink = post.embed.external.uri;
+        }
+        break;
     }
   }
 }
@@ -111,78 +123,50 @@ const rpc = new XRPC({
   }),
 });
 
-const getDidsFromPDS = async () : Promise<At.Did[]> => {
+const getDidsFromPDS = async (): Promise<At.Did[]> => {
   const { data } = await rpc.get("com.atproto.sync.listRepos", {
     params: {},
   });
-  return data.repos.map((repo: any) => (repo.did)) as At.Did[];
+  return data.repos.map((repo: any) => repo.did) as At.Did[];
 };
-const getAccountMetadata = async (did: `did:${string}:${string}`) : Promise<AccountMetadata> => {
+const getAccountMetadata = async (
+  did: `did:${string}:${string}`,
+) => {
   // gonna assume self exists in the app.bsky.actor.profile
   try {
-  const { data } = await rpc.get("com.atproto.repo.getRecord", {
-    params: {
-      repo: did,
-      collection: "app.bsky.actor.profile",
-      rkey: "self",
-    },
-  });
-  const value = data.value as AppBskyActorProfile.Record;
-  const handle = await blueskyHandleFromDid(did);
-  const account: AccountMetadata = {
-    did: did,
-    handle: handle,
-    displayName: value.displayName || "",
-    avatarCid: null,
-  };
-  if (value.avatar) {
-    account.avatarCid = value.avatar.ref["$link"];
-  }
-  return account;
-  }
-  catch (e) {
-    console.error(`Error fetching metadata for ${did}:`, e);
-    return {
-      did: "error",
-      displayName: "",
+    const { data } = await rpc.get("com.atproto.repo.getRecord", {
+      params: {
+        repo: did,
+        collection: "app.bsky.actor.profile",
+        rkey: "self",
+      },
+    });
+    const value = data.value as AppBskyActorProfile.Record;
+    const handle = await blueskyHandleFromDid(did);
+    const account: AccountMetadata = {
+      did: did,
+      handle: handle,
+      displayName: value.displayName || "",
       avatarCid: null,
-      handle: "error",
     };
+    if (value.avatar) {
+      account.avatarCid = value.avatar.ref["$link"];
+    }
+    return account;
+  } catch (e) {
+    console.error(`Error fetching metadata for ${did}:`, e);
+    return null;
   }
 };
 
-const getAllMetadataFromPds = async () : Promise<AccountMetadata[]> => {
+const getAllMetadataFromPds = async (): Promise<AccountMetadata[]> => {
   const dids = await getDidsFromPDS();
   const metadata = await Promise.all(
     dids.map(async (repo: `did:${string}:${string}`) => {
       return await getAccountMetadata(repo);
     }),
   );
-  return metadata.filter(account => account.did !== "error");
-};
-
-const fetchPosts = async (did: string) => {
-  try {
-    const { data } = await rpc.get("com.atproto.repo.listRecords", {
-      params: {
-        repo: did as At.Identifier,
-        collection: "app.bsky.feed.post",
-        limit: Config.MAX_POSTS,
-      },
-    });
-    return {
-      records: data.records as ComAtprotoRepoListRecords.Record[],
-      did: did,
-      error: false
-    };
-  } catch (e) {
-    console.error(`Error fetching posts for ${did}:`, e);
-    return {
-      records: [],
-      did: did,
-      error: true
-    };
-  }
+  return metadata.filter((account) => account !== null) as AccountMetadata[];
 };
 
 const identityResolve = async (did: At.Did) => {
@@ -218,27 +202,151 @@ const blueskyHandleFromDid = async (did: At.Did) => {
   }
 };
 
-const fetchAllPosts = async () => {
-  const users: AccountMetadata[] = await getAllMetadataFromPds();
-  const postRecords = await Promise.all(
-    users.map(async (metadata: AccountMetadata) =>
-      await fetchPosts(metadata.did)
-    ),
-  );
-  const validPostRecords = postRecords.filter(record => !record.error);
-  const posts: Post[] = validPostRecords.flatMap((userFetch) =>
-    userFetch.records.map((record) => {
-      const user = users.find((user: AccountMetadata) =>
-        user.did == userFetch.did
-      );
-      if (!user) {
-        throw new Error(`User with DID ${userFetch.did} not found`);
+interface PostsAcc {
+  posts: ComAtprotoRepoListRecords.Record[];
+  account: AccountMetadata;
+}
+const getCutoffDate = (postAccounts: PostsAcc[]) => {
+  const now = Date.now();
+  let cutoffDate: Date | null = null;
+  postAccounts.forEach((postAcc) => {
+    const latestPost = new Date(
+      (postAcc.posts[postAcc.posts.length - 1].value as AppBskyFeedPost.Record)
+        .createdAt,
+    );
+    if (!cutoffDate) {
+      cutoffDate = latestPost;
+    } else {
+      if (latestPost > cutoffDate) {
+        cutoffDate = latestPost;
       }
-      return new Post(record, user);
-    })
-  );
-  posts.sort((a, b) => b.timestamp - a.timestamp);
-  return posts.slice(0, Config.MAX_POSTS);
+    }
+  });
+  if (cutoffDate) {
+    return cutoffDate;
+  } else {
+    return new Date(now);
+  }
 };
-export { fetchAllPosts, getAllMetadataFromPds, Post };
+
+const filterPostsByDate = (posts: PostsAcc[], cutoffDate: Date) => {
+  // filter posts for each account that are older than the cutoff date and save the cursor of the last post included
+  const filteredPosts: PostsAcc[] = posts.map((postAcc) => {
+    const filtered = postAcc.posts.filter((post) => {
+      const postDate = new Date(
+        (post.value as AppBskyFeedPost.Record).createdAt,
+      );
+      return postDate >= cutoffDate;
+    });
+    if (filtered.length > 0) {
+      postAcc.account.currentCursor = processAtUri(filtered[filtered.length - 1].uri).rkey;
+    }
+    return {
+      posts: filtered,
+      account: postAcc.account,
+    };
+  });
+  return filteredPosts;
+};
+
+const postsMutex = new Mutex();
+// nightmare function. However it works so I am not touching it
+const getNextPosts = async () => {
+  const release = await postsMutex.obtain();
+  if (!accountsMetadata.length) {
+    accountsMetadata = await getAllMetadataFromPds();
+  }
+
+  const postsAcc: PostsAcc[] = await Promise.all(
+    accountsMetadata.map(async (account) => {
+      const posts = await fetchPostsForUser(
+        account.did,
+        account.currentCursor || null,
+      );
+      if (posts) {
+        return {
+          posts: posts,
+          account: account,
+        };
+      } else {
+        return {
+          posts: [],
+          account: account,
+        };
+      }
+    }),
+  );
+  const recordsFiltered = postsAcc.filter((postAcc) =>
+    postAcc.posts.length > 0
+  );
+  const cutoffDate = getCutoffDate(recordsFiltered);
+  const recordsCutoff = filterPostsByDate(recordsFiltered, cutoffDate);
+  // update the accountMetadata with the new cursor
+  accountsMetadata = accountsMetadata.map((account) => {
+    const postAcc = recordsCutoff.find(
+      (postAcc) => postAcc.account.did == account.did,
+    );
+    if (postAcc) {
+      account.currentCursor = postAcc.account.currentCursor;
+    }
+    return account;
+  }
+  );
+  // throw the records in a big single array
+  let records = recordsCutoff.flatMap((postAcc) => postAcc.posts);
+  // sort the records by timestamp
+  records = records.sort((a, b) => {
+    const aDate = new Date(
+      (a.value as AppBskyFeedPost.Record).createdAt,
+    ).getTime();
+    const bDate = new Date(
+      (b.value as AppBskyFeedPost.Record).createdAt,
+    ).getTime();
+    return bDate - aDate;
+  });
+  // filter out posts that are in the future
+  if (!Config.SHOW_FUTURE_POSTS) {
+    const now = Date.now();
+    records = records.filter((post) => {
+      const postDate = new Date(
+        (post.value as AppBskyFeedPost.Record).createdAt,
+      ).getTime();
+      return postDate <= now;
+    });
+  }
+
+  const newPosts = records.map((record) => {
+    const account = accountsMetadata.find(
+      (account) => account.did == processAtUri(record.uri).repo,
+    );
+    if (!account) {
+      throw new Error(
+        `Account with DID ${processAtUri(record.uri).repo} not found`,
+      );
+    }
+    return new Post(record, account);
+  });
+  // release the mutex
+  release();
+  return newPosts;
+};
+
+const fetchPostsForUser = async (did: At.Did, cursor: string | null) => {
+  try {
+    const { data } = await rpc.get("com.atproto.repo.listRecords", {
+      params: {
+        repo: did as At.Identifier,
+        collection: "app.bsky.feed.post",
+        limit: Config.MAX_POSTS,
+        cursor: cursor || undefined,
+      },
+    });
+    return data.records as ComAtprotoRepoListRecords.Record[];
+  } catch (e) {
+    console.error(`Error fetching posts for ${did}:`, e);
+    return null;
+  }
+};
+
+export { getAllMetadataFromPds, getNextPosts, Post };
 export type { AccountMetadata };
diff --git a/src/main.ts b/src/main.ts
index 664a057..d47b930 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,9 +1,9 @@
-import { mount } from 'svelte'
-import './app.css'
-import App from './App.svelte'
+import { mount } from "svelte";
+import "./app.css";
+import App from "./App.svelte";
 
 const app = mount(App, {
-  target: document.getElementById('app')!,
-})
+  target: document.getElementById("app")!,
+});
 
-export default app
+export default app;
diff --git a/svelte.config.js b/svelte.config.js
index b0683fd..de2ddd6 100644
--- a/svelte.config.js
+++ b/svelte.config.js
@@ -1,7 +1,7 @@
-import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
+import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
 
 export default {
   // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
   // for more information about preprocessors
   preprocess: vitePreprocess(),
-}
+};
diff --git a/vite.config.ts b/vite.config.ts
index d32eba1..20d2272 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,7 +1,7 @@
-import { defineConfig } from 'vite'
-import { svelte } from '@sveltejs/vite-plugin-svelte'
+import { defineConfig } from "vite";
+import { svelte } from "@sveltejs/vite-plugin-svelte";
 
 // https://vite.dev/config/
 export default defineConfig({
   plugins: [svelte()],
-})
+});