Prechádzať zdrojové kódy

implementing styling

Medowar 1 mesiac pred
rodič
commit
9634c95219

+ 23 - 12
admin/application.php

@@ -9,6 +9,7 @@ use App\Storage\JsonStore;
 
 require dirname(__DIR__) . '/src/autoload.php';
 Bootstrap::init();
+$app = Bootstrap::config('app');
 
 $auth = new Auth();
 $auth->requireLogin();
@@ -33,7 +34,15 @@ $csrf = Csrf::token();
     <link rel="stylesheet" href="/assets/css/tokens.css">
     <link rel="stylesheet" href="/assets/css/base.css">
 </head>
-<body>
+<body class="admin-page">
+<header class="site-header">
+    <div class="container header-inner">
+        <a class="brand" href="/admin/index.php">
+            <img class="brand-logo" src="/assets/images/feuerwehr-Logo-invers.webp" alt="Feuerwehr Logo">
+            <div class="brand-title"><?= htmlspecialchars((string) ($app['project_name'] ?? 'Admin')) ?></div>
+        </a>
+    </div>
+</header>
 <main class="container">
     <section class="card">
         <p><a href="/admin/index.php">Zur Übersicht</a></p>
@@ -42,16 +51,18 @@ $csrf = Csrf::token();
         <p><strong>Eingereicht:</strong> <?= htmlspecialchars((string) ($submission['submitted_at'] ?? '')) ?></p>
 
         <h2>Formulardaten</h2>
-        <table>
-            <tbody>
-                <?php foreach ((array) ($submission['form_data'] ?? []) as $key => $value): ?>
-                    <tr>
-                        <th><?= htmlspecialchars((string) $key) ?></th>
-                        <td><?= htmlspecialchars(is_scalar($value) ? (string) $value : json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) ?></td>
-                    </tr>
-                <?php endforeach; ?>
-            </tbody>
-        </table>
+        <div class="table-responsive">
+            <table class="table-compact">
+                <tbody>
+                    <?php foreach ((array) ($submission['form_data'] ?? []) as $key => $value): ?>
+                        <tr>
+                            <th><?= htmlspecialchars((string) $key) ?></th>
+                            <td><?= htmlspecialchars(is_scalar($value) ? (string) $value : json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) ?></td>
+                        </tr>
+                    <?php endforeach; ?>
+                </tbody>
+            </table>
+        </div>
 
         <h2>Uploads</h2>
         <?php if (empty($submission['uploads'])): ?>
@@ -75,7 +86,7 @@ $csrf = Csrf::token();
         <form method="post" action="/admin/delete.php" onsubmit="return confirm('Antrag wirklich löschen?');">
             <input type="hidden" name="csrf" value="<?= htmlspecialchars($csrf) ?>">
             <input type="hidden" name="id" value="<?= htmlspecialchars((string) ($submission['application_key'] ?? '')) ?>">
-            <button type="submit">Antrag löschen</button>
+            <button type="submit" class="btn">Antrag löschen</button>
         </form>
     </section>
 </main>

+ 28 - 17
admin/index.php

@@ -8,6 +8,7 @@ use App\Storage\JsonStore;
 
 require dirname(__DIR__) . '/src/autoload.php';
 Bootstrap::init();
+$app = Bootstrap::config('app');
 
 $auth = new Auth();
 $auth->requireLogin();
