From ca9a6b2882b2314e3110ad1431877b69157020d6 Mon Sep 17 00:00:00 2001 From: orfelorfel23 Date: Sun, 21 Jun 2026 11:46:31 +0200 Subject: [PATCH] first commit --- app.js | 275 ++++++++++++++++ index.html | 282 ++++++++++++++++ style.css | 953 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1510 insertions(+) create mode 100644 app.js create mode 100644 index.html create mode 100644 style.css diff --git a/app.js b/app.js new file mode 100644 index 0000000..9c3f985 --- /dev/null +++ b/app.js @@ -0,0 +1,275 @@ +document.addEventListener('DOMContentLoaded', () => { + // --- Mobile Menu Toggle --- + const menuToggle = document.querySelector('.menu-toggle'); + const navLinks = document.querySelector('.nav-links'); + + if (menuToggle && navLinks) { + menuToggle.addEventListener('click', () => { + navLinks.classList.toggle('mobile-open'); + const icon = menuToggle.querySelector('i'); + if (icon) { + icon.classList.toggle('fa-bars'); + icon.classList.toggle('fa-xmark'); + } + }); + } + + // --- Accordion FAQ --- + const accordionHeaders = document.querySelectorAll('.accordion-header'); + + accordionHeaders.forEach(header => { + header.addEventListener('click', () => { + const item = header.parentElement; + const isActive = item.classList.contains('active'); + + // Close all items + document.querySelectorAll('.accordion-item').forEach(i => { + i.classList.remove('active'); + const content = i.querySelector('.accordion-content'); + if (content) content.style.maxHeight = null; + }); + + // Toggle active item + if (!isActive) { + item.classList.add('active'); + const content = item.querySelector('.accordion-content'); + if (content) { + content.style.maxHeight = content.scrollHeight + "px"; + } + } + }); + }); + + // --- Feedback Hub Logic --- + const feedbackForm = document.getElementById('feedback-form'); + const feedbackList = document.getElementById('feedback-list'); + const filterButtons = document.querySelectorAll('.filter-btn'); + + // Default Seed Data + const defaultFeedback = [ + { + id: 'fb-1', + author: 'Markus T.', + type: 'feature', + title: 'QR-Code Download für Gäste', + text: 'Es wäre super cool, wenn nach der Aufnahme ein QR-Code angezeigt wird, über den sich die Gäste das Foto direkt auf das Handy laden können.', + upvotes: 42, + voted: false + }, + { + id: 'fb-2', + author: 'Sabrina', + type: 'love', + title: 'Sticker-Editor ist genial!', + text: 'Unsere Hochzeitsgäste haben stundenlang Emojis und Eventtexte verschoben. Die Bedienung ist wirklich super intuitiv und einfach.', + upvotes: 28, + voted: false + }, + { + id: 'fb-3', + author: 'EventTech GmbH', + type: 'bug', + title: 'Drucker-Timeout bei schlechtem WLAN', + text: 'Manchmal meldet die App einen Fehler, wenn der Drucker im lokalen Netzwerk kurz die Verbindung verliert. Ein automatischer Retry wäre hilfreich.', + upvotes: 15, + voted: false + } + ]; + + // Load from LocalStorage or seed defaults + let feedbackData = JSON.parse(localStorage.getItem('schnappix_feedback')); + if (!feedbackData) { + feedbackData = defaultFeedback; + localStorage.setItem('schnappix_feedback', JSON.stringify(feedbackData)); + } + + // Upvote storage track (local to session/device) + let upvotedIds = JSON.parse(localStorage.getItem('schnappix_upvoted_ids')) || []; + + const saveToLocalStorage = () => { + localStorage.setItem('schnappix_feedback', JSON.stringify(feedbackData)); + localStorage.setItem('schnappix_upvoted_ids', JSON.stringify(upvotedIds)); + }; + + const renderFeedback = (filter = 'all') => { + if (!feedbackList) return; + feedbackList.innerHTML = ''; + + // Sort by upvotes descending + const sortedData = [...feedbackData].sort((a, b) => b.upvotes - a.upvotes); + + sortedData.forEach(item => { + if (filter !== 'all' && item.type !== filter) return; + + const isVoted = upvotedIds.includes(item.id); + const card = document.createElement('div'); + card.className = 'feedback-item glass'; + + let badgeIcon = '✨'; + let categoryLabel = 'Feature'; + if (item.type === 'bug') { + badgeIcon = '🐛'; + categoryLabel = 'Bug'; + } else if (item.type === 'love') { + badgeIcon = '💖'; + categoryLabel = 'Lob'; + } + + card.innerHTML = ` +
+
+ ${badgeIcon} ${categoryLabel} + von ${escapeHtml(item.author)} +
+

${escapeHtml(item.title)}

+

${escapeHtml(item.text)}

+
+
+ +
+ `; + + feedbackList.appendChild(card); + }); + + setupUpvoteButtons(); + }; + + const escapeHtml = (str) => { + return str.replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + }; + + const setupUpvoteButtons = () => { + const buttons = document.querySelectorAll('.upvote-btn'); + buttons.forEach(btn => { + btn.addEventListener('click', () => { + const id = btn.getAttribute('data-id'); + const index = feedbackData.findIndex(item => item.id === id); + + if (index !== -1) { + if (upvotedIds.includes(id)) { + // Undo upvote + feedbackData[index].upvotes--; + upvotedIds = upvotedIds.filter(vId => vId !== id); + } else { + // Upvote + feedbackData[index].upvotes++; + upvotedIds.push(id); + } + saveToLocalStorage(); + renderFeedback(getActiveFilter()); + } + }); + }); + }; + + const getActiveFilter = () => { + const activeBtn = document.querySelector('.filter-btn.active'); + return activeBtn ? activeBtn.getAttribute('data-filter') : 'all'; + }; + + // Filter switching + filterButtons.forEach(btn => { + btn.addEventListener('click', () => { + filterButtons.forEach(b => b.classList.remove('active')); + btn.classList.add('active'); + renderFeedback(btn.getAttribute('data-filter')); + }); + }); + + // Form Submission + if (feedbackForm) { + feedbackForm.addEventListener('submit', (e) => { + e.preventDefault(); + + const authorInput = document.getElementById('fb-author'); + const typeSelect = document.getElementById('fb-type'); + const titleInput = document.getElementById('fb-title'); + const textInput = document.getElementById('fb-text'); + + const newFb = { + id: 'fb-' + Date.now(), + author: authorInput.value, + type: typeSelect.value, + title: titleInput.value, + text: textInput.value, + upvotes: 1, + voted: true + }; + + feedbackData.push(newFb); + upvotedIds.push(newFb.id); // Auto-upvote own post + saveToLocalStorage(); + + // Reset form + feedbackForm.reset(); + + // Re-render + renderFeedback(getActiveFilter()); + + // Smooth scroll to list + feedbackList.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + }); + } + + // --- Modal Management for Impressum & Privacy --- + const modalTriggers = document.querySelectorAll('.legal-trigger'); + const modals = document.querySelectorAll('.modal-overlay'); + const closeButtons = document.querySelectorAll('.modal-close'); + + modalTriggers.forEach(trigger => { + trigger.addEventListener('click', (e) => { + e.preventDefault(); + const targetId = trigger.getAttribute('href').substring(1); + const targetModal = document.getElementById(targetId); + if (targetModal) { + targetModal.classList.add('open'); + document.body.style.overflow = 'hidden'; // Stop scrolling background + } + }); + }); + + const closeModal = (modal) => { + modal.classList.remove('open'); + // Only restore scroll if no other modals are open + const anyOpen = Array.from(modals).some(m => m.classList.contains('open')); + if (!anyOpen) { + document.body.style.overflow = ''; + } + }; + + closeButtons.forEach(btn => { + btn.addEventListener('click', () => { + const modal = btn.closest('.modal-overlay'); + if (modal) closeModal(modal); + }); + }); + + modals.forEach(modal => { + // Close modal when clicking outside of the content block + modal.addEventListener('click', (e) => { + if (e.target === modal) { + closeModal(modal); + } + }); + }); + + // Close modal on Escape key press + window.addEventListener('keydown', (e) => { + if (e.key === 'Escape') { + modals.forEach(modal => { + if (modal.classList.contains('open')) closeModal(modal); + }); + } + }); + + // Initial render + renderFeedback(); +}); diff --git a/index.html b/index.html new file mode 100644 index 0000000..1184d85 --- /dev/null +++ b/index.html @@ -0,0 +1,282 @@ + + + + + + Schnappix - Die ultimative Photobooth App + + + + + + + + + + + + +
+ +
+ + +
+
+
+
+
🚀 Version 23.01.X
+

Erstelle unvergessliche Momente mit Schnappix

+

Die professionelle, Tablet-optimierte Photobooth-App für dein Event. Kiosk-Modus, Direktdruck, kreative Sticker und maßgeschneiderte Rahmen vereint in einem atemberaubenden Synthwave-Design.

+ +
+
+
+
+
+
+
+ + Schnappix +
+
+
+
+ +
+

Zeit für ein Erinnerungsfoto!

+ +
+
+
+
+
+
+
+
+ + +
+
+

Geniale Features für dein Event

+
+
+
+

Echter Kiosk-Modus

+

Dank nativer Kotlin-Module sperrt die App das Tablet bombensicher für Gäste. Kein Verlassen der App ohne Admin-Code möglich.

+
+
+
+

Direkter Netzwerkdruck

+

Integrierter IPP-Drucker-Support (z.B. Canon Selphy). Fotos werden direkt aus dem Vorschaubildschirm gedruckt.

+
+
+
+

Sticker & Emojis

+

Interaktiver Editor wie bei Social Media. Emojis, Datumsanzeige und Eventname frei verschieben, drehen und skalieren.

+
+
+
+

Serienbild & Collagen

+

Nimm 1-5 Fotos hintereinander auf. Die App generiert automatisch eine wunderschöne Collage zum Drucken und Speichern.

+
+
+
+

Individuelle Rahmen

+

Blende maßgeschneiderte, transparente PNG-Rahmen über die Aufnahmen. Perfekt für Geburtstage, Hochzeiten oder Firmenfeiern.

+
+
+
+

USB-Kamera Support

+

Nutze entweder die Frontkamera deines Tablets oder schließe eine externe USB-Kamera an für überragende Bildqualität.

+
+
+
+
+ + +
+
+

Häufig gestellte Fragen

+ +
+
+ +
+

Damit der Kiosk-Modus (LockTask) ohne Bestätigungsdialoge funktioniert, muss die App als Device Owner festgelegt werden. Führe dazu folgenden ADB-Befehl aus:

+ adb shell dpm set-device-owner de.orfel.schnappix/de.orfel.schnappix.KioskDeviceAdminReceiver +

Danach kannst du den Kiosk-Modus einfach im passwortgeschützten Admin-Bereich ein- und ausschalten.

+
+
+ +
+ +
+

Erstelle ein transparentes PNG im Format 1500 x 1000 px (3:2 Querformat, max. 500 KB) und lege es im Ordner assets/frames/ als frame_name.png ab. Trage das Template anschließend in src/data/frames.ts ein und führe einen neuen Build aus.

+
+
+ +
+ +
+

Es werden alle Drucker unterstützt, die das IPP (Internet Printing Protocol) im lokalen Netzwerk unterstützen. Beliebte Fotodrucker wie der Canon CP1300 lassen sich direkt über ihre lokale IP-Adresse ansteuern.

+
+
+ +
+ +
+

Schnappix nutzt die moderne Android MediaStore API und speichert Collagen sowie Originale direkt in der Galerie deines Tablets unter den Ordnern:

+
    +
  • Schnappix - <EventName> - Collagen
  • +
  • Schnappix - <EventName> - Originale
  • +
+
+
+
+
+
+ + +
+
+

Feedback & Ideen

+

Deine Meinung hilft uns, Schnappix noch besser zu machen. Hinterlasse Feedback oder stimme für Ideen ab!

+ + +
+
+ + + + + + + + + + + + diff --git a/style.css b/style.css new file mode 100644 index 0000000..a5cdb36 --- /dev/null +++ b/style.css @@ -0,0 +1,953 @@ +/* + SCHNAPPIX Synthwave Neon CSS Stylesheet + Matching THEME from App: + - Deep background: #050510 (with linear gradients) + - Magenta glow (primary): #ff2bd6 + - Cyan glow (accent): #28dfff + - Mid Purple: #a033ff +*/ + +:root { + --bg-color: #050510; + --surface-color: #0d0d1a; + --surface-secondary: #151528; + --primary-color: #ff2bd6; + --primary-dark: #cc22ab; + --accent-color: #28dfff; + --accent-dark: #1fb3cc; + --text-color: #ffffff; + --text-muted: #7a7a9e; + --border-color: rgba(40, 223, 255, 0.15); + + --font-titles: 'Outfit', sans-serif; + --font-body: 'Inter', sans-serif; + + --shadow-neon: 0 0 15px rgba(255, 43, 214, 0.35); + --shadow-cyan: 0 0 15px rgba(40, 223, 255, 0.35); + --transition-smooth: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +/* Reset */ +* { + margin: 0; + padding: 0; + box-box: border-box; + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + background-color: var(--bg-color); + color: var(--text-color); + font-family: var(--font-body); + line-height: 1.6; + overflow-x: hidden; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 24px; +} + +/* Header */ +.main-header { + background: rgba(5, 5, 16, 0.85); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border-bottom: 1px solid var(--border-color); + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 1000; +} + +.nav-container { + height: 80px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo { + font-family: var(--font-titles); + font-size: 28px; + font-weight: 800; + letter-spacing: 1px; +} + +.logo-glow { + color: #fff; + text-shadow: 0 0 10px var(--primary-color), 0 0 20px var(--primary-color); + animation: logoGlowPulse 4s infinite alternate; +} + +.nav-links { + display: flex; + gap: 32px; +} + +.nav-links a { + color: var(--text-muted); + text-decoration: none; + font-weight: 500; + transition: var(--transition-smooth); +} + +.nav-links a:hover { + color: var(--accent-color); + text-shadow: 0 0 8px rgba(40, 223, 255, 0.5); +} + +.menu-toggle { + display: none; + background: none; + border: none; + color: var(--text-color); + font-size: 24px; + cursor: pointer; +} + +/* Hero Section */ +.hero { + padding: 160px 0 100px; + position: relative; + overflow: hidden; +} + +.hero-bg-glow { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 600px; + height: 600px; + background: radial-gradient(circle, rgba(160, 51, 255, 0.15) 0%, rgba(5, 5, 16, 0) 70%); + z-index: 0; + pointer-events: none; +} + +.hero-container { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 60px; + align-items: center; + position: relative; + z-index: 1; +} + +.badge { + display: inline-block; + background: rgba(255, 43, 214, 0.1); + border: 1px solid var(--primary-color); + color: var(--primary-color); + padding: 6px 16px; + border-radius: 9999px; + font-size: 14px; + font-weight: 600; + margin-bottom: 24px; + text-shadow: 0 0 8px rgba(255, 43, 214, 0.3); +} + +.hero-content h1 { + font-family: var(--font-titles); + font-size: 54px; + font-weight: 800; + line-height: 1.15; + margin-bottom: 24px; +} + +.text-gradient { + background: linear-gradient(90deg, var(--primary-color), #a033ff, var(--accent-color)); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + filter: drop-shadow(0 0 10px rgba(160, 51, 255, 0.3)); +} + +.hero-lead { + font-size: 18px; + color: var(--text-muted); + margin-bottom: 40px; +} + +.hero-actions { + display: flex; + gap: 20px; +} + +/* Buttons */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 14px 28px; + border-radius: 12px; + font-family: var(--font-body); + font-weight: 600; + text-decoration: none; + font-size: 16px; + cursor: pointer; + transition: var(--transition-smooth); +} + +.btn-primary { + background: linear-gradient(90deg, var(--primary-color), #a033ff); + color: #fff; + border: none; + box-shadow: var(--shadow-neon); +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 0 25px rgba(255, 43, 214, 0.6); +} + +.btn-secondary { + background: rgba(21, 21, 40, 0.6); + border: 1px solid var(--border-color); + color: var(--text-color); +} + +.btn-secondary:hover { + border-color: var(--accent-color); + box-shadow: var(--shadow-cyan); + transform: translateY(-2px); +} + +.btn-block { + width: 100%; +} + +/* Interactive Device Mockup styling */ +.hero-visual { + display: flex; + justify-content: center; + align-items: center; +} + +.mockup-container { + position: relative; +} + +.tablet-mockup { + width: 440px; + height: 300px; + background: #181824; + border: 12px solid #000; + border-radius: 24px; + box-shadow: var(--shadow-neon), 0 20px 40px rgba(0,0,0,0.8); + position: relative; + overflow: hidden; +} + +.tablet-screen { + width: 100%; + height: 100%; + background-color: #050510; + position: relative; +} + +.app-mockup-content { + display: flex; + flex-direction: column; + height: 100%; + justify-content: space-between; + padding: 20px; +} + +.app-header { + display: flex; + align-items: center; + gap: 8px; +} + +.app-logo-icon { + color: var(--primary-color); + font-size: 20px; + filter: drop-shadow(0 0 5px var(--primary-color)); +} + +.app-title { + font-family: var(--font-titles); + font-weight: 700; + font-size: 18px; + letter-spacing: 0.5px; +} + +.app-body { + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + position: relative; +} + +.app-circle-btn { + width: 70px; + height: 70px; + border-radius: 50%; + background: linear-gradient(135deg, var(--primary-color), var(--accent-color)); + display: flex; + justify-content: center; + align-items: center; + color: #fff; + font-size: 28px; + cursor: pointer; + box-shadow: 0 0 15px rgba(160, 51, 255, 0.6); + position: relative; + z-index: 2; + transition: var(--transition-smooth); +} + +.app-circle-btn:hover { + transform: scale(1.1); + box-shadow: 0 0 25px rgba(160, 51, 255, 0.9); +} + +.pulse-ring { + position: absolute; + width: 70px; + height: 70px; + border-radius: 50%; + border: 2px solid var(--primary-color); + animation: ringPulse 2s infinite ease-out; + pointer-events: none; + z-index: 1; +} + +.app-welcome { + margin-top: 16px; + font-size: 14px; + font-weight: 600; +} + +.app-footer-text { + margin-top: 4px; + font-size: 10px; + color: var(--text-muted); +} + +/* Features Grid */ +.features-section { + padding: 100px 0; + background: linear-gradient(180deg, var(--bg-color) 0%, var(--surface-color) 100%); + border-top: 1px solid var(--border-color); +} + +.section-title { + font-family: var(--font-titles); + font-size: 40px; + font-weight: 800; + text-align: center; + margin-bottom: 60px; +} + +.features-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); + gap: 30px; +} + +.feature-card { + background: rgba(13, 13, 26, 0.65); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: 1px solid var(--border-color); + border-radius: 20px; + padding: 40px 30px; + transition: var(--transition-smooth); +} + +.feature-card:hover { + transform: translateY(-8px); + border-color: var(--primary-color); + box-shadow: var(--shadow-neon); +} + +.feature-icon { + width: 60px; + height: 60px; + border-radius: 14px; + background: rgba(40, 223, 255, 0.1); + color: var(--accent-color); + display: flex; + justify-content: center; + align-items: center; + font-size: 24px; + margin-bottom: 24px; + border: 1px solid rgba(40, 223, 255, 0.2); + transition: var(--transition-smooth); +} + +.feature-card:hover .feature-icon { + background: rgba(255, 43, 214, 0.15); + color: var(--primary-color); + border-color: rgba(255, 43, 214, 0.3); + box-shadow: 0 0 10px rgba(255, 43, 214, 0.2); +} + +.feature-card h3 { + font-family: var(--font-titles); + font-size: 22px; + font-weight: 700; + margin-bottom: 12px; +} + +.feature-card p { + color: var(--text-muted); +} + +/* FAQ Section */ +.faq-section { + padding: 100px 0; + background: var(--bg-color); +} + +.faq-container { + max-width: 800px; +} + +.accordion { + margin-top: 40px; +} + +.accordion-item { + border: 1px solid var(--border-color); + border-radius: 12px; + margin-bottom: 16px; + background: var(--surface-color); + overflow: hidden; + transition: var(--transition-smooth); +} + +.accordion-item:hover { + border-color: rgba(40, 223, 255, 0.35); +} + +.accordion-header { + width: 100%; + padding: 20px 24px; + background: none; + border: none; + color: #fff; + font-family: var(--font-body); + font-size: 18px; + font-weight: 600; + text-align: left; + display: flex; + justify-content: space-between; + align-items: center; + cursor: pointer; +} + +.accordion-header i { + color: var(--accent-color); + transition: var(--transition-smooth); +} + +.accordion-content { + max-height: 0; + overflow: hidden; + transition: max-height 0.35s ease-out; + padding: 0 24px; +} + +.accordion-content p { + padding-bottom: 20px; + color: var(--text-muted); +} + +.accordion-content code { + display: block; + background: #050510; + padding: 12px 16px; + border-radius: 6px; + font-family: monospace; + color: var(--accent-color); + margin-bottom: 16px; + word-break: break-all; + border-left: 3px solid var(--primary-color); +} + +.accordion-content ul { + margin-left: 20px; + color: var(--text-muted); + padding-bottom: 20px; +} + +/* Active State Accordion */ +.accordion-item.active { + border-color: var(--accent-color); + box-shadow: 0 0 15px rgba(40, 223, 255, 0.15); +} + +.accordion-item.active .accordion-header i { + transform: rotate(180deg); +} + +.accordion-item.active .accordion-content { + max-height: 500px; +} + +/* Feedback Section */ +.feedback-section { + padding: 100px 0; + background: linear-gradient(180deg, var(--surface-color) 0%, var(--bg-color) 100%); + border-top: 1px solid var(--border-color); +} + +.section-subtitle { + text-align: center; + color: var(--text-muted); + max-width: 600px; + margin: -40px auto 60px; +} + +.feedback-grid { + display: grid; + grid-template-columns: 1fr 1.5fr; + gap: 40px; + align-items: start; +} + +/* Glassmorphism panel style */ +.glass { + background: rgba(13, 13, 26, 0.8); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border: 1px solid rgba(40, 223, 255, 0.15); + border-radius: 20px; + box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37); +} + +.feedback-form-card { + padding: 30px; +} + +.feedback-form-card h3 { + font-family: var(--font-titles); + font-size: 22px; + margin-bottom: 24px; +} + +.form-group { + margin-bottom: 20px; +} + +.form-group label { + display: block; + margin-bottom: 8px; + font-size: 14px; + font-weight: 500; + color: var(--text-muted); +} + +.form-group input, +.form-group select, +.form-group textarea { + width: 100%; + padding: 12px 16px; + background: rgba(5, 5, 16, 0.6); + border: 1px solid var(--border-color); + border-radius: 8px; + color: #fff; + font-family: var(--font-body); + font-size: 15px; + transition: var(--transition-smooth); +} + +.form-group input:focus, +.form-group select:focus, +.form-group textarea:focus { + outline: none; + border-color: var(--accent-color); + box-shadow: 0 0 10px rgba(40, 223, 255, 0.3); +} + +/* Feedback List Column */ +.feedback-list-container { + display: flex; + flex-direction: column; + gap: 24px; +} + +.list-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.list-header h3 { + font-family: var(--font-titles); + font-size: 22px; +} + +.list-filters { + display: flex; + gap: 8px; +} + +.filter-btn { + background: rgba(21, 21, 40, 0.6); + border: 1px solid var(--border-color); + color: var(--text-muted); + padding: 6px 12px; + border-radius: 6px; + font-size: 13px; + font-weight: 600; + cursor: pointer; + transition: var(--transition-smooth); +} + +.filter-btn.active, +.filter-btn:hover { + border-color: var(--primary-color); + color: #fff; +} + +.feedback-list { + display: flex; + flex-direction: column; + gap: 16px; +} + +.feedback-item { + background: var(--surface-color); + border: 1px solid var(--border-color); + border-radius: 12px; + padding: 20px; + display: flex; + justify-content: space-between; + gap: 20px; + transition: var(--transition-smooth); +} + +.feedback-item:hover { + border-color: rgba(255, 43, 214, 0.25); + box-shadow: 0 4px 20px rgba(255, 43, 214, 0.05); +} + +.fb-card-content { + flex: 1; +} + +.fb-meta { + display: flex; + align-items: center; + gap: 10px; + margin-bottom: 8px; +} + +.fb-tag { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + padding: 3px 8px; + border-radius: 4px; +} + +.fb-tag.feature { + background: rgba(40, 223, 255, 0.1); + color: var(--accent-color); + border: 1px solid rgba(40, 223, 255, 0.25); +} + +.fb-tag.bug { + background: rgba(255, 68, 102, 0.1); + color: #ff4466; + border: 1px solid rgba(255, 68, 102, 0.25); +} + +.fb-tag.love { + background: rgba(255, 43, 214, 0.1); + color: var(--primary-color); + border: 1px solid rgba(255, 43, 214, 0.25); +} + +.fb-author { + font-size: 13px; + color: var(--text-muted); +} + +.feedback-item h4 { + font-size: 18px; + font-weight: 600; + margin-bottom: 8px; +} + +.feedback-item p { + color: var(--text-muted); + font-size: 14px; +} + +.fb-upvote-section { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.upvote-btn { + background: rgba(21, 21, 40, 0.8); + border: 1px solid var(--border-color); + border-radius: 8px; + width: 48px; + height: 54px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + color: var(--text-muted); + cursor: pointer; + transition: var(--transition-smooth); +} + +.upvote-btn i { + font-size: 14px; + margin-bottom: 4px; +} + +.upvote-btn span { + font-size: 12px; + font-weight: 700; +} + +.upvote-btn:hover, +.upvote-btn.voted { + border-color: var(--primary-color); + color: var(--primary-color); + box-shadow: 0 0 10px rgba(255, 43, 214, 0.2); +} + +.upvote-btn.voted i { + transform: translateY(-2px); +} + +/* Legal & Imprint Modals */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(5, 5, 16, 0.85); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + z-index: 2000; + display: flex; + justify-content: center; + align-items: center; + opacity: 0; + pointer-events: none; + transition: opacity 0.3s ease; +} + +.modal-overlay.open { + opacity: 1; + pointer-events: auto; +} + +.modal-content { + width: 90%; + max-width: 650px; + max-height: 80vh; + padding: 40px; + position: relative; + display: flex; + flex-direction: column; + transform: scale(0.9); + transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +.modal-overlay.open .modal-content { + transform: scale(1); +} + +.modal-close { + position: absolute; + top: 20px; + right: 20px; + background: none; + border: none; + color: var(--text-muted); + font-size: 32px; + cursor: pointer; + line-height: 1; + transition: var(--transition-smooth); +} + +.modal-close:hover { + color: var(--primary-color); +} + +.modal-content h2 { + font-family: var(--font-titles); + font-size: 28px; + margin-bottom: 24px; + border-bottom: 1px solid var(--border-color); + padding-bottom: 12px; +} + +.modal-scroll-area { + overflow-y: auto; + flex: 1; + padding-right: 12px; +} + +.modal-scroll-area::-webkit-scrollbar { + width: 6px; +} + +.modal-scroll-area::-webkit-scrollbar-track { + background: rgba(5, 5, 16, 0.3); +} + +.modal-scroll-area::-webkit-scrollbar-thumb { + background: var(--border-color); + border-radius: 4px; +} + +.modal-scroll-area h3 { + margin: 20px 0 10px; + font-size: 18px; +} + +.modal-scroll-area h4 { + margin: 15px 0 8px; + font-size: 16px; + color: var(--accent-color); +} + +.modal-scroll-area p { + margin-bottom: 12px; + font-size: 14px; + color: var(--text-muted); +} + +.modal-scroll-area a { + color: var(--accent-color); +} + +/* Footer styling */ +.main-footer { + border-top: 1px solid var(--border-color); + padding: 40px 0; + font-size: 14px; + color: var(--text-muted); +} + +.footer-container { + display: flex; + justify-content: space-between; + align-items: center; +} + +.footer-links { + display: flex; + gap: 24px; +} + +.footer-links a { + color: var(--text-muted); + text-decoration: none; + transition: var(--transition-smooth); +} + +.footer-links a:hover { + color: var(--primary-color); +} + +/* Keyframes */ +@keyframes logoGlowPulse { + 0% { + text-shadow: 0 0 10px var(--primary-color), 0 0 20px var(--primary-color); + } + 100% { + text-shadow: 0 0 15px var(--primary-color), 0 0 30px var(--primary-color), 0 0 40px var(--accent-color); + } +} + +@keyframes ringPulse { + 0% { + transform: scale(0.95); + opacity: 0.8; + } + 100% { + transform: scale(1.6); + opacity: 0; + } +} + +/* Responsiveness */ +@media (max-width: 992px) { + .hero-container { + grid-template-columns: 1fr; + text-align: center; + gap: 40px; + } + + .hero-actions { + justify-content: center; + } + + .feedback-grid { + grid-template-columns: 1fr; + } +} + +@media (max-width: 768px) { + .menu-toggle { + display: block; + } + + .nav-links { + display: none; + position: absolute; + top: 80px; + left: 0; + width: 100%; + background: var(--bg-color); + border-bottom: 1px solid var(--border-color); + flex-direction: column; + padding: 24px; + gap: 20px; + box-shadow: 0 10px 20px rgba(0,0,0,0.5); + } + + .nav-links.mobile-open { + display: flex; + } + + .hero-content h1 { + font-size: 38px; + } + + .tablet-mockup { + width: 320px; + height: 220px; + border-width: 8px; + } + + .app-circle-btn { + width: 50px; + height: 50px; + font-size: 20px; + } + + .pulse-ring { + width: 50px; + height: 50px; + } + + .footer-container { + flex-direction: column; + gap: 20px; + text-align: center; + } +}