Forráskód Böngészése

implementing Freising Stylesheet

Medowar 1 hónapja
szülő
commit
e971f64b68

+ 2 - 2
README.md

@@ -1,8 +1,8 @@
-# PSA-Bestellsystem Feuerwehr Freising
+# Stadt Freising PSA-Service
 
 ## Zweck
 
-Dieses Projekt ist ein internes Bestellsystem für persönliche Schutzausrüstung der Feuerwehr Freising.
+Dieses Projekt ist ein internes Bestellsystem für persönliche Schutzausrüstung der Stadt Freising und wird organisatorisch durch Amt 32 - Öffentliche Sicherheit und Ordnung betreut.
 
 ## Kernfunktionen
 

+ 2 - 2
admin/admins.php

@@ -227,7 +227,7 @@ include __DIR__ . '/../includes/header.php';
                     <td data-label="Benutzername">
                         <strong><?php echo htmlspecialchars($username); ?></strong>
                         <?php if ($username === $currentAdmin): ?>
-                            <span class="status status-open" style="margin-left: 0.5rem;">Du</span>
+                            <span class="status status-open status-self">Du</span>
                         <?php endif; ?>
                     </td>
                     <td data-label="Beschreibung">
@@ -239,7 +239,7 @@ include __DIR__ . '/../includes/header.php';
                     <td data-label="Aktionen">
                         <a href="admins.php?edit_description=<?php echo urlencode($username); ?>" class="btn btn-small btn-secondary">Profil ändern</a>
                         <a href="admins.php?change=<?php echo urlencode($username); ?>" class="btn btn-small btn-secondary">Passwort ändern</a>
-                        <form method="POST" style="display: inline;" onsubmit="return confirm('Admin wirklich löschen?');">
+                        <form method="POST" class="inline-form" onsubmit="return confirm('Admin wirklich löschen?');">
                             <input type="hidden" name="target_username" value="<?php echo htmlspecialchars($username); ?>">
                             <button type="submit" name="delete_admin" class="btn btn-small">Löschen</button>
                         </form>

+ 3 - 3
admin/categories.php

@@ -121,7 +121,7 @@ include __DIR__ . '/../includes/header.php';
 <?php endif; ?>
 
 <?php if ($editingCategory !== null): ?>
-    <div class="panel" style="padding: 2rem;">
+    <div class="panel panel-lg">
         <h3>Kategorie bearbeiten</h3>
         <form method="POST">
             <input type="hidden" name="category_id" value="<?php echo htmlspecialchars($editingCategory['id']); ?>">
@@ -139,7 +139,7 @@ include __DIR__ . '/../includes/header.php';
         </form>
     </div>
 <?php else: ?>
-    <div class="panel" style="padding: 2rem;">
+    <div class="panel panel-lg">
         <h3>Neue Kategorie anlegen</h3>
         <form method="POST">
             <div class="form-group">
@@ -180,7 +180,7 @@ include __DIR__ . '/../includes/header.php';
                         <td data-label="Produkte"><?php echo $productCount; ?></td>
                         <td data-label="Aktionen">
                             <a href="categories.php?edit=<?php echo urlencode($category['id']); ?>" class="btn btn-small btn-secondary">Bearbeiten</a>
-                            <form method="POST" style="display: inline;" onsubmit="return confirm('Kategorie wirklich löschen?');">
+                            <form method="POST" class="inline-form" onsubmit="return confirm('Kategorie wirklich löschen?');">
                                 <input type="hidden" name="category_id" value="<?php echo htmlspecialchars($category['id']); ?>">
                                 <button type="submit" name="delete_category" class="btn btn-small">Löschen</button>
                             </form>

+ 1 - 1
admin/faq.php

@@ -38,7 +38,7 @@ include __DIR__ . '/../includes/header.php';
     </div>
 <?php endif; ?>
 
-<div class="panel" style="padding: 2rem;">
+<div class="panel panel-lg">
     <p class="mb-2">
         Unterstützte Markdown-Syntax: <code>#</code>, <code>##</code>, <code>###</code>, <code>**fett**</code>, <code>*kursiv*</code>, Listen mit <code>-</code> oder <code>1.</code>
     </p>

+ 1 - 1
admin/index.php

@@ -98,7 +98,7 @@ include __DIR__ . '/../includes/header.php';
     </div>
 </div>
 
-<h3 style="margin-top: 2rem;">Letzte Bestellungen</h3>
+<h3 class="section-title mt-4">Letzte Bestellungen</h3>
 
 <?php if (empty($recentOrders)): ?>
     <p>Keine Bestellungen vorhanden.</p>

+ 19 - 12
admin/login.php

