Dateien nach "/" hochladen
This commit is contained in:
@@ -0,0 +1,207 @@
|
|||||||
|
<?php
|
||||||
|
require __DIR__ . '/includes/auth.php';
|
||||||
|
require __DIR__ . '/includes/functions.php';
|
||||||
|
$pdo = require __DIR__ . '/includes/db.php';
|
||||||
|
require_login();
|
||||||
|
|
||||||
|
$success = '';
|
||||||
|
$error = '';
|
||||||
|
|
||||||
|
// ---- Aktionen verarbeiten ----
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
csrf_check();
|
||||||
|
$action = $_POST['action'] ?? '';
|
||||||
|
|
||||||
|
if ($action === 'add_entry' || $action === 'update_entry') {
|
||||||
|
$type = ($_POST['type'] ?? '') === 'termin' ? 'termin' : 'neuigkeit';
|
||||||
|
$title = trim($_POST['title'] ?? '');
|
||||||
|
$date = trim($_POST['entry_date'] ?? '') ?: null;
|
||||||
|
$body = trim($_POST['body'] ?? '');
|
||||||
|
|
||||||
|
if ($title === '' || $body === '') {
|
||||||
|
$error = 'Bitte Titel und Text ausfüllen.';
|
||||||
|
} else {
|
||||||
|
if ($action === 'add_entry') {
|
||||||
|
$stmt = $pdo->prepare('INSERT INTO eintraege (type, title, entry_date, body, author_name) VALUES (?, ?, ?, ?, ?)');
|
||||||
|
$stmt->execute([$type, $title, $date, $body, current_user_name()]);
|
||||||
|
$success = 'Eintrag wurde gespeichert.';
|
||||||
|
} else {
|
||||||
|
$id = (int)($_POST['id'] ?? 0);
|
||||||
|
$stmt = $pdo->prepare('UPDATE eintraege SET type=?, title=?, entry_date=?, body=? WHERE id=?');
|
||||||
|
$stmt->execute([$type, $title, $date, $body, $id]);
|
||||||
|
$success = 'Eintrag wurde aktualisiert.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action === 'delete_entry') {
|
||||||
|
$id = (int)($_POST['id'] ?? 0);
|
||||||
|
$stmt = $pdo->prepare('DELETE FROM eintraege WHERE id=?');
|
||||||
|
$stmt->execute([$id]);
|
||||||
|
$success = 'Eintrag wurde gelöscht.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action === 'add_user') {
|
||||||
|
$name = trim($_POST['new_name'] ?? '');
|
||||||
|
$username = trim($_POST['new_username'] ?? '');
|
||||||
|
$password = $_POST['new_password'] ?? '';
|
||||||
|
|
||||||
|
if ($name === '' || $username === '' || strlen($password) < 8) {
|
||||||
|
$error = 'Bitte Name und Benutzername ausfüllen — das Passwort braucht mindestens 8 Zeichen.';
|
||||||
|
} else {
|
||||||
|
$exists = $pdo->prepare('SELECT COUNT(*) FROM users WHERE username = ?');
|
||||||
|
$exists->execute([$username]);
|
||||||
|
if ($exists->fetchColumn() > 0) {
|
||||||
|
$error = 'Dieser Benutzername ist bereits vergeben.';
|
||||||
|
} else {
|
||||||
|
$stmt = $pdo->prepare('INSERT INTO users (username, password_hash, name) VALUES (?, ?, ?)');
|
||||||
|
$stmt->execute([$username, password_hash($password, PASSWORD_DEFAULT), $name]);
|
||||||
|
$success = 'Neues Mitgliedskonto für ' . $name . ' wurde angelegt.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---- Daten für die Anzeige laden ----
|
||||||
|
$editEntry = null;
|
||||||
|
if (isset($_GET['edit'])) {
|
||||||
|
$stmt = $pdo->prepare('SELECT * FROM eintraege WHERE id = ?');
|
||||||
|
$stmt->execute([(int)$_GET['edit']]);
|
||||||
|
$editEntry = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
|
||||||
|
$eintraege = $pdo->query("SELECT * FROM eintraege ORDER BY (entry_date IS NULL), entry_date DESC, created_at DESC")->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
$users = $pdo->query("SELECT id, name, username, created_at FROM users ORDER BY created_at ASC")->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (isset($_GET['willkommen'])) {
|
||||||
|
$success = 'Konto eingerichtet — willkommen im Mitgliederbereich!';
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Mitgliederbereich · Geburtsverein Ernsthausen</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,600;9..144,700;9..144,800&family=Libre+Franklin:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?php include __DIR__ . '/includes/nav.php'; ?>
|
||||||
|
|
||||||
|
<div class="pagehead">
|
||||||
|
<div class="pagehead-inner">
|
||||||
|
<p class="kicker on-dark">Angemeldet als <?= e(current_user_name()) ?></p>
|
||||||
|
<h1>Mitgliederbereich</h1>
|
||||||
|
<p>Hier trägst du Termine und Neuigkeiten ein, die auf der Startseite unter „Aktuelles" erscheinen — und kannst weitere Mitglieder freischalten.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<?php if ($success): ?><div class="alert success"><?= e($success) ?></div><?php endif; ?>
|
||||||
|
<?php if ($error): ?><div class="alert error"><?= e($error) ?></div><?php endif; ?>
|
||||||
|
|
||||||
|
<div class="two-col">
|
||||||
|
<div>
|
||||||
|
<div class="form-card">
|
||||||
|
<h2><?= $editEntry ? 'Eintrag bearbeiten' : 'Neuen Eintrag erstellen' ?></h2>
|
||||||
|
<form method="post" novalidate>
|
||||||
|
<input type="hidden" name="csrf_token" value="<?= e(csrf_token()) ?>">
|
||||||
|
<input type="hidden" name="action" value="<?= $editEntry ? 'update_entry' : 'add_entry' ?>">
|
||||||
|
<?php if ($editEntry): ?><input type="hidden" name="id" value="<?= (int)$editEntry['id'] ?>"><?php endif; ?>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label for="type">Art</label>
|
||||||
|
<select id="type" name="type">
|
||||||
|
<option value="termin" <?= ($editEntry['type'] ?? '') === 'termin' ? 'selected' : '' ?>>Termin</option>
|
||||||
|
<option value="neuigkeit" <?= ($editEntry['type'] ?? '') === 'neuigkeit' ? 'selected' : '' ?>>Neuigkeit</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="title">Titel</label>
|
||||||
|
<input type="text" id="title" name="title" required value="<?= e($editEntry['title'] ?? '') ?>" placeholder="z. B. Generalversammlung">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="entry_date">Datum (optional)</label>
|
||||||
|
<input type="date" id="entry_date" name="entry_date" value="<?= e($editEntry['entry_date'] ?? '') ?>">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="body">Text</label>
|
||||||
|
<textarea id="body" name="body" required placeholder="z. B. 19:00 Uhr in der Schmiedebar bei Armin Kopp."><?= e($editEntry['body'] ?? '') ?></textarea>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn"><?= $editEntry ? 'Änderungen speichern' : 'Eintrag veröffentlichen' ?></button>
|
||||||
|
<?php if ($editEntry): ?><a href="admin.php" class="btn secondary">Abbrechen</a><?php endif; ?>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-card" style="margin-top:24px;">
|
||||||
|
<h3>Neues Mitglied freischalten</h3>
|
||||||
|
<p style="font-size:14px; color:var(--ink-soft); margin-top:-8px;">Jedes Vereinsmitglied bekommt einen eigenen Zugang zum Mitgliederbereich.</p>
|
||||||
|
<form method="post" novalidate>
|
||||||
|
<input type="hidden" name="csrf_token" value="<?= e(csrf_token()) ?>">
|
||||||
|
<input type="hidden" name="action" value="add_user">
|
||||||
|
<div class="field">
|
||||||
|
<label for="new_name">Name</label>
|
||||||
|
<input type="text" id="new_name" name="new_name" required>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="new_username">Benutzername</label>
|
||||||
|
<input type="text" id="new_username" name="new_username" required>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="new_password">Passwort (mind. 8 Zeichen)</label>
|
||||||
|
<input type="password" id="new_password" name="new_password" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn secondary">Mitglied anlegen</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 style="margin-bottom:12px;">Bestehende Einträge</h3>
|
||||||
|
<div class="admin-list">
|
||||||
|
<?php if (empty($eintraege)): ?>
|
||||||
|
<p class="log-empty">Noch keine Einträge vorhanden.</p>
|
||||||
|
<?php else: foreach ($eintraege as $eintrag): ?>
|
||||||
|
<div class="admin-row">
|
||||||
|
<div class="date"><?= e(format_datum($eintrag['entry_date']) ?? '—') ?></div>
|
||||||
|
<div class="content">
|
||||||
|
<span class="tag"><?= $eintrag['type'] === 'termin' ? 'Termin' : 'Neuigkeit' ?></span>
|
||||||
|
<h4><?= e($eintrag['title']) ?></h4>
|
||||||
|
<p><?= e($eintrag['body']) ?></p>
|
||||||
|
<span class="byline">eingetragen von <?= e($eintrag['author_name']) ?></span>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<a href="admin.php?edit=<?= (int)$eintrag['id'] ?>" class="btn secondary">Bearbeiten</a>
|
||||||
|
<form method="post" onsubmit="return confirm('Diesen Eintrag wirklich löschen?');">
|
||||||
|
<input type="hidden" name="csrf_token" value="<?= e(csrf_token()) ?>">
|
||||||
|
<input type="hidden" name="action" value="delete_entry">
|
||||||
|
<input type="hidden" name="id" value="<?= (int)$eintrag['id'] ?>">
|
||||||
|
<button type="submit" class="btn danger">Löschen</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; endif; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 style="margin:32px 0 12px;">Mitglieder mit Zugang</h3>
|
||||||
|
<div class="admin-list">
|
||||||
|
<?php foreach ($users as $u): ?>
|
||||||
|
<div class="admin-row" style="grid-template-columns:1fr auto;">
|
||||||
|
<div class="content">
|
||||||
|
<h4><?= e($u['name']) ?></h4>
|
||||||
|
<p>Benutzername: <?= e($u['username']) ?></p>
|
||||||
|
</div>
|
||||||
|
<div></div>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php include __DIR__ . '/includes/footer.php'; ?>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
require __DIR__ . '/includes/auth.php';
|
||||||
|
require __DIR__ . '/includes/functions.php';
|
||||||
|
$pdo = require __DIR__ . '/includes/db.php';
|
||||||
|
|
||||||
|
// Wenn noch gar kein Mitglied existiert, zur Ersteinrichtung schicken
|
||||||
|
$userCount = (int)$pdo->query('SELECT COUNT(*) FROM users')->fetchColumn();
|
||||||
|
if ($userCount === 0) {
|
||||||
|
header('Location: setup.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_logged_in()) {
|
||||||
|
header('Location: admin.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
csrf_check();
|
||||||
|
$username = trim($_POST['username'] ?? '');
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare('SELECT * FROM users WHERE username = ?');
|
||||||
|
$stmt->execute([$username]);
|
||||||
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($user && password_verify($password, $user['password_hash'])) {
|
||||||
|
session_regenerate_id(true);
|
||||||
|
$_SESSION['user_id'] = $user['id'];
|
||||||
|
$_SESSION['user_name'] = $user['name'];
|
||||||
|
header('Location: admin.php');
|
||||||
|
exit;
|
||||||
|
} else {
|
||||||
|
$error = 'Benutzername oder Passwort ist falsch.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Mitglieder-Login · Geburtsverein Ernsthausen</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,600;9..144,700;9..144,800&family=Libre+Franklin:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?php include __DIR__ . '/includes/nav.php'; ?>
|
||||||
|
|
||||||
|
<div class="pagehead">
|
||||||
|
<div class="pagehead-inner">
|
||||||
|
<p class="kicker on-dark">Mitgliederbereich</p>
|
||||||
|
<h1>Anmelden</h1>
|
||||||
|
<p>Mit deinem persönlichen Zugang kannst du Termine und Neuigkeiten für die Vereinsseite eintragen.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="form-card">
|
||||||
|
<?php if ($error): ?><div class="alert error"><?= e($error) ?></div><?php endif; ?>
|
||||||
|
<form method="post" novalidate>
|
||||||
|
<input type="hidden" name="csrf_token" value="<?= e(csrf_token()) ?>">
|
||||||
|
<div class="field">
|
||||||
|
<label for="username">Benutzername</label>
|
||||||
|
<input type="text" id="username" name="username" required autofocus>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="password">Passwort</label>
|
||||||
|
<input type="password" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn">Anmelden</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php include __DIR__ . '/includes/footer.php'; ?>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
require __DIR__ . '/includes/auth.php';
|
||||||
|
$_SESSION = [];
|
||||||
|
session_destroy();
|
||||||
|
header('Location: index.php');
|
||||||
|
exit;
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
<?php
|
||||||
|
require __DIR__ . '/includes/auth.php';
|
||||||
|
require __DIR__ . '/includes/functions.php';
|
||||||
|
$pdo = require __DIR__ . '/includes/db.php';
|
||||||
|
|
||||||
|
$userCount = (int)$pdo->query('SELECT COUNT(*) FROM users')->fetchColumn();
|
||||||
|
|
||||||
|
// Sobald ein Mitglied existiert, ist die Ersteinrichtung für immer gesperrt.
|
||||||
|
if ($userCount > 0) {
|
||||||
|
header('Location: login.php');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
csrf_check();
|
||||||
|
$name = trim($_POST['name'] ?? '');
|
||||||
|
$username = trim($_POST['username'] ?? '');
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
$password2 = $_POST['password2'] ?? '';
|
||||||
|
|
||||||
|
if ($name === '' || $username === '' || $password === '') {
|
||||||
|
$error = 'Bitte alle Felder ausfüllen.';
|
||||||
|
} elseif (strlen($password) < 8) {
|
||||||
|
$error = 'Das Passwort sollte mindestens 8 Zeichen haben.';
|
||||||
|
} elseif ($password !== $password2) {
|
||||||
|
$error = 'Die beiden Passwörter stimmen nicht überein.';
|
||||||
|
} else {
|
||||||
|
$stmt = $pdo->prepare('INSERT INTO users (username, password_hash, name) VALUES (?, ?, ?)');
|
||||||
|
$stmt->execute([$username, password_hash($password, PASSWORD_DEFAULT), $name]);
|
||||||
|
session_regenerate_id(true);
|
||||||
|
$_SESSION['user_id'] = $pdo->lastInsertId();
|
||||||
|
$_SESSION['user_name'] = $name;
|
||||||
|
header('Location: admin.php?willkommen=1');
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Ersteinrichtung · Geburtsverein Ernsthausen</title>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,600;9..144,700;9..144,800&family=Libre+Franklin:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?php include __DIR__ . '/includes/nav.php'; ?>
|
||||||
|
|
||||||
|
<div class="pagehead">
|
||||||
|
<div class="pagehead-inner">
|
||||||
|
<p class="kicker on-dark">Einmalige Ersteinrichtung</p>
|
||||||
|
<h1>Erstes Mitgliedskonto anlegen</h1>
|
||||||
|
<p>Diese Seite ist nur sichtbar, solange noch kein Mitgliedskonto existiert. Danach ist sie automatisch gesperrt. Lege hier den ersten Zugang an — weitere Mitglieder können später im Mitgliederbereich selbst hinzugefügt werden.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<div class="form-card">
|
||||||
|
<?php if ($error): ?><div class="alert error"><?= e($error) ?></div><?php endif; ?>
|
||||||
|
<form method="post" novalidate>
|
||||||
|
<input type="hidden" name="csrf_token" value="<?= e(csrf_token()) ?>">
|
||||||
|
<div class="field">
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<input type="text" id="name" name="name" required autofocus placeholder="z. B. Andreas Kopp">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="username">Benutzername</label>
|
||||||
|
<input type="text" id="username" name="username" required placeholder="z. B. andreas">
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="password">Passwort</label>
|
||||||
|
<input type="password" id="password" name="password" required>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="password2">Passwort wiederholen</label>
|
||||||
|
<input type="password" id="password2" name="password2" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn">Konto anlegen & einrichten</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<?php include __DIR__ . '/includes/footer.php'; ?>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
:root{
|
||||||
|
--paper:#efeee0;
|
||||||
|
--paper-2:#e6e3d1;
|
||||||
|
--ink:#23301f;
|
||||||
|
--ink-soft:#4a5644;
|
||||||
|
--cover:#1f2e22;
|
||||||
|
--cover-2:#182319;
|
||||||
|
--sage:#7e9471;
|
||||||
|
--sage-deep:#556b48;
|
||||||
|
--gold:#c9a05c;
|
||||||
|
--line: rgba(35,48,31,0.16);
|
||||||
|
--line-dark: rgba(239,238,224,0.16);
|
||||||
|
--red:#a94438;
|
||||||
|
}
|
||||||
|
*{box-sizing:border-box;}
|
||||||
|
html{scroll-behavior:smooth;}
|
||||||
|
body{
|
||||||
|
margin:0;
|
||||||
|
background:var(--paper);
|
||||||
|
color:var(--ink);
|
||||||
|
font-family:'Libre Franklin', sans-serif;
|
||||||
|
font-size:17px;
|
||||||
|
line-height:1.6;
|
||||||
|
-webkit-font-smoothing:antialiased;
|
||||||
|
}
|
||||||
|
h1,h2,h3{
|
||||||
|
font-family:'Fraunces', serif;
|
||||||
|
font-weight:700;
|
||||||
|
margin:0;
|
||||||
|
color:inherit;
|
||||||
|
}
|
||||||
|
a{color:inherit;}
|
||||||
|
.kicker{
|
||||||
|
font-family:'Space Mono', monospace;
|
||||||
|
font-size:12.5px;
|
||||||
|
letter-spacing:0.14em;
|
||||||
|
text-transform:uppercase;
|
||||||
|
color:var(--sage-deep);
|
||||||
|
}
|
||||||
|
.kicker.on-dark{color:var(--sage);}
|
||||||
|
|
||||||
|
/* NAV */
|
||||||
|
header{
|
||||||
|
position:sticky; top:0; z-index:50;
|
||||||
|
background:rgba(239,238,224,0.92);
|
||||||
|
backdrop-filter:blur(8px);
|
||||||
|
border-bottom:1px solid var(--line);
|
||||||
|
}
|
||||||
|
nav{
|
||||||
|
max-width:1080px; margin:0 auto;
|
||||||
|
display:flex; align-items:center; justify-content:space-between;
|
||||||
|
padding:16px 24px;
|
||||||
|
}
|
||||||
|
.brand{
|
||||||
|
font-family:'Fraunces', serif; font-weight:700; font-size:18px;
|
||||||
|
letter-spacing:0.01em; text-decoration:none; color:var(--ink);
|
||||||
|
}
|
||||||
|
.brand span{color:var(--sage-deep);}
|
||||||
|
.navlinks{display:flex; align-items:center; gap:26px; list-style:none; margin:0; padding:0;}
|
||||||
|
.navlinks a{
|
||||||
|
font-size:14.5px; text-decoration:none; color:var(--ink-soft);
|
||||||
|
font-weight:600; letter-spacing:0.01em;
|
||||||
|
transition:color .2s ease;
|
||||||
|
}
|
||||||
|
.navlinks a:hover, .navlinks a:focus-visible{color:var(--sage-deep);}
|
||||||
|
.navlinks a.pill{
|
||||||
|
background:var(--cover); color:var(--paper); padding:9px 16px; border-radius:2px;
|
||||||
|
}
|
||||||
|
.navlinks a.pill:hover{background:var(--sage-deep); color:var(--paper);}
|
||||||
|
|
||||||
|
/* PAGE HEAD (non-hero pages) */
|
||||||
|
.pagehead{
|
||||||
|
background:radial-gradient(120% 140% at 50% -10%, var(--cover) 0%, var(--cover-2) 65%);
|
||||||
|
color:var(--paper);
|
||||||
|
padding:56px 24px 44px;
|
||||||
|
}
|
||||||
|
.pagehead-inner{max-width:1080px; margin:0 auto;}
|
||||||
|
.pagehead h1{font-size:clamp(28px,4vw,40px); color:var(--paper); margin-bottom:8px;}
|
||||||
|
.pagehead p{color:rgba(239,238,224,0.75); margin:0; max-width:60ch;}
|
||||||
|
|
||||||
|
/* SECTIONS */
|
||||||
|
section{max-width:1080px; margin:0 auto; padding:56px 24px;}
|
||||||
|
.section-head{margin-bottom:32px; max-width:60ch;}
|
||||||
|
.section-head h2{font-size:clamp(24px,3.2vw,32px); margin-top:8px; color:var(--ink);}
|
||||||
|
.divider{border:none; border-top:1px solid var(--line); margin:0;}
|
||||||
|
|
||||||
|
/* LOG / AKTUELLES */
|
||||||
|
.log{border-top:1px solid var(--line);}
|
||||||
|
.log-entry{
|
||||||
|
display:grid; grid-template-columns:140px 1fr; gap:24px;
|
||||||
|
padding:22px 0; border-bottom:1px solid var(--line);
|
||||||
|
}
|
||||||
|
.log-entry .date{font-family:'Space Mono', monospace; font-size:13.5px; color:var(--sage-deep); padding-top:2px;}
|
||||||
|
.log-entry .tag{
|
||||||
|
display:inline-block; font-family:'Space Mono', monospace; font-size:10.5px; text-transform:uppercase;
|
||||||
|
letter-spacing:.08em; background:var(--paper-2); color:var(--ink-soft); padding:2px 8px; border-radius:2px; margin-bottom:6px;
|
||||||
|
}
|
||||||
|
.log-entry h3{font-size:18px; margin-bottom:6px;}
|
||||||
|
.log-entry p{margin:0; color:var(--ink-soft); font-size:15px; white-space:pre-line;}
|
||||||
|
.log-empty{color:var(--ink-soft); font-style:italic; padding:20px 0;}
|
||||||
|
|
||||||
|
/* FORMS */
|
||||||
|
.form-card{
|
||||||
|
background:var(--paper-2); border:1px solid var(--line); border-radius:3px;
|
||||||
|
padding:28px; max-width:640px;
|
||||||
|
}
|
||||||
|
.form-card h2, .form-card h3{margin-bottom:18px;}
|
||||||
|
.field{margin-bottom:16px;}
|
||||||
|
.field label{display:block; font-size:13.5px; font-weight:600; margin-bottom:6px; color:var(--ink-soft);}
|
||||||
|
.field input[type=text], .field input[type=password], .field input[type=date], .field select, .field textarea{
|
||||||
|
width:100%; padding:11px 12px; border:1px solid var(--line); border-radius:2px;
|
||||||
|
background:var(--paper); font-family:'Libre Franklin', sans-serif; font-size:15px; color:var(--ink);
|
||||||
|
}
|
||||||
|
.field textarea{min-height:110px; resize:vertical; font-family:inherit;}
|
||||||
|
.field input:focus, .field select:focus, .field textarea:focus{
|
||||||
|
outline:2px solid var(--sage); outline-offset:1px;
|
||||||
|
}
|
||||||
|
.btn{
|
||||||
|
display:inline-flex; align-items:center; gap:8px; cursor:pointer;
|
||||||
|
background:var(--gold); color:var(--cover-2); text-decoration:none; border:none;
|
||||||
|
padding:12px 22px; border-radius:2px; font-weight:700; font-size:14.5px;
|
||||||
|
font-family:inherit; white-space:nowrap;
|
||||||
|
transition:background .2s ease;
|
||||||
|
}
|
||||||
|
.btn:hover{background:#dab371;}
|
||||||
|
.btn.secondary{background:transparent; color:var(--ink); border:1px solid var(--line);}
|
||||||
|
.btn.secondary:hover{background:var(--paper);}
|
||||||
|
.btn.danger{background:transparent; color:var(--red); border:1px solid var(--red);}
|
||||||
|
.btn.danger:hover{background:var(--red); color:var(--paper);}
|
||||||
|
|
||||||
|
.alert{
|
||||||
|
padding:13px 16px; border-radius:2px; font-size:14.5px; margin-bottom:20px;
|
||||||
|
}
|
||||||
|
.alert.error{background:#f4dcd8; color:var(--red); border:1px solid var(--red);}
|
||||||
|
.alert.success{background:#e2ead9; color:var(--sage-deep); border:1px solid var(--sage-deep);}
|
||||||
|
|
||||||
|
/* ADMIN LIST */
|
||||||
|
.admin-list{border-top:1px solid var(--line); margin-top:8px;}
|
||||||
|
.admin-row{
|
||||||
|
display:grid; grid-template-columns:110px 1fr auto; gap:16px; align-items:start;
|
||||||
|
padding:16px 0; border-bottom:1px solid var(--line);
|
||||||
|
}
|
||||||
|
.admin-row .date{font-family:'Space Mono', monospace; font-size:13px; color:var(--sage-deep); padding-top:3px;}
|
||||||
|
.admin-row .content h4{margin:0 0 4px; font-family:'Fraunces', serif; font-size:16.5px;}
|
||||||
|
.admin-row .content p{margin:0; font-size:14px; color:var(--ink-soft); white-space:pre-line;}
|
||||||
|
.admin-row .actions{display:flex; gap:8px; align-items:flex-start;}
|
||||||
|
.admin-row .actions form{display:inline;}
|
||||||
|
.byline{font-family:'Space Mono', monospace; font-size:11px; color:var(--ink-soft); margin-top:6px; display:block;}
|
||||||
|
|
||||||
|
.two-col{display:grid; grid-template-columns:1.1fr 0.9fr; gap:32px; align-items:start;}
|
||||||
|
|
||||||
|
footer{
|
||||||
|
background:var(--cover-2); color:rgba(239,238,224,0.7);
|
||||||
|
padding:40px 24px 28px;
|
||||||
|
}
|
||||||
|
.foot-inner{max-width:1080px; margin:0 auto; display:flex; justify-content:space-between; gap:24px; flex-wrap:wrap; align-items:flex-end;}
|
||||||
|
footer .brand{color:var(--paper);}
|
||||||
|
footer .brand span{color:var(--sage);}
|
||||||
|
footer p{font-size:13.5px; margin:6px 0 0;}
|
||||||
|
footer .foot-links{display:flex; gap:20px; list-style:none; padding:0; margin:0; font-size:13.5px;}
|
||||||
|
footer .foot-links a{text-decoration:none; color:rgba(239,238,224,0.7);}
|
||||||
|
footer .foot-links a:hover{color:var(--gold);}
|
||||||
|
|
||||||
|
@media (max-width:760px){
|
||||||
|
.navlinks{gap:14px;}
|
||||||
|
.two-col{grid-template-columns:1fr;}
|
||||||
|
.admin-row{grid-template-columns:1fr; gap:6px;}
|
||||||
|
.foot-inner{flex-direction:column; align-items:flex-start;}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user