@@ -30,7 +31,15 @@ if ($query !== '') {
     <link rel="stylesheet" href="/assets/css/tokens.css">
     <link rel="stylesheet" href="/assets/css/base.css">
 </head>
-<body>
+<body class="admin-page">
+<header class="site-header">
+    <div class="container header-inner">
+        <a class="brand" href="/admin/index.php">
+            <img class="brand-logo" src="/assets/images/feuerwehr-Logo-invers.webp" alt="Feuerwehr Logo">
+            <div class="brand-title"><?= htmlspecialchars((string) ($app['project_name'] ?? 'Admin')) ?></div>
+        </a>
+    </div>
+</header>
 <main class="container">
     <section class="card">
         <h1>Abgeschlossene Anträge</h1>
@@ -44,24 +53,26 @@ if ($query !== '') {
         <?php if (empty($list)): ?>
             <p>Keine Anträge vorhanden.</p>
         <?php else: ?>
-            <table>
-                <thead>
-                    <tr>
-                        <th>E-Mail</th>
-                        <th>Eingereicht</th>
-                        <th>Aktion</th>
-                    </tr>
-                </thead>
-                <tbody>
-                    <?php foreach ($list as $item): ?>
+            <div class="table-responsive">
+                <table class="responsive-table">
+                    <thead>
                         <tr>
-                            <td><?= htmlspecialchars((string) ($item['email'] ?? '')) ?></td>
-                            <td><?= htmlspecialchars((string) ($item['submitted_at'] ?? '')) ?></td>
-                            <td><a href="/admin/application.php?id=<?= urlencode((string) ($item['application_key'] ?? '')) ?>">Details</a></td>
+                            <th>E-Mail</th>
+                            <th>Eingereicht</th>
+                            <th>Aktion</th>
                         </tr>
-                    <?php endforeach; ?>
-                </tbody>
-            </table>
+                    </thead>
+                    <tbody>
+                        <?php foreach ($list as $item): ?>
+                            <tr>
+                                <td data-label="E-Mail"><?= htmlspecialchars((string) ($item['email'] ?? '')) ?></td>
+                                <td data-label="Eingereicht"><?= htmlspecialchars((string) ($item['submitted_at'] ?? '')) ?></td>
+                                <td data-label="Aktion"><a href="/admin/application.php?id=<?= urlencode((string) ($item['application_key'] ?? '')) ?>">Details</a></td>
+                            </tr>
+                        <?php endforeach; ?>
+                    </tbody>
+                </table>
+            </div>
         <?php endif; ?>
     </section>
 </main>

+ 13 - 4
admin/login.php

@@ -8,6 +8,7 @@ use App\Security\Csrf;
 
 require dirname(__DIR__) . '/src/autoload.php';
 Bootstrap::init();
+$app = Bootstrap::config('app');
 
 $auth = new Auth();
 
@@ -46,12 +47,20 @@ $csrf = Csrf::token();
     <link rel="stylesheet" href="/assets/css/tokens.css">
     <link rel="stylesheet" href="/assets/css/base.css">
 </head>
-<body>
+<body class="admin-page">
+<header class="site-header">
+    <div class="container header-inner">
+        <a class="brand" href="/admin/login.php">
+            <img class="brand-logo" src="/assets/images/feuerwehr-Logo-invers.webp" alt="Feuerwehr Logo">
+            <div class="brand-title"><?= htmlspecialchars((string) ($app['project_name'] ?? 'Admin')) ?></div>
+        </a>
+    </div>
+</header>
 <main class="container">
-    <section class="card">
+    <section class="card auth-container">
         <h1>Admin Login</h1>
         <?php if ($error !== ''): ?>
-            <p class="error"><?= htmlspecialchars($error) ?></p>
+            <p class="alert alert-error"><?= htmlspecialchars($error) ?></p>
         <?php endif; ?>
         <form method="post">
             <input type="hidden" name="csrf" value="<?= htmlspecialchars($csrf) ?>">
@@ -59,7 +68,7 @@ $csrf = Csrf::token();
                 <label for="password">Passwort</label>
                 <input id="password" name="password" type="password" required>
             </div>
-            <button type="submit">Anmelden</button>
+            <button type="submit" class="btn">Anmelden</button>
         </form>
     </section>
 </main>

+ 664 - 89
assets/css/base.css

@@ -4,63 +4,204 @@
 
 body {
   margin: 0;
-  padding: var(--space-2);
-  background: linear-gradient(140deg, #f9fbfc 0%, #edf2f6 100%);
-  color: var(--text);
+  background: var(--brand-bg);
+  color: var(--brand-text);
   font-family: var(--font-body);
+  line-height: 1.6;
+}
+
+a {
+  color: var(--brand-accent);
+  text-decoration: none;
+}
+
+a:hover,
+a:focus-visible {
+  color: #e0d700;
 }
 
 .container {
-  max-width: 900px;
+  width: 100%;
+  max-width: 1200px;
   margin: 0 auto;
+  padding: 0 20px;
+}
+
+body.admin-page .container {
+  max-width: none;
+  padding: 0 20px;
+}
+
+main {
+  min-height: calc(100vh - 200px);
+  padding: 2rem 0;
 }
 
 h1,
 h2,
-h3 {
+h3,
+h4,
+h5,
+h6 {
   margin-top: 0;
+  color: var(--brand-text);
 }
 
-.card {
-  background: var(--surface);
-  border: 1px solid var(--border);
-  border-radius: var(--radius);
-  padding: var(--space-4);
-  margin-bottom: var(--space-4);
+p {
+  margin-top: 0;
 }
 
-#startSection.compact-mode {
-  padding: var(--space-2) var(--space-3);
+.site-header {
+  padding: 0.85rem 0;
+  background: var(--brand-primary);
+  color: #ffffff;
+  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.18);
 }
 
-.disclaimer-text {
-  white-space: pre-line;
-  color: var(--text);
+.header-inner {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 1rem;
+}
+
+.brand {
+  display: inline-flex;
+  align-items: center;
+  gap: 0.8rem;
+  color: #ffffff;
+  text-decoration: none;
+}
+
+.brand-logo {
+  height: 52px;
+  width: auto;
+  filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.25));
+}
+
+.brand-title {
+  font-size: 1.3rem;
+  font-weight: 700;
+  letter-spacing: 0.02em;
+  line-height: 1.1;
+}
+
+.brand-subtitle {
+  margin-top: 0.15rem;
+  font-size: 0.9rem;
+  font-weight: 500;
+  opacity: 0.9;
+  line-height: 1.1;
+}
+
+.site-nav {
+  display: flex;
+  align-items: center;
+  gap: 0.8rem;
+}
+
+.site-nav a {
+  color: #ffffff;
+  font-weight: 600;
+  letter-spacing: 0.02em;
+}
+
+.site-nav a:hover,
+.site-nav a:focus-visible {
+  color: var(--brand-accent);
+}
+
+.card,
+.panel {
+  background: var(--brand-surface);
+  border: 1px solid var(--brand-border);
+  border-radius: 8px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.35);
+  padding: 1.5rem;
+  margin-bottom: 1.5rem;
+}
+
+.panel-compact {
+  padding: 1rem;
 }
 
+.panel-spacious {
+  padding: 2rem;
+  margin: 2rem 0;
+}
+
+.admin-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 1rem;
+  flex-wrap: wrap;
+}
+
+.admin-stats {
+  display: grid;
+  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
+  gap: 1rem;
+}
+
+.stat-card {
+  background: var(--brand-surface-alt);
+  border: 1px solid var(--brand-border);
+  border-radius: 8px;
+  padding: 1rem;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.35);
+}
+
+.stat-value {
+  font-size: 1.4rem;
+  font-weight: 700;
+}
+
+.form-group,
 .field {
-  margin-bottom: var(--space-3);
+  margin-bottom: 1rem;
 }
 
 label {
   display: block;
-  margin-bottom: var(--space-1);
+  margin-bottom: 0.5rem;
   font-weight: 600;
 }
 
