summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Weipert <code@drogueronin.de>2023-10-18 14:36:57 +0200
committerDaniel Weipert <code@drogueronin.de>2023-10-18 14:44:34 +0200
commitefa766aa8b93c484148a497f628512c4ec096f0f (patch)
tree14f9f29aec83f49e403b7b171c8c0958076d5cb2
initial commit
-rw-r--r--.gitignore1
-rw-r--r--build.php45
-rw-r--r--src/assets/favicon.pngbin0 -> 2440 bytes
-rw-r--r--src/assets/img/dweipert-x178.pngbin0 -> 969 bytes
-rw-r--r--src/assets/img/dweipert-x92.pngbin0 -> 2440 bytes
-rw-r--r--src/assets/img/dweipert.svg1
-rw-r--r--src/assets/img/social/Git-Icon-1788C.pngbin0 -> 2383 bytes
-rw-r--r--src/assets/img/social/GitHub-Mark-Light-32px.pngbin0 -> 1571 bytes
-rw-r--r--src/assets/img/social/favicon-7901bd695fb93edb07975966062049829afb56cf11511236e61bcf425070e36e.pngbin0 -> 1592 bytes
-rw-r--r--src/assets/img/social/gd-favicon.pngbin0 -> 2856 bytes
-rw-r--r--src/assets/img/volunteering/EWL.pngbin0 -> 24558 bytes
-rw-r--r--src/assets/img/volunteering/bns-favicon-32x32.pngbin0 -> 1517 bytes
-rw-r--r--src/assets/img/volunteering/gwoe_icon_57x57.pngbin0 -> 809 bytes
-rw-r--r--src/assets/img/volunteering/logo_weiss.pngbin0 -> 4306 bytes
-rw-r--r--src/assets/img/volunteering/mge-favicon-32x32.pngbin0 -> 1192 bytes
-rw-r--r--src/assets/img/volunteering/myc_weiss.pngbin0 -> 4067 bytes
-rw-r--r--src/i18n.php29
-rw-r--r--src/layout.php267
-rw-r--r--src/pages/index.php183
-rw-r--r--src/pages/projects.php156
20 files changed, 682 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..84c048a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/build/
diff --git a/build.php b/build.php
new file mode 100644
index 0000000..10d93d8
--- /dev/null
+++ b/build.php
@@ -0,0 +1,45 @@
+<?php
+
+$options = getopt("",
+ [
+ 'build_dir:',
+ 'pages:',
+ 'languages:',
+ 'server:',
+ ]
+);
+
+$buildDir = $options['build_dir'] ?? __DIR__ . '/build';
+$pages = explode(',', $options['pages']);
+$languages = explode(',', $options['languages']);
+$server = $options['server'];
+
+
+@mkdir($buildDir, 0777, true);
+$defaultLanguage = $languages[0];
+
+foreach ($pages as $page) {
+ $pageName = trim($page, '/');
+
+ foreach ($languages as $language) {
+ $html = file_get_contents(
+ "$server/" .
+ ($pageName ? "$pageName.php" : '') .
+ "?page=$pageName&lang=$language"
+ );
+
+ $buildPath = "$language/$pageName/index.html";
+ $buildPath = str_replace('//', '/', $buildPath);
+
+ @mkdir(dirname("$buildDir/$buildPath"), 0777, true);
+ file_put_contents("$buildDir/$buildPath", $html);
+
+ if ($language == $defaultLanguage) {
+ $buildPath = str_replace("$defaultLanguage/", '', $buildPath);
+ @mkdir(dirname("$buildDir/$buildPath"), 0777, true);
+ file_put_contents("$buildDir/$buildPath", $html);
+ }
+ }
+}
+
+exec("cp -r ./src/assets $buildDir");
diff --git a/src/assets/favicon.png b/src/assets/favicon.png
new file mode 100644
index 0000000..eb94af1
--- /dev/null
+++ b/src/assets/favicon.png
Binary files differ
diff --git a/src/assets/img/dweipert-x178.png b/src/assets/img/dweipert-x178.png
new file mode 100644
index 0000000..90db39d
--- /dev/null
+++ b/src/assets/img/dweipert-x178.png
Binary files differ
diff --git a/src/assets/img/dweipert-x92.png b/src/assets/img/dweipert-x92.png
new file mode 100644
index 0000000..eb94af1
--- /dev/null
+++ b/src/assets/img/dweipert-x92.png
Binary files differ
diff --git a/src/assets/img/dweipert.svg b/src/assets/img/dweipert.svg
new file mode 100644
index 0000000..8ca2b41
--- /dev/null
+++ b/src/assets/img/dweipert.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 9.07 10.22"><g id="Ebene_2" data-name="Ebene 2"><g id="Ebene_1-2" data-name="Ebene 1"><path d="M7.59,1.52A4.89,4.89,0,0,0,4,0,4.88,4.88,0,0,0,.33,1.52,4.23,4.23,0,0,0,0,1.93V8.34a4.12,4.12,0,0,0,.33.4A4.92,4.92,0,0,0,4,10.22,5,5,0,0,0,7.59,8.78,4.81,4.81,0,0,0,9.07,5.19,5.06,5.06,0,0,0,7.59,1.52Z"/></g></g></svg> \ No newline at end of file
diff --git a/src/assets/img/social/Git-Icon-1788C.png b/src/assets/img/social/Git-Icon-1788C.png
new file mode 100644
index 0000000..51f4ae5
--- /dev/null
+++ b/src/assets/img/social/Git-Icon-1788C.png
Binary files differ
diff --git a/src/assets/img/social/GitHub-Mark-Light-32px.png b/src/assets/img/social/GitHub-Mark-Light-32px.png
new file mode 100644
index 0000000..628da97
--- /dev/null
+++ b/src/assets/img/social/GitHub-Mark-Light-32px.png
Binary files differ
diff --git a/src/assets/img/social/favicon-7901bd695fb93edb07975966062049829afb56cf11511236e61bcf425070e36e.png b/src/assets/img/social/favicon-7901bd695fb93edb07975966062049829afb56cf11511236e61bcf425070e36e.png
new file mode 100644
index 0000000..67693e4
--- /dev/null
+++ b/src/assets/img/social/favicon-7901bd695fb93edb07975966062049829afb56cf11511236e61bcf425070e36e.png
Binary files differ
diff --git a/src/assets/img/social/gd-favicon.png b/src/assets/img/social/gd-favicon.png
new file mode 100644
index 0000000..b014cbc
--- /dev/null
+++ b/src/assets/img/social/gd-favicon.png
Binary files differ
diff --git a/src/assets/img/volunteering/EWL.png b/src/assets/img/volunteering/EWL.png
new file mode 100644
index 0000000..4780348
--- /dev/null
+++ b/src/assets/img/volunteering/EWL.png
Binary files differ
diff --git a/src/assets/img/volunteering/bns-favicon-32x32.png b/src/assets/img/volunteering/bns-favicon-32x32.png
new file mode 100644
index 0000000..a960d89
--- /dev/null
+++ b/src/assets/img/volunteering/bns-favicon-32x32.png
Binary files differ
diff --git a/src/assets/img/volunteering/gwoe_icon_57x57.png b/src/assets/img/volunteering/gwoe_icon_57x57.png
new file mode 100644
index 0000000..431c7a8
--- /dev/null
+++ b/src/assets/img/volunteering/gwoe_icon_57x57.png
Binary files differ
diff --git a/src/assets/img/volunteering/logo_weiss.png b/src/assets/img/volunteering/logo_weiss.png
new file mode 100644
index 0000000..e97e9af
--- /dev/null
+++ b/src/assets/img/volunteering/logo_weiss.png
Binary files differ
diff --git a/src/assets/img/volunteering/mge-favicon-32x32.png b/src/assets/img/volunteering/mge-favicon-32x32.png
new file mode 100644
index 0000000..0996edb
--- /dev/null
+++ b/src/assets/img/volunteering/mge-favicon-32x32.png
Binary files differ
diff --git a/src/assets/img/volunteering/myc_weiss.png b/src/assets/img/volunteering/myc_weiss.png
new file mode 100644
index 0000000..2aa9d6c
--- /dev/null
+++ b/src/assets/img/volunteering/myc_weiss.png
Binary files differ
diff --git a/src/i18n.php b/src/i18n.php
new file mode 100644
index 0000000..a458002
--- /dev/null
+++ b/src/i18n.php
@@ -0,0 +1,29 @@
+<?php
+
+function i18n($i18n)
+{
+ return [
+ 'translate' => function ($id) use ($i18n) {
+ return $i18n[$_GET['lang'] ?? 'de'][$id] ?? $i18n['de'][$id];
+ },
+
+ 'link_page' => function ($page) {
+ $locale = $_GET['lang'] ?? 'de';
+ $page = trim($page, '/');
+ $link = "/$locale/$page";
+
+ return $link;
+ },
+
+ 'link_language' => function ($locale) {
+ $page = trim($_GET['page'], '/');
+ $link = "/$locale/$page";
+
+ return $link;
+ },
+
+ 'inactive_languages' => function () {
+ return array_values(array_diff(['de', 'en', 'jp'], [$_GET['lang'] ?? 'de']));
+ },
+ ];
+}
diff --git a/src/layout.php b/src/layout.php
new file mode 100644
index 0000000..60ce7f7
--- /dev/null
+++ b/src/layout.php
@@ -0,0 +1,267 @@
+<?php
+
+require_once __DIR__ . '/i18n.php';
+
+function layout($head = '', $content = '')
+{
+ $i18n = i18n([
+ 'de' => [
+ 'pages.projects' => 'Projekte',
+
+ 'de' => 'Deutsch',
+ 'en' => 'English',
+ 'jp' => '日本語',
+ ],
+ 'en' => [
+ 'pages.projects' => 'Projects',
+
+ 'de' => 'Deutsch',
+ 'en' => 'English',
+ 'jp' => '日本語',
+ ],
+ 'jp' => [
+ 'de' => 'Deutsch',
+ 'en' => 'English',
+ 'jp' => '日本語',
+ ],
+ ]);
+ $t = $i18n['translate'];
+
+ ?>
+<html lang="<?php echo $_GET['lang'] ?? 'de'; ?>">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+
+ <title>dweipert.de</title>
+ <meta name="description" content="Open-Source und Free-Software Advokat aus Bad Neustadt an der Saale">
+ <meta name="keywords" content="open-source, free software, Softwareentwicklung, Bad Neustadt">
+
+ <link rel="icon" type="image/x-icon" href="/assets/favicon.png">
+
+ <style>
+ html {
+ font-family:
+ Roboto,
+ 'Helvetica Neue',
+ Arial,
+ sans-serif;
+ font-size: 16px;
+ box-sizing: border-box;
+ }
+
+ html, body {
+ margin: 0;
+ padding: 0;
+ }
+
+ *,
+ *::before,
+ *::after {
+ box-sizing: border-box;
+ }
+
+ nav ul {
+ list-style-type: none;
+ margin: 0; padding: 0;
+ }
+
+ nav a:hover {
+ text-decoration: underline;
+ }
+
+ a {
+ color: inherit;
+ text-decoration: none;
+ }
+
+ .wrap {
+ background-color: #000;
+ color: #ebebeb;
+ }
+
+ .site-wrap {
+ padding: 0 2rem;
+
+ min-height: 100vh;
+ display: grid;
+ grid-template-rows: auto 1fr auto;
+ grid-template-columns: 100%;
+ }
+
+ header {
+ padding: 1em 0;
+
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ header .menu {
+ display: flex;
+ align-items: center;
+ }
+
+ header .menu-button {
+ font-size: 1.5em;
+ }
+
+ main {
+ padding: 2em 0;
+ }
+
+ main a {
+ text-decoration: underline;
+ }
+
+ .menu > * + * {
+ margin-left: 1em;
+ }
+
+
+
+ /* Logo */
+
+ .logo {
+ display: block;
+ width: 30px;
+ filter: brightness(0) invert(1);
+ }
+
+
+
+ /* Menu */
+
+ .nav-main {
+ position: fixed;
+ height: 100vh;
+ width: 100vw;
+ top: 0; left: 0;
+ z-index: 100;
+
+ background: rgba(0, 0, 0, 0.95);
+
+ display: none;
+ }
+
+ .nav-main.active {
+ display: block;
+ }
+
+ .nav-main ul {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ height: 100%;
+ }
+
+ .nav-main ul li {
+ font-size: 1.5em;
+ }
+
+ .nav-main ul li + li {
+ margin-top: 0.5em;
+ }
+
+ .menu-button {
+ cursor: pointer;
+ }
+
+ .close-btn {
+ position: absolute;
+ top: 1rem;
+ right: 2rem;
+
+ font-size: 1.5em;
+ cursor: pointer;
+ }
+
+
+ .nav-lang {
+ position: relative;
+ }
+
+ .nav-lang__active {
+ cursor: pointer;
+ }
+
+ .nav-lang__sub {
+ position: absolute;
+ flex-direction: column;
+ display: none;
+ }
+
+ .nav-lang__sub.active {
+ display: flex;
+ }
+ </style>
+
+ <?php echo $head; ?>
+ </head>
+ <body>
+ <div class="wrap">
+ <div class="site-wrap">
+ <nav class="nav-main" ref="nav-main">
+ <span class="close-btn" ref="nav-main_close">X</span>
+ <ul>
+ <li>
+ <a href="<?php echo $i18n['link_page']('/projects'); ?>"><?php echo $t('pages.projects'); ?></a>
+ </li>
+ </ul>
+ </nav>
+
+ <header>
+ <a href="<?php echo $i18n['link_page']('/'); ?>">
+ <img src="/assets/img/dweipert-x92.png" alt="Logo" class="logo">
+ </a>
+ <div class="menu">
+ <nav class="nav-lang">
+ <span class="nav-lang__active" ref="nav-lang_active">
+ <?php echo $t($_GET['lang'] ?? 'de'); ?>
+ </span>
+ <ul class="nav-lang__sub" ref="nav-lang_sub">
+ <?php foreach ($i18n['inactive_languages']() as $locale): ?>
+ <li>
+ <a href="<?php echo $i18n['link_language']($locale); ?>">
+ <?php echo $t($locale); ?>
+ </a>
+ </li>
+ <?php endforeach; ?>
+ </ul>
+ </nav>
+
+ <span class="menu-button" ref="nav-main_open">=</span>
+ </div>
+ </header>
+
+ <main>
+ <?php echo $content; ?>
+ </main>
+ </div>
+ </div>
+
+ <script>
+ const navMain = document.querySelector('[ref="nav-main"]');
+ const navMainOpen = document.querySelector('[ref="nav-main_open"]');
+ const navMainClose = navMain.querySelector('[ref="nav-main_close"]');
+
+ navMainOpen.addEventListener('click', function () {
+ navMain.classList.toggle('active');
+ });
+ navMainClose.addEventListener('click', function () {
+ navMain.classList.toggle('active');
+ });
+
+
+ const navLangActive = document.querySelector('[ref="nav-lang_active"]');
+ const navLangSub = document.querySelector('[ref="nav-lang_sub"]');
+
+ navLangActive.addEventListener('click', function () {
+ navLangSub.classList.toggle('active');
+ });
+ </script>
+ </body>
+ </html>
+ <?php
+}
diff --git a/src/pages/index.php b/src/pages/index.php
new file mode 100644
index 0000000..c9bf2bc
--- /dev/null
+++ b/src/pages/index.php
@@ -0,0 +1,183 @@
+<?php
+
+require_once dirname(__DIR__) . '/layout.php';
+
+$i18n = i18n([
+ 'de' => [
+ 'topics' => 'Themenbereiche',
+ 'volunteering' => 'Ehrenämter & Mitgliedschaften',
+ 'social' => 'Social & Media',
+ 'other' => 'Weitere Links',
+ ],
+ 'en' => [
+ 'topics' => 'Topics',
+ 'volunteering' => 'Volunteering & Memberships',
+ 'social' => 'Social & Media',
+ 'other' => 'Other Links',
+ ],
+ 'jp' => [
+ 'topics' => '話題',
+ 'volunteering' => 'Ehrenämterと会員',
+ 'social' => 'ソーシャルとメディア',
+ 'other' => 'その他',
+ ],
+]);
+$t = $i18n['translate'];
+
+$volunteering = [
+ [
+ 'link' => 'https://web.ecogood.org/de/',
+ 'title' => 'International Federation for the Economy for the Common Good e.V.',
+ 'extra' => [
+ 'link' => 'https://bayern.ecogood.org/',
+ 'title' => 'in Bayern',
+ ],
+ 'icon' => '/assets/img/volunteering/gwoe_icon_57x57.png',
+ ],
+ [
+ 'link' => 'https://www.bund-naturschutz.de',
+ 'title' => 'BUND Naturschutz in Bayern e.V.',
+ 'extra' => [
+ 'link' => 'https://rhoen-grabfeld.bund-naturschutz.de',
+ 'title' => 'Kreisgruppe Rhön-Grabfeld',
+ ],
+ 'icon' => '/assets/img/volunteering/bns-favicon-32x32.png',
+ ],
+ [
+ 'link' => 'https://www.fairhandeln.de',
+ 'title' => 'Aktion Eine Welt e.V.',
+ 'icon' => '/assets/img/volunteering/EWL.png',
+ ],
+];
+
+$media = [
+ [
+ 'link' => 'https://gemeinwohl-development.de',
+ 'title' => 'GEMEINWOHL.development',
+ 'icon' => '/assets/img/social/gd-favicon.png',
+ ],
+ [
+ 'link' => 'https://gitlab.com/dweipert-3138720606',
+ 'title' => 'GitLab',
+ 'icon' => '/assets/img/social/favicon-7901bd695fb93edb07975966062049829afb56cf11511236e61bcf425070e36e.png',
+ ],
+ [
+ 'link' => 'https://github.com/dweipert-3138720606',
+ 'title' => 'GitHub',
+ 'icon' => '/assets/img/social/GitHub-Mark-Light-32px.png',
+ ],
+ [
+ 'link' => 'https://git.dweipert.de',
+ 'title' => 'Git Projects',
+ 'icon' => '/assets/img/social/Git-Icon-1788C.png',
+ ],
+];
+
+$other = [
+ [
+ 'link' => 'https://www.bad-neustadt.de/unsere-stadt/familie-freizeit/vereine',
+ 'title' => 'Vereine in Bad Neustadt',
+ ],
+];
+
+ob_start();
+?>
+<style>
+.container {
+ margin: 0 auto;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ justify-content: space-around;
+}
+
+.container a {
+ text-decoration: none;
+}
+
+.container ul {
+ padding: 0;
+ margin: 0;
+ list-style-type: none;
+}
+
+.container li + li {
+ margin-top: 0.75rem;
+}
+
+.volunteering h2 {
+ margin-top: 0;
+}
+
+.volunteering__list li {
+ text-align: left;
+}
+.volunteering__list li > img {
+ max-height: 1.5em;
+ max-width: 1.5em;
+ vertical-align: -6px;
+}
+
+.social__list li > a {
+ display: flex;
+ align-items: center;
+}
+.social__list li > a > img {
+ max-height: 1.5em;
+ max-width: 1.5em;
+}
+</style>
+<?php
+$head = ob_get_clean();
+
+ob_start();
+?>
+<div class="container">
+ <div class="volunteering">
+ <h2><?php echo $t('volunteering'); ?></h2>
+ <ul class="volunteering__list">
+ <?php foreach ($volunteering as $idx => $v): ?>
+ <li>
+ <img src="<?php echo $v['icon']; ?>" alt="<?php echo $v['title']; ?>">&nbsp;
+ <a href="<?php echo $v['link']; ?>" target="_blank"><?php echo $v['title']; ?></a>
+ <?php if (isset($v['extra'])): ?>
+ <span>&mdash; <a href="<?php echo $v['extra']['link']; ?>"><?php echo $v['extra']['title']; ?></a></span>
+ <?php endif; ?>
+ </li>
+ <?php endforeach; ?>
+ </ul>
+ </div>
+
+ <div class="social">
+ <h2><?php echo $t('social'); ?></h2>
+ <ul class="social__list">
+ <?php foreach ($media as $idx => $m): ?>
+ <li>
+ <a href="<?php echo $m['link']; ?>" target="_blank">
+ <img src="<?php echo $m['icon']; ?>" alt="<?php echo $m['title']; ?>">&nbsp;
+ <?php echo $m['title']; ?>
+ </a>
+ </li>
+ <?php endforeach; ?>
+ </ul>
+ </div>
+
+ <div class="other">
+ <h2><?php echo $t('other'); ?></h2>
+ <ul class="social__list">
+ <?php foreach ($other as $idx => $o): ?>
+ <li>
+ <a href="<?php echo $o['link']; ?>" target="_blank">
+ <?php echo $o['title']; ?>
+ </a>
+ </li>
+ <?php endforeach; ?>
+ </ul>
+ </div>
+</div>
+<?php
+$content = ob_get_clean();
+
+layout($head, $content);
diff --git a/src/pages/projects.php b/src/pages/projects.php
new file mode 100644
index 0000000..ab42342
--- /dev/null
+++ b/src/pages/projects.php
@@ -0,0 +1,156 @@
+<?php
+
+require_once dirname(__DIR__) . '/layout.php';
+
+$i18n = i18n([
+ 'de' => [
+ 'intro.0' => 'Auflistung aller Projekte, an denen ich arbeite oder arbeiten möchte.',
+ 'intro.1' => 'Alle genannten Softwareprojekte sollen Open-Source sein.',
+ 'intro.2' => 'Alle genannten Unternehmungen sollen als (gemeinnützige) Vereine bzw genossenschaftlich organisiert sein.',
+ 'intro.3' => 'Keine kapitalistischen "Geld ist Selbstzweck" Bestrebungen.',
+
+ 'footnotes.0' => '*Projekt, das ich einfach nur verwirklicht sehen möchte und nicht die persönliche Anforderung habe, dort direkt mitwirken zu müssen',
+ ],
+]);
+$t = $i18n['translate'];
+
+$projects = [
+ 'stores_places' => [
+ 'title' => 'Läden & Orte',
+ 'items' => [
+ [
+ 'title' => 'Repair-Café*',
+ ],
+ [
+ 'title' => 'Offener, kreativer Begegnungsort*',
+ ],
+ [
+ 'title' => 'Japanische Kultur-Events',
+ ],
+ [
+ 'title' => 'Spielwarenladen mit offenem Treffpunkt',
+ 'description' => 'ausreichend Platz und Tische (für zB Magic the Gathering)',
+ ],
+ ],
+ ],
+
+ 'it' => [
+ 'title' => 'Soft- und Hardware',
+ 'items' => [
+ [
+ 'title' => 'Lokale, regionale, dezentralisierte Alternative zu Lieferando',
+ ],
+ [
+ 'title' => 'Nachhaltige:r Server-Hardware-Infrastruktur Lieferant*in/Dienstleister*in',
+ ],
+ [
+ 'title' => 'Einfaches, niederschwelliges, erweiterbares, flexibles Server-Software-Installations-Management-Interface',
+ ],
+ [
+ 'title' => 'Modularer Web-Browser*',
+ 'description' => 'HTML-, JS, CSS-Parser, etc. als standalone Module als Alternativen zu bestehenden tightly-coupled Browsern',
+ 'link' => 'https://ladybird.dev',
+ ],
+ [
+ 'title' => 'Single person (Flat-File?) Matrix Server',
+ ],
+ [
+ 'title' => 'Source-to-Source Compiler Framework',
+ 'description' => 'In Kombination mit eigener Programmiersprache als Input',
+ ],
+ ],
+ ],
+
+ 'educational' => [
+ 'title' => 'Educational',
+ 'items' => [
+ [
+ 'title' => 'GPS-Game mit Pflanzen und Tieren',
+ 'description' => 'Mobile-Game - Pokémon Go-like',
+ ],
+ [
+ 'title' => 'Elements',
+ 'description' => 'Tabletop - Nachhaltig produziertes, offenes, educational, Magic-like Kartenspiel mit historischen Referenzmaterialien',
+ ],
+ [
+ 'title' => 'Historische Kriege und Scharmützel',
+ 'description' => 'Computerspiel - Fire Emblem-like',
+ ],
+ ],
+ ],
+
+ 'hobby' => [
+ 'title' => 'Hobby-Projekte',
+ 'items' => [
+ [
+ 'title' => 'BomberTux Story',
+ 'link' => 'https://gitlab.com/bombertux/bombertux-story',
+ ],
+ [
+ 'title' => 'BomberTux Hero',
+ 'link' => 'https://gitlab.com/bombertux/bombertux-hero',
+ ],
+ [
+ 'title' => 'Flat-File Forms',
+ 'link' => 'https://gitlab.com/dweipert-3138720606/flat-file-forms',
+ ],
+ ],
+ ],
+
+ 'other' => [
+ 'title' => 'Sonstige',
+ 'items' => [
+ [
+ 'title' => '"Wie nachhaltig leben?"-Buch',
+ 'description' => 'Leitfaden für nachhaltiges Leben im Raum Bad Neustadt',
+ 'link' => 'https://book.dweipert.de',
+ ],
+ ],
+ ],
+];
+
+ob_start();
+?>
+<div class="container">
+ <p>
+ <?php echo $t('intro.0'); ?>
+ </p>
+ <p>
+ <?php echo $t('intro.1'); ?><br>
+ <?php echo $t('intro.2'); ?><br>
+ <?php echo $t('intro.3'); ?>
+ </p>
+
+ <?php foreach ($projects as $project): ?>
+ <div>
+ <h2><?php echo $project['title']; ?></h2>
+ <ul>
+ <?php foreach ($project['items'] as $item): ?>
+ <li>
+ <?php if (isset($item['link'])): ?>
+ <a href="<?php echo $item['link']; ?>" target="_blank">
+ <?php echo $item['title']; ?>
+ </a>
+ <?php else: ?>
+ <span v-else><?php echo $item['title']; ?></span>
+ <?php endif; ?>
+
+ <?php if (isset($item['description'])): ?>
+ <span>&mdash; <?php echo $item['description']; ?></span>
+ <?php endif; ?>
+ </li>
+ <?php endforeach; ?>
+ </ul>
+ </div>
+ <?php endforeach; ?>
+
+ <p>
+ <small>
+ <?php echo $t('footnotes.0'); ?>
+ </small>
+ </p>
+</div>
+<?php
+$content = ob_get_clean();
+
+layout(content: $content);