diff --git a/public/ticketview/admin-view.css b/public/ticketview/admin-view.css
new file mode 100644
index 0000000..444fd1b
--- /dev/null
+++ b/public/ticketview/admin-view.css
@@ -0,0 +1,122 @@
+/* ==== ADMIN COMMENT STYLING ==== */
+.comment.admin .comment-content {
+ background-color: var(--color-admin-highlight);
+ border-left: 4px solid var(--color-primary);
+}
+.comment.admin .comment-author::after {
+ content: " (Admin)";
+ color: var(--color-primary);
+ font-weight: normal;
+ margin-left: 4px;
+}
+
+/* ==== ACTION BUTTONS (nur Admin hat mehrere Buttons) ==== */
+.ticket-actions-bar {
+ margin-top: 20px;
+ display: flex;
+ gap: 10px;
+ flex-wrap: wrap;
+}
+.btn-secondary {
+ padding: 10px 16px;
+ border: none;
+ border-radius: var(--radius);
+ cursor: pointer;
+ font-weight: 600;
+ transition: background 0.2s ease;
+}
+.btn-secondary {
+ background-color: var(--color-accent);
+ color: white;
+}
+.btn-secondary:hover {
+ background-color: #1976d2;
+}
+
+/* ==== CUSTOM ADMIN LAYOUT FIX ==== */
+.ticket-meta-block {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+@media (min-width: 600px) {
+ .ticket-info-grid {
+ grid-template-columns: repeat(3, 1fr);
+ }
+}
+/* GRID ADMIN SIDE ORDER */
+
+.ticket-info-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr); /* 3 equal columns */
+ grid-template-rows: auto auto auto; /* 3 rows */
+ gap: 15px;
+}
+
+/* Requester: span all 3 columns on row 1 */
+.info-item.requester {
+ grid-column: 1 / 4;
+ grid-row: 1 / 2;
+}
+
+/* Description: span all 3 columns on row 2 */
+.info-item.description {
+ grid-column: 1 / 4;
+ grid-row: 2 / 3;
+}
+
+/* Created, Updated, Time: row 3, columns 1-3 */
+.info-item.created {
+ grid-column: 1 / 2;
+ grid-row: 3 / 4;
+}
+.info-item.updated {
+ grid-column: 2 / 3;
+ grid-row: 3 / 4;
+}
+/* Time div isn’t .info-item, so select it directly by nth-child */
+.ticket-info-grid > div:nth-child(5) {
+ grid-column: 3 / 4;
+ grid-row: 3 / 4;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+/* Status, Priority, Category: row 4, columns 1-3 */
+.info-item.status {
+ grid-column: 1 / 2;
+ grid-row: 4 / 5;
+}
+.info-item.priority {
+ grid-column: 2 / 3;
+ grid-row: 4 / 5;
+}
+.info-item.category {
+ grid-column: 3 / 4;
+ grid-row: 4 / 5;
+}
+
+.forms-row {
+ display: flex;
+ gap: 20px;
+ align-items: flex-start;
+}
+
+.compact-form {
+ flex: 1;
+ display: grid;
+ grid-template-columns: 1fr;
+ gap: 10px;
+}
+
+.forms-row input,
+.forms-row textarea {
+ width: 100%;
+}
+
+@media (max-width: 700px) {
+ .forms-row {
+ flex-direction: column;
+ }
+}
diff --git a/public/ticketview/ticket-admin.js b/public/ticketview/ticket-admin.js
new file mode 100644
index 0000000..c37d097
--- /dev/null
+++ b/public/ticketview/ticket-admin.js
@@ -0,0 +1,311 @@
+import {
+ fetchTicket, fetchComments, fetchHistory, createComment, logTime,
+ formatDateTime, formatMinutes, getHistoryIcon
+} from './ticket-shared.js';
+
+document.addEventListener('DOMContentLoaded', async () => {
+ const ticketId = new URLSearchParams(window.location.search).get('id');
+ if (!ticketId) return window.location.href = '../admin.html';
+
+ const statusSelect = document.getElementById('status-select');
+ const priorityInput = document.getElementById('priority-text');
+ const categorySelect = document.getElementById('category-select');
+ const commentForm = document.getElementById('comment-form');
+ const titleText = document.getElementById('ticket-title-text');
+ const titleInput = document.getElementById('ticket-title-input');
+ const descText = document.getElementById('ticket-description-text');
+ const descInput = document.getElementById('ticket-description-input');
+ const commentAuthorInput = document.getElementById('comment-author');
+ const totalTimeDisplay = document.getElementById('total-time');
+ const timeForm = document.getElementById('time-booking-form');
+
+ let webhookConfig = null;
+
+ // Fetch Webhook Configuration
+ async function loadWebhookConfig() {
+ try {
+ const response = await fetch('/api/webhooks');
+ if (response.ok) {
+ webhookConfig = await response.json();
+ console.log('Webhook configuration loaded:', webhookConfig.configured);
+ } else {
+ console.warn('Failed to load webhook configuration');
+ webhookConfig = { admin: null, user: null, configured: { admin: false, user: false } };
+ }
+ } catch (error) {
+ console.error('Error loading webhook configuration:', error);
+ webhookConfig = { admin: null, user: null, configured: { admin: false, user: false } };
+ }
+ }
+
+ // Send Webhook Notification
+ async function sendWebhook(message) {
+ if (!webhookConfig?.user) {
+ console.log('User webhook not configured, skipping notification');
+ return;
+ }
+
+ try {
+ const res = await fetch(webhookConfig.user, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ content: message })
+ });
+
+ if (!res.ok) {
+ console.error('User webhook failed:', res.statusText);
+ }
+ } catch (err) {
+ console.error('Error sending user webhook:', err);
+ }
+ }
+
+ let originalStatus = '', originalPriority = '', originalTitle = '', originalDesc = '', originalCategory = '';
+
+ async function loadPage() {
+ loadWebhookConfig().then(() => {
+ loadTicketDetails();
+ loadComments();
+ loadHistory();
+ });
+ }
+
+ async function loadTicketDetails() {
+ const ticket = await fetchTicket(ticketId);
+ if (!ticket) return alert('Ticket not found');
+
+ titleText.textContent = ticket.title;
+ descText.textContent = ticket.description;
+ priorityInput.value = ticket.priority;
+ statusSelect.value = ticket.status;
+ categorySelect.value = ticket.category || '';
+
+ document.getElementById('ticket-id-val').textContent = ticket.id;
+ document.getElementById('ticket-requester').textContent = ticket.requester;
+ document.getElementById('ticket-created').textContent = formatDateTime(ticket.created_at);
+ document.getElementById('ticket-updated').textContent = formatDateTime(ticket.updated_at);
+ const totalMins = typeof ticket.total_time === 'number' ? ticket.total_time : 0;
+ totalTimeDisplay.textContent = formatMinutes(totalMins);
+
+
+ statusSelect.className = 'form-control status-' + (ticket.status || '').toLowerCase().replace(/\s+/g, '-');
+
+ originalStatus = ticket.status;
+ originalPriority = ticket.priority;
+ originalTitle = ticket.title;
+ originalDesc = ticket.description;
+ originalCategory = ticket.category || '';
+ }
+
+ async function updateTicket({ status, priority, title, description, category, actor = 'Admin' }) {
+ try {
+ const ticket = await fetchTicket(ticketId); // Fetch ticket to get the requester
+ const requester = ticket.requester || ''; // Ensure requester is included in the update request
+
+ const res = await fetch(`/api/tickets/${ticketId}`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ status, priority, title, description, category, actor, requester }) // Add requester
+ });
+
+ if (!res.ok) throw new Error('Failed to update');
+ alert('Ticket updated successfully');
+
+ if (status !== originalStatus) {
+ await sendWebhook(`Ticket #${ticketId} status changed from "${originalStatus}" to "${status}"`);
+ originalStatus = status;
+ }
+
+ if (priority !== originalPriority) {
+ await sendWebhook(`Ticket #${ticketId} priority changed from "${originalPriority}" to "${priority}"`);
+ originalPriority = priority;
+ }
+
+ if (title !== originalTitle) {
+ await sendWebhook(`Ticket #${ticketId} title changed by ${actor}`);
+ originalTitle = title;
+ }
+
+ if (description !== originalDesc) {
+ await sendWebhook(`Ticket #${ticketId} description changed by ${actor}`);
+ originalDesc = description;
+ }
+
+ if (category !== originalCategory) {
+ await sendWebhook(`Ticket #${ticketId} category changed from "${originalCategory}" to "${category}"`);
+ originalCategory = category;
+ }
+
+ titleText.textContent = title;
+ descText.textContent = description;
+
+ await loadPage();
+ } catch (err) {
+ console.error('Update failed:', err);
+ alert('Error updating ticket');
+ }
+ }
+
+
+ document.getElementById('update-ticket-btn').addEventListener('click', async () => {
+ const isEditingTitle = titleInput.style.display === 'block';
+ const isEditingDesc = descInput.style.display === 'block';
+
+ const newTitle = isEditingTitle ? titleInput.value.trim() : titleText.textContent;
+ const newDesc = isEditingDesc ? descInput.value.trim() : descText.textContent;
+ const newStatus = statusSelect.value;
+ const newPriority = priorityInput.value;
+ const newCategory = categorySelect.value;
+
+ const confirmed = confirm('Möchten Sie die Änderungen wirklich speichern?');
+ if (!confirmed) return;
+
+ await updateTicket({ status: newStatus, priority: newPriority, title: newTitle, description: newDesc, category: newCategory });
+
+ titleInput.style.display = 'none';
+ titleText.style.display = 'block';
+ descInput.style.display = 'none';
+ descText.style.display = 'block';
+ });
+
+ document.getElementById('close-ticket-btn').addEventListener('click', async () => {
+ const confirmed = confirm('Möchtest du das Ticket wirklich schließen?');
+ if (!confirmed) return;
+
+ const newStatus = 'Closed';
+ const newPriority = priorityInput.value;
+ const newTitle = titleText.textContent;
+ const newDesc = descText.textContent;
+ const newCategory = categorySelect.value;
+
+ await updateTicket({ status: newStatus, priority: newPriority, title: newTitle, description: newDesc, category: newCategory });
+ });
+
+ titleText.addEventListener('dblclick', () => {
+ titleInput.value = titleText.textContent;
+ titleText.style.display = 'none';
+ titleInput.style.display = 'block';
+ titleInput.focus();
+ });
+
+ descText.addEventListener('dblclick', () => {
+ descInput.value = descText.textContent;
+ descText.style.display = 'none';
+ descInput.style.display = 'block';
+ descInput.focus();
+ });
+
+ const savedAuthor = localStorage.getItem('commentAuthor');
+ if (savedAuthor) commentAuthorInput.value = savedAuthor;
+
+ async function loadComments() {
+ const comments = await fetchComments(ticketId);
+ const container = document.getElementById('comments-container');
+ container.innerHTML = comments.map(comment => {
+ const role = comment.role || 'user';
+ const isAdmin = role === 'admin';
+ const avatarUrl = isAdmin ? '/assets/admin.jpg' : '/assets/user.jpg';
+
+ return `
+
`;
+ }).join('');
+ }
+
+ async function loadHistory() {
+ const history = await fetchHistory(ticketId);
+ const container = document.getElementById('history-container');
+ container.innerHTML = history.map(h => `
+
+
+
+
${h.action}
+
${h.message || ''}
+
+ By ${h.author || h.actor || 'unknown'}
+ ${formatDateTime(h.date || h.timestamp)}
+
+
+
`).join('');
+ }
+
+ commentForm.addEventListener('submit', async (e) => {
+ e.preventDefault();
+
+ const author = commentAuthorInput.value.trim();
+ if (!author) return alert('Bitte gib deinen Namen ein');
+ const content = document.getElementById('comment-content').value.trim();
+ if (!content) return alert('Comment required');
+
+ const role = 'admin';
+ const timestamp = new Date().toISOString();
+
+ try {
+ await createComment(ticketId, { author, role, content, timestamp });
+ await sendWebhook(`New comment added by ${author} (${role}): "${content}" on ticket #${ticketId}`);
+ localStorage.setItem('commentAuthor', author);
+
+ document.getElementById('comment-content').value = '';
+ await loadComments();
+ await loadHistory();
+ } catch (err) {
+ console.error('Failed to add comment:', err);
+ alert('Failed to add comment');
+ }
+ });
+
+ timeForm.addEventListener('submit', async (e) => {
+ e.preventDefault();
+ const minutes = parseInt(document.getElementById('time-minutes').value);
+ const comment = document.getElementById('time-comment').value.trim();
+ const actor = commentAuthorInput.value || 'Admin';
+
+ if (isNaN(minutes) || minutes <= 0) return alert('Gültige Zeit in Minuten eingeben');
+
+ try {
+ // Save the time log
+ await logTime(ticketId, { minutes, comment, author: actor });
+
+ // Also add it as a comment
+ const timeComment = `⏱ Zeit gebucht: ${formatMinutes(minutes)}${comment ? ` – ${comment}` : ''}`;
+ await createComment(ticketId, {
+ author: actor,
+ role: 'admin',
+ content: timeComment,
+ timestamp: new Date().toISOString()
+ });
+
+ // Webhook notification
+ await sendWebhook(`Time booked: ${formatMinutes(minutes)} by ${actor} on ticket #${ticketId}${comment ? ` – "${comment}"` : ''}`);
+
+ // Clear inputs and reload
+ document.getElementById('time-minutes').value = '';
+ document.getElementById('time-comment').value = '';
+ await loadPage();
+ } catch (err) {
+ console.error('Fehler beim Speichern der Zeit:', err);
+ alert('Fehler beim Speichern');
+ }
+ });
+
+ document.querySelectorAll('.tab-btn').forEach(btn => {
+ btn.addEventListener('click', () => {
+ const tab = btn.dataset.tab;
+ document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
+ btn.classList.add('active');
+ document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active'));
+ document.getElementById(`${tab}-tab`).classList.add('active');
+ });
+ });
+
+ await loadPage();
+});
\ No newline at end of file
diff --git a/public/ticketview/ticket-view-admin.html b/public/ticketview/ticket-view-admin.html
new file mode 100644
index 0000000..5f75152
--- /dev/null
+++ b/public/ticketview/ticket-view-admin.html
@@ -0,0 +1,153 @@
+
+
+
+
+
+ Ticket Details (Admin) - Ticket System
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Description
+
Loading...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/public/ticketview/ticket-view-user.html b/public/ticketview/ticket-view-user.html
new file mode 100644
index 0000000..3a7ec7f
--- /dev/null
+++ b/public/ticketview/ticket-view-user.html
@@ -0,0 +1,119 @@
+
+
+
+
+
+ My Ticket - Ticket System
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Description
+
Loading...
+
+
+
+
+
+
+
+
Status
+ Open
+
+
+
Priority
+ -
+
+
+
Category
+ -
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/ticketview/ticket-view.css b/public/ticketview/ticket-view.css
new file mode 100644
index 0000000..079b7cd
--- /dev/null
+++ b/public/ticketview/ticket-view.css
@@ -0,0 +1,380 @@
+:root {
+ --color-bg: #2e2e2e;
+ --color-surface: #3c3c3c;
+ --color-primary: #4caf50;
+ --color-primary-hover: #45a049;
+ --color-accent: #2196f3;
+ --color-text: #f5f5f5;
+ --color-muted: #b0b0b0;
+ --color-border: #4f4f4f;
+ --color-danger: #e53935;
+ --color-admin-highlight: #424242;
+ --radius: 8px;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+ background-color: var(--color-bg);
+ color: var(--color-text);
+ line-height: 1.6;
+ padding: 20px;
+}
+
+a {
+ color: var(--color-accent);
+ text-decoration: none;
+}
+a:hover {
+ text-decoration: underline;
+}
+
+.container {
+ max-width: 1200px;
+ margin: 0 auto;
+ background-color: var(--color-surface);
+ border-radius: var(--radius);
+ overflow: hidden;
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
+}
+
+.header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 20px;
+ border-bottom: 1px solid var(--color-border);
+}
+.header h1 {
+ font-size: 1.8rem;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+.back-link {
+ font-size: 1rem;
+}
+.back-link i {
+ margin-right: 5px;
+}
+
+.main-content {
+ padding: 20px;
+}
+.ticket-details-layout {
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+@media (min-width: 900px) {
+ .ticket-details-layout {
+ flex-direction: row;
+ }
+}
+.ticket-details,
+.ticket-activity {
+ background-color: var(--color-bg);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius);
+ padding: 20px;
+ flex: 1;
+ min-width: 0;
+}
+
+.ticket-activity {
+ display: flex;
+ flex-direction: column;
+ height: 600px;
+}
+.tab-content {
+ display: none;
+ flex-direction: column;
+ flex-grow: 1;
+ overflow: hidden;
+ height: 100%;
+}
+.tab-content.active {
+ display: flex;
+}
+#comments-container,
+#history-container {
+ flex-grow: 1;
+ overflow-y: auto;
+ max-height: 100%;
+ padding-right: 8px;
+ scrollbar-width: thin;
+ scrollbar-color: var(--color-primary) transparent;
+}
+#comments-container::-webkit-scrollbar,
+#history-container::-webkit-scrollbar {
+ width: 8px;
+}
+#comments-container::-webkit-scrollbar-thumb,
+#history-container::-webkit-scrollbar-thumb {
+ background-color: var(--color-primary);
+ border-radius: 4px;
+}
+
+.ticket-header {
+ display: flex;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ margin-bottom: 20px;
+}
+.ticket-header-left {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ max-width: 100%;
+}
+.ticket-header-right {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ gap: 8px;
+}
+.ticket-header h2 {
+ font-size: 1.4rem;
+ font-weight: bold;
+}
+
+.ticket-status-priority-requester {
+ display: flex;
+ gap: 15px;
+ align-items: center;
+ margin-top: 5px;
+ flex-wrap: wrap;
+}
+
+.ticket-status,
+.ticket-priority,
+.ticket-requester,
+.ticket-category {
+ padding: 6px 14px;
+ border-radius: 20px;
+ font-weight: bold;
+ font-size: 0.9rem;
+ white-space: nowrap;
+ display: inline-block;
+ overflow: visible;
+ max-width: 100%;
+ box-sizing: border-box;
+}
+.ticket-status {
+ background-color: var(--color-primary);
+ color: white;
+}
+.ticket-priority {
+ background-color: var(--color-accent);
+ color: white;
+}
+.ticket-requester,
+.ticket-category {
+ background-color: var(--color-muted);
+ color: var(--color-bg);
+}
+
+.ticket-meta {
+ margin-top: 10px;
+ font-size: 0.9rem;
+ color: var(--color-muted);
+ display: flex;
+ flex-wrap: wrap;
+ gap: 20px;
+}
+
+.ticket-body h3 {
+ margin: 20px 0 10px;
+ font-size: 1.1rem;
+}
+#ticket-description-text {
+ white-space: pre-wrap;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ max-width: 100%;
+ max-height: 200px;
+ overflow-y: auto;
+ overflow-x: hidden;
+ padding-right: 10px;
+ margin-bottom: 20px;
+}
+@media (min-width: 600px) {
+ .ticket-info-grid {
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
+ }
+}
+.info-item h4 {
+ margin-bottom: 4px;
+ font-size: 1rem;
+ font-weight: 600;
+}
+
+.form-control {
+ padding: 10px 12px;
+ border-radius: 6px;
+ border: 1px solid var(--color-border);
+ background-color: var(--color-surface);
+ color: var(--color-text);
+}
+.form-control:focus {
+ outline: none;
+ border-color: var(--color-primary);
+}
+
+.ticket-info-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr 1fr; /* 3 columns */
+ gap: 15px;
+ grid-auto-rows: auto;
+}
+.ticket-info-grid .info-item {
+ min-width: 0;
+ overflow: hidden;
+}
+.info-item .form-control {
+ width: 100%;
+ box-sizing: border-box;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.tabs {
+ display: flex;
+ margin-bottom: 15px;
+ gap: 10px;
+}
+.tab-btn {
+ flex: 1;
+ padding: 10px;
+ border: none;
+ border-radius: var(--radius);
+ background-color: var(--color-surface);
+ color: var(--color-text);
+ cursor: pointer;
+ transition: background 0.2s ease;
+}
+.tab-btn:hover {
+ background-color: var(--color-border);
+}
+.tab-btn.active {
+ background-color: var(--color-primary);
+}
+
+.comment {
+ display: flex;
+ align-items: flex-start;
+ gap: 16px;
+ margin-bottom: 20px;
+ max-width: 800px;
+}
+.comment-avatar {
+ width: 50px;
+ height: 50px;
+ border-radius: 50%;
+ object-fit: cover;
+ flex-shrink: 0;
+ border: 2px solid var(--color-border);
+}
+.comment-content {
+ background-color: var(--color-surface);
+ padding: 12px 16px;
+ border-radius: var(--radius);
+ width: 100%;
+}
+.comment-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 5px;
+ font-size: 0.85rem;
+}
+.comment-author {
+ font-weight: bold;
+}
+.comment-date {
+ color: var(--color-muted);
+ font-size: 0.8rem;
+}
+.comment-text {
+ font-size: 0.95rem;
+ line-height: 1.5;
+ word-wrap: break-word;
+}
+
+.comment-form {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ margin-bottom: 20px;
+}
+.comment-form input,
+.comment-form textarea {
+ background-color: var(--color-surface);
+ color: var(--color-text);
+ border: 1px solid var(--color-border);
+ border-radius: var(--radius);
+ padding: 10px;
+ font-size: 1rem;
+}
+.comment-form input:focus,
+.comment-form textarea:focus {
+ outline: none;
+ border-color: var(--color-primary);
+}
+
+.history-item {
+ display: flex;
+ gap: 12px;
+ margin-bottom: 15px;
+}
+.history-icon {
+ font-size: 1.2rem;
+ color: var(--color-primary);
+}
+.history-content {
+ background-color: var(--color-surface);
+ padding: 10px 15px;
+ border-radius: var(--radius);
+ width: 100%;
+}
+.history-action {
+ font-weight: bold;
+ margin-bottom: 4px;
+}
+.history-meta {
+ display: flex;
+ justify-content: space-between;
+ font-size: 0.8rem;
+ color: var(--color-muted);
+}
+
+/* Post Comment Button Styling */
+.btn-primary {
+ padding: 10px 16px;
+ background-color: var(--color-primary);
+ color: white;
+ border: none;
+ border-radius: var(--radius);
+ cursor: pointer;
+ font-weight: 600;
+ transition: background 0.2s ease;
+ align-self: flex-start; /* damit er nicht breit wird */
+}
+
+.btn-primaryt-btn:hover {
+ background-color: var(--color-primary-hover);
+}
+
+.footer {
+ padding: 15px;
+ text-align: center;
+ font-size: 0.85rem;
+ color: var(--color-muted);
+ border-top: 1px solid var(--color-border);
+}
\ No newline at end of file