From 7b7fc528f14588b2a7b9dffd0ef75c1c466accd6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Vivian=20Lim=20=E2=AD=90?=
 <1565930+vivlim@users.noreply.github.com>
Date: Mon, 27 Jan 2025 05:24:31 -0800
Subject: [PATCH] [feature/frontend] Add login button to index page which
 reiterates info about clients (#3377)

* Add login button to index page which reiterates info about clients

* bit of CSS fiddling, move apps from front page to login info

* fix indentation

---------

Co-authored-by: tobi <tobi.smethurst@protonmail.com>
---
 internal/web/index.go          |   9 +-
 internal/web/login-info.go     |  60 +++++++++++++
 internal/web/web.go            |  18 ++--
 web/assets/ellipsis.svg        |   5 ++
 web/source/css/index.css       |  50 -----------
 web/source/css/login-info.css  | 114 ++++++++++++++++++++++++
 web/source/css/page.css        |   6 ++
 web/template/index.tmpl        |   1 -
 web/template/index_apps.tmpl   | 118 -------------------------
 web/template/login_button.tmpl |  22 +++++
 web/template/login_info.tmpl   | 157 +++++++++++++++++++++++++++++++++
 web/template/page.tmpl         |   5 +-
 12 files changed, 386 insertions(+), 179 deletions(-)
 create mode 100644 internal/web/login-info.go
 create mode 100644 web/assets/ellipsis.svg
 create mode 100644 web/source/css/login-info.css
 delete mode 100644 web/template/index_apps.tmpl
 create mode 100644 web/template/login_button.tmpl
 create mode 100644 web/template/login_info.tmpl

diff --git a/internal/web/index.go b/internal/web/index.go
index 25960cf7f..02ce30138 100644
--- a/internal/web/index.go
+++ b/internal/web/index.go
@@ -60,7 +60,14 @@ func (m *Module) indexHandler(c *gin.Context) {
 		Instance:    instance,
 		OGMeta:      apiutil.OGBase(instance),
 		Stylesheets: []string{cssAbout, cssIndex},
-		Extra:       map[string]any{"showStrap": true},
+		Extra: map[string]any{
+			// Render "home to x
+			// users [etc]" strap.
+			"showStrap": true,
+			// Show "log in" button
+			// in top-right corner.
+			"showLoginButton": true,
+		},
 	}
 
 	apiutil.TemplateWebPage(c, page)
diff --git a/internal/web/login-info.go b/internal/web/login-info.go
new file mode 100644
index 000000000..bd52f72ef
--- /dev/null
+++ b/internal/web/login-info.go
@@ -0,0 +1,60 @@
+// GoToSocial
+// Copyright (C) GoToSocial Authors admin@gotosocial.org
+// SPDX-License-Identifier: AGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+package web
+
+import (
+	"context"
+
+	"github.com/gin-gonic/gin"
+	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
+	apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
+	"github.com/superseriousbusiness/gotosocial/internal/gtserror"
+)
+
+const (
+	loginPath = "/login"
+)
+
+func (m *Module) loginGETHandler(c *gin.Context) {
+	instance, errWithCode := m.processor.InstanceGetV1(c.Request.Context())
+	if errWithCode != nil {
+		apiutil.WebErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
+		return
+	}
+
+	// Return instance we already got from the db,
+	// don't try to fetch it again when erroring.
+	instanceGet := func(ctx context.Context) (*apimodel.InstanceV1, gtserror.WithCode) {
+		return instance, nil
+	}
+
+	// We only serve text/html at this endpoint.
+	if _, err := apiutil.NegotiateAccept(c, apiutil.TextHTML); err != nil {
+		apiutil.WebErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), instanceGet)
+		return
+	}
+
+	page := apiutil.WebPage{
+		Template:    "login_info.tmpl",
+		Instance:    instance,
+		OGMeta:      apiutil.OGBase(instance),
+		Stylesheets: []string{cssAbout, cssLoginInfo},
+	}
+
+	apiutil.TemplateWebPage(c, page)
+}
diff --git a/internal/web/web.go b/internal/web/web.go
index 35f8f21b0..ddf7d53ea 100644
--- a/internal/web/web.go
+++ b/internal/web/web.go
@@ -59,14 +59,15 @@ const (
 	eTagHeader            = "ETag"              // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag
 	lastModifiedHeader    = "Last-Modified"     // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Last-Modified
 
-	cssFA       = assetsPathPrefix + "/Fork-Awesome/css/fork-awesome.min.css"
-	cssAbout    = distPathPrefix + "/about.css"
-	cssIndex    = distPathPrefix + "/index.css"
-	cssStatus   = distPathPrefix + "/status.css"
-	cssThread   = distPathPrefix + "/thread.css"
-	cssProfile  = distPathPrefix + "/profile.css"
-	cssSettings = distPathPrefix + "/settings-style.css"
-	cssTag      = distPathPrefix + "/tag.css"
+	cssFA        = assetsPathPrefix + "/Fork-Awesome/css/fork-awesome.min.css"
+	cssAbout     = distPathPrefix + "/about.css"
+	cssIndex     = distPathPrefix + "/index.css"
+	cssLoginInfo = distPathPrefix + "/login-info.css"
+	cssStatus    = distPathPrefix + "/status.css"
+	cssThread    = distPathPrefix + "/thread.css"
+	cssProfile   = distPathPrefix + "/profile.css"
+	cssSettings  = distPathPrefix + "/settings-style.css"
+	cssTag       = distPathPrefix + "/tag.css"
 
 	jsFrontend = distPathPrefix + "/frontend.js" // Progressive enhancement frontend JS.
 	jsSettings = distPathPrefix + "/settings.js" // Settings panel React application.
@@ -121,6 +122,7 @@ func (m *Module) Route(r *router.Router, mi ...gin.HandlerFunc) {
 	r.AttachHandler(http.MethodPost, confirmEmailPath, m.confirmEmailPOSTHandler)
 	r.AttachHandler(http.MethodGet, robotsPath, m.robotsGETHandler)
 	r.AttachHandler(http.MethodGet, aboutPath, m.aboutGETHandler)
+	r.AttachHandler(http.MethodGet, loginPath, m.loginGETHandler)
 	r.AttachHandler(http.MethodGet, domainBlockListPath, m.domainBlockListGETHandler)
 	r.AttachHandler(http.MethodGet, tagsPath, m.tagGETHandler)
 	r.AttachHandler(http.MethodGet, signupPath, m.signupGETHandler)
diff --git a/web/assets/ellipsis.svg b/web/assets/ellipsis.svg
new file mode 100644
index 000000000..e1b114020
--- /dev/null
+++ b/web/assets/ellipsis.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
+<svg xmlns="http://www.w3.org/2000/svg" fill="#fafaff" width="800px" height="800px" viewBox="0 0 56 56">
+  <path d="M 27.9999 51.9063 C 41.0546 51.9063 51.9063 41.0781 51.9063 28 C 51.9063 14.9453 41.0312 4.0937 27.9765 4.0937 C 14.8983 4.0937 4.0937 14.9453 4.0937 28 C 4.0937 41.0781 14.9218 51.9063 27.9999 51.9063 Z M 19.7265 28.0234 C 19.7265 29.8750 18.1093 31.5156 16.2343 31.5156 C 14.3358 31.5156 12.7655 29.8750 12.7655 28.0234 C 12.7655 26.1484 14.3124 24.5078 16.2343 24.5078 C 18.1093 24.5078 19.7265 26.1719 19.7265 28.0234 Z M 31.4452 28.0234 C 31.4452 29.8750 29.8514 31.5156 27.9765 31.5156 C 26.1014 31.5156 24.5077 29.8750 24.5077 28.0234 C 24.5077 26.1484 26.0780 24.5078 27.9765 24.5078 C 29.8514 24.5078 31.4452 26.1719 31.4452 28.0234 Z M 43.1874 28.0234 C 43.1874 29.8750 41.5936 31.5156 39.6952 31.5156 C 37.8436 31.5156 36.2030 29.8750 36.2030 28.0234 C 36.2030 26.1484 37.8202 24.5078 39.6952 24.5078 C 41.5936 24.5078 43.1874 26.1719 43.1874 28.0234 Z"/>
+</svg>
diff --git a/web/source/css/index.css b/web/source/css/index.css
index 382cd68c6..9f8e662d4 100644
--- a/web/source/css/index.css
+++ b/web/source/css/index.css
@@ -75,53 +75,3 @@
 		max-width: 100%;
 	}
 }
