summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Friesel <derf@finalrewind.org>2021-08-20 22:19:25 +0200
committerDaniel Friesel <derf@finalrewind.org>2021-08-20 22:19:25 +0200
commit7186853043d79c84e8f88549c759df347ce1b5d9 (patch)
tree5e86074e3a3238826d113144dea8c58152612cf5
parentfa05d5ce9b06627a1c1a194fff059ce0cb608c5c (diff)
add footer with version info and about/imprint/privacy links
-rwxr-xr-xbin/nvm42
-rw-r--r--sass/app.scss11
-rw-r--r--sass/dark.scss5
-rw-r--r--sass/light.scss5
-rw-r--r--static/v0/css/dark.min.css2
-rw-r--r--static/v0/css/light.min.css2
-rw-r--r--templates/about.html25
-rw-r--r--templates/departure_list.html3
-rw-r--r--templates/footer.html11
-rw-r--r--templates/landing_page.html1
-rw-r--r--templates/stops.html1
11 files changed, 103 insertions, 5 deletions
diff --git a/bin/nvm b/bin/nvm
index 3f1c945..f923b91 100755
--- a/bin/nvm
+++ b/bin/nvm
@@ -32,6 +32,7 @@ db_rest_api = os.getenv("NVM_DB_REST_API", "https://v5.db.transport.rest")
env = Environment(loader=FileSystemLoader("templates"), autoescape=select_autoescape())
apis = None
+nvm_version = None
class EFA:
@@ -261,6 +262,30 @@ class Line:
return self.name
+def meta_about(request):
+ about = env.get_template("about.html")
+ return web.Response(
+ body=about.render(version=nvm_version),
+ headers=headers,
+ )
+
+
+def meta_imprint(request):
+ about = env.get_template("imprint.html")
+ return web.Response(
+ body=about.render(version=nvm_version),
+ headers=headers,
+ )
+
+
+def meta_privacy(request):
+ about = env.get_template("privacy.html")
+ return web.Response(
+ body=about.render(version=nvm_version),
+ headers=headers,
+ )
+
+
async def show_departure_board(request, eva=None):
if eva is None:
@@ -328,7 +353,10 @@ async def show_departure_board(request, eva=None):
departure_board = env.get_template("departure_list.html")
return web.Response(
body=departure_board.render(
- title=station_name, departures=departures, warning=warning
+ title=station_name,
+ departures=departures,
+ warning=warning,
+ version=nvm_version,
),
headers=headers,
)
@@ -351,6 +379,7 @@ async def redirect_to_departure_board(request):
"body": "",
"code": f"Server returned HTTP {response.status} '{error[:10224]}'",
},
+ version=nvm_version,
),
headers=headers,
status=500,
@@ -363,7 +392,9 @@ async def redirect_to_departure_board(request):
stops_page = env.get_template("stops.html")
return web.Response(
- body=stops_page.render(title=f"Suche nach „{stop_name}“", stops=stops),
+ body=stops_page.render(
+ title=f"Suche nach „{stop_name}“", stops=stops, version=nvm_version
+ ),
headers=headers,
)
@@ -371,7 +402,7 @@ async def redirect_to_departure_board(request):
async def show_landing_page(request):
landing_page = env.get_template("landing_page.html")
return web.Response(
- body=landing_page.render(title="NVM"),
+ body=landing_page.render(title="NVM", version=nvm_version),
headers=headers,
)
@@ -417,6 +448,7 @@ if __name__ == "__main__":
default="warning",
help="Set log level",
)
+ parser.add_argument("--version-str", type=str, metavar="VERSION", default="git")
args = parser.parse_args()
if args.log_level:
@@ -427,11 +459,15 @@ if __name__ == "__main__":
logging.basicConfig(level=numeric_level)
apis = TransportAPIs()
+ nvm_version = args.version_str
app = web.Application()
app.router.add_get(args.prefix, show_landing_page)
app.router.add_get(f"{args.prefix}board/{{eva}}", show_departure_board)
app.router.add_post(f"{args.prefix}geolocation", ajax_geolocation)
app.router.add_get(f"{args.prefix}find/stop", redirect_to_departure_board)
+ app.router.add_get(f"{args.prefix}meta/about", meta_about)
+ app.router.add_get(f"{args.prefix}meta/imprint", meta_imprint)
+ app.router.add_get(f"{args.prefix}meta/privacy", meta_privacy)
app.router.add_static(f"{args.prefix}static", "static")
web.run_app(app, host="localhost", port=args.port)
diff --git a/sass/app.scss b/sass/app.scss
index a4f6840..9703217 100644
--- a/sass/app.scss
+++ b/sass/app.scss
@@ -14,6 +14,11 @@ html {
margin-right: auto;
}
+a {
+ color: $link-color;
+ text-decoration: none;
+}
+
.globalnote {
margin-top: 1em;
font-style: italic;
@@ -331,6 +336,12 @@ ul.departures {
}
}
+.about {
+ color: $fg2;
+ margin-top: 2em;
+
+}
+
@font-face {
font-family: 'Material Icons';
font-style: normal;
diff --git a/sass/dark.scss b/sass/dark.scss
index 96db6ba..8ee3458 100644
--- a/sass/dark.scss
+++ b/sass/dark.scss
@@ -3,4 +3,9 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*/
+
+$fg2: #bbbbbb;
+
+$link-color: #9999ff;
+
@import 'app.scss';
diff --git a/sass/light.scss b/sass/light.scss
index 96db6ba..9ec0f0f 100644
--- a/sass/light.scss
+++ b/sass/light.scss
@@ -3,4 +3,9 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*/
+
+$fg2: #666666;
+
+$link-color: #000099;
+
@import 'app.scss';
diff --git a/static/v0/css/dark.min.css b/static/v0/css/dark.min.css
index b27cede..53f4933 100644
--- a/static/v0/css/dark.min.css
+++ b/static/v0/css/dark.min.css
@@ -1 +1 @@
-body{margin:0;color:black;background-color:white}html{font-family:Sans-Serif}.container{max-width:60em;margin-left:auto;margin-right:auto}.globalnote{margin-top:1em;font-style:italic;text-align:center}.geolocation{margin-bottom:1em}.notice{padding:15px;margin-bottom:20px;border:1px solid #bce8f1;border-radius:4px;color:#31708f;background-color:#d9edf7;margin-left:auto;margin-right:auto}.warning{padding:15px;border:1px solid #faebcc;border-radius:4px;color:#8a6d3b;background-color:#fcf8e3;margin-left:auto;margin-right:auto}.warning .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}.error{padding:15px;margin-bottom:20px;border:1px solid #ebccd1;border-radius:4px;color:#a94442;background-color:#f2dede;margin-left:auto;margin-right:auto}.error strong{margin-right:.2em}.error .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}ul.stops{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.stops a{text-decoration:none}ul.stops>li{display:block;height:4em;width:100%;position:relative;border-bottom:1px solid grey;background-color:white;cursor:pointer}ul.stops>li .name{position:absolute;bottom:0;left:5px;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.stops>li .distance{position:absolute;top:1px;right:5px;width:30%;text-align:right;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .note{position:absolute;top:1px;left:0;width:70%;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .lines{position:absolute;bottom:0.1em;right:5px;background-color:transparent;font-weight:bold;font-size:120%}ul.stops>li .lines span{padding-left:0.1em;padding-right:0.1em;margin-left:0.2em}ul.stops>li .lines .longdistance{border:0.1em solid #ffcccc}ul.stops>li .lines .regional{background-color:#eeeeee}ul.stops>li .lines .tram{background-color:#ffcccc}ul.stops>li .lines .taxi{background-color:#eeaaee;font-weight:normal}ul.stops>li .lines .suburban{background-color:#aaffba;border-radius:30px}ul.stops>li .lines .subway{background-color:#aac0ff}ul.stops>li .lines .bus{background-color:#eeaaee;border-radius:10px}ul.departures{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.departures>li{display:block;height:3em;width:100%;position:relative;cursor:pointer;border-bottom:1px solid grey;background-color:white}ul.departures>li.cancelled{background-color:#ffe7d0}ul.departures .line{position:absolute;bottom:2px;left:2px;max-width:6em;max-height:3ex;max-width:5em;overflow:hidden;font-size:120%;background-color:#eeeeee;font-weight:bold;padding-left:0.1em;padding-right:0.1em}ul.departures .longdistance{border:0.1em solid #ffcccc}ul.departures .tram{background-color:#ffcccc}ul.departures .suburban{background-color:#aaffba;border-radius:30px}ul.departures .subway{background-color:#aac0ff}ul.departures .bus{background-color:#eeaaee;border-radius:10px}ul.departures .dest{position:absolute;bottom:0;left:4em;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.departures .destsuffix{position:absolute;top:1px;left:6.1em;width:70%;height:1.2em;overflow:hidden}ul.departures .time{position:absolute;right:5px;top:1px;background-color:transparent;padding-left:0.2em;color:black}ul.departures .time .delay{padding-right:1ex;color:#ff0000}ul.departures .platform{position:absolute;bottom:0;right:5px;overflow:hidden;background-color:transparent;font-size:140%;font-weight:bold;color:black}.navbar-fixed{position:relative;z-index:997}.navbar-fixed nav{position:fixed;width:100%;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);overflow:hidden}.navbar-fixed nav a{color:#ffffff}.navbar-fixed nav .container{position:relative;height:100%}.navbar-fixed nav .main{position:absolute;display:inline-block;padding-left:0.5rem}.navbar-fixed nav ul{float:right;margin:0;padding-left:0;list-style-type:none}.navbar-fixed nav ul li{float:left;padding:0;list-style-type:none;padding-left:.8em;padding-right:.8em}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:local("Material Icons"),local("MaterialIcons-Regular"),url(/static/v0/font/MaterialIcons-Regular.ttf) format("truetype")}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr}@media only screen and (max-width: 600px){ul.departures>li,ul.stops>li{font-size:85%}.navbar-fixed{height:50px}nav{height:50px;line-height:50px}nav .main{font-size:120%}nav .material-icons{height:50px;line-height:50px}}@media only screen and (min-width: 600px){.navbar-fixed{height:60px}nav{height:60px;line-height:60px}nav .main{font-size:140%}nav .material-icons{height:60px;line-height:60px}}
+body{margin:0;color:black;background-color:white}html{font-family:Sans-Serif}.container{max-width:60em;margin-left:auto;margin-right:auto}a{color:#99f;text-decoration:none}.globalnote{margin-top:1em;font-style:italic;text-align:center}.geolocation{margin-bottom:1em}.notice{padding:15px;margin-bottom:20px;border:1px solid #bce8f1;border-radius:4px;color:#31708f;background-color:#d9edf7;margin-left:auto;margin-right:auto}.warning{padding:15px;border:1px solid #faebcc;border-radius:4px;color:#8a6d3b;background-color:#fcf8e3;margin-left:auto;margin-right:auto}.warning .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}.error{padding:15px;margin-bottom:20px;border:1px solid #ebccd1;border-radius:4px;color:#a94442;background-color:#f2dede;margin-left:auto;margin-right:auto}.error strong{margin-right:.2em}.error .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}ul.stops{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.stops a{text-decoration:none}ul.stops>li{display:block;height:4em;width:100%;position:relative;border-bottom:1px solid grey;background-color:white;cursor:pointer}ul.stops>li .name{position:absolute;bottom:0;left:5px;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.stops>li .distance{position:absolute;top:1px;right:5px;width:30%;text-align:right;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .note{position:absolute;top:1px;left:0;width:70%;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .lines{position:absolute;bottom:0.1em;right:5px;background-color:transparent;font-weight:bold;font-size:120%}ul.stops>li .lines span{padding-left:0.1em;padding-right:0.1em;margin-left:0.2em}ul.stops>li .lines .longdistance{border:0.1em solid #ffcccc}ul.stops>li .lines .regional{background-color:#eeeeee}ul.stops>li .lines .tram{background-color:#ffcccc}ul.stops>li .lines .taxi{background-color:#eeaaee;font-weight:normal}ul.stops>li .lines .suburban{background-color:#aaffba;border-radius:30px}ul.stops>li .lines .subway{background-color:#aac0ff}ul.stops>li .lines .bus{background-color:#eeaaee;border-radius:10px}ul.departures{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.departures>li{display:block;height:3em;width:100%;position:relative;cursor:pointer;border-bottom:1px solid grey;background-color:white}ul.departures>li.cancelled{background-color:#ffe7d0}ul.departures .line{position:absolute;bottom:2px;left:2px;max-width:6em;max-height:3ex;max-width:5em;overflow:hidden;font-size:120%;background-color:#eeeeee;font-weight:bold;padding-left:0.1em;padding-right:0.1em}ul.departures .longdistance{border:0.1em solid #ffcccc}ul.departures .tram{background-color:#ffcccc}ul.departures .suburban{background-color:#aaffba;border-radius:30px}ul.departures .subway{background-color:#aac0ff}ul.departures .bus{background-color:#eeaaee;border-radius:10px}ul.departures .dest{position:absolute;bottom:0;left:4em;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.departures .destsuffix{position:absolute;top:1px;left:6.1em;width:70%;height:1.2em;overflow:hidden}ul.departures .time{position:absolute;right:5px;top:1px;background-color:transparent;padding-left:0.2em;color:black}ul.departures .time .delay{padding-right:1ex;color:#ff0000}ul.departures .platform{position:absolute;bottom:0;right:5px;overflow:hidden;background-color:transparent;font-size:140%;font-weight:bold;color:black}.navbar-fixed{position:relative;z-index:997}.navbar-fixed nav{position:fixed;width:100%;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);overflow:hidden}.navbar-fixed nav a{color:#ffffff}.navbar-fixed nav .container{position:relative;height:100%}.navbar-fixed nav .main{position:absolute;display:inline-block;padding-left:0.5rem}.navbar-fixed nav ul{float:right;margin:0;padding-left:0;list-style-type:none}.navbar-fixed nav ul li{float:left;padding:0;list-style-type:none;padding-left:.8em;padding-right:.8em}.about{color:#bbb;margin-top:2em}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:local("Material Icons"),local("MaterialIcons-Regular"),url(/static/v0/font/MaterialIcons-Regular.ttf) format("truetype")}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr}@media only screen and (max-width: 600px){ul.departures>li,ul.stops>li{font-size:85%}.navbar-fixed{height:50px}nav{height:50px;line-height:50px}nav .main{font-size:120%}nav .material-icons{height:50px;line-height:50px}}@media only screen and (min-width: 600px){.navbar-fixed{height:60px}nav{height:60px;line-height:60px}nav .main{font-size:140%}nav .material-icons{height:60px;line-height:60px}}
diff --git a/static/v0/css/light.min.css b/static/v0/css/light.min.css
index b27cede..8e955c6 100644
--- a/static/v0/css/light.min.css
+++ b/static/v0/css/light.min.css
@@ -1 +1 @@
-body{margin:0;color:black;background-color:white}html{font-family:Sans-Serif}.container{max-width:60em;margin-left:auto;margin-right:auto}.globalnote{margin-top:1em;font-style:italic;text-align:center}.geolocation{margin-bottom:1em}.notice{padding:15px;margin-bottom:20px;border:1px solid #bce8f1;border-radius:4px;color:#31708f;background-color:#d9edf7;margin-left:auto;margin-right:auto}.warning{padding:15px;border:1px solid #faebcc;border-radius:4px;color:#8a6d3b;background-color:#fcf8e3;margin-left:auto;margin-right:auto}.warning .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}.error{padding:15px;margin-bottom:20px;border:1px solid #ebccd1;border-radius:4px;color:#a94442;background-color:#f2dede;margin-left:auto;margin-right:auto}.error strong{margin-right:.2em}.error .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}ul.stops{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.stops a{text-decoration:none}ul.stops>li{display:block;height:4em;width:100%;position:relative;border-bottom:1px solid grey;background-color:white;cursor:pointer}ul.stops>li .name{position:absolute;bottom:0;left:5px;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.stops>li .distance{position:absolute;top:1px;right:5px;width:30%;text-align:right;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .note{position:absolute;top:1px;left:0;width:70%;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .lines{position:absolute;bottom:0.1em;right:5px;background-color:transparent;font-weight:bold;font-size:120%}ul.stops>li .lines span{padding-left:0.1em;padding-right:0.1em;margin-left:0.2em}ul.stops>li .lines .longdistance{border:0.1em solid #ffcccc}ul.stops>li .lines .regional{background-color:#eeeeee}ul.stops>li .lines .tram{background-color:#ffcccc}ul.stops>li .lines .taxi{background-color:#eeaaee;font-weight:normal}ul.stops>li .lines .suburban{background-color:#aaffba;border-radius:30px}ul.stops>li .lines .subway{background-color:#aac0ff}ul.stops>li .lines .bus{background-color:#eeaaee;border-radius:10px}ul.departures{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.departures>li{display:block;height:3em;width:100%;position:relative;cursor:pointer;border-bottom:1px solid grey;background-color:white}ul.departures>li.cancelled{background-color:#ffe7d0}ul.departures .line{position:absolute;bottom:2px;left:2px;max-width:6em;max-height:3ex;max-width:5em;overflow:hidden;font-size:120%;background-color:#eeeeee;font-weight:bold;padding-left:0.1em;padding-right:0.1em}ul.departures .longdistance{border:0.1em solid #ffcccc}ul.departures .tram{background-color:#ffcccc}ul.departures .suburban{background-color:#aaffba;border-radius:30px}ul.departures .subway{background-color:#aac0ff}ul.departures .bus{background-color:#eeaaee;border-radius:10px}ul.departures .dest{position:absolute;bottom:0;left:4em;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.departures .destsuffix{position:absolute;top:1px;left:6.1em;width:70%;height:1.2em;overflow:hidden}ul.departures .time{position:absolute;right:5px;top:1px;background-color:transparent;padding-left:0.2em;color:black}ul.departures .time .delay{padding-right:1ex;color:#ff0000}ul.departures .platform{position:absolute;bottom:0;right:5px;overflow:hidden;background-color:transparent;font-size:140%;font-weight:bold;color:black}.navbar-fixed{position:relative;z-index:997}.navbar-fixed nav{position:fixed;width:100%;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);overflow:hidden}.navbar-fixed nav a{color:#ffffff}.navbar-fixed nav .container{position:relative;height:100%}.navbar-fixed nav .main{position:absolute;display:inline-block;padding-left:0.5rem}.navbar-fixed nav ul{float:right;margin:0;padding-left:0;list-style-type:none}.navbar-fixed nav ul li{float:left;padding:0;list-style-type:none;padding-left:.8em;padding-right:.8em}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:local("Material Icons"),local("MaterialIcons-Regular"),url(/static/v0/font/MaterialIcons-Regular.ttf) format("truetype")}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr}@media only screen and (max-width: 600px){ul.departures>li,ul.stops>li{font-size:85%}.navbar-fixed{height:50px}nav{height:50px;line-height:50px}nav .main{font-size:120%}nav .material-icons{height:50px;line-height:50px}}@media only screen and (min-width: 600px){.navbar-fixed{height:60px}nav{height:60px;line-height:60px}nav .main{font-size:140%}nav .material-icons{height:60px;line-height:60px}}
+body{margin:0;color:black;background-color:white}html{font-family:Sans-Serif}.container{max-width:60em;margin-left:auto;margin-right:auto}a{color:#009;text-decoration:none}.globalnote{margin-top:1em;font-style:italic;text-align:center}.geolocation{margin-bottom:1em}.notice{padding:15px;margin-bottom:20px;border:1px solid #bce8f1;border-radius:4px;color:#31708f;background-color:#d9edf7;margin-left:auto;margin-right:auto}.warning{padding:15px;border:1px solid #faebcc;border-radius:4px;color:#8a6d3b;background-color:#fcf8e3;margin-left:auto;margin-right:auto}.warning .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}.error{padding:15px;margin-bottom:20px;border:1px solid #ebccd1;border-radius:4px;color:#a94442;background-color:#f2dede;margin-left:auto;margin-right:auto}.error strong{margin-right:.2em}.error .errcode{font-family:Monospace;margin-top:2em;font-size:100%;color:#aaaaaa}ul.stops{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.stops a{text-decoration:none}ul.stops>li{display:block;height:4em;width:100%;position:relative;border-bottom:1px solid grey;background-color:white;cursor:pointer}ul.stops>li .name{position:absolute;bottom:0;left:5px;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.stops>li .distance{position:absolute;top:1px;right:5px;width:30%;text-align:right;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .note{position:absolute;top:1px;left:0;width:70%;height:1.2em;overflow:hidden;opacity:50%}ul.stops>li .lines{position:absolute;bottom:0.1em;right:5px;background-color:transparent;font-weight:bold;font-size:120%}ul.stops>li .lines span{padding-left:0.1em;padding-right:0.1em;margin-left:0.2em}ul.stops>li .lines .longdistance{border:0.1em solid #ffcccc}ul.stops>li .lines .regional{background-color:#eeeeee}ul.stops>li .lines .tram{background-color:#ffcccc}ul.stops>li .lines .taxi{background-color:#eeaaee;font-weight:normal}ul.stops>li .lines .suburban{background-color:#aaffba;border-radius:30px}ul.stops>li .lines .subway{background-color:#aac0ff}ul.stops>li .lines .bus{background-color:#eeaaee;border-radius:10px}ul.departures{position:relative;width:100%;border-width:1px 2px;margin-bottom:5em;list-style-type:none;margin:0;padding:0}ul.departures>li{display:block;height:3em;width:100%;position:relative;cursor:pointer;border-bottom:1px solid grey;background-color:white}ul.departures>li.cancelled{background-color:#ffe7d0}ul.departures .line{position:absolute;bottom:2px;left:2px;max-width:6em;max-height:3ex;max-width:5em;overflow:hidden;font-size:120%;background-color:#eeeeee;font-weight:bold;padding-left:0.1em;padding-right:0.1em}ul.departures .longdistance{border:0.1em solid #ffcccc}ul.departures .tram{background-color:#ffcccc}ul.departures .suburban{background-color:#aaffba;border-radius:30px}ul.departures .subway{background-color:#aac0ff}ul.departures .bus{background-color:#eeaaee;border-radius:10px}ul.departures .dest{position:absolute;bottom:0;left:4em;width:70%;overflow:hidden;background-color:transparent;font-size:150%;color:black}ul.departures .destsuffix{position:absolute;top:1px;left:6.1em;width:70%;height:1.2em;overflow:hidden}ul.departures .time{position:absolute;right:5px;top:1px;background-color:transparent;padding-left:0.2em;color:black}ul.departures .time .delay{padding-right:1ex;color:#ff0000}ul.departures .platform{position:absolute;bottom:0;right:5px;overflow:hidden;background-color:transparent;font-size:140%;font-weight:bold;color:black}.navbar-fixed{position:relative;z-index:997}.navbar-fixed nav{position:fixed;width:100%;box-shadow:0 2px 2px 0 rgba(0,0,0,0.14),0 3px 1px -2px rgba(0,0,0,0.12),0 1px 5px 0 rgba(0,0,0,0.2);overflow:hidden}.navbar-fixed nav a{color:#ffffff}.navbar-fixed nav .container{position:relative;height:100%}.navbar-fixed nav .main{position:absolute;display:inline-block;padding-left:0.5rem}.navbar-fixed nav ul{float:right;margin:0;padding-left:0;list-style-type:none}.navbar-fixed nav ul li{float:left;padding:0;list-style-type:none;padding-left:.8em;padding-right:.8em}.about{color:#666;margin-top:2em}@font-face{font-family:'Material Icons';font-style:normal;font-weight:400;src:local("Material Icons"),local("MaterialIcons-Regular"),url(/static/v0/font/MaterialIcons-Regular.ttf) format("truetype")}.material-icons{font-family:'Material Icons';font-weight:normal;font-style:normal;font-size:24px;line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr}@media only screen and (max-width: 600px){ul.departures>li,ul.stops>li{font-size:85%}.navbar-fixed{height:50px}nav{height:50px;line-height:50px}nav .main{font-size:120%}nav .material-icons{height:50px;line-height:50px}}@media only screen and (min-width: 600px){.navbar-fixed{height:60px}nav{height:60px;line-height:60px}nav .main{font-size:140%}nav .material-icons{height:60px;line-height:60px}}
diff --git a/templates/about.html b/templates/about.html
new file mode 100644
index 0000000..f6f9b26
--- /dev/null
+++ b/templates/about.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="de">
+<head>
+ <title>{{ title }}</title>
+ {% include 'header.html' %}
+</head>
+<body>
+
+{% include 'navbar.html' %}
+
+<div class="container">
+ <p>
+ NVM ist ein inoffizieller Abfahrtsmonitor für Nahverkehr in Deutschland
+ und Umgebung.
+ © 2021 <a href="https://finalrewind.org/me">Daniel Friesel</a>
+ / <a href="https://twitter.com/derfnull">@derfnull</a>.
+ Der <a href="https://github.com/derf/nvm">Quelltext</a> steht unter
+ der GNU AGPL v3 als Open Source zur Verfügung.
+ </p>
+</div>
+
+{% include 'footer.html' %}
+
+</body>
+</html>
diff --git a/templates/departure_list.html b/templates/departure_list.html
index 42ce550..a9cda01 100644
--- a/templates/departure_list.html
+++ b/templates/departure_list.html
@@ -52,5 +52,8 @@
</ul>
</div>
</div>
+
+{% include 'footer.html' %}
+
</body>
</html>
diff --git a/templates/footer.html b/templates/footer.html
new file mode 100644
index 0000000..671007e
--- /dev/null
+++ b/templates/footer.html
@@ -0,0 +1,11 @@
+<div class="container">
+<div class="about">
+<a href="/meta/about">Über NVM</a>
+<a href="/meta/privacy">Datenschutz</a>
+<a href="/meta/imprint">Impressum</a>
+<br/>
+Version {{ version }}
+</div>
+</div>
diff --git a/templates/landing_page.html b/templates/landing_page.html
index a97f55e..a3b7397 100644
--- a/templates/landing_page.html
+++ b/templates/landing_page.html
@@ -39,5 +39,6 @@
</div>
</div>
</div>
+{% include 'footer.html' %}
</body>
</html>
diff --git a/templates/stops.html b/templates/stops.html
index 5c47014..899376d 100644
--- a/templates/stops.html
+++ b/templates/stops.html
@@ -49,5 +49,6 @@
</ul>
</div>
</div>
+{% include 'footer.html' %}
</body>
</html>