@@ -39,18 +39,25 @@ if (isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in']) {
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Admin Login - <?php echo escape(SITE_NAME); ?></title>
-    <link rel="stylesheet" href="<?php echo SITE_URL; ?>/assets/css/style.css">
+    <title>Administration - <?php echo escape(SITE_FULL_NAME); ?></title>
+    <link rel="stylesheet" href="<?php echo escape(SITE_URL); ?>/assets/css/style.css">
 </head>
-<body>
-    <header>
-        <div class="container">
-            <h1><?php echo escape(SITE_NAME); ?> - Admin</h1>
+<body class="admin-page">
+    <header class="site-header">
+        <div class="container header-inner">
+            <a class="brand" href="<?php echo escape(SITE_URL); ?>/index.php">
+                <img class="brand-logo" src="<?php echo escape(SITE_URL); ?>/assets/branding/stadt-freising-logo.png" alt="Wappen der Stadt Freising">
+                <div class="brand-text">
+                    <span class="brand-title"><?php echo escape(SITE_NAME); ?></span>
+                    <span class="brand-subtitle"><?php echo escape(SITE_SERVICE_NAME); ?></span>
+                </div>
+            </a>
+            <a href="<?php echo escape(SITE_URL); ?>/index.php" class="btn btn-secondary">Zum PSA-Service</a>
         </div>
     </header>
     <main>
-        <div class="container" style="max-width: 400px; margin-top: 4rem;">
-            <h2>Admin Login</h2>
+        <div class="container container-narrow page-top-gap">
+            <h2>Administration</h2>
             
             <?php if ($error): ?>
                 <div class="alert alert-error">
@@ -58,7 +65,7 @@ if (isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in']) {
                 </div>
             <?php endif; ?>
             
-            <form method="POST" class="panel" style="padding: 2rem;">
+            <form method="POST" class="panel panel-lg">
                 <div class="form-group">
                     <label for="username">Benutzername:</label>
                     <input type="text" id="username" name="username" required autofocus>
@@ -67,11 +74,11 @@ if (isset($_SESSION['admin_logged_in']) && $_SESSION['admin_logged_in']) {
                     <label for="password">Passwort:</label>
                     <input type="password" id="password" name="password" required>
                 </div>
-                <button type="submit" class="btn" style="width: 100%;">Anmelden</button>
+                <button type="submit" class="btn btn-block">Anmelden</button>
             </form>
             
-            <div style="text-align: center; margin-top: 1rem;">
-                <a href="<?php echo SITE_URL; ?>/index.php">Zurück zum Shop</a>
+            <div class="text-center mt-2">
+                <a href="<?php echo escape(SITE_URL); ?>/index.php">Zurück zum PSA-Service</a>
             </div>
         </div>
     </main>

+ 3 - 3
admin/orders.php

@@ -81,8 +81,8 @@ include __DIR__ . '/../includes/header.php';
 <?php endif; ?>
 
 <div class="panel">
-    <form method="GET" style="display: flex; gap: 1rem; align-items: end; flex-wrap: wrap;">
-        <div style="flex: 1; min-width: 220px;">
+    <form method="GET" class="admin-filter-form">
+        <div class="admin-filter-field admin-filter-field-wide">
             <label for="order_id">Bestellnummer suchen</label>
             <input type="text" id="order_id" name="order_id" value="<?php echo escape($searchOrderId); ?>" placeholder="z. B. FWFS-2026-001">
         </div>
@@ -98,7 +98,7 @@ include __DIR__ . '/../includes/header.php';
                 <option value="cancelled" <?php echo $filter === 'cancelled' ? 'selected' : ''; ?>>Storniert</option>
             </select>
         </div>
-        <div>
+        <div class="admin-filter-actions">
             <button type="submit" class="btn">Filtern</button>
             <a href="orders.php" class="btn btn-secondary">Zurücksetzen</a>
         </div>

+ 2 - 2
admin/organizations.php

@@ -101,7 +101,7 @@ include __DIR__ . '/../includes/header.php';
     </div>
 <?php endif; ?>
 
-<div class="panel" style="padding: 2rem;">
+<div class="panel panel-lg">
     <h3><?php echo $editingOrganization ? 'Organisation bearbeiten' : 'Neue Organisation'; ?></h3>
     <form method="POST">
         <?php if ($editingOrganization): ?>
@@ -160,7 +160,7 @@ include __DIR__ . '/../includes/header.php';
                         </td>
                         <td data-label="Aktionen">
                             <a href="organizations.php?edit=<?php echo urlencode($organization['id']); ?>" class="btn btn-small">Bearbeiten</a>
-                            <form method="POST" style="display: inline;" onsubmit="return confirm('Organisation wirklich löschen?');">
+                            <form method="POST" class="inline-form" onsubmit="return confirm('Organisation wirklich löschen?');">
                                 <input type="hidden" name="organization_id" value="<?php echo escape($organization['id']); ?>">
                                 <button type="submit" name="delete_organization" class="btn btn-secondary btn-small">Löschen</button>
                             </form>

+ 2 - 2
admin/products.php

@@ -219,7 +219,7 @@ if (empty($currentSizes)) {
 }
 ?>
 
-<div class="panel" style="padding: 2rem;">
+<div class="panel panel-lg">
     <h3><?php echo $editingProduct ? 'Produkt bearbeiten' : 'Neues Produkt anlegen'; ?></h3>
     <form method="POST" enctype="multipart/form-data">
         <?php if ($editingProduct): ?>
@@ -349,7 +349,7 @@ function updateAvailabilityFields() {
                         </td>
                         <td data-label="Aktionen">
                             <a href="?edit=<?php echo (int) $product['id']; ?>" class="btn btn-small">Bearbeiten</a>
-                            <form method="POST" style="display: inline;" onsubmit="return confirm('Produkt wirklich löschen?');">
+                            <form method="POST" class="inline-form" onsubmit="return confirm('Produkt wirklich löschen?');">
                                 <input type="hidden" name="product_id" value="<?php echo (int) $product['id']; ?>">
                                 <button type="submit" name="delete_product" class="btn btn-secondary btn-small">Löschen</button>
                             </form>

+ 1 - 1
admin/settings.php

@@ -43,7 +43,7 @@ include __DIR__ . '/../includes/header.php';
     </div>
 <?php endif; ?>
 
-<div class="panel" style="padding: 2rem;">
+<div class="panel panel-lg">
     <form method="POST">
         <div class="form-group">
             <label for="order_recipient_email">Empfängeradresse für interne Bestellungen *</label>

BIN
assets/branding/stadt-freising-logo.png


+ 324 - 143
assets/css/style.css

@@ -1,17 +1,40 @@
-/* Reset and Base Styles */
+@font-face {
+    font-family: 'Freising Sans';
+    src: url('../fonts/FreisingSans-Regular.woff2') format('woff2');
+    font-weight: 400;
+    font-style: normal;
+    font-display: swap;
+}
+
+@font-face {
+    font-family: 'Freising Sans';
+    src: url('../fonts/FreisingSans-Semibold.woff2') format('woff2');
+    font-weight: 600;
+    font-style: normal;
+    font-display: swap;
+}
+
 :root {
-    --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;
+    --brand-primary: #111111;
+    --brand-primary-soft: #403d31;
+    --brand-danger: #b42318;
+    --brand-danger-dark: #8f1d14;
+    --brand-accent: #ffd71c;
+    --brand-accent-dark: #be9c00;
+    --brand-accent-soft: #fff4b7;
+    --brand-success: #196b3b;
+    --brand-success-soft: #edf8f0;
+    --brand-info: #1e3a5f;
+    --brand-info-soft: #edf3fb;
+    --brand-text: #111111;
+    --brand-muted: #5e5a4d;
+    --brand-surface: #ffffff;
+    --brand-surface-alt: #f8f4e7;
+    --brand-bg: #f4efe1;
+    --brand-bg-alt: #fffdf6;
+    --brand-border: #d7ceb5;
+    --brand-border-strong: #c2b48e;
+    --brand-shadow: rgba(16, 16, 16, 0.08);
 }
 
 * {
@@ -21,32 +44,36 @@
 }
 
 body {
-    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
+    font-family: 'Freising Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
     line-height: 1.6;
     color: var(--brand-text);
-    background-color: var(--brand-bg);
+    background:
+        radial-gradient(circle at top right, rgba(255, 215, 28, 0.22), transparent 22rem),
+        linear-gradient(180deg, #faf7ef 0%, var(--brand-bg) 100%);
 }
 
 a {
-    color: var(--brand-accent);
+    color: var(--brand-primary);
+    text-underline-offset: 0.18em;
 }
 
 a:hover {
-    color: #e0d700;
+    color: var(--brand-primary-soft);
 }
 
 .container {
-    max-width: 1200px;
+    max-width: 1180px;
     margin: 0 auto;
     padding: 0 20px;
 }
 
-/* Header */
 .site-header {
-    background-color: var(--brand-primary);
-    color: white;
-    padding: 0.9rem 0;
-    box-shadow: 0 2px 6px rgba(0,0,0,0.18);
+    background: rgba(255, 255, 255, 0.96);
+    color: var(--brand-text);
+    border-top: 12px solid var(--brand-accent);
+    border-bottom: 1px solid var(--brand-border);
+    backdrop-filter: blur(12px);
+    box-shadow: 0 18px 40px var(--brand-shadow);
 }
 
 .header-inner {
@@ -54,21 +81,22 @@ a:hover {
     align-items: center;
     justify-content: space-between;
     gap: 2rem;
+    padding: 1rem 0;
 }
 
 .brand {
     display: flex;
     align-items: center;
     gap: 1rem;
-    color: white;
+    color: var(--brand-text);
     text-decoration: none;
 }
 
 .brand-logo {
-    height: 52px;
+    height: 70px;
     width: auto;
     display: block;
-    filter: drop-shadow(0 1px 2px rgba(0,0,0,0.25));
+    filter: drop-shadow(0 10px 18px rgba(16, 16, 16, 0.12));
 }
 
 .brand-text {
@@ -78,15 +106,13 @@ a:hover {
 }
 
 .brand-title {
-    font-size: 1.3rem;
-    font-weight: 700;
-    letter-spacing: 0.02em;
+    font-size: 1.45rem;
+    font-weight: 600;
 }
 
 .brand-subtitle {
-    font-size: 0.9rem;
-    font-weight: 500;
-    opacity: 0.9;
+    font-size: 0.95rem;
+    color: var(--brand-muted);
 }
 
 .site-nav {
@@ -96,57 +122,102 @@ a:hover {
 }
 
 .site-nav a {
-    color: white;
+    color: var(--brand-primary);
     text-decoration: none;
     font-weight: 600;
-    letter-spacing: 0.02em;
+    padding-bottom: 0.25rem;
+    border-bottom: 2px solid transparent;
 }
 
 .site-nav a:hover {
-    color: var(--brand-accent);
+    color: var(--brand-primary);
+    border-color: var(--brand-accent);
 }
 
-/* Main Content */
 main {
     min-height: calc(100vh - 200px);
-    padding: 2rem 0;
+    padding: 2.5rem 0 3rem;
+}
+
+h1,
+h2,
+h3,
+h4 {
+    color: var(--brand-primary);
+    line-height: 1.3;
+}
+
+h1 {
+    font-size: 1.9rem;
+    font-weight: 400;
+    margin-bottom: 0.9rem;
+}
+
+h2 {
+    font-size: 1.65rem;
+    font-weight: 600;
+    margin-bottom: 0.9rem;
+}
+
+h3 {
+    font-size: 1.15rem;
+    font-weight: 600;
+    margin-bottom: 0.75rem;
+}
+
+code {
+    display: inline-block;
+    padding: 0.08rem 0.35rem;
+    border-radius: 8px;
+    background: var(--brand-surface-alt);
+    border: 1px solid var(--brand-border);
 }
 
-/* Buttons */
 .btn {
     display: inline-block;
-    padding: 0.75rem 1.5rem;
-    background-color: var(--brand-danger);
-    color: white;
+    padding: 0.72rem 1.5rem;
+    background-color: var(--brand-accent);
+    color: var(--brand-primary);
     text-decoration: none;
-    border: none;
-    border-radius: 4px;
+    border: 2px solid var(--brand-accent);
+    border-radius: 999px;
     cursor: pointer;
-    font-size: 1rem;
-    font-weight: 500;
-    transition: background-color 0.3s;
+    font-size: 0.96rem;
+    font-weight: 600;
+    transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
+    box-shadow: 0 12px 24px rgba(16, 16, 16, 0.08);
 }
 
 .btn:hover {
-    background-color: var(--brand-danger-dark);
+    background-color: var(--brand-accent);
+    border-color: var(--brand-accent-dark);
+    color: var(--brand-primary);
+    transform: translateY(-1px);
 }
 
 .btn-secondary {
-    background-color: transparent;
-    border: 1px solid var(--brand-border);
-    color: var(--brand-text);
+    background-color: #ece7d3;
+    border-color: #cfc7af;
+    color: #5a5648;
 }
 
 .btn-secondary:hover {
-    background-color: var(--brand-surface-alt);
+    background-color: #ded6bc;
+    border-color: #9b9b9b;
+    color: #4c4c4c;
 }
 
 .btn-small {
-    padding: 0.5rem 1rem;
-    font-size: 0.9rem;
+    padding: 0.48rem 1rem;
+    font-size: 0.88rem;
+}
+
+.btn-block {
+    display: block;
+    width: 100%;
+    text-align: center;
 }
 
-/* Product Grid */
 .products-grid {
     display: grid;
     grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
@@ -156,22 +227,24 @@ main {
 
 .product-card {
     background: var(--brand-surface);
-    border-radius: 8px;
+    border-radius: 28px;
     overflow: hidden;
-    box-shadow: 0 2px 8px rgba(0,0,0,0.4);
-    transition: transform 0.3s, box-shadow 0.3s;
+    border: 1px solid var(--brand-border);
+    box-shadow: 0 20px 40px var(--brand-shadow);
+    transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease;
 }
 
 .product-card:hover {
-    transform: translateY(-5px);
-    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
+    transform: translateY(-6px);
+    box-shadow: 0 26px 50px rgba(16, 16, 16, 0.12);
+    border-color: var(--brand-border-strong);
 }
 
 .product-card img {
     width: 100%;
-    height: 200px;
+    height: 220px;
     object-fit: cover;
-    background-color: var(--brand-border);
+    background-color: #ece7d8;
 }
 
 .product-card-content {
@@ -179,14 +252,17 @@ main {
 }
 
 .product-card h3 {
-    font-size: 1.2rem;
-    margin-bottom: 0.5rem;
-    color: var(--brand-accent);
+    margin-bottom: 0.35rem;
+}
+
+.product-card-title-link {
+    color: inherit;
+    text-decoration: none;
 }
 
 .product-card .price {
     font-size: 1.5rem;
-    font-weight: bold;
+    font-weight: 600;
     color: var(--brand-text);
     margin: 0.5rem 0;
 }
@@ -210,7 +286,6 @@ main {
     color: var(--brand-muted);
 }
 
-/* Forms */
 .form-group {
     margin-bottom: 1.5rem;
 }
@@ -225,12 +300,12 @@ main {
 .form-group textarea,
 .form-group select {
     width: 100%;
-    padding: 0.75rem;
+    padding: 0.8rem 0.95rem;
     border: 1px solid var(--brand-border);
-    border-radius: 4px;
+    border-radius: 18px;
     font-size: 1rem;
     font-family: inherit;
-    background: var(--brand-surface-alt);
+    background: var(--brand-bg-alt);
     color: var(--brand-text);
 }
 
@@ -243,16 +318,22 @@ main {
 .form-group select:focus {
     outline: none;
     border-color: var(--brand-accent);
-    box-shadow: 0 0 0 3px rgba(202, 195, 0, 0.2);
+    box-shadow: 0 0 0 3px rgba(255, 215, 28, 0.25);
+}
+
+.form-group small {
+    display: block;
+    margin-top: 0.45rem;
+    color: var(--brand-muted);
 }
 
-/* Cart */
 .cart-item {
     background: var(--brand-surface);
     padding: 1.5rem;
     margin-bottom: 1rem;
-    border-radius: 8px;
-    box-shadow: 0 2px 4px rgba(0,0,0,0.35);
+    border-radius: 24px;
+    border: 1px solid var(--brand-border);
+    box-shadow: 0 18px 36px var(--brand-shadow);
     display: flex;
     justify-content: space-between;
     align-items: center;
@@ -274,13 +355,13 @@ main {
     text-align: center;
 }
 
-/* Tables */
 table {
     width: 100%;
     background: var(--brand-surface);
     border-collapse: collapse;
-    box-shadow: 0 2px 4px rgba(0,0,0,0.35);
-    border-radius: 8px;
+    border: 1px solid var(--brand-border);
+    box-shadow: 0 18px 36px var(--brand-shadow);
+    border-radius: 24px;
     overflow: hidden;
 }
 
@@ -301,7 +382,6 @@ table tr:hover {
     background-color: var(--brand-surface-alt);
 }
 
-/* Responsive table wrapper: full width, horizontal scroll when needed, cell wrapping */
 .table-responsive {
     width: 100%;
     overflow-x: auto;
@@ -327,40 +407,42 @@ body.admin-page .container {
     padding-right: 1rem;
 }
 
-/* Alerts */
 .alert {
     padding: 1rem;
-    border-radius: 4px;
+    border-radius: 20px;
     margin-bottom: 1.5rem;
-    background: var(--brand-surface);
+    background: var(--brand-bg-alt);
     color: var(--brand-text);
     border: 1px solid var(--brand-border);
 }
 
 .alert-success {
-    border-color: var(--brand-accent);
+    background: var(--brand-success-soft);
+    border-color: var(--brand-success);
 }
 
 .alert-error {
     border-color: var(--brand-danger);
+    background: #fff3f1;
 }
 
 .alert-info {
-    border-color: #ffffff;
+    background: var(--brand-info-soft);
+    border-color: rgba(30, 58, 95, 0.2);
 }
 
 .alert-warning {
     border-color: var(--brand-accent);
+    background: #fff9da;
 }
 
-/* Main page disclaimer */
 .disclaimer-box {
     margin: 1rem 0 1.5rem;
-    padding: 1rem;
+    padding: 1.2rem 1.3rem;
     border: 1px solid var(--brand-border);
-    border-left: 4px solid var(--brand-accent);
-    background: var(--brand-surface);
-    border-radius: 6px;
+    border-left: 6px solid var(--brand-accent);
+    background: linear-gradient(135deg, rgba(255, 215, 28, 0.16), rgba(255, 255, 255, 0.92));
+    border-radius: 22px;
 }
 
 .disclaimer-box p + p {
@@ -383,50 +465,65 @@ body.admin-page .container {
     white-space: nowrap;
 }
 
-/* Footer */
-footer {
-    background-color: var(--brand-primary);
-    color: white;
-    text-align: center;
+.site-footer {
+    background: var(--brand-surface);
+    border-top: 6px solid var(--brand-accent);
     padding: 2rem 0;
     margin-top: 3rem;
 }
 
+.site-footer-inner {
+    display: flex;
+    justify-content: space-between;
+    gap: 2rem;
+    align-items: flex-start;
+}
+
+.site-footer-title {
+    font-weight: 600;
+    margin-bottom: 0.2rem;
+}
+
+.site-footer-meta,
+.site-footer-address {
+    color: var(--brand-muted);
+    margin-bottom: 0.15rem;
+}
+
 .footer-links {
-    margin-top: 0.8rem;
     display: flex;
     flex-wrap: wrap;
-    justify-content: center;
+    justify-content: flex-end;
     gap: 0.5rem 1rem;
 }
 
 .footer-links a {
-    color: #ffffff;
-    text-decoration: underline;
+    color: var(--brand-primary);
+    text-decoration: none;
+    font-weight: 600;
 }
 
 .footer-links a:hover {
-    color: var(--brand-accent);
+    color: var(--brand-primary);
+    text-decoration: underline;
 }
 
-/* Reservation Code */
 .order-number {
     background: var(--brand-surface-alt);
     border: 2px solid var(--brand-accent);
     padding: 2rem;
-    border-radius: 8px;
+    border-radius: 24px;
     text-align: center;
     margin: 2rem 0;
 }
 
 .order-number h2 {
     font-size: 2rem;
-    color: var(--brand-accent);
+    color: var(--brand-primary);
     letter-spacing: 0.2rem;
     font-family: 'Courier New', monospace;
 }
 
-/* Admin Styles */
 .admin-header {
     display: flex;
     justify-content: space-between;
@@ -468,8 +565,8 @@ footer {
     min-width: 220px;
     background: var(--brand-surface);
     border: 1px solid var(--brand-border);
-    border-radius: 6px;
-    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.35);
+    border-radius: 20px;
+    box-shadow: 0 18px 36px var(--brand-shadow);
     z-index: 30;
     padding: 0.35rem;
 }
@@ -522,8 +619,9 @@ footer {
 .stat-card {
     background: var(--brand-surface);
     padding: 1.5rem;
-    border-radius: 8px;
-    box-shadow: 0 2px 4px rgba(0,0,0,0.35);
+    border-radius: 24px;
+    border: 1px solid var(--brand-border);
+    box-shadow: 0 18px 36px var(--brand-shadow);
 }
 
 .stat-card h3 {
@@ -534,73 +632,95 @@ footer {
 
 .stat-card .stat-value {
     font-size: 2rem;
-    font-weight: bold;
-    color: var(--brand-accent);
+    font-weight: 600;
+    color: var(--brand-primary);
 }
 
 .panel {
     background: var(--brand-surface);
     border: 1px solid var(--brand-border);
     padding: 1.5rem;
-    border-radius: 8px;
-    box-shadow: 0 2px 4px rgba(0,0,0,0.35);
+    border-radius: 24px;
+    box-shadow: 0 18px 36px var(--brand-shadow);
     margin-bottom: 2rem;
 }
 
+.panel-lg {
+    padding: 2rem;
+}
+
+.panel-compact {
+    padding: 1rem;
+    margin-bottom: 1rem;
+}
+
 .status {
     display: inline-block;
-    padding: 0.2rem 0.6rem;
+    padding: 0.3rem 0.8rem;
     border-radius: 999px;
     font-size: 0.85rem;
     font-weight: 600;
     letter-spacing: 0.02em;
     border: 1px solid transparent;
+    background: rgba(255, 255, 255, 0.7);
 }
 
 .status-open {
-    color: var(--brand-accent);
-    border-color: var(--brand-accent);
+    color: var(--brand-primary);
+    background: #fff4b7;
+    border-color: #e0c038;
 }
 
-.status-notified,
-.status-picked {
-    color: #ffffff;
-    border-color: #ffffff;
+.status-notified {
+    color: var(--brand-info);
+    background: var(--brand-info-soft);
+    border-color: rgba(30, 58, 95, 0.2);
+}
+
+.status-picked,
+.status-processed {
+    color: var(--brand-success);
+    background: var(--brand-success-soft);
+    border-color: rgba(25, 107, 59, 0.2);
 }
 
 .status-expired {
     color: var(--brand-danger);
-    border-color: var(--brand-danger);
+    background: #fff3f1;
+    border-color: rgba(180, 35, 24, 0.2);
 }
 
 .status-hidden {
     color: #9ca3af;
     border-color: #6b7280;
+    background: #f1f3f5;
 }
 
 .status-unconfirmed {
-    color: #facc15;
-    border-color: #facc15;
+    color: var(--brand-primary);
+    background: #fff0a3;
+    border-color: #e0c038;
 }
 
 .status-partial {
-    color: #93c5fd;
-    border-color: #93c5fd;
+    color: #7c6517;
+    background: #f9efcb;
+    border-color: #e2ce7f;
 }
 
-.status-processed {
-    color: #86efac;
-    border-color: #86efac;
+.status-cancelled {
+    color: var(--brand-danger);
+    background: #fff3f1;
+    border-color: rgba(180, 35, 24, 0.2);
 }
 
-.status-cancelled {
-    color: #fca5a5;
-    border-color: #fca5a5;
+.status-self {
+    margin-left: 0.5rem;
 }
 
 .order-highlight {
     font-size: 1.5rem;
-    color: var(--brand-accent);
+    color: var(--brand-primary);
     letter-spacing: 0.2rem;
     font-family: 'Courier New', monospace;
 }
@@ -622,7 +742,7 @@ footer {
     background: var(--brand-surface);
     color: var(--brand-text);
     padding: 2rem;
-    border-radius: 8px;
+    border-radius: 24px;
     max-width: 1000px;
     max-height: 95vh;
     overflow-y: auto;
@@ -640,11 +760,11 @@ footer {
     width: 100%;
     height: 400px;
     background-color: var(--brand-surface-alt);
-    border-radius: 8px;
+    border-radius: 28px;
     display: flex;
     align-items: center;
     justify-content: center;
-    color: var(--brand-text);
+    color: var(--brand-muted);
 }
 
 .table-compact th,
@@ -653,7 +773,6 @@ footer {
     font-size: 0.9rem;
 }
 
-/* Product Detail Grid */
 .product-detail-grid {
     display: grid;
     grid-template-columns: 1fr 1fr;
@@ -661,7 +780,6 @@ footer {
     margin-top: 2rem;
 }
 
-/* Checkout two-column layout (responsive via media query) */
 .checkout-grid {
     display: grid;
     grid-template-columns: 1fr 1fr;
@@ -669,7 +787,25 @@ footer {
     margin-top: 2rem;
 }
 
-/* Cart total / actions block */
+.product-image {
+    width: 100%;
+    border-radius: 28px;
+    box-shadow: 0 20px 40px var(--brand-shadow);
+}
+
+.product-description-block {
+    margin: 1.5rem 0 2rem;
+}
+
+.product-description {
+    margin-top: 0.5rem;
+    line-height: 1.8;
+}
+
+.product-form {
+    margin-top: 2rem;
+}
+
 .cart-actions {
     text-align: right;
     margin: 2rem 0;
@@ -692,7 +828,51 @@ footer {
     margin-left: 0;
 }
 
-/* Responsive */
+.container-narrow {
+    max-width: 400px;
+}
+
+.page-top-gap {
+    margin-top: 4rem;
+}
+
+.inline-form {
+    display: inline;
+}
+
+.admin-filter-form {
+    display: flex;
+    gap: 1rem;
+    align-items: end;
+    flex-wrap: wrap;
+}
+
+.admin-filter-field {
+    min-width: 220px;
+}
+
+.admin-filter-field-wide {
+    flex: 1;
+}
+
+.admin-filter-actions {
+    display: flex;
+    gap: 0.5rem;
+    flex-wrap: wrap;
+}
+
+.section-title {
+    margin-bottom: 1rem;
+}
+
+.list-indent {
+    margin-left: 1.5rem;
+}
+
+.is-hidden {
+    display: none;
+}
+
 @media (max-width: 768px) {
     .header-inner {
         flex-direction: column;
@@ -706,11 +886,11 @@ footer {
 
     .footer-links {
         flex-direction: column;
-        align-items: center;
+        align-items: flex-start;
     }
 
     .brand-logo {
-        height: 46px;
+        height: 62px;
     }
 
     .products-grid {
@@ -807,8 +987,11 @@ footer {
     .admin-stats {
         grid-template-columns: 1fr;
     }
+
+    .site-footer-inner {
+        flex-direction: column;
+    }
     
-    /* Admin tables: use full width, allow buttons to wrap */
     body.admin-page .table-responsive {
         margin-left: -1rem;
         margin-right: -1rem;
@@ -827,7 +1010,6 @@ footer {
         padding-right: 1rem;
     }
 
-    /* Admin tables: stack rows into blocks on narrow screens */
     body.admin-page table.responsive-table {
         border: 0;
         box-shadow: none;
@@ -846,9 +1028,9 @@ footer {
         display: block;
         background: var(--brand-surface);
         border: 1px solid var(--brand-border);
-        border-radius: 8px;
+        border-radius: 20px;
         margin-bottom: 0.8rem;
-        box-shadow: 0 2px 4px rgba(0,0,0,0.35);
+        box-shadow: 0 18px 36px var(--brand-shadow);
     }
 
     body.admin-page table.responsive-table td {
@@ -906,13 +1088,11 @@ footer {
         gap: 1rem;
     }
     
-    /* Very narrow: admin tables scroll so columns stay readable */
     .table-responsive table:not(.responsive-table) {
         min-width: 520px;
     }
 }
 
-/* Utility Classes */
 .text-center {
     text-align: center;
 }
@@ -920,6 +1100,7 @@ footer {
 .mt-1 { margin-top: 0.5rem; }
 .mt-2 { margin-top: 1rem; }
 .mt-3 { margin-top: 1.5rem; }
+.mt-4 { margin-top: 2rem; }
 .mb-1 { margin-bottom: 0.5rem; }
 .mb-2 { margin-bottom: 1rem; }
 .mb-3 { margin-bottom: 1.5rem; }

BIN
assets/fonts/FreisingSans-Regular.woff2


BIN
assets/fonts/FreisingSans-Semibold.woff2


+ 7 - 2
cart.php

@@ -9,12 +9,19 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['remove_item_index']))
 }
 
 $cartItems = getCartItemsDetailed();
+$cartNotice = consumeFlashMessage('cart_notice');
 
 include __DIR__ . '/includes/header.php';
 ?>
 
 <h2>Warenkorb</h2>
 
+<?php if ($cartNotice !== null): ?>
+    <div class="alert alert-<?php echo escape($cartNotice['type']); ?>">
+        <?php echo escape($cartNotice['message']); ?>
+    </div>
+<?php endif; ?>
+
 <?php if (empty($cartItems)): ?>
     <div class="alert alert-info">
         <p>Ihr Warenkorb ist leer.</p>
@@ -31,7 +38,6 @@ include __DIR__ . '/includes/header.php';
                 <?php if ($cartItem['availability_label'] !== ''): ?>
                     <p><strong>Lieferhinweis:</strong> <?php echo escape($cartItem['availability_label']); ?></p>
                 <?php endif; ?>
-                <p><strong>Menge:</strong> 1</p>
             </div>
             <div class="cart-item-actions">
                 <form method="POST">
@@ -42,7 +48,6 @@ include __DIR__ . '/includes/header.php';
     <?php endforeach; ?>
 
     <div class="cart-actions">
-        <div class="cart-total">Artikel im Warenkorb: <?php echo count($cartItems); ?></div>
         <div class="cart-buttons">
             <a href="index.php" class="btn btn-secondary">Weiter auswählen</a>
             <a href="checkout.php" class="btn">Zur Bestellung</a>

+ 4 - 5
checkout.php

@@ -35,7 +35,7 @@ include __DIR__ . '/includes/header.php';
 
 <?php if (!empty($errors)): ?>
     <div class="alert alert-error">
-        <ul style="margin-left: 1.5rem;">
+        <ul class="list-indent">
             <?php foreach ($errors as $error): ?>
                 <li><?php echo escape($error); ?></li>
             <?php endforeach; ?>
@@ -48,12 +48,11 @@ include __DIR__ . '/includes/header.php';
         <h3>Ihre Auswahl</h3>
 
         <?php foreach ($cartItems as $cartItem): ?>
-            <div class="panel" style="padding: 1rem; margin-bottom: 1rem;">
+            <div class="panel panel-compact">
                 <strong><?php echo escape($cartItem['product']['name']); ?></strong><br>
                 <?php if ($cartItem['size'] !== ''): ?>
                     Größe: <?php echo escape($cartItem['size']); ?><br>
                 <?php endif; ?>
-                Menge: 1<br>
                 <?php if ($cartItem['availability_label'] !== ''): ?>
                     Lieferhinweis: <?php echo escape($cartItem['availability_label']); ?>
                 <?php endif; ?>
@@ -99,10 +98,10 @@ include __DIR__ . '/includes/header.php';
                 <?php endif; ?>
             </div>
 
-            <button type="submit" name="create_order" class="btn" style="width: 100%;">Bestellung absenden</button>
+            <button type="submit" name="create_order" class="btn btn-block">Bestellung absenden</button>
         </form>
 
-        <div style="margin-top: 1rem;">
+        <div class="mt-2">
             <a href="cart.php" class="btn btn-secondary">Zurück zum Warenkorb</a>
         </div>
     </div>

+ 10 - 4
config.sample.php

@@ -2,12 +2,18 @@
 // Configuration for the PSA order system.
 
 // Site settings
-define('SITE_NAME', 'PSA-Bestellung Feuerwehr Freising');
+define('SITE_NAME', 'Stadt Freising');
+define('SITE_SERVICE_NAME', 'PSA-Service');
+define('SITE_DEPARTMENT_NAME', 'Amt 32 - Öffentliche Sicherheit und Ordnung');
+define('SITE_ADDRESS_LINE', 'Dr.-von-Daller-Straße 7, 85354 Freising');
+define('SITE_IMPRINT_URL', 'https://www.freising.de/impressum/');
+define('SITE_PRIVACY_URL', 'https://www.freising.de/datenschutz');
+define('SITE_FULL_NAME', SITE_NAME . ' - ' . SITE_SERVICE_NAME);
 define('SITE_URL', '/shop'); // Leave empty for root, or use absolute URL
 
 define('DISCLAIMER_LINES', [
-    'Dieses System dient der internen Bestellung von persönlicher Schutzausrüstung für die Feuerwehr Freising.',
-    'Bestellungen werden ausschließlich intern bearbeitet.',
+    'Dieses System dient der internen Bestellung persönlicher Schutzausrüstung der Stadt Freising.',
+    'Die Bearbeitung erfolgt durch Amt 32 - Öffentliche Sicherheit und Ordnung.',
 ]);
 
 // Admin settings
@@ -23,7 +29,7 @@ define('ATTACH_ORDER_PDF_TO_ADMIN_EMAIL', true);
 // Email settings
 define('ADMIN_EMAIL', 'psa@feuerwehr-freising.de'); // Fallback for admin profile email defaults
 define('FROM_EMAIL', 'shop@example.org');
-define('FROM_NAME', SITE_NAME);
+define('FROM_NAME', SITE_FULL_NAME);
 
 // Data file paths
 define('DATA_DIR', __DIR__ . '/data/');

+ 7 - 1
docs/CONFIG_REFERENCE.md

@@ -4,7 +4,13 @@
 
 | Konstante | Zweck |
 |---|---|
-| `SITE_NAME` | Name des Systems in Oberfläche und Mails |
+| `SITE_NAME` | Primärer Markenname in Oberfläche und Mails |
+| `SITE_SERVICE_NAME` | Servicename im Header und in Betreffzeilen |
+| `SITE_DEPARTMENT_NAME` | Fachbereichskennzeichnung für Footer und Kommunikation |
+| `SITE_ADDRESS_LINE` | Postanschrift des verantwortlichen Amts im Footer |
+| `SITE_IMPRINT_URL` | Ziel-URL für den Impressumslink |
+| `SITE_PRIVACY_URL` | Ziel-URL für den Datenschutzlink |
+| `SITE_FULL_NAME` | Kombinierter Anzeigename aus Marke und Service |
 | `SITE_URL` | Basispfad oder Basis-URL für Links, Assets und Bestätigungslinks |
 | `DISCLAIMER_LINES` | Hinweistext auf der Startseite |
 | `ORDER_PREFIX` | Präfix für Bestellnummern |

+ 10 - 6
includes/footer.php

@@ -1,12 +1,16 @@
         </div>
     </main>
-    <footer>
-        <div class="container">
-            <p>Freiwillige Feuerwehr Freising e.V.</p>
+    <footer class="site-footer">
+        <div class="container site-footer-inner">
+            <div class="site-footer-copy">
+                <p class="site-footer-title"><?php echo escape(SITE_NAME); ?></p>
+                <p class="site-footer-meta"><?php echo escape(SITE_DEPARTMENT_NAME); ?></p>
+                <p class="site-footer-address"><?php echo escape(SITE_ADDRESS_LINE); ?></p>
+            </div>
             <nav class="footer-links" aria-label="Rechtliche Hinweise">
-                <a href="https://www.feuerwehr-freising.de/impressum">Impressum</a>
-                <a href="https://www.feuerwehr-freising.de/datenschutz/">Datenschutz</a>
-                <a href="<?php echo SITE_URL; ?>/admin/">Admin</a>
+                <a href="<?php echo escape(SITE_IMPRINT_URL); ?>">Impressum</a>
+                <a href="<?php echo escape(SITE_PRIVACY_URL); ?>">Datenschutz</a>
+                <a href="<?php echo escape(SITE_URL); ?>/admin/">Admin</a>
             </nav>
         </div>
     </footer>

+ 104 - 31
includes/functions.php

@@ -32,6 +32,47 @@ function escape($value) {
     return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
 }
 
+function setFlashMessage($key, $type, $message) {
+    $key = trim((string) $key);
+    $type = trim((string) $type);
+    $message = trim((string) $message);
+
+    if ($key === '' || $type === '' || $message === '') {
+        return;
+    }
+
+    $_SESSION['flash_messages'][$key] = [
+        'type' => $type,
+        'message' => $message,
+    ];
+}
+
+function consumeFlashMessage($key) {
+    $key = trim((string) $key);
+    if ($key === '') {
+        return null;
+    }
+
+    $messages = $_SESSION['flash_messages'] ?? [];
+    if (!is_array($messages) || !isset($messages[$key]) || !is_array($messages[$key])) {
+        return null;
+    }
+
+    $message = $messages[$key];
+    unset($_SESSION['flash_messages'][$key]);
+
+    $type = trim((string) ($message['type'] ?? ''));
+    $text = trim((string) ($message['message'] ?? ''));
+    if ($type === '' || $text === '') {
+        return null;
+    }
+
+    return [
+        'type' => $type,
+        'message' => $text,
+    ];
+}
+
 function normalizeAdminUsername($username) {
     return trim((string) $username);
 }
@@ -592,7 +633,7 @@ function getDefaultOrganizations() {
     return [
         [
             'id' => 'feuerwehr-freising',
-            'label' => 'Feuerwehr Freising',
+            'label' => 'Amt 32 - Feuerwehr Freising',
             'sort_order' => 10,
             'active' => true,
         ],
@@ -1315,7 +1356,6 @@ function getCart() {
     }
 
     $normalized = [];
-    $seen = [];
 
     foreach ($cart as $item) {
         $productId = isset($item['product_id']) ? (int) $item['product_id'] : 0;
@@ -1334,13 +1374,11 @@ function getCart() {
             $size = '';
         }
 
-        $key = $productId . '|' . $size;
-        if (isset($seen[$key])) {
-            continue;
+        if (isset($normalized[$productId])) {
+            unset($normalized[$productId]);
         }
 
-        $seen[$key] = true;
-        $normalized[] = [
+        $normalized[$productId] = [
             'product_id' => $productId,
             'size' => $size,
         ];
@@ -1355,23 +1393,48 @@ function addCartItem($productId, $size = '') {
     $size = trim((string) $size);
     $product = getProductById($productId);
     if ($product === null) {
-        return false;
+        return [
+            'success' => false,
+            'status' => 'error',
+        ];
     }
 
     $sizes = getProductSizes($product);
     if (!empty($sizes)) {
         if ($size === '' || !in_array($size, $sizes, true)) {
-            return false;
+            return [
+                'success' => false,
+                'status' => 'error',
+            ];
         }
     } else {
         $size = '';
     }
 
     $cart = getCart();
-    foreach ($cart as $item) {
-        if ((int) $item['product_id'] === $productId && (string) $item['size'] === $size) {
-            return true;
+    foreach ($cart as $index => $item) {
+        if ((int) $item['product_id'] !== $productId) {
+            continue;
+        }
+
+        $existingSize = trim((string) ($item['size'] ?? ''));
+        if ($existingSize === $size) {
+            return [
+                'success' => true,
+                'status' => 'unchanged',
+                'size' => $size,
+            ];
         }
+
+        $cart[$index]['size'] = $size;
+        $_SESSION['cart'] = array_values($cart);
+
+        return [
+            'success' => true,
+            'status' => 'replaced',
+            'size' => $size,
+            'previous_size' => $existingSize,
+        ];
     }
 
     $cart[] = [
@@ -1380,7 +1443,11 @@ function addCartItem($productId, $size = '') {
     ];
 
     $_SESSION['cart'] = array_values($cart);
-    return true;
+    return [
+        'success' => true,
+        'status' => 'added',
+        'size' => $size,
+    ];
 }
 
 function removeCartItemByIndex($index) {
@@ -1444,7 +1511,7 @@ function buildOrderItemsHtml($order) {
         if (!empty($item['availability_label'])) {
             $label .= '<br><small>' . nl2br(escape($item['availability_label'])) . '</small>';
         }
-        $parts[] = '<li style="margin: 0 0 0.6rem 0; padding: 0.75rem 0.9rem; background: #28292a; border: 1px solid #4a5263; border-radius: 6px;">' . $label . '</li>';
+        $parts[] = '<li style="margin: 0 0 0.75rem 0; padding: 0.9rem 1rem; background: #f8f4e7; border: 1px solid #d7ceb5; border-radius: 18px; color: #111111;">' . $label . '</li>';
     }
 
     return '<ul style="list-style: none; margin: 0; padding: 0;">' . implode('', $parts) . '</ul>';
@@ -1458,34 +1525,39 @@ function buildOrderSummaryHtml($order, $title, $introHtml, $extraHtml = '') {
     <head>
         <meta charset="UTF-8">
     </head>
-    <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #f5f7fb; background: #28292a; padding: 1.5rem;">
-        <div style="max-width: 720px; margin: 0 auto; background: #2f3541; padding: 1.5rem 2rem; border-radius: 10px; border: 1px solid #3b4252;">
-            <h2 style="color: #cac300; margin-top: 0;">' . escape($title) . '</h2>
+    <body style="font-family: \'Freising Sans\', Arial, sans-serif; line-height: 1.6; color: #111111; background: #f4efe1; padding: 1.5rem;">
+        <div style="max-width: 720px; margin: 0 auto; background: #ffffff; padding: 2rem; border-radius: 28px; border: 1px solid #d7ceb5; box-shadow: 0 18px 40px rgba(16, 16, 16, 0.08);">
+            <p style="margin: 0 0 0.5rem; font-size: 0.8rem; letter-spacing: 0.08em; text-transform: uppercase; color: #5e5a4d;">' . escape(SITE_DEPARTMENT_NAME) . '</p>
+            <h1 style="margin: 0; font-size: 1.75rem; line-height: 1.2; color: #111111;">' . escape(SITE_NAME) . '</h1>
+            <p style="margin: 0.25rem 0 1.5rem; font-size: 1rem; color: #5e5a4d;">' . escape(SITE_SERVICE_NAME) . '</p>
+            <div style="width: 80px; height: 6px; border-radius: 999px; background: #ffd71c; margin-bottom: 1.5rem;"></div>
+            <h2 style="color: #111111; margin-top: 0;">' . escape($title) . '</h2>
             ' . $introHtml . '
-            <div style="background: #28292a; border: 2px solid #cac300; padding: 1.5rem; margin: 1.5rem 0; border-radius: 8px;">
+            <div style="background: #f8f4e7; border: 2px solid #ffd71c; padding: 1.5rem; margin: 1.5rem 0; border-radius: 20px;">
                 <h3 style="margin-top: 0;">Bestellnummer</h3>
-                <p style="margin: 0; color: #cac300; font-family: monospace;">' . escape($order['id']) . '</p>
+                <p style="margin: 0; color: #111111; font-family: monospace; font-size: 1.05rem;">' . escape($order['id']) . '</p>
             </div>
             <p><strong>Name:</strong> ' . escape($order['customer_name']) . '</p>
             <p><strong>E-Mail:</strong> ' . escape($order['customer_email']) . '</p>
             <p><strong>Organisation:</strong> ' . escape($order['organization_label']) . '</p>
             <p><strong>Erstellt am:</strong> ' . escape(formatDate($order['created_at'])) . '</p>
             <h3>Bestellte Artikel</h3>
-            <div style="background: #303745; border: 1px solid #4a5263; border-left: 4px solid #cac300; border-radius: 8px; padding: 1rem;">' . $itemsHtml . '</div>
+            <div style="background: #fffdf6; border: 1px solid #d7ceb5; border-left: 6px solid #ffd71c; border-radius: 20px; padding: 1rem;">' . $itemsHtml . '</div>
             <p><strong>Kommentar:</strong><br>' . ($order['comment'] !== '' ? nl2br(escape($order['comment'])) : 'Kein Kommentar') . '</p>
             ' . $extraHtml . '
+            <p style="margin: 2rem 0 0; color: #5e5a4d; font-size: 0.9rem;">' . escape(SITE_NAME) . ' | ' . escape(SITE_DEPARTMENT_NAME) . '<br>' . escape(SITE_ADDRESS_LINE) . '</p>
         </div>
     </body>
     </html>';
 }
 
 function sendOrderConfirmationRequestEmail($order) {
-    $subject = 'Bitte Bestellung bestätigen: ' . $order['id'];
+    $subject = SITE_SERVICE_NAME . ': Bestellung bestätigen - ' . $order['id'];
     $link = buildOrderConfirmationUrl($order['confirmation_token']);
     $expiryText = formatDate($order['confirmation_expires_at']);
-    $intro = '<p>Guten Tag ' . escape($order['customer_name']) . ',</p><p>bitte bestätigen Sie Ihre PSA-Bestellung über den folgenden Link.</p>';
+    $intro = '<p>Guten Tag ' . escape($order['customer_name']) . ',</p><p>bitte bestätigen Sie Ihre Bestellung im ' . escape(SITE_SERVICE_NAME) . ' der Stadt Freising über den folgenden Link.</p>';
     $extra = '
-        <p><a href="' . escape($link) . '" style="display: inline-block; padding: 0.75rem 1.25rem; background: #cf2e2e; color: #ffffff; text-decoration: none; border-radius: 4px;">Bestellung bestätigen</a></p>
+        <p><a href="' . escape($link) . '" style="display: inline-block; padding: 0.75rem 1.5rem; background: #ffd71c; color: #111111; text-decoration: none; border: 2px solid #ffd71c; border-radius: 999px; font-weight: 700;">Bestellung bestätigen</a></p>
         <p>Der Link ist gültig bis: <strong>' . escape($expiryText) . '</strong></p>
         <p>Falls der Button nicht funktioniert, verwenden Sie bitte diesen Link:<br>' . escape($link) . '</p>';
     $message = buildOrderSummaryHtml($order, 'Bestellung bestätigen', $intro, $extra);
@@ -1494,16 +1566,16 @@ function sendOrderConfirmationRequestEmail($order) {
 }
 
 function sendOrderCreatedCustomerEmail($order) {
-    $subject = 'Ihre PSA-Bestellung: ' . $order['id'];
-    $intro = '<p>Guten Tag ' . escape($order['customer_name']) . ',</p><p>Ihre Bestellung wurde erfasst und an die zuständige Stelle weitergeleitet.</p>';
+    $subject = SITE_SERVICE_NAME . ': Ihre Bestellung - ' . $order['id'];
+    $intro = '<p>Guten Tag ' . escape($order['customer_name']) . ',</p><p>Ihre Bestellung wurde erfasst und an ' . escape(SITE_DEPARTMENT_NAME) . ' weitergeleitet.</p>';
     $message = buildOrderSummaryHtml($order, 'Bestellung eingegangen', $intro);
 
     return sendEmail($order['customer_email'], $subject, $message);
 }
 
 function sendOrderConfirmedCustomerEmail($order) {
-    $subject = 'Ihre PSA-Bestellung wurde bestätigt: ' . $order['id'];
-    $intro = '<p>Guten Tag ' . escape($order['customer_name']) . ',</p><p>Ihre Bestellung wurde bestätigt und intern weitergeleitet.</p>';
+    $subject = SITE_SERVICE_NAME . ': Bestellung bestätigt - ' . $order['id'];
+    $intro = '<p>Guten Tag ' . escape($order['customer_name']) . ',</p><p>Ihre Bestellung wurde bestätigt und an ' . escape(SITE_DEPARTMENT_NAME) . ' weitergeleitet.</p>';
     $message = buildOrderSummaryHtml($order, 'Bestellung bestätigt', $intro);
 
     return sendEmail($order['customer_email'], $subject, $message);
@@ -1515,8 +1587,8 @@ function sendConfirmedOrderAdminNotification($order) {
         return false;
     }
 
-    $subject = 'Neue PSA-Bestellung: ' . $order['id'];
-    $intro = '<p>Eine neue PSA-Bestellung wurde freigegeben und muss bearbeitet werden.</p>';
+    $subject = SITE_SERVICE_NAME . ': Neue Bestellung - ' . $order['id'];
+    $intro = '<p>Eine neue Bestellung im ' . escape(SITE_SERVICE_NAME) . ' der Stadt Freising wurde freigegeben und muss bearbeitet werden.</p>';
     $message = buildOrderSummaryHtml($order, 'Neue PSA-Bestellung', $intro);
 
     $attachments = [];
@@ -1584,9 +1656,10 @@ function sendEmail($to, $subject, $message, $isHtml = true, $attachments = []) {
 
 function buildOrderPdfLines($order) {
     $lines = [
-        SITE_NAME,
+        SITE_FULL_NAME,
+        SITE_DEPARTMENT_NAME,
         '',
-        'PSA-Bestellung',
+        'Bestellservice',
         'Bestellnummer: ' . $order['id'],
         'Erstellt am: ' . formatDate($order['created_at']),
         'Name: ' . $order['customer_name'],

+ 4 - 4
includes/header.php

@@ -3,7 +3,7 @@
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title><?php echo isset($pageTitle) ? escape($pageTitle) . ' - ' : ''; ?><?php echo escape(SITE_NAME); ?></title>
+    <title><?php echo isset($pageTitle) ? escape($pageTitle) . ' - ' : ''; ?><?php echo escape(SITE_FULL_NAME); ?></title>
     <link rel="shortcut icon" href="<?php echo escape(SITE_URL); ?>/favicon.png">
     <link rel="stylesheet" href="<?php echo escape(SITE_URL); ?>/assets/css/style.css">
 </head>
@@ -11,13 +11,13 @@
     <header class="site-header">
         <div class="container header-inner">
             <a class="brand" href="<?php echo escape(SITE_URL); ?>/index.php">
-                <img class="brand-logo" src="<?php echo escape(SITE_URL); ?>/assets/feuerwehr-Logo-invers.webp" alt="Freiwillige Feuerwehr Freising Logo">
+                <img class="brand-logo" src="<?php echo escape(SITE_URL); ?>/assets/branding/stadt-freising-logo.png" alt="Wappen der Stadt Freising">
                 <div class="brand-text">
                     <span class="brand-title"><?php echo escape(SITE_NAME); ?></span>
-                    <span class="brand-subtitle">PSA-Bestellung</span>
+                    <span class="brand-subtitle"><?php echo escape(SITE_SERVICE_NAME); ?></span>
                 </div>
             </a>
-            <nav class="site-nav">
+            <nav class="site-nav" aria-label="Hauptnavigation">
                 <a href="<?php echo escape(SITE_URL); ?>/index.php">Startseite</a>
                 <a href="<?php echo escape(SITE_URL); ?>/cart.php">Warenkorb</a>
                 <a href="<?php echo escape(SITE_URL); ?>/faq.php">FAQ</a>

+ 3 - 3
index.php

@@ -18,7 +18,7 @@ if ($category !== '' && getCategoryById($category) !== null) {
 include __DIR__ . '/includes/header.php';
 ?>
 
-<h2>PSA bestellen</h2>
+<h2>PSA-Service</h2>
 
 <div class="disclaimer-box">
     <?php foreach (DISCLAIMER_LINES as $line): ?>
@@ -51,9 +51,9 @@ include __DIR__ . '/includes/header.php';
                     <?php endif; ?>
                 </a>
                 <div class="product-card-content">
-                    <h3><a href="product.php?id=<?php echo (int) $product['id']; ?>" style="text-decoration: none; color: inherit;"><?php echo escape($product['name']); ?></a></h3>
+                    <h3><a href="product.php?id=<?php echo (int) $product['id']; ?>" class="product-card-title-link"><?php echo escape($product['name']); ?></a></h3>
                     <p class="product-summary"><?php echo escape(implode(', ', getProductSizes($product))); ?></p>
-                    <a href="product.php?id=<?php echo (int) $product['id']; ?>" class="btn" style="width: 100%; text-align: center; margin-top: 1rem;">Details ansehen</a>
+                    <a href="product.php?id=<?php echo (int) $product['id']; ?>" class="btn btn-block mt-2">Details ansehen</a>
                 </div>
             </div>
         <?php endforeach; ?>

+ 1 - 1
order-confirm.php

@@ -13,7 +13,7 @@ include __DIR__ . '/includes/header.php';
 
 <?php if ($result['success']): ?>
     <div class="alert alert-success">
-        <p>Die Bestellung wurde bestätigt und intern weitergeleitet.</p>
+        <p>Die Bestellung wurde bestätigt und an <?php echo escape(SITE_DEPARTMENT_NAME); ?> weitergeleitet.</p>
     </div>
     <div class="panel">
         <p><strong>Bestellnummer:</strong> <span class="order-highlight"><?php echo escape($result['order']['id']); ?></span></p>

+ 2 - 2
order-success.php

@@ -12,7 +12,7 @@ include __DIR__ . '/includes/header.php';
 
 <?php if ($order === null): ?>
     <div class="alert alert-info">
-        <p>Die Bestellung wurde gespeichert.</p>
+        <p>Die Bestellung wurde im PSA-Service der Stadt Freising gespeichert.</p>
     </div>
 <?php else: ?>
     <div class="panel">
@@ -20,7 +20,7 @@ include __DIR__ . '/includes/header.php';
         <?php if ($order['confirmation_status'] === 'pending'): ?>
             <p>Bitte bestätigen Sie die Bestellung über den Link in der E-Mail an <strong><?php echo escape($order['customer_email']); ?></strong>.</p>
         <?php else: ?>
-            <p>Ihre Bestellung wurde erfasst und intern weitergeleitet.</p>
+            <p>Ihre Bestellung wurde erfasst und an <?php echo escape(SITE_DEPARTMENT_NAME); ?> weitergeleitet.</p>
         <?php endif; ?>
     </div>
 <?php endif; ?>

+ 28 - 20
product.php

@@ -18,11 +18,23 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['add_to_cart'])) {
 
     if (!empty($sizes) && ($size === '' || !in_array($size, $sizes, true))) {
         $error = 'Bitte wählen Sie eine Größe aus.';
-    } elseif (!addCartItem($product['id'], $size)) {
-        $error = 'Der Artikel konnte nicht in den Warenkorb gelegt werden.';
     } else {
-        header('Location: cart.php');
-        exit;
+        $result = addCartItem($product['id'], $size);
+        if (!$result['success']) {
+            $error = 'Der Artikel konnte nicht in den Warenkorb gelegt werden.';
+        } elseif ($result['status'] === 'replaced') {
+            setFlashMessage('cart_notice', 'success', 'Die Größe für diesen Artikel wurde im Warenkorb aktualisiert.');
+            header('Location: cart.php');
+            exit;
+        } elseif ($result['status'] === 'unchanged') {
+            setFlashMessage('cart_notice', 'info', 'Dieser Artikel ist bereits mit der gewählten Größe im Warenkorb.');
+            header('Location: cart.php');
+            exit;
+        } else {
+            setFlashMessage('cart_notice', 'success', 'Der Artikel wurde zum Warenkorb hinzugefügt.');
+            header('Location: cart.php');
+            exit;
+        }
     }
 }
 
@@ -38,42 +50,38 @@ include __DIR__ . '/includes/header.php';
 <div class="product-detail-grid">
     <div>
         <?php if (!empty($product['image']) && file_exists(__DIR__ . '/assets/images/' . $product['image'])): ?>
-            <img src="<?php echo escape(SITE_URL); ?>/assets/images/<?php echo escape($product['image']); ?>" alt="<?php echo escape($product['name']); ?>" style="width: 100%; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
+            <img class="product-image" src="<?php echo escape(SITE_URL); ?>/assets/images/<?php echo escape($product['image']); ?>" alt="<?php echo escape($product['name']); ?>">
         <?php else: ?>
             <div class="product-placeholder">Kein Bild verfügbar</div>
         <?php endif; ?>
     </div>
 
-    <div>
+    <div class="product-copy">
         <h1><?php echo escape($product['name']); ?></h1>
 
-        <div style="margin: 1.5rem 0 2rem;">
+        <div class="product-description-block">
             <h3>Beschreibung</h3>
-            <p style="margin-top: 0.5rem; line-height: 1.8;"><?php echo nl2br(escape($product['description'])); ?></p>
+            <p class="product-description"><?php echo nl2br(escape($product['description'])); ?></p>
         </div>
 
-        <form method="POST" style="margin-top: 2rem;">
+        <form method="POST" class="product-form">
             <?php if (!empty($sizes)): ?>
                 <div class="form-group">
                     <label for="size">Größe *</label>
-                    <select id="size" name="size" required style="width: 100%;" onchange="updateAvailabilityNotice()">
+                    <select id="size" name="size" required onchange="updateAvailabilityNotice()">
                         <option value="">Bitte wählen</option>
                         <?php foreach ($sizes as $sizeOption): ?>
                             <?php $label = getAvailabilityLabel($product, $sizeOption); ?>
                             <option value="<?php echo escape($sizeOption); ?>" data-label="<?php echo escape($label); ?>">
-                                <?php echo escape($sizeOption); ?><?php echo $label !== '' ? ' - ' . escape($label) : ''; ?>
+                                <?php echo escape($sizeOption); ?>
                             </option>
                         <?php endforeach; ?>
                     </select>
                 </div>
-                <div id="availabilityNotice" class="alert alert-warning" style="display: none;"></div>
+                <div id="availabilityNotice" class="alert alert-warning is-hidden"></div>
             <?php endif; ?>
 
-            <div class="alert alert-info">
-                Jeder Artikel wird mit Menge 1 in den Warenkorb gelegt.
-            </div>
-
-            <button type="submit" name="add_to_cart" class="btn" style="width: 100%;">In den Warenkorb</button>
+            <button type="submit" name="add_to_cart" class="btn btn-block">In den Warenkorb</button>
         </form>
 
         <?php if (!empty($sizes)): ?>
@@ -86,16 +94,16 @@ include __DIR__ . '/includes/header.php';
 
                     if (text) {
                         notice.textContent = text;
-                        notice.style.display = 'block';
+                        notice.classList.remove('is-hidden');
                     } else {
                         notice.textContent = '';
-                        notice.style.display = 'none';
+                        notice.classList.add('is-hidden');
                     }
                 }
             </script>
         <?php endif; ?>
 
-        <div style="margin-top: 2rem;">
+        <div class="mt-4">
             <a href="index.php" class="btn btn-secondary">Zurück zur Übersicht</a>
         </div>
     </div>