-
-.apps {
-	align-self: start;
-
-	.applist {
-		margin: 0;
-		padding: 0;
-
-		display: grid;
-		grid-template-columns: 1fr 1fr;
-		grid-gap: 0.5rem;
-		align-content: start;
-
-		.applist-entry {
-			display: grid;
-			grid-template-columns: 25% 1fr;
-			grid-template-areas: "logo text";
-			gap: 1.5rem;
-			padding: 0.5rem;
-
-			.applist-logo {
-				grid-area: logo;
-				align-self: center;
-				justify-self: center;
-				width: 100%;
-				object-fit: contain;
-				flex: 1 1 auto;
-			}
-
-			.applist-logo.redraw {
-				fill: $fg;
-				stroke: $fg;
-			}
-
-			.applist-text {
-				grid-area: text;
-				
-				a {
-					font-weight: bold;
-				}
-			}
-		}
-	}
-}
-
-@media screen and (max-width: 600px) {
-	.apps .applist {
-		grid-template-columns: 1fr;
-	}
-}
diff --git a/web/source/css/login-info.css b/web/source/css/login-info.css
new file mode 100644
index 000000000..6605b46be
--- /dev/null
+++ b/web/source/css/login-info.css
@@ -0,0 +1,114 @@
+/*
+	GoToSocial
+	Copyright (C) GoToSocial Authors admin@gotosocial.org
+	SPDX-License-Identifier: AGPL-3.0-or-later
+
+	This program is free software: you can redistribute it and/or modify
+	it under the terms of the GNU Affero General Public License as published by
+	the Free Software Foundation, either version 3 of the License, or
+	(at your option) any later version.
+
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU Affero General Public License for more details.
+
+	You should have received a copy of the GNU Affero General Public License
+	along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+	Reuse about styling, but rework it
+	to separate sections a bit more.
+*/
+.about {
+	padding: 0;
+	
+	background: initial;
+	box-shadow: initial;
+	border: initial;
+	border-radius: initial;
+
+	.about-section {
+		padding: 2rem;
+		background: $bg-accent;
+		box-shadow: $boxshadow;
+		border: $boxshadow-border;
+		border-radius: $br;
+
+		h3 {
+			margin-top: 0px;
+		}
+	}
+
+	& > .about-section.settings {
+		display: flex;
+		flex-direction: row;
+		gap: 1rem;
+		align-items: center;
+		justify-content: center;
+	
+		padding-top: 1rem;
+		padding-bottom: 1rem;
+	
+		p.settings-text {
+			margin-top: auto;
+			margin-bottom: auto;
+			flex: auto;
+		}
+	
+		.settings-button {
+			flex: auto;
+		}
+	}
+
+	& > .about-section.apps {
+		align-self: start;
+	
+		.applist {
+			margin: 0;
+			padding: 0;
+	
+			display: grid;
+			grid-template-columns: 1fr 1fr;
+			grid-gap: 0.5rem;
+			align-content: start;
+	
+			.applist-entry {
+				display: grid;
+				grid-template-columns: 25% 1fr;
+				grid-template-areas: "logo text";
+				gap: 1.5rem;
+				padding: 0.5rem;
+	
+				.applist-logo {
+					grid-area: logo;
+					align-self: center;
+					justify-self: center;
+					width: 100%;
+					object-fit: contain;
+					flex: 1 1 auto;
+				}
+	
+				.applist-logo.redraw {
+					fill: $fg;
+					stroke: $fg;
+				}
+	
+				.applist-text {
+					grid-area: text;
+					
+					a {
+						font-weight: bold;
+					}
+				}
+			}
+		}
+
+		@media screen and (max-width: 600px) {
+			.applist {
+				grid-template-columns: 1fr;
+			}
+		}
+	}
+}
diff --git a/web/source/css/page.css b/web/source/css/page.css
index 752b264ee..822d095c4 100644
--- a/web/source/css/page.css
+++ b/web/source/css/page.css
@@ -135,3 +135,9 @@
 		text-align: center;
 	}
 }