+.checkbox-label {
+  display: inline-flex;
+  align-items: center;
+  gap: 0.5rem;
+  font-weight: 500;
+}
+
 input,
 select,
 textarea,
 button {
+  font: inherit;
+}
+
+.form-group input,
+.form-group textarea,
+.form-group select,
+.field input,
+.field textarea,
+.field select,
+input,
+select,
+textarea {
   width: 100%;
-  padding: var(--space-2);
-  border: 1px solid var(--border);
-  border-radius: 8px;
-  font-size: 1rem;
+  padding: 0.65rem 0.75rem;
+  color: var(--brand-text);
+  background: var(--brand-surface-alt);
+  border: 1px solid var(--brand-border);
+  border-radius: 4px;
 }
 
 input[type='checkbox'] {
   width: auto;
+  accent-color: var(--brand-accent);
 }
 
 textarea {
@@ -68,34 +209,85 @@ textarea {
   resize: vertical;
 }
 
+input:focus-visible,
+select:focus-visible,
+textarea:focus-visible,
+button:focus-visible,
+.btn:focus-visible {
+  outline: none;
+  border-color: var(--brand-accent);
+  box-shadow: 0 0 0 3px rgba(202, 195, 0, 0.2);
+}
+
+.btn,
 button {
-  background: #f5f8fb;
+  display: inline-block;
+  width: 100%;
+  padding: 0.65rem 0.9rem;
+  border: 1px solid var(--brand-danger);
+  border-radius: 4px;
+  color: #ffffff;
+  background: var(--brand-danger);
   cursor: pointer;
+  transition: background-color 0.3s;
 }
 
+.btn:hover,
 button:hover {
-  background: #e9f0f7;
+  background: var(--brand-danger-dark);
 }
 
-.wizard-actions {
-  display: grid;
-  grid-template-columns: 1fr;
-  gap: var(--space-2);
+.btn:disabled,
+button:disabled {
+  opacity: 0.6;
+  cursor: not-allowed;
+}
+
+.btn-secondary {
+  border-color: var(--brand-border);
+  color: var(--brand-text);
+  background: transparent;
+}
+
+.btn-secondary:hover {
+  background: var(--brand-surface-alt);
+}
+
+.btn-small {
+  width: auto;
+  padding: 0.45rem 0.7rem;
+  font-size: 0.9rem;
+}
+
+.btn-block {
+  width: 100%;
+  text-align: center;
+}
+
+.disclaimer-text {
+  white-space: pre-line;
+  color: var(--brand-text);
+}
+
+.progress {
+  margin-bottom: 1rem;
+  color: var(--brand-muted);
 }
 
-.inline-actions {
+.wizard-actions,
+.inline-actions,
+.upload-actions {
   display: grid;
   grid-template-columns: 1fr;
-  gap: var(--space-2);
+  gap: 0.75rem;
 }
 
-.progress {
-  margin-bottom: var(--space-3);
-  color: var(--muted);
+.error,
+.error-text {
+  color: var(--brand-danger);
 }
 
 .error {
-  color: var(--danger);
   min-height: 1.2rem;
   margin-top: 0.2rem;
   font-size: 0.9rem;
@@ -104,8 +296,8 @@ button:hover {
 .hint,
 small {
   display: block;
-  color: var(--muted);
   margin-top: 0.25rem;
+  color: var(--brand-muted);
   font-size: 0.85rem;
 }
 
@@ -114,47 +306,33 @@ small {
   display: none;
 }
 
-.upload-item {
-  font-size: 0.9rem;
-  color: var(--text);
-  margin-top: 0.35rem;
-  padding: 0.45rem 0.6rem;
-  border-radius: 8px;
-  background: #eef6ff;
-  border: 1px solid #c8ddf8;
-}
-
 .upload-control {
-  border: 1px dashed var(--border);
-  border-radius: 10px;
-  padding: var(--space-2);
-  background: #fbfdff;
-}
-
-.upload-actions {
-  display: grid;
-  grid-template-columns: 1fr;
-  gap: var(--space-2);
+  border: 1px dashed var(--brand-border);
+  border-radius: 8px;
+  padding: 0.8rem;
+  background: var(--brand-surface-alt);
 }
 
 .upload-action-btn {
   display: block;
   width: 100%;
   text-align: center;
-  border: 1px solid var(--border);
-  border-radius: 8px;
-  padding: var(--space-2);
-  background: #f3f8fd;
+  padding: 0.65rem 0.9rem;
+  border: 1px solid var(--brand-border);
+  border-radius: 4px;
+  color: var(--brand-text);
+  background: transparent;
   font-weight: 600;
   cursor: pointer;
+  transition: background-color 0.3s;
 }
 
-.upload-action-btn-camera {
-  background: #eef7ef;
+.upload-action-btn:hover {
+  background: var(--brand-surface);
 }
 
-.upload-action-btn:hover {
-  filter: brightness(0.98);
+.upload-action-btn-camera {
+  border-color: var(--brand-accent);
 }
 
 .upload-native-input {
@@ -162,35 +340,49 @@ small {
 }
 
 .upload-selected {
-  margin: var(--space-2) 0 0;
-  color: var(--muted);
+  margin: 0.75rem 0 0;
+  color: var(--brand-muted);
   font-size: 0.9rem;
 }
 
 .upload-list {
-  margin-top: var(--space-2);
-  padding: var(--space-2);
-  border: 1px solid #cdd9e5;
-  border-radius: 10px;
-  background: #f7fbff;
+  margin-top: 0.75rem;
+  padding: 0.75rem;
+  border: 1px solid var(--brand-border);
+  border-radius: 8px;
+  background: var(--brand-surface-alt);
 }
 
 .upload-list:empty {
   display: none;
 }
 
+.upload-item {
+  margin-top: 0.35rem;
+  padding: 0.45rem 0.6rem;
+  border: 1px solid var(--brand-border);
+  border-radius: 8px;
+  background: var(--brand-surface);
+  color: var(--brand-text);
+  font-size: 0.9rem;
+}
+
+.upload-item:first-child {
+  margin-top: 0;
+}
+
 .status-text {
-  margin-top: var(--space-2);
-  color: var(--muted);
   min-height: 1.2rem;
+  margin-top: 1rem;
+  color: var(--brand-muted);
 }
 
 .compact-status {
-  margin-top: var(--space-2);
-  padding: var(--space-2);
-  border: 1px solid #d2dce7;
-  border-radius: 10px;
-  background: #f8fbff;
+  margin-top: 1rem;
+  padding: 1rem;
+  border: 1px solid var(--brand-border);
+  border-radius: 8px;
+  background: var(--brand-surface-alt);
 }
 
 .compact-status p {
@@ -198,40 +390,423 @@ small {
 }
 
 .compact-status p:last-of-type {
-  margin-bottom: var(--space-2);
+  margin-bottom: 1rem;
 }
 
-.error-text {
-  color: var(--danger);
+.alert {
+  margin-bottom: 1rem;
+  padding: 0.8rem 1rem;
+  border: 1px solid var(--brand-border);
+  border-radius: 4px;
+  background: var(--brand-surface-alt);
+}
+
+.alert-success,
+.alert-warning {
+  border-color: var(--brand-accent);
+}
+
+.alert-error {
+  border-color: var(--brand-danger);
+}
+
+.alert-info {
+  border-color: #ffffff;
+}
+
+.status {
+  display: inline-flex;
+  align-items: center;
+  gap: 0.35rem;
+  padding: 0.2rem 0.75rem;
+  border: 1px solid var(--brand-border);
+  border-radius: 999px;
+  font-size: 0.85rem;
+  font-weight: 600;
+  letter-spacing: 0.02em;
+}
+
+.status-open {
+  color: var(--brand-accent);
+  border-color: var(--brand-accent);
+}
+
+.status-notified,
+.status-picked {
+  color: #ffffff;
+  border-color: #ffffff;
+}
+
+.status-expired {
+  color: var(--brand-danger);
+  border-color: var(--brand-danger);
+}
+
+.status-hidden {
+  color: #9ca3af;
+  border-color: #6b7280;
+}
+
+.table-responsive {
+  width: 100%;
+  overflow-x: auto;
 }
 
 table {
   width: 100%;
   border-collapse: collapse;
+  border: 1px solid var(--brand-border);
+  border-radius: 8px;
+  overflow: hidden;
+  background: var(--brand-surface);
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.35);
 }
 
 th,
 td {
-  border: 1px solid var(--border);
-  padding: var(--space-2);
-  vertical-align: top;
+  padding: 0.75rem;
+  border-bottom: 1px solid var(--brand-border);
   text-align: left;
+  vertical-align: top;
 }
 
-@media (min-width: 720px) {
-  body {
-    padding: var(--space-4);
+th {
+  background: var(--brand-primary);
+  color: #ffffff;
+}
+
+tr:hover td {
+  background: var(--brand-surface-alt);
+}
+
+.table-compact th,
+.table-compact td {
+  padding: 0.5rem;
+  font-size: 0.9rem;
+}
+
+.products-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+  gap: 1rem;
+}
+
+.product-card {
+  background: var(--brand-surface);
+  border: 1px solid var(--brand-border);
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
+  transition: transform 0.3s, box-shadow 0.3s;
+}
+
+.product-card:hover {
+  transform: translateY(-5px);
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+}
+
+.product-card-content {
+  padding: 1rem;
+}
+
+.product-detail-grid,
+.checkout-grid {
+  display: grid;
+  grid-template-columns: 1fr 1fr;
+  gap: 1rem;
+}
+
+.price {
+  font-size: 1.2rem;
+  font-weight: 700;
+}
+
+.stock.in-stock {
+  color: var(--brand-accent);
+}
+
+.stock.out-of-stock {
+  color: var(--brand-danger);
+}
+
+.cart-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 1rem;
+  padding: 1rem;
+  border: 1px solid var(--brand-border);
+  border-radius: 8px;
+}
+
+.cart-actions {
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  gap: 1rem;
+}
+
+.modal {
+  position: fixed;
+  inset: 0;
+  z-index: 1000;
+  display: none;
+  align-items: center;
+  justify-content: center;
+  padding: 1.25rem;
+  background: rgba(0, 0, 0, 0.6);
+}
+
+.modal-content {
+  position: relative;
+  width: 100%;
+  max-width: 1000px;
+  max-height: 95vh;
+  overflow: auto;
+  background: var(--brand-surface);
+  border: 1px solid var(--brand-border);
+  border-radius: 8px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.35);
+  padding: 1.5rem;
+}
+
+.modal-close {
+  position: absolute;
+  top: 0.5rem;
+  right: 0.5rem;
+  width: auto;
+}
+
+.media-hero-image {
+  width: 100%;
+  border-radius: 8px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+
+.auth-container {
+  max-width: 400px;
+  margin-top: 4rem;
+}
+
+.inline-form {
+  display: inline;
+}
+
+.filter-form {
+  display: flex;
+  gap: 1rem;
+  align-items: end;
+  flex-wrap: wrap;
+}
+
+.filter-field-grow {
+  flex: 1;
+  min-width: 200px;
+}
+
+.quantity-input {
+  width: 90px;
+  text-align: center;
+}
+
+.text-center {
+  text-align: center;
+}
+
+.u-w-full {
+  width: 100%;
+}
+
+.list-indent {
+  margin-left: 1.5rem;
+}
+
+.ml-2 {
+  margin-left: 1rem;
+}
+
+.mt-1 {
+  margin-top: 0.5rem;
+}
+
+.mt-2 {
+  margin-top: 1rem;
+}
+
+.mt-3 {
+  margin-top: 1.5rem;
+}
+
+.mb-1 {
+  margin-bottom: 0.5rem;
+}
+
+.mb-2 {
+  margin-bottom: 1rem;
+}
+
+.mb-3 {
+  margin-bottom: 1.5rem;
+}
+
+#startSection.compact-mode {
+  padding-top: 1rem;
+  padding-bottom: 1rem;
+}
+
+@media (max-width: 768px) {
+  .header-inner {
+    flex-direction: column;
+    align-items: flex-start;
+  }
+
+  .site-nav {
+    flex-wrap: wrap;
+  }
+
+  .brand-logo {
+    height: 46px;
+  }
+
+  .products-grid {
+    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+  }
+
+  .product-detail-grid,
+  .checkout-grid {
+    grid-template-columns: 1fr;
+  }
+
+  .cart-item {
+    flex-direction: column;
+    align-items: stretch;
+  }
+
+  .cart-actions {
+    justify-content: flex-start;
+    flex-direction: column;
+    align-items: stretch;
   }
 
   .wizard-actions {
     grid-template-columns: repeat(3, 1fr);
   }
 
-  .inline-actions {
+  .inline-actions,
+  .upload-actions {
     grid-template-columns: repeat(2, 1fr);
   }
 
-  .upload-actions {
-    grid-template-columns: repeat(2, 1fr);
+  .admin-header {
+    flex-direction: column;
+    align-items: flex-start;
+  }
+
+  th,
+  td {
+    padding: 0.65rem;
+    font-size: 0.95rem;
+  }
+
+  .admin-stats {
+    grid-template-columns: 1fr;
+  }
+
+  body.admin-page .table-responsive {
+    margin: 0 -20px;
+    padding: 0 20px;
+  }
+
+  .table-responsive .btn {
+    white-space: normal;
+  }
+
+  body.admin-page table.responsive-table,
+  body.admin-page table.responsive-table thead,
+  body.admin-page table.responsive-table tbody,
+  body.admin-page table.responsive-table tr,
+  body.admin-page table.responsive-table th,
+  body.admin-page table.responsive-table td {
+    display: block;
+    width: 100%;
+  }
+
+  body.admin-page table.responsive-table thead {
+    display: none;
+  }
+
+  body.admin-page table.responsive-table tr {
+    margin-bottom: 0.85rem;
+    border: 1px solid var(--brand-border);
+    border-radius: 8px;
+    overflow: hidden;
+    background: var(--brand-surface);
+  }
+
+  body.admin-page table.responsive-table td {
+    border: 0;
+    border-bottom: 1px solid var(--brand-border);
+    padding: 0.65rem 0.75rem;
+  }
+
+  body.admin-page table.responsive-table td:last-child {
+    border-bottom: 0;
+  }
+
+  body.admin-page table.responsive-table td::before {
+    content: attr(data-label);
+    display: block;
+    margin-bottom: 0.2rem;
+    color: var(--brand-muted);
+    font-size: 0.8rem;
+    font-weight: 600;
+    letter-spacing: 0.02em;
+    text-transform: uppercase;
+  }
+}
+
+@media (max-width: 480px) {
+  .container,
+  body.admin-page .container {
+    padding: 0 12px;
+  }
+
+  main {
+    padding: 1.25rem 0;
+  }
+
+  .brand-title {
+    font-size: 1.1rem;
+  }
+
+  .brand-subtitle {
+    font-size: 0.8rem;
+  }
+
+  .products-grid {
+    grid-template-columns: 1fr;
+  }
+
+  .table-responsive table:not(.responsive-table) {
+    min-width: 520px;
+  }
+}
+
+@media print {
+  header,
+  footer,
+  .btn,
+  nav {
+    display: none !important;
+  }
+
+  body {
+    padding: 20px;
+    background: #ffffff;
+    color: #000000;
+  }
+
+  .order-number,
+  table {
+    page-break-inside: avoid;
   }
 }

+ 29 - 13
assets/css/tokens.css

@@ -1,16 +1,32 @@
 :root {
-  --bg: #f6f8fa;
-  --surface: #ffffff;
-  --text: #1c2733;
-  --muted: #586574;
-  --border: #d7dee5;
-  --primary: #005b96;
-  --danger: #a11d2d;
-  --radius: 10px;
+  --brand-primary: #2f3541;
+  --brand-primary-dark: #242a33;
+  --brand-danger: #cf2e2e;
+  --brand-danger-dark: #b12727;
+  --brand-accent: #cac300;
+  --brand-dark: #1b1b1b;
+  --brand-text: #f5f7fb;
+  --brand-muted: #c7ccd6;
+  --brand-surface: #2f3541;
+  --brand-surface-alt: #3a4150;
+  --brand-bg: #28292a;
+  --brand-border: #3b4252;
+
+  /* Compatibility aliases for existing selectors */
+  --bg: var(--brand-bg);
+  --surface: var(--brand-surface);
+  --text: var(--brand-text);
+  --muted: var(--brand-muted);
+  --border: var(--brand-border);
+  --primary: var(--brand-primary);
+  --danger: var(--brand-danger);
+
+  --radius: 8px;
   --space-1: 0.5rem;
-  --space-2: 0.75rem;
-  --space-3: 1rem;
-  --space-4: 1.5rem;
-  --space-5: 2rem;
-  --font-body: 'Segoe UI', 'Helvetica Neue', Helvetica, Arial, sans-serif;
+  --space-2: 1rem;
+  --space-3: 1.5rem;
+  --space-4: 2rem;
+  --space-5: 2.5rem;
+
+  --font-body: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
 }

+ 0 - 0
assets/feuerwehr-Logo-invers.webp → assets/images/feuerwehr-Logo-invers.webp


+ 17 - 6
index.php

@@ -94,13 +94,24 @@ function renderField(array $field): void
     <link rel="stylesheet" href="/assets/css/base.css">
 </head>
 <body>
+<header class="site-header">
+    <div class="container header-inner">
+        <a class="brand" href="/">
+            <img class="brand-logo" src="/assets/images/feuerwehr-Logo-invers.webp" alt="Feuerwehr Logo">
+            <div>
+                <div class="brand-title"><?= htmlspecialchars((string) $app['project_name']) ?></div>
+                <div class="brand-subtitle">Feuerwehr Freising</div>
+            </div>
+        </a>
+    </div>
+</header>
 <main class="container">
     <h1>Digitaler Mitgliedsantrag Feuerwehrverein</h1>
 
     <section id="disclaimerSection" class="card">
         <h2><?= htmlspecialchars($disclaimerTitle) ?></h2>
         <p class="disclaimer-text"><?= nl2br(htmlspecialchars($disclaimerText)) ?></p>
-        <button id="acceptDisclaimerBtn" type="button"><?= htmlspecialchars($disclaimerAcceptLabel) ?></button>
+        <button id="acceptDisclaimerBtn" type="button" class="btn"><?= htmlspecialchars($disclaimerAcceptLabel) ?></button>
     </section>
 
     <section id="startSection" class="card hidden">
@@ -117,12 +128,12 @@ function renderField(array $field): void
                 <input id="startEmail" type="email" name="email" required inputmode="email" autocomplete="email">
             </div>
             <div class="inline-actions" id="startActions">
-                <button id="startSubmitBtn" type="submit">Formular laden</button>
+                <button id="startSubmitBtn" type="submit" class="btn">Formular laden</button>
             </div>
             <div id="compactStatusBox" class="compact-status hidden">
                 <p><strong>E-Mail:</strong> <span id="statusEmailValue">-</span></p>
                 <p><strong>Speicherstatus:</strong> <span id="draftStatusValue">Noch nicht gespeichert</span></p>
-                <button id="resetDataBtn" type="button">Gespeicherte Daten löschen und neu starten</button>
+                <button id="resetDataBtn" type="button" class="btn btn-secondary btn-small">Gespeicherte Daten löschen und neu starten</button>
             </div>
             <p id="feedbackMessage" class="status-text" role="status" aria-live="polite"></p>
         </form>
@@ -148,9 +159,9 @@ function renderField(array $field): void
             <?php endforeach; ?>
 
             <div class="wizard-actions">
-                <button type="button" id="prevBtn">Zurück</button>
-                <button type="button" id="nextBtn">Weiter</button>
-                <button type="button" id="submitBtn" class="hidden">Verbindlich absenden</button>
+                <button type="button" id="prevBtn" class="btn btn-secondary">Zurück</button>
+                <button type="button" id="nextBtn" class="btn">Weiter</button>
+                <button type="button" id="submitBtn" class="btn hidden">Verbindlich absenden</button>
             </div>
         </form>
     </section>