+
+.login {
+	position: absolute;
+	top: 2vh;
+	right: 2vh;
+}
diff --git a/web/template/index.tmpl b/web/template/index.tmpl
index 358bc081e..0adba1741 100644
--- a/web/template/index.tmpl
+++ b/web/template/index.tmpl
@@ -36,6 +36,5 @@
     </section>
     {{- include "index_what_is_this.tmpl" . | indent 1 }}
     {{- include "index_register.tmpl" . | indent 1 }}
-    {{- include "index_apps.tmpl" . | indent 1 }}
 </main>
 {{- end }}
\ No newline at end of file
diff --git a/web/template/index_apps.tmpl b/web/template/index_apps.tmpl
deleted file mode 100644
index 480a12f0b..000000000
--- a/web/template/index_apps.tmpl
+++ /dev/null
@@ -1,118 +0,0 @@
-{{- /*
-// GoToSocial
-// Copyright (C) GoToSocial Authors admin@gotosocial.org
-// SPDX-License-Identifier: AGPL-3.0-or-later
-//
-// This program is free software: you can redistribute it and/or modify
-// it under the terms of the GNU Affero General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU Affero General Public License for more details.
-//
-// You should have received a copy of the GNU Affero General Public License
-// along with this program.  If not, see <http://www.gnu.org/licenses/>.
-*/ -}}
-
-{{- with . }}
-<section role="region" class="about-section apps" aria-labelledby="apps">
-    <h3 id="apps">Client applications</h3>
-    <div class="about-section-contents">
-        <p>
-            Have an account on this instance and want to log in? 
-            GoToSocial does not provide its own webclient, but implements the Mastodon client API.
-            You can use a variety of clients to log in to your account here:
-        </p>
-        <ul class="applist nodot" role="group">
-            <li class="applist-entry">
-                <div class="applist-text">
-                    <p><strong>Pinafore</strong> is a web client designed for speed and simplicity.</p>
-                    <a
-                        href="https://pinafore.social/"
-                        rel="nofollow noreferrer noopener"
-                        target="_blank"
-                    >
-                        Use Pinafore
-                    </a>
-                </div>
-                <svg
-                    role="img"
-                    aria-labelledby="pinafore-title pinafore-desc"
-                    class="applist-logo redraw"
-                    xmlns="http://www.w3.org/2000/svg"
-                    viewBox="0 0 10000 10000"
-                    width="100"
-                    height="100"
-                >
-                    <title id="pinafore-title">The Pinafore logo</title>
-                    <desc id="pinafore-desc">A sailboat</desc>
-                    <path d="M9212 5993H5987V823c1053 667 2747 2177 3225 5170zM3100 2690A12240 12240 0 01939 6035h2161zm676 7210h2448a3067 3067 0 003067-3067H5052V627a527 527 0 00-1052 0v6206H709a3067 3067 0 003067 3067z"></path>
-                </svg>
-            </li>
-            <li class="applist-entry">
-                <div class="applist-text">
-                    <p><strong>Tusky</strong> is a lightweight mobile client for Android.</p>
-                    <a
-                        href="https://tusky.app"
-                        rel="nofollow noreferrer noopener"
-                        target="_blank"
-                    >
-                        Get Tusky
-                    </a>
-                </div>
-                <img
-                    class="applist-logo"
-                    src="/assets/tusky.svg"
-                    alt="The Tusky mascot, a cartoon elephant tooting happily"
-                    title="The Tusky mascot, a cartoon elephant tooting happily"
-                    width="100"
-                    height="100"
-                />
-            </li>
-            <li class="applist-entry">
-                <div class="applist-text">
-                    <p><strong>Feditext</strong> (beta) is a beautiful client for iOS, iPadOS and macOS.</p>
-                    <a
-                        href="https://github.com/feditext/feditext"
-                        rel="nofollow noreferrer noopener"
-                        target="_blank"
-                    >
-                        Get Feditext
-                    </a>
-                </div>
-                <img
-                    class="applist-logo"
-                    src="/assets/feditext.svg"
-                    alt="The Feditext logo, the characters 'ft' at a slight angle"
-                    title="The Feditext logo, the characters 'ft' at a slight angle"
-                    width="100"
-                    height="100"
-                />
-            </li>
-            <li class="applist-entry">
-                <div class="applist-text">
-                    <p>Or try one of the <strong>Mastodon clients</strong> listed on the official Mastodon page.</p>
-                    <a
-                        href="https://joinmastodon.org/apps"
-                        rel="nofollow noreferrer noopener"
-                        target="_blank"
-                    >
-                        Get Mastodon apps
-                    </a>
-                </div>
-                <img
-                    class="applist-logo"
-                    src="/assets/mastodon.svg"
-                    alt="The Mastodon logo, the character 'M' in a speech bubble"
-                    title="The Mastodon logo, the character 'M' in a speech bubble"
-                    width="100"
-                    height="100"
-                />
-            </li>
-        </ul>
-    </div>
-</section>
-{{- end }}
diff --git a/web/template/login_button.tmpl b/web/template/login_button.tmpl
new file mode 100644
index 000000000..5c961545b
--- /dev/null
+++ b/web/template/login_button.tmpl
@@ -0,0 +1,22 @@
+{{- /*
+// GoToSocial
+// Copyright (C) GoToSocial Authors admin@gotosocial.org
+// SPDX-License-Identifier: AGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/ -}}
+
+{{- if .showLoginButton }}
+<div class="login"><a href="/login" class="button with-icon">Log in</a></div>
+{{- end }}
\ No newline at end of file
diff --git a/web/template/login_info.tmpl b/web/template/login_info.tmpl
new file mode 100644
index 000000000..238e3e9d2
--- /dev/null
+++ b/web/template/login_info.tmpl
@@ -0,0 +1,157 @@
+{{- /*
+// GoToSocial
+// Copyright (C) GoToSocial Authors admin@gotosocial.org
+// SPDX-License-Identifier: AGPL-3.0-or-later
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/ -}}
+
+{{- with . }}
+<main class="about">
+    <section role="region" class="about-section settings">
+        <p class="settings-text">
+            Looking to configure your profile and other settings?
+        </p>
+        <a
+            class="settings-button button with-icon"
+            href="/settings"
+            alt="Log in to the GoToSocial settings panel"
+            title="Log in to the GoToSocial settings panel"
+        >
+            Settings
+        </a>
+    </section>
+    <section role="region" class="about-section apps" aria-labelledby="apps">
+        <h3 id="apps">Client applications</h3>
+        <div class="about-section-contents">
+            <p>
+                Want to log in and start posting? 
+                Unlike other ActivityPub softwares, GoToSocial does not provide its own
+                webclient. Instead it implements the Mastodon client API, so you can use
+                a variety of third-party clients to log in to your account here:
+            </p>
+            <ul class="applist nodot" role="group">
+                <li class="applist-entry">
+                    <div class="applist-text">
+                        <p><strong>Pinafore</strong> is a web client designed for speed and simplicity.</p>
+                        <a
+                            href="https://pinafore.social/"
+                            rel="nofollow noreferrer noopener"
+                            target="_blank"
+                        >
+                            Use Pinafore
+                        </a>
+                    </div>
+                    <svg
+                        role="img"
+                        aria-labelledby="pinafore-title pinafore-desc"
+                        class="applist-logo redraw"
+                        xmlns="http://www.w3.org/2000/svg"
+                        viewBox="0 0 10000 10000"
+                        width="100"
+                        height="100"
+                    >
+                        <title id="pinafore-title">The Pinafore logo</title>
+                        <desc id="pinafore-desc">A sailboat</desc>
+                        <path d="M9212 5993H5987V823c1053 667 2747 2177 3225 5170zM3100 2690A12240 12240 0 01939 6035h2161zm676 7210h2448a3067 3067 0 003067-3067H5052V627a527 527 0 00-1052 0v6206H709a3067 3067 0 003067 3067z"></path>
+                    </svg>
+                </li>
+                <li class="applist-entry">
+                    <div class="applist-text">
+                        <p><strong>Tusky</strong> is a lightweight mobile client for Android.</p>
+                        <a
+                            href="https://tusky.app"
+                            rel="nofollow noreferrer noopener"
+                            target="_blank"
+                        >
+                            Get Tusky
+                        </a>
+                    </div>
+                    <img
+                        class="applist-logo"
+                        src="/assets/tusky.svg"
+                        alt="The Tusky mascot, a cartoon elephant tooting happily"
+                        title="The Tusky mascot, a cartoon elephant tooting happily"
+                        width="100"
+                        height="100"
+                    />
+                </li>
+                <li class="applist-entry">
+                    <div class="applist-text">
+                        <p><strong>Feditext</strong> (beta) is a beautiful client for iOS, iPadOS and macOS.</p>
+                        <a
+                            href="https://github.com/feditext/feditext"
+                            rel="nofollow noreferrer noopener"
+                            target="_blank"
+                        >
+                            Get Feditext
+                        </a>
+                    </div>
+                    <img
+                        class="applist-logo"
+                        src="/assets/feditext.svg"
+                        alt="The Feditext logo, the characters 'ft' at a slight angle"
+                        title="The Feditext logo, the characters 'ft' at a slight angle"
+                        width="100"
+                        height="100"
+                    />
+                </li>
+                <li class="applist-entry">
+                    <div class="applist-text">
+                        <p>
+                            <strong>Masto-FE (🦥 flavour)</strong> is an (experimental!) client based on
+                            the Mastodon Glitch web frontend, with some small changes specific to GoToSocial.
+                        </p>
+                        <a
+                            href="https://masto-fe.superseriousbusiness.org"
+                            rel="nofollow noreferrer noopener"
+                            target="_blank"
+                        >
+                            Try Masto-FE (🦥 flavour)
+                        </a>
+                    </div>
+                    <img
+                        class="applist-logo"
+                        src="/assets/mastodon.svg"
+                        alt="The Mastodon logo, the character 'M' in a speech bubble"
+                        title="The Mastodon logo, the character 'M' in a speech bubble"
+                        width="100"
+                        height="100"
+                    />
+                </li>
+                <li class="applist-entry">
+                    <div class="applist-text">
+                        <p>Or try one of the <strong>Mastodon clients</strong> listed on the official Mastodon page.</p>
+                        <a
+                            href="https://joinmastodon.org/apps"
+                            rel="nofollow noreferrer noopener"
+                            target="_blank"
+                        >
+                            Browse Mastodon apps
+                        </a>
+                    </div>
+                    <img
+                        class="applist-logo"
+                        src="/assets/ellipsis.svg"
+                        alt="Three ellipses in a row, MIT license svgrepo.com"
+                        title="Three ellipses in a row, MIT license svgrepo.com"
+                        width="100"
+                        height="100"
+                    />
+                </li>
+            </ul>
+        </div>
+    </section>
+</main>
+{{- end }}
\ No newline at end of file
diff --git a/web/template/page.tmpl b/web/template/page.tmpl
index d2edc5277..52599a531 100644
--- a/web/template/page.tmpl
+++ b/web/template/page.tmpl
@@ -71,7 +71,9 @@ image/webp
         {{- end }}
         <title>{{- template "instanceTitle" . -}}</title>
     </head>
-    <body class="page">
+    <body>
+        {{- include "login_button.tmpl" . | indent 3 }}
+        <div class="page">
         <header class="page-header">
             {{- include "page_header.tmpl" . | indent 3 }}
         </header>
@@ -81,5 +83,6 @@ image/webp
         <footer class="page-footer">
             {{- include "page_footer.tmpl" . | indent 3 }}
         </footer>
+        </div>
     </body>
 </html>
\ No newline at end of file