From de9cbe37409520da345f5069ce94b400047d2c32 Mon Sep 17 00:00:00 2001 From: Till Tomczak Date: Sun, 1 Jun 2025 23:53:00 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A7=20Aktualisierung=20der=20README.md?= =?UTF-8?q?=20zur=20Verbesserung=20der=20Backend-=20und=20Frontend-Install?= =?UTF-8?q?ationsanweisungen.=20Einf=C3=BChrung=20eines=20konsolidierten?= =?UTF-8?q?=20Setup-Skripts=20f=C3=BCr=20die=20automatische=20Installation?= =?UTF-8?q?=20und=20Optimierung=20der=20Kiosk-Modus-Anweisungen.=20Hinzuf?= =?UTF-8?q?=C3=BCgen=20eines=20neuen=20Dokuments=20f=C3=BCr=20Verbindungst?= =?UTF-8?q?ests=20zwischen=20Frontend=20und=20Backend.=20Verbesserte=20Far?= =?UTF-8?q?bpalette=20und=20CSS-Styles=20f=C3=BCr=20Kiosk-Modus=20in=20tai?= =?UTF-8?q?lwind.config.js=20und=20input.css.=20=F0=9F=93=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 104 +- backend/database/myp.db | Bin 118784 -> 118784 bytes backend/database/myp.db-shm | Bin 32768 -> 32768 bytes backend/database/myp.db-wal | Bin 8272 -> 37112 bytes backend/logs/analytics/analytics.log | 1 + backend/logs/app/app.log | 30 + backend/logs/backup/backup.log | 1 + backend/logs/dashboard/dashboard.log | 4 + backend/logs/database/database.log | 1 + .../email_notification/email_notification.log | 1 + backend/logs/maintenance/maintenance.log | 2 + .../logs/multi_location/multi_location.log | 2 + backend/logs/permissions/permissions.log | 1 + .../logs/printer_monitor/printer_monitor.log | 33 + backend/logs/printers/printers.log | 30 + backend/logs/scheduler/scheduler.log | 205 + backend/logs/security/security.log | 1 + .../shutdown_manager/shutdown_manager.log | 1 + backend/logs/startup/startup.log | 9 + backend/logs/windows_fixes/windows_fixes.log | 4 + backend/static/css/input.css | 3421 +---------------- backend/static/css/tailwind.min.css | 2 +- backend/tailwind.config.js | 204 +- docs/FRONTEND_INSTALLATION.md | 382 ++ docs/FRONTEND_SETUP_CHANGELOG.md | 296 ++ docs/VERBINDUNGSTEST.md | 299 +- frontend/setup.sh | 695 ++++ 27 files changed, 2339 insertions(+), 3390 deletions(-) create mode 100644 docs/FRONTEND_INSTALLATION.md create mode 100644 docs/FRONTEND_SETUP_CHANGELOG.md create mode 100644 frontend/setup.sh diff --git a/README.md b/README.md index 35920a5c..4b0558a0 100644 --- a/README.md +++ b/README.md @@ -73,51 +73,58 @@ Dieses Repository enthält **zwei sich ergänzende Projektarbeiten** für die IH ### Backend-System (Hardware & APIs) ```bash -# Backend-Server starten (Till Tomczaks System) +# Backend-Server automatisch installieren (Till Tomczaks System) cd backend -sudo ./setup.sh # Automatische Installation -python app.py # Oder für Development +sudo ./setup.sh # Konsolidiertes Setup-Skript -# Kiosk-Modus auf Raspberry Pi -sudo systemctl start myp-https.service +# Oder manuell für Development +python app.py ``` -### Frontend-System (Web-Interface) +### Frontend-System (Web-Interface) - NEU: Automatische Installation ```bash -# Frontend-Server starten (Torben Haacks System) +# Frontend-Server automatisch installieren (Torben Haacks System) cd frontend +sudo ./setup.sh # Konsolidiertes Setup-Skript mit Mercedes SSL + +# Oder manuell für Development pnpm install pnpm db # Datenbank einrichten pnpm dev # Development-Server - -# Produktions-Deployment -pnpm build && pnpm start ``` ### Vollständiges System ```bash # Backend (API-Server) -cd backend && python app.py --host 0.0.0.0 --port 5000 & +cd backend && sudo ./setup.sh -# Frontend (Web-Interface) -cd frontend && pnpm build && pnpm start & +# Frontend (Web-Interface mit HTTPS) +cd frontend && sudo ./setup.sh ``` ## 🌐 Systemzugriff -### Produktions-URLs -- **Web-Interface**: `http://localhost:3000` (Torben Haacks Frontend) -- **API-Backend**: `https://192.168.0.105:443/api` (Till Tomczaks APIs auf separatem Server) +### Produktions-URLs (Nach Setup-Skript Installation) +- **Frontend (HTTPS)**: `https://m040tbaraspi001.de040.corpintra.net` (Torben Haacks Frontend) +- **Frontend (Lokal)**: `https://localhost` (Fallback-Zugang) +- **API-Backend**: `https://192.168.0.105:443/api` (Till Tomczaks APIs) - **Kiosk-Modus**: `https://192.168.0.105:443` (Lokales Touch-Interface) +### Development-URLs +- **Frontend (Dev)**: `http://localhost:3000` (Development-Server) +- **Backend (Dev)**: `http://localhost:5000` (Development-API) + ### Standard-Anmeldedaten - **Benutzername**: `admin` - **Passwort**: `admin123` -### Netzwerk-Konfiguration -- **Backend-Server**: `192.168.0.105:443` (HTTPS) -- **Frontend-Server**: `localhost:3000` (HTTP Development) -- **SSL-Zertifikate**: Selbstsigniert (automatisch akzeptiert) +### SSL-Zertifikate (Mercedes) +Nach der automatischen Installation sind selbstsignierte Mercedes-Zertifikate verfügbar: +- **Domain**: `m040tbaraspi001.de040.corpintra.net` +- **Organisation**: Mercedes-Benz AG +- **Abteilung**: IT-Abteilung +- **Standort**: Stuttgart, Baden-Württemberg +- **Gültigkeit**: 365 Tage ## 📁 Projektstruktur & Integration @@ -222,7 +229,18 @@ export const API_BASE_URL = { ## 🖥️ Deployment-Szenarien -### Szenario 1: Separate Server (Empfohlen) +### Szenario 1: Automatische Produktions-Installation (Neu - Empfohlen) +```bash +# Backend-Server (Raspberry Pi oder Linux-Server) +cd backend +sudo ./setup.sh # Automatische Installation mit Kiosk-Modus + +# Frontend-Server (separater Server oder gleicher Server) +cd frontend +sudo ./setup.sh # Automatische Installation mit HTTPS auf Port 443 +``` + +### Szenario 2: Separate Server (Manuell) ```bash # Backend-Server (z.B. Raspberry Pi oder Linux-Server) cd backend @@ -233,7 +251,7 @@ cd frontend npm run build && npm start ``` -### Szenario 2: Docker-Deployment +### Szenario 3: Docker-Deployment ```yaml # docker-compose.yml services: @@ -243,16 +261,16 @@ services: frontend: build: ./frontend - ports: ["3000:3000"] + ports: ["80:80", "443:443"] environment: - NEXT_PUBLIC_API_URL=http://backend:5000/api ``` -### Szenario 3: Raspberry Pi Kiosk (Lokal) +### Szenario 4: Raspberry Pi Kiosk (Lokal) ```bash -# Vollständige Kiosk-Installation -cd backend && sudo ./setup.sh -# Automatischer Start: Touch-Interface + Smart-Plug-Steuerung +# Vollständige Kiosk-Installation (Backend + Frontend) +cd backend && sudo ./setup.sh # Backend mit Kiosk-Interface +cd frontend && sudo ./setup.sh # Frontend mit HTTPS-Server ``` ## 🔧 Konfiguration & Environment @@ -276,7 +294,7 @@ OFFLINE_MODE=true ### Frontend-Konfiguration (.env.local) ```env -# Frontend-Server Einstellungen - Separater Backend-Server +# Frontend-Server Einstellungen - HTTPS mit Mercedes SSL NEXT_PUBLIC_API_URL=https://192.168.0.105:443 DATABASE_URL=file:./db/frontend.db @@ -286,6 +304,11 @@ NODE_TLS_REJECT_UNAUTHORIZED=0 # Analytics-Features ENABLE_ADVANCED_ANALYTICS=true CHART_REFRESH_INTERVAL=30000 + +# Production HTTPS (Nach Setup-Skript) +HTTPS_ENABLED=true +SSL_CERT_PATH=/etc/ssl/certs/myp/frontend.crt +SSL_KEY_PATH=/etc/ssl/certs/myp/frontend.key ``` ## 📊 Features im Überblick @@ -321,6 +344,14 @@ python app.py --debug ``` ### Frontend-Entwicklung (Torben Haack) + +#### Automatische Installation (Empfohlen) +```bash +cd frontend +sudo ./setup.sh # Interaktives Setup-Menü +``` + +#### Manuelle Entwicklung ```bash cd frontend pnpm install @@ -328,13 +359,26 @@ pnpm db:migrate pnpm dev ``` +#### Frontend-Setup-Skript Features +Das neue `frontend/setup.sh` bietet: +- **Vollständige Installation**: Docker, SSL-Zertifikate, Caddy Reverse Proxy +- **Mercedes SSL-Zertifikate**: Selbstsignierte Zertifikate für `m040tbaraspi001.de040.corpintra.net` +- **Automatischer HTTPS-Server**: Verfügbar auf Port 443 (nicht 3000) +- **Systemd-Integration**: Automatischer Start beim Boot +- **Interaktives Menü**: + 1. Vollständige Frontend-Installation + 2. SSL-Zertifikate neu generieren + 3. Service-Status prüfen + 4. Beenden + ### Integration testen ```bash # Backend-APIs testen curl http://localhost:5000/api/printers -# Frontend mit Backend-Integration -# Frontend auf :3000 konsumiert Backend-APIs von :5000 +# Frontend mit HTTPS (nach Setup-Skript) +curl -k https://m040tbaraspi001.de040.corpintra.net/health +curl -k https://localhost/health ``` ## 📚 Dokumentation diff --git a/backend/database/myp.db b/backend/database/myp.db index a4bed4bd48c29c0578b32d6e2dde5a06044d9071..c2e8b230a7adce302f733c76106699669a2a8207 100644 GIT binary patch delta 140 zcmZozz}~QceS$ROv57LyjK?-6aO;~|x(53P`kN+3W;g{|SUMG?xSE;x`lnb#8T*C# zC8qnNnq(Asrn#m?M&|f>CF>XD`WslfPhOxO%583CVyI_mYG!6=vB}`DHjr;>s%L6! lWNBi#`CK#)lK@P>%+ko%sL_CV^2KP?X8ySC{BevQ3jqSbDro=! delta 140 zcmZozz}~QceS$RO(TOt7j7K*naO<0z_?uQ#1{a(8WcZg9`Gw?E`B)n2`-dBNC+mgy zyChlqI~pV=W@cnpmSh?lrzI6TS?FbDPF|oN%57|AVyS0pXlZO@xyj(LHjr;%re|Sl kU}|W*`CK#)lK@P>*c_;)(SUjK#c0)L{jciS@D@*919DJRq?7pVKeCjhi_10B+|oS^xk5 diff --git a/backend/database/myp.db-wal b/backend/database/myp.db-wal index 759f78ff40d8597c250049c8a5d6d9966dee2fac..a2cc3d98fc1da1901a24f9f9bf89c30050fc79eb 100644 GIT binary patch literal 37112 zcmeI5Z-^sj8Nid>+hqUVTH%=LxiGRGjmbiCl6q@tgU7(48Lq z)l;)6=8;E|pYzoHy6)brqbXKhPxXZG^V4g)BMx8oRMwDPJn@vfDcqcKhn)=6QB{ z%2XRV`)p}y6RdcTPZ(R!7sI@qk4vARvU)tzelvZ z>FI8`u7<|&bghaO{sIC~+1M)ME_l*SxA%tK_p=?t^mL~hOaZsGJiA)6G+KIGaoyUF zG*z44k%ju(y^-b4Oz;>@w_lC}qpnveG@>k%xsw`O^^QVAb;hb$wn0zQcC4E2;^FsO z;mHT(k?E^8s=B4ttEPo{`G6h$x?UrMBjlU5Hi%+pnEqLH#_2EbwWr1GHK&U)GyRTj zIJz5MKGk)NLleEh;1zp%cVnxxdyRdne2rZ;G{W=Z&Te_*(l#zz4tizS-SWlqZh3pH z{0!SIb_e$9EH>316%B41aNloHJ&+1GgS&*#jMyVYZNwe}s8Kzs`A=u=&Q7P9rKRM( zJz^7(su%}GjqE^|dNE5`1ga{=AYc}u?L(OZuVn|@4Q(N+(pFn_OVxT>J7kL^71r3* zot@2cX&dj>`zb|N9*Rh?UelTO3sY%kWhHq}AUH^KTXz};(nZU3qpxQuDA8KXg_H%R zhMHqdHL)>wyed($&@@b4*U*0GZs70)ai6Xl_4T4A-+kl0ZQ+-P_XIBMXe?5Q%4C{Z zTui=bb$m|C_4G!yZXLRA@N}A*cc>yp6+EMX{EXVLdp4SaQP*$jfpZWe>VUes>S3TR z$hSmWSX#4AH6T-u@W(o0*i}{2kiiLDL)U` zIEJ7m{9r4M-{wKRkT3dhE47|VGYpfw+ufrm&T%ngcal;kQWUo%QTKYsyLQ&B8`9x9 zc%%+1Bnpcfc3nsDm9HN`{g73ut0=JSh7*(`MA=Sf?ya9nGYbpJmuh5R_}f^ zOJn9lg;{+OXahxffsw)0crJCT{RkP)F2$*wmKBT-_1J>Hq|GLvC! za=sciDA-`-atxxQIRw`3RsAkMtumo?@P0 zhUo`c<*G;?CNg99Zz_yY{MottZt|B%b@&dz5NYoq$(FSF6{A;6uMj!G5uFnVZ0#lF| zfV@ED^G?VMI4Hl=mlqhvpV#~61$N$g`RA{GdGAMaIAJiKO`v~p0|Gz*2mk>f00e*l z5C8%|00;m9AOHjc0`1Gmk0u^x+7nC6RH;k`LbYn1kGSOuO_8hejjmQ`!@Xy>`xTjiT~ znns~j!s8mQhfafA4jE14MR|pnSHuD<-0C~{xrkPaWn4) z2;dnI00KY&2mk>f00e*l5I7M8+R~}hiO-&SeCg38hFL`6e?_$|_Gv-j*=3<1a)OfQ zcuwH68B`|k>CO$7mw0xCUDcgBI@Y|xR#rlK@vKSUw7**t)EX9eO97$w;RFQdFWFagvKcdKjBE(Qe+mFf%%>Mh0 zUwrTHKl?P~1x|#YgGmAbAOHk_01yBIKmZ8D6M(!xC+`7y0rcVPvCIpg4~0p(C@b(1 z-%C_%CmH?{9o-5%L1@4gzX{01yBIKmZ5;0U!VbP6UBtmKRVs zDW5}EfN)*_9pRuqV;y!f_&3N4oCrS$lLP`l z00;m9AOHk_01${L0C@p8FMz+sO}?ysY|jgjbWu)0UnYR>5x{wY?r##rpBG4d@+aea zKmE?{ATJQ_AfOfq00AHX1b_e#00KbZL=ZS$c>!7CgshyG;k*F;$p<(uz>4I@' not supported between instances of 'NoneType' and 'int' +2025-06-01 23:48:16 - [app] app - [INFO] INFO - Dashboard-Refresh erfolgreich: {'active_jobs': 0, 'available_printers': 0, 'total_jobs': 0, 'pending_jobs': 0, 'success_rate': 0, 'completed_jobs': 0, 'failed_jobs': 0, 'cancelled_jobs': 0, 'total_users': 0, 'online_printers': 0, 'offline_printers': 0} +2025-06-01 23:48:16 - [app] app - [ERROR] ERROR - Fehler beim Abrufen der Dashboard-Statistiken: '>' not supported between instances of 'NoneType' and 'int' +2025-06-01 23:48:16 - [app] app - [INFO] INFO - Dashboard-Refresh erfolgreich: {'active_jobs': 0, 'available_printers': 0, 'total_jobs': 0, 'pending_jobs': 0, 'success_rate': 0, 'completed_jobs': 0, 'failed_jobs': 0, 'cancelled_jobs': 0, 'total_users': 0, 'online_printers': 0, 'offline_printers': 0} +2025-06-01 23:48:47 - [app] app - [INFO] INFO - Dashboard-Refresh angefordert von User 1 +2025-06-01 23:48:47 - [app] app - [INFO] INFO - Dashboard-Refresh angefordert von User 1 +2025-06-01 23:48:47 - [app] app - [INFO] INFO - Dashboard-Refresh erfolgreich: {'active_jobs': 0, 'available_printers': 2, 'total_jobs': 16, 'pending_jobs': 0, 'success_rate': 0.0, 'completed_jobs': 0, 'failed_jobs': 0, 'cancelled_jobs': 0, 'total_users': None, 'online_printers': 0, 'offline_printers': 2} +2025-06-01 23:48:47 - [app] app - [INFO] INFO - Dashboard-Refresh erfolgreich: {'active_jobs': 0, 'available_printers': 2, 'total_jobs': 16, 'pending_jobs': 0, 'success_rate': 0.0, 'completed_jobs': None, 'failed_jobs': 0, 'cancelled_jobs': 0, 'total_users': 1, 'online_printers': 0, 'offline_printers': 2} +2025-06-01 23:50:28 - [app] app - [INFO] INFO - Optimierte SQLite-Engine erstellt: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend\database\myp.db +2025-06-01 23:50:29 - [app] app - [INFO] INFO - SQLite für Raspberry Pi optimiert (reduzierte Cache-Größe, SD-Karten I/O) +2025-06-01 23:50:29 - [app] app - [INFO] INFO - ✅ Timeout Force-Quit Manager geladen +2025-06-01 23:50:29 - [app] app - [INFO] INFO - ✅ Zentraler Shutdown-Manager initialisiert +2025-06-01 23:50:29 - [app] app - [INFO] INFO - 🔄 Starte Datenbank-Setup und Migrationen... +2025-06-01 23:50:29 - [app] app - [INFO] INFO - Datenbank mit Optimierungen initialisiert +2025-06-01 23:50:29 - [app] app - [INFO] INFO - ✅ JobOrder-Tabelle bereits vorhanden +2025-06-01 23:50:29 - [app] app - [INFO] INFO - Admin-Benutzer admin (admin@mercedes-benz.com) existiert bereits. Passwort wurde zurückgesetzt. +2025-06-01 23:50:29 - [app] app - [INFO] INFO - ✅ Datenbank-Setup und Migrationen erfolgreich abgeschlossen +2025-06-01 23:50:29 - [app] app - [INFO] INFO - 🖨️ Starte automatische Steckdosen-Initialisierung... +2025-06-01 23:50:33 - [app] app - [INFO] INFO - ✅ Steckdosen-Initialisierung: 0/2 Drucker erfolgreich +2025-06-01 23:50:33 - [app] app - [WARNING] WARNING - ⚠️ 2 Drucker konnten nicht initialisiert werden +2025-06-01 23:50:33 - [app] app - [INFO] INFO - 🔄 Debug-Modus: Queue Manager deaktiviert für Entwicklung +2025-06-01 23:50:33 - [app] app - [INFO] INFO - Job-Scheduler gestartet +2025-06-01 23:50:33 - [app] app - [INFO] INFO - Starte Debug-Server auf 0.0.0.0:5000 (HTTP) +2025-06-01 23:50:33 - [app] app - [INFO] INFO - Windows-Debug-Modus: Auto-Reload deaktiviert +2025-06-01 23:51:41 - [app] app - [INFO] INFO - Dashboard-Refresh angefordert von User 1 +2025-06-01 23:51:41 - [app] app - [INFO] INFO - Dashboard-Refresh angefordert von User 1 +2025-06-01 23:51:41 - [app] app - [INFO] INFO - Dashboard-Refresh erfolgreich: {'active_jobs': 0, 'available_printers': 2, 'total_jobs': 16, 'pending_jobs': 0, 'success_rate': 0.0, 'completed_jobs': 0, 'failed_jobs': 0, 'cancelled_jobs': 0, 'total_users': 1, 'online_printers': 0, 'offline_printers': 2} +2025-06-01 23:51:41 - [app] app - [INFO] INFO - Dashboard-Refresh erfolgreich: {'active_jobs': 0, 'available_printers': 2, 'total_jobs': 16, 'pending_jobs': 0, 'success_rate': 0.0, 'completed_jobs': 0, 'failed_jobs': 0, 'cancelled_jobs': 0, 'total_users': 1, 'online_printers': 0, 'offline_printers': 2} diff --git a/backend/logs/backup/backup.log b/backend/logs/backup/backup.log index 0fdc1579..1aaca73a 100644 --- a/backend/logs/backup/backup.log +++ b/backend/logs/backup/backup.log @@ -99,3 +99,4 @@ 2025-06-01 23:42:51 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation) 2025-06-01 23:43:48 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation) 2025-06-01 23:47:39 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation) +2025-06-01 23:50:28 - [backup] backup - [INFO] INFO - BackupManager initialisiert (minimal implementation) diff --git a/backend/logs/dashboard/dashboard.log b/backend/logs/dashboard/dashboard.log index 1b562691..753909e3 100644 --- a/backend/logs/dashboard/dashboard.log +++ b/backend/logs/dashboard/dashboard.log @@ -381,3 +381,7 @@ 2025-06-01 23:47:40 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet 2025-06-01 23:47:40 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server wird mit threading initialisiert (eventlet-Fallback) 2025-06-01 23:47:40 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server initialisiert (async_mode: threading) +2025-06-01 23:50:28 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet +2025-06-01 23:50:29 - [dashboard] dashboard - [INFO] INFO - Dashboard-Background-Worker gestartet +2025-06-01 23:50:29 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server wird mit threading initialisiert (eventlet-Fallback) +2025-06-01 23:50:29 - [dashboard] dashboard - [INFO] INFO - Dashboard WebSocket-Server initialisiert (async_mode: threading) diff --git a/backend/logs/database/database.log b/backend/logs/database/database.log index 72a6f6ea..b6c7af06 100644 --- a/backend/logs/database/database.log +++ b/backend/logs/database/database.log @@ -95,3 +95,4 @@ 2025-06-01 23:42:51 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet 2025-06-01 23:43:48 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet 2025-06-01 23:47:39 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet +2025-06-01 23:50:28 - [database] database - [INFO] INFO - Datenbank-Wartungs-Scheduler gestartet diff --git a/backend/logs/email_notification/email_notification.log b/backend/logs/email_notification/email_notification.log index 5e9633c8..4ce87f80 100644 --- a/backend/logs/email_notification/email_notification.log +++ b/backend/logs/email_notification/email_notification.log @@ -92,3 +92,4 @@ 2025-06-01 23:42:52 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand) 2025-06-01 23:43:48 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand) 2025-06-01 23:47:40 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand) +2025-06-01 23:50:29 - [email_notification] email_notification - [INFO] INFO - 📧 Offline-E-Mail-Benachrichtigung initialisiert (kein echter E-Mail-Versand) diff --git a/backend/logs/maintenance/maintenance.log b/backend/logs/maintenance/maintenance.log index 87200e89..29212ddf 100644 --- a/backend/logs/maintenance/maintenance.log +++ b/backend/logs/maintenance/maintenance.log @@ -188,3 +188,5 @@ 2025-06-01 23:43:49 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet 2025-06-01 23:47:40 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet 2025-06-01 23:47:40 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet +2025-06-01 23:50:29 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet +2025-06-01 23:50:29 - [maintenance] maintenance - [INFO] INFO - Wartungs-Scheduler gestartet diff --git a/backend/logs/multi_location/multi_location.log b/backend/logs/multi_location/multi_location.log index 65e48683..74e2b2a1 100644 --- a/backend/logs/multi_location/multi_location.log +++ b/backend/logs/multi_location/multi_location.log @@ -188,3 +188,5 @@ 2025-06-01 23:43:49 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt 2025-06-01 23:47:40 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt 2025-06-01 23:47:40 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt +2025-06-01 23:50:29 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt +2025-06-01 23:50:29 - [multi_location] multi_location - [INFO] INFO - Standard-Standort erstellt diff --git a/backend/logs/permissions/permissions.log b/backend/logs/permissions/permissions.log index 602d7c16..6f74fde7 100644 --- a/backend/logs/permissions/permissions.log +++ b/backend/logs/permissions/permissions.log @@ -94,3 +94,4 @@ 2025-06-01 23:42:52 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert 2025-06-01 23:43:49 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert 2025-06-01 23:47:40 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert +2025-06-01 23:50:29 - [permissions] permissions - [INFO] INFO - 🔐 Permission Template Helpers registriert diff --git a/backend/logs/printer_monitor/printer_monitor.log b/backend/logs/printer_monitor/printer_monitor.log index 6e0e0f4d..17c086db 100644 --- a/backend/logs/printer_monitor/printer_monitor.log +++ b/backend/logs/printer_monitor/printer_monitor.log @@ -2720,3 +2720,36 @@ 2025-06-01 23:47:55 - [printer_monitor] printer_monitor - [WARNING] WARNING - 🔌 Tapo P110 (192.168.0.103): UNREACHABLE (Ping fehlgeschlagen) 2025-06-01 23:47:55 - [printer_monitor] printer_monitor - [INFO] INFO - ✅ Status-Update abgeschlossen für 2 Drucker 2025-06-01 23:47:59 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 4/6: 192.168.0.101 +2025-06-01 23:48:05 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 5/6: 192.168.0.102 +2025-06-01 23:48:11 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 6/6: 192.168.0.105 +2025-06-01 23:48:18 - [printer_monitor] printer_monitor - [INFO] INFO - ✅ Steckdosen-Erkennung abgeschlossen: 0/6 Steckdosen gefunden in 36.1s +2025-06-01 23:50:28 - [printer_monitor] printer_monitor - [INFO] INFO - 🖨️ Drucker-Monitor initialisiert +2025-06-01 23:50:28 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Automatische Tapo-Erkennung in separatem Thread gestartet +2025-06-01 23:50:29 - [printer_monitor] printer_monitor - [INFO] INFO - 🚀 Starte Steckdosen-Initialisierung beim Programmstart... +2025-06-01 23:50:30 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Starte automatische Tapo-Steckdosenerkennung... +2025-06-01 23:50:30 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Teste 6 Standard-IPs aus der Konfiguration +2025-06-01 23:50:30 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 1/6: 192.168.0.103 +2025-06-01 23:50:31 - [printer_monitor] printer_monitor - [WARNING] WARNING - ❌ Tapo P110 (192.168.0.103): Steckdose konnte nicht ausgeschaltet werden +2025-06-01 23:50:33 - [printer_monitor] printer_monitor - [WARNING] WARNING - ❌ Tapo P110 (192.168.0.104): Steckdose konnte nicht ausgeschaltet werden +2025-06-01 23:50:33 - [printer_monitor] printer_monitor - [INFO] INFO - 🎯 Steckdosen-Initialisierung abgeschlossen: 0/2 erfolgreich +2025-06-01 23:50:36 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 23:50:36 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Prüfe Status von 2 aktiven Druckern... +2025-06-01 23:50:36 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 2/6: 192.168.0.104 +2025-06-01 23:50:37 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 23:50:37 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Prüfe Status von 2 aktiven Druckern... +2025-06-01 23:50:42 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 3/6: 192.168.0.100 +2025-06-01 23:50:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus... +2025-06-01 23:50:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Prüfe Status von 2 aktiven Druckern... +2025-06-01 23:50:45 - [printer_monitor] printer_monitor - [WARNING] WARNING - 🔌 Tapo P110 (192.168.0.103): UNREACHABLE (Ping fehlgeschlagen) +2025-06-01 23:50:45 - [printer_monitor] printer_monitor - [WARNING] WARNING - 🔌 Tapo P110 (192.168.0.104): UNREACHABLE (Ping fehlgeschlagen) +2025-06-01 23:50:45 - [printer_monitor] printer_monitor - [INFO] INFO - ✅ Status-Update abgeschlossen für 2 Drucker +2025-06-01 23:50:46 - [printer_monitor] printer_monitor - [WARNING] WARNING - 🔌 Tapo P110 (192.168.0.103): UNREACHABLE (Ping fehlgeschlagen) +2025-06-01 23:50:46 - [printer_monitor] printer_monitor - [WARNING] WARNING - 🔌 Tapo P110 (192.168.0.104): UNREACHABLE (Ping fehlgeschlagen) +2025-06-01 23:50:46 - [printer_monitor] printer_monitor - [INFO] INFO - ✅ Status-Update abgeschlossen für 2 Drucker +2025-06-01 23:50:48 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 4/6: 192.168.0.101 +2025-06-01 23:50:54 - [printer_monitor] printer_monitor - [WARNING] WARNING - 🔌 Tapo P110 (192.168.0.103): UNREACHABLE (Ping fehlgeschlagen) +2025-06-01 23:50:54 - [printer_monitor] printer_monitor - [WARNING] WARNING - 🔌 Tapo P110 (192.168.0.104): UNREACHABLE (Ping fehlgeschlagen) +2025-06-01 23:50:54 - [printer_monitor] printer_monitor - [INFO] INFO - ✅ Status-Update abgeschlossen für 2 Drucker +2025-06-01 23:50:54 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 5/6: 192.168.0.102 +2025-06-01 23:51:00 - [printer_monitor] printer_monitor - [INFO] INFO - 🔍 Teste IP 6/6: 192.168.0.105 +2025-06-01 23:51:06 - [printer_monitor] printer_monitor - [INFO] INFO - ✅ Steckdosen-Erkennung abgeschlossen: 0/6 Steckdosen gefunden in 36.1s diff --git a/backend/logs/printers/printers.log b/backend/logs/printers/printers.log index 86ae00cb..e125255b 100644 --- a/backend/logs/printers/printers.log +++ b/backend/logs/printers/printers.log @@ -5203,3 +5203,33 @@ 2025-06-01 23:47:46 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) 2025-06-01 23:47:55 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker 2025-06-01 23:47:55 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 9023.39ms +2025-06-01 23:48:16 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 23:48:16 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker +2025-06-01 23:48:16 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 2.50ms +2025-06-01 23:48:17 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 23:48:17 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker +2025-06-01 23:48:17 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 0.56ms +2025-06-01 23:48:47 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 23:48:47 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker +2025-06-01 23:48:47 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 0.68ms +2025-06-01 23:48:48 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 23:48:48 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker +2025-06-01 23:48:48 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 0.50ms +2025-06-01 23:50:36 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 23:50:37 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 23:50:45 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 23:50:45 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker +2025-06-01 23:50:45 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 9026.16ms +2025-06-01 23:50:46 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker +2025-06-01 23:50:46 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 9043.59ms +2025-06-01 23:50:54 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker +2025-06-01 23:50:54 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 9042.87ms +2025-06-01 23:51:11 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 23:51:11 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker +2025-06-01 23:51:11 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 0.67ms +2025-06-01 23:51:41 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 23:51:41 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker +2025-06-01 23:51:41 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 1.07ms +2025-06-01 23:51:42 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1) +2025-06-01 23:51:42 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 2 Drucker +2025-06-01 23:51:42 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 1.04ms diff --git a/backend/logs/scheduler/scheduler.log b/backend/logs/scheduler/scheduler.log index f1ede7a3..cfe44c58 100644 --- a/backend/logs/scheduler/scheduler.log +++ b/backend/logs/scheduler/scheduler.log @@ -13220,3 +13220,208 @@ 2025-06-01 23:47:59 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) 2025-06-01 23:47:59 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 5 nicht einschalten 2025-06-01 23:47:59 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 6: test +2025-06-01 23:48:01 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:01 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten +2025-06-01 23:48:01 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi +2025-06-01 23:48:03 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:48:03 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 9 nicht einschalten +2025-06-01 23:48:03 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 10: zi +2025-06-01 23:48:05 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:48:05 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 10 nicht einschalten +2025-06-01 23:48:05 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 11: fee +2025-06-01 23:48:08 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:48:08 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 11 nicht einschalten +2025-06-01 23:48:08 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 12: fee +2025-06-01 23:48:10 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:48:10 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 12 nicht einschalten +2025-06-01 23:48:10 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 13: e2 +2025-06-01 23:48:12 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:12 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 13 nicht einschalten +2025-06-01 23:48:12 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 14: e2 +2025-06-01 23:48:14 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:14 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 14 nicht einschalten +2025-06-01 23:48:14 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 15: test +2025-06-01 23:48:16 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:16 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 15 nicht einschalten +2025-06-01 23:48:16 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 16: test +2025-06-01 23:48:18 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:18 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 16 nicht einschalten +2025-06-01 23:48:19 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test +2025-06-01 23:48:21 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:21 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten +2025-06-01 23:48:21 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test +2025-06-01 23:48:23 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:23 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten +2025-06-01 23:48:23 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test +2025-06-01 23:48:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten +2025-06-01 23:48:25 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test +2025-06-01 23:48:27 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:27 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten +2025-06-01 23:48:27 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test +2025-06-01 23:48:29 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:29 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 3 nicht einschalten +2025-06-01 23:48:29 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 4: test +2025-06-01 23:48:32 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:32 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 4 nicht einschalten +2025-06-01 23:48:32 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 5: test +2025-06-01 23:48:34 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:34 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 5 nicht einschalten +2025-06-01 23:48:34 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 6: test +2025-06-01 23:48:36 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:36 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten +2025-06-01 23:48:36 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi +2025-06-01 23:48:38 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:48:38 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 9 nicht einschalten +2025-06-01 23:48:38 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 10: zi +2025-06-01 23:48:40 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:48:40 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 10 nicht einschalten +2025-06-01 23:48:40 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 11: fee +2025-06-01 23:48:42 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:48:42 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 11 nicht einschalten +2025-06-01 23:48:42 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 12: fee +2025-06-01 23:48:44 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:48:44 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 12 nicht einschalten +2025-06-01 23:48:44 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 13: e2 +2025-06-01 23:48:46 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:46 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 13 nicht einschalten +2025-06-01 23:48:46 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 14: e2 +2025-06-01 23:48:48 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:48 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 14 nicht einschalten +2025-06-01 23:48:48 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 15: test +2025-06-01 23:48:51 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:51 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 15 nicht einschalten +2025-06-01 23:48:51 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 16: test +2025-06-01 23:48:53 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:53 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 16 nicht einschalten +2025-06-01 23:48:54 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test +2025-06-01 23:48:56 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:56 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten +2025-06-01 23:48:56 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test +2025-06-01 23:48:58 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:48:58 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten +2025-06-01 23:48:58 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test +2025-06-01 23:49:00 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:49:00 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten +2025-06-01 23:49:00 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test +2025-06-01 23:49:02 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:49:02 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten +2025-06-01 23:49:02 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test +2025-06-01 23:50:28 - [scheduler] scheduler - [INFO] INFO - Task check_jobs registriert: Intervall 30s, Enabled: True +2025-06-01 23:50:33 - [scheduler] scheduler - [INFO] INFO - Scheduler-Thread gestartet +2025-06-01 23:50:33 - [scheduler] scheduler - [INFO] INFO - Scheduler gestartet +2025-06-01 23:50:33 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test +2025-06-01 23:50:35 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:50:35 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten +2025-06-01 23:50:35 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test +2025-06-01 23:50:37 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:50:37 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten +2025-06-01 23:50:37 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test +2025-06-01 23:50:40 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:50:40 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten +2025-06-01 23:50:40 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test +2025-06-01 23:50:42 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:50:42 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten +2025-06-01 23:50:42 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test +2025-06-01 23:50:44 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:50:44 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 3 nicht einschalten +2025-06-01 23:50:44 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 4: test +2025-06-01 23:50:46 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:50:46 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 4 nicht einschalten +2025-06-01 23:50:46 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 5: test +2025-06-01 23:50:48 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:50:48 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 5 nicht einschalten +2025-06-01 23:50:48 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 6: test +2025-06-01 23:50:50 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:50:50 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten +2025-06-01 23:50:50 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi +2025-06-01 23:50:52 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:50:52 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 9 nicht einschalten +2025-06-01 23:50:52 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 10: zi +2025-06-01 23:50:54 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:50:54 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 10 nicht einschalten +2025-06-01 23:50:54 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 11: fee +2025-06-01 23:50:56 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:50:56 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 11 nicht einschalten +2025-06-01 23:50:56 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 12: fee +2025-06-01 23:50:58 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:50:58 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 12 nicht einschalten +2025-06-01 23:50:58 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 13: e2 +2025-06-01 23:51:01 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:01 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 13 nicht einschalten +2025-06-01 23:51:01 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 14: e2 +2025-06-01 23:51:03 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:03 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 14 nicht einschalten +2025-06-01 23:51:03 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 15: test +2025-06-01 23:51:05 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:05 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 15 nicht einschalten +2025-06-01 23:51:05 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 16: test +2025-06-01 23:51:07 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:07 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 16 nicht einschalten +2025-06-01 23:51:08 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test +2025-06-01 23:51:10 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:10 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten +2025-06-01 23:51:10 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test +2025-06-01 23:51:12 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:12 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten +2025-06-01 23:51:12 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test +2025-06-01 23:51:14 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:14 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten +2025-06-01 23:51:14 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test +2025-06-01 23:51:16 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:16 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten +2025-06-01 23:51:16 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test +2025-06-01 23:51:18 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:18 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 3 nicht einschalten +2025-06-01 23:51:18 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 4: test +2025-06-01 23:51:20 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:20 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 4 nicht einschalten +2025-06-01 23:51:20 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 5: test +2025-06-01 23:51:22 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:22 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 5 nicht einschalten +2025-06-01 23:51:22 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 6: test +2025-06-01 23:51:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:25 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 6 nicht einschalten +2025-06-01 23:51:25 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 9: zi +2025-06-01 23:51:27 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:51:27 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 9 nicht einschalten +2025-06-01 23:51:27 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 10: zi +2025-06-01 23:51:29 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:51:29 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 10 nicht einschalten +2025-06-01 23:51:29 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 11: fee +2025-06-01 23:51:31 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:51:31 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 11 nicht einschalten +2025-06-01 23:51:31 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 12: fee +2025-06-01 23:51:33 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.104: HTTPConnectionPool(host='192.168.0.104', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.104 timed out. (connect timeout=2)')) +2025-06-01 23:51:33 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 12 nicht einschalten +2025-06-01 23:51:33 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 13: e2 +2025-06-01 23:51:35 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:35 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 13 nicht einschalten +2025-06-01 23:51:35 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 14: e2 +2025-06-01 23:51:37 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:37 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 14 nicht einschalten +2025-06-01 23:51:37 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 15: test +2025-06-01 23:51:39 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:39 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 15 nicht einschalten +2025-06-01 23:51:39 - [scheduler] scheduler - [INFO] INFO - ⚡ Starte Sofort-Job 16: test +2025-06-01 23:51:41 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:41 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Sofort-Job 16 nicht einschalten +2025-06-01 23:51:42 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 7: test +2025-06-01 23:51:44 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:44 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 7 nicht einschalten +2025-06-01 23:51:44 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 8: test +2025-06-01 23:51:47 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:47 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 8 nicht einschalten +2025-06-01 23:51:47 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 1: test +2025-06-01 23:51:49 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:49 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 1 nicht einschalten +2025-06-01 23:51:49 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 2: test +2025-06-01 23:51:51 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:51 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 2 nicht einschalten +2025-06-01 23:51:51 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 3: test +2025-06-01 23:51:53 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:53 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 3 nicht einschalten +2025-06-01 23:51:53 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 4: test +2025-06-01 23:51:55 - [scheduler] scheduler - [ERROR] ERROR - ❌ Fehler beim einschalten der Tapo-Steckdose 192.168.0.103: HTTPConnectionPool(host='192.168.0.103', port=80): Max retries exceeded with url: /app (Caused by ConnectTimeoutError(, 'Connection to 192.168.0.103 timed out. (connect timeout=2)')) +2025-06-01 23:51:55 - [scheduler] scheduler - [ERROR] ERROR - ❌ Konnte Steckdose für Job 4 nicht einschalten +2025-06-01 23:51:55 - [scheduler] scheduler - [INFO] INFO - 🚀 Starte geplanten Job 5: test diff --git a/backend/logs/security/security.log b/backend/logs/security/security.log index 0c2c6177..06fbef21 100644 --- a/backend/logs/security/security.log +++ b/backend/logs/security/security.log @@ -94,3 +94,4 @@ 2025-06-01 23:42:52 - [security] security - [INFO] INFO - 🔒 Security System initialisiert 2025-06-01 23:43:49 - [security] security - [INFO] INFO - 🔒 Security System initialisiert 2025-06-01 23:47:40 - [security] security - [INFO] INFO - 🔒 Security System initialisiert +2025-06-01 23:50:29 - [security] security - [INFO] INFO - 🔒 Security System initialisiert diff --git a/backend/logs/shutdown_manager/shutdown_manager.log b/backend/logs/shutdown_manager/shutdown_manager.log index ad46fcad..f774a419 100644 --- a/backend/logs/shutdown_manager/shutdown_manager.log +++ b/backend/logs/shutdown_manager/shutdown_manager.log @@ -180,3 +180,4 @@ 2025-06-01 23:42:52 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert 2025-06-01 23:43:49 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert 2025-06-01 23:47:40 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert +2025-06-01 23:50:29 - [shutdown_manager] shutdown_manager - [INFO] INFO - 🔧 Shutdown-Manager initialisiert diff --git a/backend/logs/startup/startup.log b/backend/logs/startup/startup.log index 29e3653f..2ec69d5f 100644 --- a/backend/logs/startup/startup.log +++ b/backend/logs/startup/startup.log @@ -854,3 +854,12 @@ 2025-06-01 23:47:40 - [startup] startup - [INFO] INFO - 🪟 Windows-Modus: Aktiviert 2025-06-01 23:47:40 - [startup] startup - [INFO] INFO - 🔒 Windows-sichere Log-Rotation: Aktiviert 2025-06-01 23:47:40 - [startup] startup - [INFO] INFO - ================================================== +2025-06-01 23:50:29 - [startup] startup - [INFO] INFO - ================================================== +2025-06-01 23:50:29 - [startup] startup - [INFO] INFO - 🚀 MYP Platform Backend wird gestartet... +2025-06-01 23:50:29 - [startup] startup - [INFO] INFO - 🐍 Python Version: 3.13.3 (tags/v3.13.3:6280bb5, Apr 8 2025, 14:47:33) [MSC v.1943 64 bit (AMD64)] +2025-06-01 23:50:29 - [startup] startup - [INFO] INFO - 💻 Betriebssystem: nt (win32) +2025-06-01 23:50:29 - [startup] startup - [INFO] INFO - 📁 Arbeitsverzeichnis: C:\Users\TTOMCZA.EMEA\Dev\Projektarbeit-MYP\backend +2025-06-01 23:50:29 - [startup] startup - [INFO] INFO - ⏰ Startzeit: 2025-06-01T23:50:29.083233 +2025-06-01 23:50:29 - [startup] startup - [INFO] INFO - 🪟 Windows-Modus: Aktiviert +2025-06-01 23:50:29 - [startup] startup - [INFO] INFO - 🔒 Windows-sichere Log-Rotation: Aktiviert +2025-06-01 23:50:29 - [startup] startup - [INFO] INFO - ================================================== diff --git a/backend/logs/windows_fixes/windows_fixes.log b/backend/logs/windows_fixes/windows_fixes.log index 9a214607..aff37c17 100644 --- a/backend/logs/windows_fixes/windows_fixes.log +++ b/backend/logs/windows_fixes/windows_fixes.log @@ -407,3 +407,7 @@ 2025-06-01 23:47:39 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen) 2025-06-01 23:47:39 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Globaler subprocess-Patch angewendet 2025-06-01 23:47:39 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet +2025-06-01 23:50:28 - [windows_fixes] windows_fixes - [INFO] INFO - 🔧 Wende Windows-spezifische Fixes an... +2025-06-01 23:50:28 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Subprocess automatisch gepatcht für UTF-8 Encoding (run + Popen) +2025-06-01 23:50:28 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Globaler subprocess-Patch angewendet +2025-06-01 23:50:28 - [windows_fixes] windows_fixes - [INFO] INFO - ✅ Alle Windows-Fixes erfolgreich angewendet diff --git a/backend/static/css/input.css b/backend/static/css/input.css index 8621977d..5f3c9b00 100644 --- a/backend/static/css/input.css +++ b/backend/static/css/input.css @@ -2,3384 +2,147 @@ @tailwind components; @tailwind utilities; -/* Custom Styles für Light und Dark Mode - Premium Upgrade mit verbessertem Light Mode */ +/* Kiosk-optimierte Custom Styles */ @layer base { :root { - /* Light Mode Farben - Mercedes-Benz Professional - VERBESSERT für optimale Lesbarkeit */ - --color-bg-primary: #ffffff; - --color-bg-secondary: #fafbfc; - --color-bg-tertiary: #f3f5f7; - --color-bg-accent: #fbfcfd; - --color-text-primary: #111827; /* Verstärkter Kontrast für bessere Lesbarkeit */ - --color-text-secondary: #374151; /* Erhöhter Kontrast */ - --color-text-muted: #6b7280; /* Optimierter Muted-Text */ - --color-text-accent: #0073ce; - --color-border-primary: #e5e7eb; /* Subtilere aber sichtbarere Borders */ - --color-border-secondary: #d1d5db; - --color-accent: #0073ce; /* Mercedes-Benz Professional Blau */ - --color-accent-hover: #005a9f; - --color-accent-light: #eff6ff; - --color-accent-text: #ffffff; - --color-shadow: rgba(0, 0, 0, 0.06); /* Sanftere Schatten */ - --color-shadow-strong: rgba(0, 0, 0, 0.1); - --color-shadow-accent: rgba(0, 115, 206, 0.12); - --card-radius: 1rem; /* Abgerundete Ecken für Karten */ - - /* Light Mode Gradients - VERBESSERT für sanftere Optik */ - --gradient-primary: linear-gradient(135deg, #ffffff 0%, #fafbfc 30%, #f8fafc 70%, #f3f5f7 100%); - --gradient-card: linear-gradient(135deg, #ffffff 0%, #fcfcfd 50%, #fafbfc 100%); - --gradient-hero: linear-gradient(135deg, #fafbfc 0%, #f3f5f7 40%, #eef2f5 80%, #f8fafc 100%); - --gradient-accent: linear-gradient(135deg, #0073ce 0%, #005a9f 100%); - --gradient-surface: linear-gradient(135deg, #ffffff 0%, #fbfcfd 50%, #f8fafc 100%); - - /* Neue optimierte Light Mode Glassmorphism-Variablen */ - --glass-bg: rgba(255, 255, 255, 0.92); - --glass-border: rgba(255, 255, 255, 0.3); - --glass-shadow: 0 8px 32px rgba(0, 0, 0, 0.04); - --glass-blur: blur(20px); + /* Kiosk-spezifische CSS-Variablen */ + --primary: #0073ce; + --primary-dark: #005a9f; + --bg: #fafbfc; + --surface: #ffffff; + --text: #111827; + --text-muted: #6b7280; + --border: #e5e7eb; + --shadow: 0 2px 4px rgba(0,0,0,0.05); } .dark { - /* Dark Mode Farben - Noch dunkler und eleganter - UNVERÄNDERT */ - --color-bg-primary: #000000; /* Tiefes Schwarz */ - --color-bg-secondary: #0a0a0a; /* Sehr dunkles Grau */ - --color-bg-tertiary: #1a1a1a; - --color-text-primary: #ffffff; - --color-text-secondary: #e2e8f0; - --color-text-muted: #94a3b8; - --color-border-primary: #1a1a1a; /* Dunkler Rahmen */ - --color-border-secondary: #2a2a2a; - --color-accent: #ffffff; /* Reines Weiß */ - --color-accent-hover: #f0f0f0; - --color-accent-light: #1e3a8a; - --color-accent-text: #000000; - --color-shadow: rgba(0, 0, 0, 0.8); /* Sehr dunkler Schatten */ - --color-shadow-strong: rgba(0, 0, 0, 0.9); - --mb-black: #000000; /* Mercedes-Benz Schwarz */ + --bg: #1e293b; + --surface: #334155; + --text: #f8fafc; + --text-muted: #94a3b8; + --border: #475569; + --shadow: 0 2px 4px rgba(0,0,0,0.3); } - + body { - @apply bg-white dark:bg-black text-slate-900 dark:text-white transition-colors duration-300; - position: relative; - min-height: 100vh; - background: var(--gradient-primary); - font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; - font-feature-settings: 'cv02', 'cv03', 'cv04', 'cv11'; - line-height: 1.65; /* Verbesserte Zeilenhöhe für bessere Lesbarkeit */ - font-size: 15px; /* Optimierte Schriftgröße */ + @apply bg-white dark:bg-gray-800 text-gray-900 dark:text-white; + font-family: system-ui, -apple-system, sans-serif; + background: var(--bg); + color: var(--text); + line-height: 1.5; + transition: background-color 0.2s ease, color 0.2s ease; + } +} + +@layer components { + /* Kiosk-optimierte Komponenten */ + .card { + @apply bg-white border border-gray-200 rounded-lg p-4; + background: var(--surface); + border-color: var(--border); + box-shadow: var(--shadow); } - .dark body { - background: linear-gradient(135deg, #000000 0%, #0a0a0a 50%, #000000 100%); + .dark .card { + @apply bg-gray-700 border-gray-600; } - /* Enhanced Body Background with Subtle Patterns - VERBESSERT */ - body::before { - content: ''; - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: - radial-gradient(circle at 25% 25%, rgba(0, 115, 206, 0.015) 0%, transparent 50%), - radial-gradient(circle at 75% 75%, rgba(0, 115, 206, 0.01) 0%, transparent 50%), - radial-gradient(circle at 50% 10%, rgba(0, 115, 206, 0.008) 0%, transparent 50%); - pointer-events: none; - z-index: -1; - } - - .dark body::before { - background: - radial-gradient(circle at 20% 50%, rgba(59, 130, 246, 0.03) 0%, transparent 50%), - radial-gradient(circle at 80% 20%, rgba(59, 130, 246, 0.02) 0%, transparent 50%); - } - - /* Navbar Styles - Premium Glassmorphism - VERBESSERT */ - nav { - @apply backdrop-blur-xl border-b transition-all duration-300; - background: linear-gradient(135deg, - rgba(255, 255, 255, 0.95) 0%, - rgba(250, 251, 252, 0.92) 30%, - rgba(248, 250, 252, 0.9) 70%, - rgba(255, 255, 255, 0.95) 100%); - border-bottom: 1px solid rgba(229, 231, 235, 0.7); - backdrop-filter: blur(28px) saturate(200%) brightness(110%); - -webkit-backdrop-filter: blur(28px) saturate(200%) brightness(110%); - box-shadow: - 0 4px 20px rgba(0, 0, 0, 0.04), - 0 2px 8px rgba(0, 115, 206, 0.02), - inset 0 1px 0 rgba(255, 255, 255, 0.9); - } - - .dark nav { - background: rgba(0, 0, 0, 0.85); - border-bottom-color: rgba(255, 255, 255, 0.1); - box-shadow: - 0 8px 32px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.05); - } - - /* Premium Card Styles - VERBESSERT für Light Mode */ - .card-enhanced { - background: var(--gradient-card); - border: 1px solid var(--color-border-primary); - border-radius: var(--card-radius); - box-shadow: - 0 2px 12px rgba(0, 0, 0, 0.03), - 0 1px 4px rgba(0, 115, 206, 0.02), - inset 0 1px 0 rgba(255, 255, 255, 0.8); - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - position: relative; - overflow: hidden; - } - - .card-enhanced::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 1px; - background: var(--gradient-accent); - opacity: 0; - transition: opacity 0.3s ease; - } - - .card-enhanced:hover { - transform: translateY(-2px); - box-shadow: - 0 8px 25px rgba(0, 0, 0, 0.06), - 0 4px 12px rgba(0, 115, 206, 0.04), - inset 0 1px 0 rgba(255, 255, 255, 0.9); - } - - .card-enhanced:hover::before { - opacity: 1; - } - - .dark .card-enhanced { - background: rgba(10, 10, 10, 0.8); - border-color: var(--color-border-primary); - box-shadow: 0 4px 20px var(--color-shadow); - } - - /* Premium Button Styles - VERBESSERT */ - .btn-enhanced { - background: var(--gradient-accent); - color: var(--color-accent-text); + .btn { + @apply px-4 py-2 rounded-lg font-semibold transition-colors; + background: var(--primary); + color: white; border: none; - border-radius: 0.5rem; - padding: 0.75rem 1.75rem; - font-weight: 600; - font-size: 0.875rem; - text-transform: uppercase; - letter-spacing: 0.05em; - box-shadow: - 0 2px 8px rgba(0, 115, 206, 0.2), - 0 1px 4px rgba(0, 115, 206, 0.1); - transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); - position: relative; - overflow: hidden; } - .btn-enhanced::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); - transition: left 0.5s ease; - } - - .btn-enhanced:hover { - transform: translateY(-1px); - box-shadow: - 0 4px 15px rgba(0, 115, 206, 0.3), - 0 2px 8px rgba(0, 115, 206, 0.2); - } - - .btn-enhanced:hover::before { - left: 100%; - } - - .btn-enhanced:active { - transform: translateY(0); + .btn:hover { + background: var(--primary-dark); } .btn-secondary { - background: var(--gradient-surface); - color: var(--color-text-primary); - border: 1px solid var(--color-border-primary); - box-shadow: - 0 1px 6px rgba(0, 0, 0, 0.03), - inset 0 1px 0 rgba(255, 255, 255, 0.8); - } - - .btn-secondary:hover { - background: var(--color-bg-secondary); - border-color: var(--color-accent); - color: var(--color-accent); - box-shadow: - 0 4px 12px rgba(0, 115, 206, 0.08), - inset 0 1px 0 rgba(255, 255, 255, 0.9); - } - - /* Enhanced Form Elements - VERBESSERT für bessere Lesbarkeit */ - .input-enhanced { - background: rgba(255, 255, 255, 0.95); - border: 1px solid var(--color-border-primary); - border-radius: 0.5rem; - padding: 0.75rem 1rem; - color: var(--color-text-primary); - font-size: 0.9rem; - box-shadow: - 0 1px 6px rgba(0, 0, 0, 0.02), - inset 0 1px 0 rgba(255, 255, 255, 0.9); - transition: all 0.2s ease; - backdrop-filter: blur(8px); - -webkit-backdrop-filter: blur(8px); - } - - .input-enhanced:focus { - outline: none; - border-color: var(--color-accent); - box-shadow: - 0 4px 12px rgba(0, 115, 206, 0.1), - 0 0 0 3px rgba(0, 115, 206, 0.05), - inset 0 1px 0 rgba(255, 255, 255, 0.95); - background: rgba(255, 255, 255, 0.98); - } - - .input-enhanced::placeholder { - color: var(--color-text-muted); - opacity: 0.8; - } - - .dark .input-enhanced { - background: rgba(10, 10, 10, 0.8); - border-color: var(--color-border-primary); - color: var(--color-text-primary); - box-shadow: - 0 2px 8px var(--color-shadow), - inset 0 1px 0 rgba(255, 255, 255, 0.05); - } - - .dark .input-enhanced:focus { - border-color: #60a5fa; - box-shadow: - 0 4px 15px rgba(96, 165, 250, 0.2), - 0 0 0 3px rgba(96, 165, 250, 0.1); - } - - /* Premium Alert Styles */ - .alert-enhanced { - border-radius: 1rem; - padding: 1.25rem; - border: 1px solid transparent; - position: relative; - overflow: hidden; - backdrop-filter: blur(16px); - -webkit-backdrop-filter: blur(16px); - } - - .alert-enhanced::before { - content: ''; - position: absolute; - top: 0; - left: 0; - bottom: 0; - width: 4px; - } - - .alert-info-enhanced { - background: linear-gradient(135deg, - rgba(239, 246, 255, 0.95) 0%, - rgba(219, 234, 254, 0.9) 100%); - border-color: rgba(59, 130, 246, 0.2); - color: #1e40af; - } - - .alert-info-enhanced::before { - background: var(--gradient-accent); - } - - .alert-success-enhanced { - background: linear-gradient(135deg, - rgba(236, 253, 245, 0.95) 0%, - rgba(167, 243, 208, 0.9) 100%); - border-color: rgba(16, 185, 129, 0.2); - color: #065f46; - } - - .alert-success-enhanced::before { - background: linear-gradient(180deg, #10b981 0%, #059669 100%); - } - - .alert-warning-enhanced { - background: linear-gradient(135deg, - rgba(255, 251, 235, 0.95) 0%, - rgba(254, 243, 199, 0.9) 100%); - border-color: rgba(251, 191, 36, 0.2); - color: #92400e; - } - - .alert-warning-enhanced::before { - background: linear-gradient(180deg, #fbbf24 0%, #f59e0b 100%); - } - - .alert-error-enhanced { - background: linear-gradient(135deg, - rgba(254, 242, 242, 0.95) 0%, - rgba(252, 165, 165, 0.9) 100%); - border-color: rgba(239, 68, 68, 0.2); - color: #991b1b; - } - - .alert-error-enhanced::before { - background: linear-gradient(180deg, #ef4444 0%, #dc2626 100%); - } - - /* Light Mode Flash Messages - Premium */ - .flash-message-light { - background: linear-gradient(135deg, - rgba(255, 255, 255, 0.95) 0%, - rgba(248, 250, 252, 0.9) 100%); - backdrop-filter: blur(32px) saturate(200%) brightness(120%); - -webkit-backdrop-filter: blur(32px) saturate(200%) brightness(120%); - border: 1px solid rgba(226, 232, 240, 0.6); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.1), - 0 12px 24px rgba(0, 115, 206, 0.05), - inset 0 1px 0 rgba(255, 255, 255, 0.8); - color: var(--color-text-primary); - } - - .flash-message-light.success { - border-left: 4px solid #10b981; - background: linear-gradient(135deg, - rgba(236, 253, 245, 0.95) 0%, - rgba(209, 250, 229, 0.9) 100%); - } - - .flash-message-light.error { - border-left: 4px solid #ef4444; - background: linear-gradient(135deg, - rgba(254, 242, 242, 0.95) 0%, - rgba(252, 165, 165, 0.9) 100%); - } - - .flash-message-light.warning { - border-left: 4px solid #fbbf24; - background: linear-gradient(135deg, - rgba(255, 251, 235, 0.95) 0%, - rgba(254, 243, 199, 0.9) 100%); - } - - .flash-message-light.info { - border-left: 4px solid #3b82f6; - background: linear-gradient(135deg, - rgba(239, 246, 255, 0.95) 0%, - rgba(219, 234, 254, 0.9) 100%); - } - - /* Premium Table Styles */ - .table-enhanced { - background: var(--gradient-card); - border: 1px solid var(--color-border-primary); - border-radius: var(--card-radius); - overflow: hidden; - box-shadow: - 0 4px 20px var(--color-shadow), - 0 2px 8px rgba(0, 115, 206, 0.04), - inset 0 1px 0 rgba(255, 255, 255, 0.6); - } - - .table-enhanced th { - background: linear-gradient(135deg, - var(--color-bg-secondary) 0%, - var(--color-bg-tertiary) 100%); - color: var(--color-text-primary); - font-weight: 600; - padding: 1rem 1.5rem; - border-bottom: 1px solid var(--color-border-primary); - position: relative; - } - - .table-enhanced th::after { - content: ''; - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 1px; - background: linear-gradient(90deg, - transparent 0%, - var(--color-border-secondary) 50%, - transparent 100%); - } - - .table-enhanced td { - padding: 1rem 1.5rem; - border-bottom: 1px solid var(--color-border-primary); - color: var(--color-text-secondary); - transition: all 0.2s ease; - } - - .table-enhanced tbody tr:hover { - background: var(--color-bg-secondary); - transform: scale(1.002); - } - - .dark .table-enhanced { - background: rgba(10, 10, 10, 0.8); - border-color: var(--color-border-primary); - } - - .dark .table-enhanced th { - background: rgba(26, 26, 26, 0.8); - color: var(--color-text-primary); - } - - .dark .table-enhanced tbody tr:hover { - background: rgba(26, 26, 26, 0.6); - } - - /* Premium Modal Styles */ - .modal-enhanced { - background: linear-gradient(135deg, - rgba(255, 255, 255, 0.98) 0%, - rgba(248, 250, 252, 0.95) 50%, - rgba(255, 255, 255, 0.98) 100%); - backdrop-filter: blur(32px) saturate(220%) brightness(120%); - -webkit-backdrop-filter: blur(32px) saturate(220%) brightness(120%); - border: 1px solid rgba(226, 232, 240, 0.7); - border-radius: 1.5rem; - box-shadow: - 0 50px 100px rgba(0, 0, 0, 0.15), - 0 20px 40px rgba(0, 115, 206, 0.08), - inset 0 2px 0 rgba(255, 255, 255, 0.9); - position: relative; - overflow: hidden; - } - - .modal-enhanced::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 1px; - background: linear-gradient(90deg, - transparent 0%, - rgba(226, 232, 240, 0.8) 50%, - transparent 100%); - } - - .dark .modal-enhanced { - background: rgba(0, 0, 0, 0.95); - border-color: rgba(42, 42, 42, 0.7); - box-shadow: - 0 50px 100px rgba(0, 0, 0, 0.5), - inset 0 2px 0 rgba(255, 255, 255, 0.05); - } - - /* Enhanced Status Badges */ - .status-badge-enhanced { - display: inline-flex; - align-items: center; - padding: 0.5rem 1rem; - font-size: 0.75rem; - font-weight: 700; - border-radius: 9999px; - text-transform: uppercase; - letter-spacing: 0.05em; - border: 1px solid transparent; - transition: all 0.2s ease; - position: relative; - overflow: hidden; - } - - .status-badge-enhanced::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent); - transition: left 0.5s ease; - } - - .status-badge-enhanced:hover::before { - left: 100%; - } - - .status-online-enhanced { - background: linear-gradient(135deg, #ecfdf5 0%, #a7f3d0 100%); - color: #065f46; - border-color: rgba(16, 185, 129, 0.3); - } - - .status-offline-enhanced { - background: linear-gradient(135deg, #fef2f2 0%, #fca5a5 100%); - color: #991b1b; - border-color: rgba(239, 68, 68, 0.3); - } - - .status-printing-enhanced { - background: linear-gradient(135deg, #eff6ff 0%, #bfdbfe 100%); - color: #1e40af; - border-color: rgba(59, 130, 246, 0.3); - } - - /* Dark Mode Toggle - Premium Design */ - .dark-mode-toggle-new { - position: relative; - display: flex; - cursor: pointer; - align-items: center; - justify-content: center; - border-radius: 9999px; - padding: 0.625rem; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - background: linear-gradient(135deg, - rgba(248, 250, 252, 0.9) 0%, - rgba(241, 245, 249, 0.8) 100%); - border: 1px solid rgba(226, 232, 240, 0.7); - box-shadow: - 0 4px 12px rgba(0, 0, 0, 0.06), - 0 2px 4px rgba(0, 115, 206, 0.04), - inset 0 1px 0 rgba(255, 255, 255, 0.8); - color: var(--color-text-secondary); - z-index: 100; - } - - .dark-mode-toggle-new:hover { - transform: translateY(-2px) scale(1.05); - background: linear-gradient(135deg, - rgba(248, 250, 252, 0.95) 0%, - rgba(241, 245, 249, 0.85) 100%); - box-shadow: - 0 8px 20px rgba(0, 0, 0, 0.1), - 0 4px 8px rgba(0, 115, 206, 0.08), - inset 0 1px 0 rgba(255, 255, 255, 0.9); - } - - .dark-mode-toggle-new:active { - transform: translateY(-1px) scale(0.98); - transition: transform 0.1s; - } - - .dark .dark-mode-toggle-new { - background: rgba(10, 10, 10, 0.8); - border: 1px solid rgba(42, 42, 42, 0.6); - box-shadow: - 0 4px 12px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.05); - color: var(--color-text-secondary); - } - - .dark .dark-mode-toggle-new:hover { - background: rgba(10, 10, 10, 0.9); - box-shadow: - 0 8px 20px rgba(0, 0, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.08); - } - - /* Icon-Animation für Dark Mode Toggle */ - .dark-mode-toggle-new .sun-icon, - .dark-mode-toggle-new .moon-icon { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - } - - .dark-mode-toggle-new .sun-icon:not(.hidden) { - animation: icon-appear 0.5s cubic-bezier(0.25, 1, 0.5, 1) forwards; - } - - .dark-mode-toggle-new .moon-icon:not(.hidden) { - animation: icon-appear 0.5s cubic-bezier(0.25, 1, 0.5, 1) forwards; - } - - @keyframes icon-appear { - 0% { - opacity: 0; - transform: translate(-50%, -50%) scale(0.5) rotate(-20deg); - } - 100% { - opacity: 1; - transform: translate(-50%, -50%) scale(1) rotate(0); - } - } - - .dark .sun-icon { display: none; } - .dark .moon-icon { display: block; } - .sun-icon { display: block; } - .moon-icon { display: none; } - - /* User Menu Button - Premium Design */ - .user-menu-button-new { - display: flex; - align-items: center; - gap: 0.5rem; - border-radius: 0.75rem; - padding: 0.5rem; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - background: linear-gradient(135deg, - rgba(248, 250, 252, 0.8) 0%, - rgba(241, 245, 249, 0.7) 100%); - border: 1px solid rgba(226, 232, 240, 0.6); - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.05), - inset 0 1px 0 rgba(255, 255, 255, 0.7); - } - - .user-menu-button-new:hover { - transform: translateY(-1px); - background: linear-gradient(135deg, - rgba(248, 250, 252, 0.9) 0%, - rgba(241, 245, 249, 0.8) 100%); - box-shadow: - 0 4px 12px rgba(0, 0, 0, 0.08), - 0 2px 4px rgba(0, 115, 206, 0.04), - inset 0 1px 0 rgba(255, 255, 255, 0.8); - } - - .dark .user-menu-button-new { - background: rgba(10, 10, 10, 0.7); - border-color: rgba(42, 42, 42, 0.6); - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.2), - inset 0 1px 0 rgba(255, 255, 255, 0.03); - } - - .dark .user-menu-button-new:hover { - background: rgba(10, 10, 10, 0.8); - box-shadow: - 0 4px 12px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.05); - } - - /* Enhanced Hover Effects */ - .hover-lift-enhanced { - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - } - - .hover-lift-enhanced:hover { - transform: translateY(-3px) scale(1.01); - box-shadow: - 0 12px 30px var(--color-shadow-strong), - 0 6px 15px var(--color-shadow-accent); - } - - .dark .hover-lift-enhanced:hover { - box-shadow: 0 12px 30px var(--color-shadow); - } - - /* Smooth Scrollbar for Light Mode */ - ::-webkit-scrollbar { - width: 8px; - height: 8px; - } - - ::-webkit-scrollbar-track { - background: var(--color-bg-secondary); - border-radius: 4px; - } - - ::-webkit-scrollbar-thumb { - background: linear-gradient(180deg, - var(--color-border-secondary) 0%, - var(--color-border-primary) 100%); - border-radius: 4px; - transition: background 0.2s ease; - } - - ::-webkit-scrollbar-thumb:hover { - background: linear-gradient(180deg, - var(--color-accent) 0%, - var(--color-accent-hover) 100%); - } - - .dark ::-webkit-scrollbar-track { - background: var(--color-bg-secondary); - } - - .dark ::-webkit-scrollbar-thumb { - background: var(--color-border-primary); - } - - .dark ::-webkit-scrollbar-thumb:hover { - background: #60a5fa; - } - - /* Loading States */ - .loading-enhanced { - position: relative; - overflow: hidden; - } - - .loading-enhanced::after { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, - transparent, - rgba(0, 115, 206, 0.1), - transparent); - animation: loading-shimmer 2s infinite; - } - - @keyframes loading-shimmer { - 0% { left: -100%; } - 100% { left: 100%; } - } - - /* Focus States for Accessibility */ - .focus-enhanced:focus { - outline: 2px solid var(--color-accent); - outline-offset: 2px; - box-shadow: - 0 0 0 4px rgba(0, 115, 206, 0.15), - 0 4px 12px var(--color-shadow-accent); - } - - .dark .focus-enhanced:focus { - outline-color: #60a5fa; - box-shadow: - 0 0 0 4px rgba(96, 165, 250, 0.15), - 0 4px 12px rgba(96, 165, 250, 0.2); - } - - /* Responsive Design Enhancements */ - @media (max-width: 768px) { - .card-enhanced { - padding: 1rem; - border-radius: 0.75rem; - } - - .btn-enhanced { - padding: 0.75rem 1.5rem; - font-size: 0.8rem; - } - - .modal-enhanced { - border-radius: 1rem; - margin: 1rem; - } - - .dark-mode-toggle-new { - padding: 0.5rem; - } - } - - /* Reduced Motion Support */ - @media (prefers-reduced-motion: reduce) { - * { - transition: none !important; - animation: none !important; - } - } - - /* High Contrast Mode Support */ - @media (prefers-contrast: high) { - :root { - --color-shadow: rgba(0, 0, 0, 0.2); - --color-shadow-strong: rgba(0, 0, 0, 0.3); - --color-border-primary: #000000; - } - - .dark { - --color-border-primary: #ffffff; - } - } -} - -/* Admin Panel spezifische Styles */ -@layer components { - /* Dark Mode Styles für Admin Panel */ - .dark .bg-dark-card { - @apply bg-dark-surface transition-colors; - } - - /* Alternative direkte Definition ohne Zirkularität */ - .bg-dark-surface { - background-color: #1e293b; - } - - /* Übergangseffekt für alle Komponenten */ - .transition-all-colors { - @apply transition-colors duration-300; - } - - /* Admin Panel Container */ - .admin-container { - @apply max-w-7xl mx-auto p-4 md:p-8; - } - - /* Admin Stats Cards */ - .admin-stats { - @apply grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-8; - } - - .stat-card { - @apply bg-white/60 dark:bg-black/70 rounded-xl border border-gray-200/60 dark:border-slate-700/30 p-5 relative overflow-hidden shadow-2xl hover:shadow-2xl transition-all duration-300 hover:-translate-y-1 backdrop-blur-xl; - backdrop-filter: blur(20px) saturate(180%) brightness(110%); - -webkit-backdrop-filter: blur(20px) saturate(180%) brightness(110%); - box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.1); - } - - .stat-icon { - @apply absolute top-4 right-4 opacity-15 text-4xl; - } - - .stat-title { - @apply text-sm text-slate-500 dark:text-slate-400 mb-2 font-medium uppercase; - } - - .stat-value { - @apply text-2xl font-bold text-slate-900 dark:text-white mb-1; - } - - .stat-desc { - @apply text-sm text-slate-500 dark:text-slate-400; - } - - /* Navigation Tabs */ - .nav-tabs { - @apply flex border-b border-gray-200 dark:border-slate-700/30 mb-4 overflow-x-auto; - } - - .nav-tab { - @apply py-4 px-6 text-slate-600 dark:text-slate-300 border-b-2 border-transparent cursor-pointer transition-all duration-200 whitespace-nowrap hover:text-slate-900 dark:hover:text-white hover:bg-slate-50 dark:hover:bg-slate-800/50; - } - - .nav-tab.active { - @apply text-slate-900 dark:text-white border-b-2 border-black dark:border-white font-medium; - } - - /* Tab Content */ - .tab-content { - @apply mt-8; - } - - .tab-pane { - @apply hidden; - } - - .tab-pane.active { - @apply block; - } - - /* Formulare für Admin Panel */ - .form-group { - @apply mb-4; - } - - .form-label { - @apply block mb-2 text-sm font-medium text-slate-700 dark:text-slate-300; - } - - .form-input, - .form-select, - .form-textarea { - @apply w-full px-3 py-2 bg-white/60 dark:bg-slate-800/60 border border-gray-300/60 dark:border-slate-600/60 rounded-lg text-slate-900 dark:text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-slate-500 focus:border-transparent transition-all duration-200 backdrop-blur-lg; - backdrop-filter: blur(16px) saturate(150%); - -webkit-backdrop-filter: blur(16px) saturate(150%); - box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.05); - } - - /* Tabellen im Admin Panel */ - .admin-table { - @apply min-w-full divide-y divide-gray-200 dark:divide-slate-700; - } - - .admin-table thead { - @apply bg-slate-50 dark:bg-slate-800; - } - - .admin-table th { - @apply px-6 py-3 text-left text-xs font-medium text-slate-500 dark:text-slate-400 uppercase tracking-wider; - } - - .admin-table tbody { - @apply bg-white dark:bg-dark-surface divide-y divide-gray-200 dark:divide-slate-700; - } - - .admin-table tr { - @apply hover:bg-slate-50 dark:hover:bg-slate-700/50 transition-colors; - } - - .admin-table td { - @apply px-6 py-4 whitespace-nowrap text-sm text-slate-900 dark:text-white; - } - - /* Status Badges */ - .badge { - @apply px-2 inline-flex text-xs leading-5 font-semibold rounded-full; - } - - .badge-success { - @apply bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200; - } - - .badge-error { - @apply bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200; - } - - .badge-warning { - @apply bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200; - } - - .badge-info { - @apply bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200; - } - - /* Drucker-Karten */ - .printer-card { - @apply bg-white/60 dark:bg-black/70 rounded-xl border border-gray-200/60 dark:border-slate-700/30 p-6 shadow-2xl hover:shadow-2xl transition-all duration-300 hover:-translate-y-1 backdrop-blur-xl; - backdrop-filter: blur(20px) saturate(180%) brightness(110%); - -webkit-backdrop-filter: blur(20px) saturate(180%) brightness(110%); - box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.1); - } - - .printer-header { - @apply flex justify-between items-center mb-4; - } - - .printer-name { - @apply text-xl font-bold text-slate-900 dark:text-white; - } - - .printer-actions { - @apply flex space-x-2; - } - - .printer-info { - @apply grid grid-cols-2 gap-4 mb-4; - } - - .printer-status { - @apply flex items-center mt-4; - } - - /* Status Indikatoren */ - .status-indicator { - @apply w-3 h-3 rounded-full mr-2; - } - - .status-running { - @apply bg-green-500; - animation: pulse 2s infinite; - } - - .status-stopped { - @apply bg-red-500; - } - - /* Pulse Animation */ - @keyframes pulse { - 0% { - opacity: 1; - transform: scale(1); - } - 50% { - opacity: 0.5; - transform: scale(1.1); - } - 100% { - opacity: 1; - transform: scale(1); - } - } - - /* Log-Einträge */ - .log-entry { - @apply p-3 border-l-4 mb-2 rounded-r-lg bg-white dark:bg-slate-800 hover:bg-slate-50 dark:hover:bg-slate-700 transition-colors; - } - - .log-debug { - @apply border-gray-400 dark:border-gray-500; - } - - .log-info { - @apply border-blue-400 dark:border-blue-500; - } - - .log-warning { - @apply border-yellow-400 dark:border-yellow-500; - } - - .log-error { - @apply border-red-400 dark:border-red-500; - } - - .log-critical { - @apply border-purple-400 dark:border-purple-500; - } - - /* Scheduler Status */ - .scheduler-status { - @apply flex items-center p-4 bg-white dark:bg-slate-800 rounded-lg border border-gray-200 dark:border-slate-700 shadow-md; - } - - /* Fortschrittsbalken */ - .progress-bar { - @apply w-full h-2 bg-gray-200 dark:bg-slate-700 rounded-full overflow-hidden; - } - - .progress-bar-fill { - @apply h-full transition-all duration-300; - } - - .progress-bar-fill-blue { - @apply bg-blue-500 dark:bg-blue-600; - } - - .progress-bar-fill-green { - @apply bg-green-500 dark:bg-green-600; - } - - .progress-bar-fill-purple { - @apply bg-purple-500 dark:bg-purple-600; - } - - /* Benachrichtigungen */ - .notification { - @apply fixed top-4 right-4 max-w-md p-4 rounded-2xl shadow-2xl transform translate-x-full opacity-0 transition-all duration-500 z-50; - background: rgba(255, 255, 255, 0.08); - backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%); - -webkit-backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%); - border: 1px solid rgba(255, 255, 255, 0.25); - box-shadow: - 0 32px 64px rgba(0, 0, 0, 0.25), - 0 12px 24px rgba(0, 0, 0, 0.15), - inset 0 1px 0 rgba(255, 255, 255, 0.4), - 0 0 0 1px rgba(255, 255, 255, 0.1); - animation: notification-slide-in 0.6s cubic-bezier(0.4, 0, 0.2, 1); - } - - .dark .notification { - background: rgba(0, 0, 0, 0.2); - backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(115%); - -webkit-backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(115%); - border: 1px solid rgba(255, 255, 255, 0.15); - box-shadow: - 0 32px 64px rgba(0, 0, 0, 0.6), - 0 12px 24px rgba(0, 0, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.2), - 0 0 0 1px rgba(255, 255, 255, 0.05); - } - - .notification.show { - @apply translate-x-0 opacity-100; - } - - .notification:hover { - transform: translateY(-2px) scale(1.02); - box-shadow: - 0 40px 80px rgba(0, 0, 0, 0.3), - 0 16px 32px rgba(0, 0, 0, 0.2), - inset 0 1px 0 rgba(255, 255, 255, 0.5), - 0 0 0 1px rgba(255, 255, 255, 0.15); - } - - .dark .notification:hover { - box-shadow: - 0 40px 80px rgba(0, 0, 0, 0.7), - 0 16px 32px rgba(0, 0, 0, 0.5), - inset 0 1px 0 rgba(255, 255, 255, 0.3), - 0 0 0 1px rgba(255, 255, 255, 0.1); - } - - .notification-success { - @apply text-green-100; - background: linear-gradient(135deg, - rgba(34, 197, 94, 0.25) 0%, - rgba(134, 239, 172, 0.18) 50%, - rgba(34, 197, 94, 0.12) 100%); - border: 1px solid rgba(34, 197, 94, 0.4); - box-shadow: - 0 32px 64px rgba(34, 197, 94, 0.2), - 0 12px 24px rgba(34, 197, 94, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.4), - 0 0 0 1px rgba(34, 197, 94, 0.3); - } - - .notification-error { - @apply text-red-100; - background: linear-gradient(135deg, - rgba(239, 68, 68, 0.25) 0%, - rgba(252, 165, 165, 0.18) 50%, - rgba(239, 68, 68, 0.12) 100%); - border: 1px solid rgba(239, 68, 68, 0.4); - box-shadow: - 0 32px 64px rgba(239, 68, 68, 0.2), - 0 12px 24px rgba(239, 68, 68, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.4), - 0 0 0 1px rgba(239, 68, 68, 0.3); - } - - .notification-warning { - @apply text-yellow-100; - background: linear-gradient(135deg, - rgba(245, 158, 11, 0.25) 0%, - rgba(252, 211, 77, 0.18) 50%, - rgba(245, 158, 11, 0.12) 100%); - border: 1px solid rgba(245, 158, 11, 0.4); - box-shadow: - 0 32px 64px rgba(245, 158, 11, 0.2), - 0 12px 24px rgba(245, 158, 11, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.4), - 0 0 0 1px rgba(245, 158, 11, 0.3); - } - - .notification-info { - @apply text-blue-100; - background: linear-gradient(135deg, - rgba(59, 130, 246, 0.25) 0%, - rgba(147, 197, 253, 0.18) 50%, - rgba(59, 130, 246, 0.12) 100%); - border: 1px solid rgba(59, 130, 246, 0.4); - box-shadow: - 0 32px 64px rgba(59, 130, 246, 0.2), - 0 12px 24px rgba(59, 130, 246, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.4), - 0 0 0 1px rgba(59, 130, 246, 0.3); - } - - /* Toast-Benachrichtigungen - Einheitlich */ - .toast-notification { - @apply fixed z-50 p-4 rounded-2xl shadow-2xl transform transition-all duration-500 text-sm font-medium; - background: rgba(255, 255, 255, 0.08); - backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%); - -webkit-backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%); - border: 1px solid rgba(255, 255, 255, 0.25); - box-shadow: - 0 32px 64px rgba(0, 0, 0, 0.25), - 0 12px 24px rgba(0, 0, 0, 0.15), - inset 0 1px 0 rgba(255, 255, 255, 0.4), - 0 0 0 1px rgba(255, 255, 255, 0.1); - } - - .dark .toast-notification { - background: rgba(0, 0, 0, 0.2); - backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(115%); - -webkit-backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(115%); - border: 1px solid rgba(255, 255, 255, 0.15); - box-shadow: - 0 32px 64px rgba(0, 0, 0, 0.6), - 0 12px 24px rgba(0, 0, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.2), - 0 0 0 1px rgba(255, 255, 255, 0.05); - } - - /* Alert-Benachrichtigungen - Verbessert */ - .alert { - @apply p-6 rounded-2xl border mb-6 shadow-2xl; - background: rgba(255, 255, 255, 0.12); - backdrop-filter: blur(30px) saturate(200%) brightness(120%) contrast(110%); - -webkit-backdrop-filter: blur(30px) saturate(200%) brightness(120%) contrast(110%); - border: 1px solid rgba(255, 255, 255, 0.25); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.15), - 0 8px 16px rgba(0, 0, 0, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.3), - 0 0 0 1px rgba(255, 255, 255, 0.1); - animation: alert-fade-in 0.5s ease-out; - } - - .dark .alert { - background: rgba(0, 0, 0, 0.3); - backdrop-filter: blur(30px) saturate(180%) brightness(110%) contrast(120%); - -webkit-backdrop-filter: blur(30px) saturate(180%) brightness(110%) contrast(120%); - border: 1px solid rgba(255, 255, 255, 0.15); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.4), - 0 8px 16px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.15), - 0 0 0 1px rgba(255, 255, 255, 0.05); - } - - .alert-success { - @apply text-green-900 dark:text-green-100; - background: linear-gradient(135deg, - rgba(34, 197, 94, 0.15) 0%, - rgba(134, 239, 172, 0.1) 50%, - rgba(34, 197, 94, 0.08) 100%); - border: 1px solid rgba(34, 197, 94, 0.3); - } - - .alert-error { - @apply text-red-900 dark:text-red-100; - background: linear-gradient(135deg, - rgba(239, 68, 68, 0.15) 0%, - rgba(252, 165, 165, 0.1) 50%, - rgba(239, 68, 68, 0.08) 100%); - border: 1px solid rgba(239, 68, 68, 0.3); - } - - .alert-warning { - @apply text-yellow-900 dark:text-yellow-100; - background: linear-gradient(135deg, - rgba(245, 158, 11, 0.15) 0%, - rgba(252, 211, 77, 0.1) 50%, - rgba(245, 158, 11, 0.08) 100%); - border: 1px solid rgba(245, 158, 11, 0.3); - } - - .alert-info { - @apply text-blue-900 dark:text-blue-100; - background: linear-gradient(135deg, - rgba(59, 130, 246, 0.15) 0%, - rgba(147, 197, 253, 0.1) 50%, - rgba(59, 130, 246, 0.08) 100%); - border: 1px solid rgba(59, 130, 246, 0.3); - } - - /* Browser-Notification-Container */ - .browser-notification { - @apply fixed top-4 left-4 max-w-sm p-4 rounded-2xl shadow-2xl z-50; - background: rgba(255, 255, 255, 0.08); - backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%); - -webkit-backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%); - border: 1px solid rgba(255, 255, 255, 0.25); - box-shadow: - 0 32px 64px rgba(0, 0, 0, 0.25), - 0 12px 24px rgba(0, 0, 0, 0.15), - inset 0 1px 0 rgba(255, 255, 255, 0.4), - 0 0 0 1px rgba(255, 255, 255, 0.1); - animation: notification-slide-left 0.6s cubic-bezier(0.4, 0, 0.2, 1); - } - - .dark .browser-notification { - background: rgba(0, 0, 0, 0.2); - backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(115%); - -webkit-backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(115%); - border: 1px solid rgba(255, 255, 255, 0.15); - box-shadow: - 0 32px 64px rgba(0, 0, 0, 0.6), - 0 12px 24px rgba(0, 0, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.2), - 0 0 0 1px rgba(255, 255, 255, 0.05); - } - - /* Notification-Animationen */ - @keyframes notification-slide-in { - 0% { - opacity: 0; - transform: translateX(100%) translateY(-20px) scale(0.9); - backdrop-filter: blur(0px); - } - 50% { - opacity: 0.8; - transform: translateX(20px) translateY(-10px) scale(1.05); - backdrop-filter: blur(20px); - } - 100% { - opacity: 1; - transform: translateX(0) translateY(0) scale(1); - backdrop-filter: blur(40px); - } - } - - @keyframes notification-slide-out { - 0% { - opacity: 1; - transform: translateX(0) translateY(0) scale(1); - } - 100% { - opacity: 0; - transform: translateX(100%) translateY(-20px) scale(0.9); - } - } - - @keyframes notification-slide-left { - 0% { - opacity: 0; - transform: translateX(-100%) translateY(-20px) scale(0.9); - backdrop-filter: blur(0px); - } - 50% { - opacity: 0.8; - transform: translateX(-20px) translateY(-10px) scale(1.05); - backdrop-filter: blur(20px); - } - 100% { - opacity: 1; - transform: translateX(0) translateY(0) scale(1); - backdrop-filter: blur(40px); - } - } - - @keyframes alert-fade-in { - 0% { - opacity: 0; - transform: translateY(-20px) scale(0.95); - } - 100% { - opacity: 1; - transform: translateY(0) scale(1); - } - } - - .notification.hiding { - animation: notification-slide-out 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards; - } - - /* Notification-Icons mit Glassmorphism */ - .notification-icon { - @apply flex items-center justify-center w-8 h-8 rounded-full mr-3 flex-shrink-0; - background: rgba(255, 255, 255, 0.2); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); - border: 1px solid rgba(255, 255, 255, 0.3); - box-shadow: - 0 8px 16px rgba(0, 0, 0, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.4); - } - - .notification-content { - @apply flex-1; - } - - .notification-title { - @apply font-semibold text-sm mb-1; - } - - .notification-message { - @apply text-sm opacity-90; - } - - .notification-close { - @apply ml-3 p-1 rounded-lg opacity-70 hover:opacity-100 transition-opacity; - background: rgba(255, 255, 255, 0.1); - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.2); - } - - .notification-close:hover { - background: rgba(255, 255, 255, 0.2); - transform: scale(1.1); - } - - /* Multiple Notifications Container */ - .notifications-container { - @apply fixed top-4 right-4 z-50 space-y-3 max-w-md; - } - - .notifications-container-left { - @apply fixed top-4 left-4 z-50 space-y-3 max-w-sm; - } - - /* Flash-Message-Verbesserungen - Vereinheitlicht */ - .flash-message-light { - background: linear-gradient(135deg, - rgba(255, 255, 255, 0.95) 0%, - rgba(248, 250, 252, 0.9) 100%); - backdrop-filter: blur(32px) saturate(200%) brightness(120%); - -webkit-backdrop-filter: blur(32px) saturate(200%) brightness(120%); - border: 1px solid rgba(226, 232, 240, 0.6); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.1), - 0 12px 24px rgba(0, 115, 206, 0.05), - inset 0 1px 0 rgba(255, 255, 255, 0.8); - color: var(--color-text-primary); - } - - .flash-message-light.success { - border-left: 4px solid #10b981; - background: linear-gradient(135deg, - rgba(236, 253, 245, 0.95) 0%, - rgba(209, 250, 229, 0.9) 100%); - } - - .flash-message-light.error { - border-left: 4px solid #ef4444; - background: linear-gradient(135deg, - rgba(254, 242, 242, 0.95) 0%, - rgba(252, 165, 165, 0.9) 100%); - } - - .flash-message-light.warning { - border-left: 4px solid #fbbf24; - background: linear-gradient(135deg, - rgba(255, 251, 235, 0.95) 0%, - rgba(254, 243, 199, 0.9) 100%); - } - - .flash-message-light.info { - border-left: 4px solid #3b82f6; - background: linear-gradient(135deg, - rgba(239, 246, 255, 0.95) 0%, - rgba(219, 234, 254, 0.9) 100%); - } - - /* Premium Table Styles */ - .table-enhanced { - background: var(--gradient-card); - border: 1px solid var(--color-border-primary); - border-radius: var(--card-radius); - overflow: hidden; - box-shadow: - 0 4px 20px var(--color-shadow), - 0 2px 8px rgba(0, 115, 206, 0.04), - inset 0 1px 0 rgba(255, 255, 255, 0.6); - } - - .table-enhanced th { - background: linear-gradient(135deg, - var(--color-bg-secondary) 0%, - var(--color-bg-tertiary) 100%); - color: var(--color-text-primary); - font-weight: 600; - padding: 1rem 1.5rem; - border-bottom: 1px solid var(--color-border-primary); - position: relative; - } - - .table-enhanced th::after { - content: ''; - position: absolute; - bottom: 0; - left: 0; - right: 0; - height: 1px; - background: linear-gradient(90deg, - transparent 0%, - var(--color-border-secondary) 50%, - transparent 100%); - } - - .table-enhanced td { - padding: 1rem 1.5rem; - border-bottom: 1px solid var(--color-border-primary); - color: var(--color-text-secondary); - transition: all 0.2s ease; - } - - .table-enhanced tbody tr:hover { - background: var(--color-bg-secondary); - transform: scale(1.002); - } - - .dark .table-enhanced { - background: rgba(10, 10, 10, 0.8); - border-color: var(--color-border-primary); - } - - .dark .table-enhanced th { - background: rgba(26, 26, 26, 0.8); - color: var(--color-text-primary); - } - - .dark .table-enhanced tbody tr:hover { - background: rgba(26, 26, 26, 0.6); - } - - /* Premium Modal Styles */ - .modal-enhanced { - background: linear-gradient(135deg, - rgba(255, 255, 255, 0.98) 0%, - rgba(248, 250, 252, 0.95) 50%, - rgba(255, 255, 255, 0.98) 100%); - backdrop-filter: blur(32px) saturate(220%) brightness(120%); - -webkit-backdrop-filter: blur(32px) saturate(220%) brightness(120%); - border: 1px solid rgba(226, 232, 240, 0.7); - border-radius: 1.5rem; - box-shadow: - 0 50px 100px rgba(0, 0, 0, 0.15), - 0 20px 40px rgba(0, 115, 206, 0.08), - inset 0 2px 0 rgba(255, 255, 255, 0.9); - position: relative; - overflow: hidden; - } - - .modal-enhanced::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 1px; - background: linear-gradient(90deg, - transparent 0%, - rgba(226, 232, 240, 0.8) 50%, - transparent 100%); - } - - .dark .modal-enhanced { - background: rgba(0, 0, 0, 0.95); - border-color: rgba(42, 42, 42, 0.7); - box-shadow: - 0 50px 100px rgba(0, 0, 0, 0.5), - inset 0 2px 0 rgba(255, 255, 255, 0.05); - } - - /* Enhanced Status Badges */ - .status-badge-enhanced { - display: inline-flex; - align-items: center; - padding: 0.5rem 1rem; - font-size: 0.75rem; - font-weight: 700; - border-radius: 9999px; - text-transform: uppercase; - letter-spacing: 0.05em; - border: 1px solid transparent; - transition: all 0.2s ease; - position: relative; - overflow: hidden; - } - - .status-badge-enhanced::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent); - transition: left 0.5s ease; - } - - .status-badge-enhanced:hover::before { - left: 100%; - } - - .status-online-enhanced { - background: linear-gradient(135deg, #ecfdf5 0%, #a7f3d0 100%); - color: #065f46; - border-color: rgba(16, 185, 129, 0.3); - } - - .status-offline-enhanced { - background: linear-gradient(135deg, #fef2f2 0%, #fca5a5 100%); - color: #991b1b; - border-color: rgba(239, 68, 68, 0.3); - } - - .status-printing-enhanced { - background: linear-gradient(135deg, #eff6ff 0%, #bfdbfe 100%); - color: #1e40af; - border-color: rgba(59, 130, 246, 0.3); - } - - /* Dark Mode Toggle - Premium Design */ - .dark-mode-toggle-new { - position: relative; - display: flex; - cursor: pointer; - align-items: center; - justify-content: center; - border-radius: 9999px; - padding: 0.625rem; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - background: linear-gradient(135deg, - rgba(248, 250, 252, 0.9) 0%, - rgba(241, 245, 249, 0.8) 100%); - border: 1px solid rgba(226, 232, 240, 0.7); - box-shadow: - 0 4px 12px rgba(0, 0, 0, 0.06), - 0 2px 4px rgba(0, 115, 206, 0.04), - inset 0 1px 0 rgba(255, 255, 255, 0.8); - color: var(--color-text-secondary); - z-index: 100; - } - - .dark-mode-toggle-new:hover { - transform: translateY(-2px) scale(1.05); - background: linear-gradient(135deg, - rgba(248, 250, 252, 0.95) 0%, - rgba(241, 245, 249, 0.85) 100%); - box-shadow: - 0 8px 20px rgba(0, 0, 0, 0.1), - 0 4px 8px rgba(0, 115, 206, 0.08), - inset 0 1px 0 rgba(255, 255, 255, 0.9); - } - - .dark-mode-toggle-new:active { - transform: translateY(-1px) scale(0.98); - transition: transform 0.1s; - } - - .dark .dark-mode-toggle-new { - background: rgba(10, 10, 10, 0.8); - border: 1px solid rgba(42, 42, 42, 0.6); - box-shadow: - 0 4px 12px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.05); - color: var(--color-text-secondary); - } - - .dark .dark-mode-toggle-new:hover { - background: rgba(10, 10, 10, 0.9); - box-shadow: - 0 8px 20px rgba(0, 0, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.08); - } - - /* Icon-Animation für Dark Mode Toggle */ - .dark-mode-toggle-new .sun-icon, - .dark-mode-toggle-new .moon-icon { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - } - - .dark-mode-toggle-new .sun-icon:not(.hidden) { - animation: icon-appear 0.5s cubic-bezier(0.25, 1, 0.5, 1) forwards; - } - - .dark-mode-toggle-new .moon-icon:not(.hidden) { - animation: icon-appear 0.5s cubic-bezier(0.25, 1, 0.5, 1) forwards; - } - - @keyframes icon-appear { - 0% { - opacity: 0; - transform: translate(-50%, -50%) scale(0.5) rotate(-20deg); - } - 100% { - opacity: 1; - transform: translate(-50%, -50%) scale(1) rotate(0); - } - } - - .dark .sun-icon { display: none; } - .dark .moon-icon { display: block; } - .sun-icon { display: block; } - .moon-icon { display: none; } - - /* User Menu Button - Premium Design */ - .user-menu-button-new { - display: flex; - align-items: center; - gap: 0.5rem; - border-radius: 0.75rem; - padding: 0.5rem; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - background: linear-gradient(135deg, - rgba(248, 250, 252, 0.8) 0%, - rgba(241, 245, 249, 0.7) 100%); - border: 1px solid rgba(226, 232, 240, 0.6); - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.05), - inset 0 1px 0 rgba(255, 255, 255, 0.7); - } - - .user-menu-button-new:hover { - transform: translateY(-1px); - background: linear-gradient(135deg, - rgba(248, 250, 252, 0.9) 0%, - rgba(241, 245, 249, 0.8) 100%); - box-shadow: - 0 4px 12px rgba(0, 0, 0, 0.08), - 0 2px 4px rgba(0, 115, 206, 0.04), - inset 0 1px 0 rgba(255, 255, 255, 0.8); - } - - .dark .user-menu-button-new { - background: rgba(10, 10, 10, 0.7); - border-color: rgba(42, 42, 42, 0.6); - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.2), - inset 0 1px 0 rgba(255, 255, 255, 0.03); - } - - .dark .user-menu-button-new:hover { - background: rgba(10, 10, 10, 0.8); - box-shadow: - 0 4px 12px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.05); - } - - /* Enhanced Hover Effects */ - .hover-lift-enhanced { - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - } - - .hover-lift-enhanced:hover { - transform: translateY(-3px) scale(1.01); - box-shadow: - 0 12px 30px var(--color-shadow-strong), - 0 6px 15px var(--color-shadow-accent); - } - - .dark .hover-lift-enhanced:hover { - box-shadow: 0 12px 30px var(--color-shadow); - } - - /* Smooth Scrollbar for Light Mode */ - ::-webkit-scrollbar { - width: 8px; - height: 8px; - } - - ::-webkit-scrollbar-track { - background: var(--color-bg-secondary); - border-radius: 4px; - } - - ::-webkit-scrollbar-thumb { - background: linear-gradient(180deg, - var(--color-border-secondary) 0%, - var(--color-border-primary) 100%); - border-radius: 4px; - transition: background 0.2s ease; - } - - ::-webkit-scrollbar-thumb:hover { - background: linear-gradient(180deg, - var(--color-accent) 0%, - var(--color-accent-hover) 100%); - } - - .dark ::-webkit-scrollbar-track { - background: var(--color-bg-secondary); - } - - .dark ::-webkit-scrollbar-thumb { - background: var(--color-border-primary); - } - - .dark ::-webkit-scrollbar-thumb:hover { - background: #60a5fa; - } - - /* Loading States */ - .loading-enhanced { - position: relative; - overflow: hidden; - } - - .loading-enhanced::after { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, - transparent, - rgba(0, 115, 206, 0.1), - transparent); - animation: loading-shimmer 2s infinite; - } - - @keyframes loading-shimmer { - 0% { left: -100%; } - 100% { left: 100%; } - } - - /* Focus States for Accessibility */ - .focus-enhanced:focus { - outline: 2px solid var(--color-accent); - outline-offset: 2px; - box-shadow: - 0 0 0 4px rgba(0, 115, 206, 0.15), - 0 4px 12px var(--color-shadow-accent); - } - - .dark .focus-enhanced:focus { - outline-color: #60a5fa; - box-shadow: - 0 0 0 4px rgba(96, 165, 250, 0.15), - 0 4px 12px rgba(96, 165, 250, 0.2); - } - - /* Responsive Design Enhancements */ - @media (max-width: 768px) { - .card-enhanced { - padding: 1rem; - border-radius: 0.75rem; - } - - .btn-enhanced { - padding: 0.75rem 1.5rem; - font-size: 0.8rem; - } - - .modal-enhanced { - border-radius: 1rem; - margin: 1rem; - } - - .dark-mode-toggle-new { - padding: 0.5rem; - } - } - - /* Reduced Motion Support */ - @media (prefers-reduced-motion: reduce) { - * { - transition: none !important; - animation: none !important; - } - } - - /* High Contrast Mode Support */ - @media (prefers-contrast: high) { - :root { - --color-shadow: rgba(0, 0, 0, 0.2); - --color-shadow-strong: rgba(0, 0, 0, 0.3); - --color-border-primary: #000000; - } - - .dark { - --color-border-primary: #ffffff; - } - } -} - -/* Glassmorphism Flash Messages */ -.flash-message { - @apply fixed top-4 right-4 px-6 py-4 rounded-2xl text-sm font-medium shadow-2xl transform transition-all duration-500 z-50 border; - /* Verstärkter Glassmorphism-Effekt */ - background: rgba(255, 255, 255, 0.08); - backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%); - -webkit-backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%); - border: 1px solid rgba(255, 255, 255, 0.25); - box-shadow: - 0 32px 64px rgba(0, 0, 0, 0.25), - 0 12px 24px rgba(0, 0, 0, 0.15), - inset 0 1px 0 rgba(255, 255, 255, 0.4), - 0 0 0 1px rgba(255, 255, 255, 0.1); - animation: flash-slide-in 0.5s cubic-bezier(0.4, 0, 0.2, 1); - transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1); -} - -.dark .flash-message { - background: rgba(0, 0, 0, 0.2); - backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(115%); - -webkit-backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(115%); - border: 1px solid rgba(255, 255, 255, 0.15); - box-shadow: - 0 32px 64px rgba(0, 0, 0, 0.6), - 0 12px 24px rgba(0, 0, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.2), - 0 0 0 1px rgba(255, 255, 255, 0.05); -} - -.flash-message:hover { - transform: translateY(-2px) scale(1.02); - box-shadow: - 0 40px 80px rgba(0, 0, 0, 0.3), - 0 16px 32px rgba(0, 0, 0, 0.2), - inset 0 1px 0 rgba(255, 255, 255, 0.5), - 0 0 0 1px rgba(255, 255, 255, 0.15); -} - -.dark .flash-message:hover { - box-shadow: - 0 40px 80px rgba(0, 0, 0, 0.7), - 0 16px 32px rgba(0, 0, 0, 0.5), - inset 0 1px 0 rgba(255, 255, 255, 0.3), - 0 0 0 1px rgba(255, 255, 255, 0.1); -} - -.flash-message.info { - @apply text-blue-100; - background: linear-gradient(135deg, - rgba(59, 130, 246, 0.2) 0%, - rgba(147, 197, 253, 0.15) 50%, - rgba(59, 130, 246, 0.1) 100%); - border: 1px solid rgba(59, 130, 246, 0.3); -} - -.flash-message.success { - @apply text-green-100; - background: linear-gradient(135deg, - rgba(34, 197, 94, 0.2) 0%, - rgba(134, 239, 172, 0.15) 50%, - rgba(34, 197, 94, 0.1) 100%); - border: 1px solid rgba(34, 197, 94, 0.3); -} - -.flash-message.warning { - @apply text-yellow-100; - background: linear-gradient(135deg, - rgba(245, 158, 11, 0.2) 0%, - rgba(252, 211, 77, 0.15) 50%, - rgba(245, 158, 11, 0.1) 100%); - border: 1px solid rgba(245, 158, 11, 0.3); -} - -.flash-message.error { - @apply text-red-100; - background: linear-gradient(135deg, - rgba(239, 68, 68, 0.2) 0%, - rgba(252, 165, 165, 0.15) 50%, - rgba(239, 68, 68, 0.1) 100%); - border: 1px solid rgba(239, 68, 68, 0.3); -} - -/* Flash Message Animation */ -@keyframes flash-slide-in { - 0% { - opacity: 0; - transform: translateX(100%) translateY(-20px) scale(0.9); - backdrop-filter: blur(0px); - } - 50% { - opacity: 0.8; - transform: translateX(20px) translateY(-10px) scale(1.05); - backdrop-filter: blur(20px); - } - 100% { - opacity: 1; - transform: translateX(0) translateY(0) scale(1); - backdrop-filter: blur(40px); - } -} - -@keyframes flash-slide-out { - 0% { - opacity: 1; - transform: translateX(0) translateY(0) scale(1); - } - 100% { - opacity: 0; - transform: translateX(100%) translateY(-20px) scale(0.9); - } -} - -.flash-message.hiding { - animation: flash-slide-out 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards; -} - -/* Do Not Disturb System Styles */ -.dnd-toggle { - @apply relative inline-flex items-center h-6 rounded-full w-11 transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500; - background: rgba(156, 163, 175, 0.3); - backdrop-filter: blur(10px); - border: 1px solid rgba(156, 163, 175, 0.2); -} - -.dnd-toggle.active { - background: rgba(239, 68, 68, 0.3); - border: 1px solid rgba(239, 68, 68, 0.4); -} - -.dnd-toggle-slider { - @apply inline-block h-4 w-4 rounded-full shadow-lg transform transition-transform duration-300; - background: rgba(255, 255, 255, 0.9); - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.3); - box-shadow: - 0 4px 8px rgba(0, 0, 0, 0.2), - 0 2px 4px rgba(0, 0, 0, 0.1); - margin: 0.125rem; -} - -.dnd-toggle.active .dnd-toggle-slider { - transform: translateX(1.25rem); - background: rgba(255, 255, 255, 1); - box-shadow: - 0 6px 12px rgba(239, 68, 68, 0.3), - 0 3px 6px rgba(239, 68, 68, 0.2); -} - -.dnd-indicator { - @apply fixed top-4 left-4 z-50 flex items-center px-3 py-2 rounded-lg text-sm font-medium transition-all duration-300; - background: rgba(239, 68, 68, 0.1); - backdrop-filter: blur(20px) saturate(150%); - -webkit-backdrop-filter: blur(20px) saturate(150%); - border: 1px solid rgba(239, 68, 68, 0.3); - color: rgb(239, 68, 68); - transform: translateY(-100%); - opacity: 0; -} - -.dnd-indicator.active { - transform: translateY(0); - opacity: 1; -} - -.dnd-modal { - @apply fixed inset-0 z-50 flex items-center justify-center p-4; - background: rgba(0, 0, 0, 0.3); - backdrop-filter: blur(20px); - -webkit-backdrop-filter: blur(20px); -} - -.dnd-modal-content { - @apply w-full max-w-md rounded-2xl p-6 shadow-2xl transform transition-all; - background: rgba(255, 255, 255, 0.1); - backdrop-filter: blur(40px) saturate(200%) brightness(120%); - -webkit-backdrop-filter: blur(40px) saturate(200%) brightness(120%); - border: 1px solid rgba(255, 255, 255, 0.3); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.25), - 0 8px 16px rgba(0, 0, 0, 0.15), - inset 0 1px 0 rgba(255, 255, 255, 0.4); -} - -.dark .dnd-modal-content { - background: rgba(0, 0, 0, 0.3); - backdrop-filter: blur(40px) saturate(180%) brightness(110%); - -webkit-backdrop-filter: blur(40px) saturate(180%) brightness(110%); - border: 1px solid rgba(255, 255, 255, 0.15); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.6), - 0 8px 16px rgba(0, 0, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.2); -} - -/* DND Flash Message Override */ -.flash-message.dnd-suppressed { - animation: flash-fade-in 0.3s ease-out; - opacity: 0.3; - transform: scale(0.95); - pointer-events: none; -} - -@keyframes flash-fade-in { - 0% { - opacity: 0; - transform: scale(0.9); - } - 100% { - opacity: 0.3; - transform: scale(0.95); - } -} - -/* Notification Counter */ -.dnd-counter { - @apply absolute -top-2 -right-2 bg-red-500 text-white text-xs rounded-full h-5 w-5 flex items-center justify-center font-bold; - background: rgba(239, 68, 68, 0.9); - backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.2); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); - animation: dnd-counter-bounce 0.5s ease-out; -} - -@keyframes dnd-counter-bounce { - 0% { - transform: scale(0); - } - 50% { - transform: scale(1.2); - } - 100% { - transform: scale(1); - } -} - -@keyframes slide-down { - 0% { - opacity: 0; - transform: translateY(-20px); - } - 100% { - opacity: 1; - transform: translateY(0); - } -} - -/* Mercedes Background Pattern */ -.mercedes-background::before { - content: ''; - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: -1; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 80' width='80' height='80' opacity='0.03' fill='currentColor'%3E%3Cpath d='M58.6,4.5C53,1.6,46.7,0,40,0c-6.7,0-13,1.6-18.6,4.5v0C8.7,11.2,0,24.6,0,40c0,15.4,8.7,28.8,21.5,35.5C27,78.3,33.3,80,40,80c6.7,0,12.9-1.7,18.5-4.6C71.3,68.8,80,55.4,80,40C80,24.6,71.3,11.2,58.6,4.5z M4,40c0-13.1,7-24.5,17.5-30.9v0C26.6,6,32.5,4.2,39,4l-4.5,32.7L21.5,46.8v0L8.3,57.1C5.6,52,4,46.2,4,40z M58.6,70.8C53.1,74.1,46.8,76,40,76c-6.8,0-13.2-1.9-18.6-5.2c-4.9-2.9-8.9-6.9-11.9-11.7l11.9-4.9v0L40,46.6l18.6,7.5v0l12,4.9C67.6,63.9,63.4,67.9,58.6,70.8z M58.6,46.8L58.6,46.8l-12.9-10L41.1,4c6.3,0.2,12.3,2,17.4,5.1v0C69,15.4,76,26.9,76,40c0,6.2-1.5,12-4.3,17.1L58.6,46.8z'/%3E%3C/svg%3E"); - background-position: center; - background-repeat: repeat; - background-size: 120px 120px; - pointer-events: none; - opacity: 0.03; - transition: opacity 0.3s ease; -} - -.dark .mercedes-background::before { - opacity: 0.015; /* Sehr subtil für eleganten Dark Mode */ - filter: invert(1) brightness(0.3); - background-size: 150px 150px; /* Größere Sterne für bessere Sichtbarkeit */ -} - -/* Monochrome Button Styles */ -@layer components { - /* Buttons mit verstärktem Glassmorphism */ - .btn-primary { - @apply text-white dark:text-slate-900 px-4 py-2 rounded-lg transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 shadow-2xl hover:-translate-y-0.5; - background: rgba(0, 0, 0, 0.7); - backdrop-filter: blur(20px) saturate(150%) brightness(110%); - -webkit-backdrop-filter: blur(20px) saturate(150%) brightness(110%); - border: 1px solid rgba(255, 255, 255, 0.2); - box-shadow: - 0 20px 40px rgba(0, 0, 0, 0.3), - 0 8px 16px rgba(0, 0, 0, 0.2), - inset 0 1px 0 rgba(255, 255, 255, 0.2), - 0 0 0 1px rgba(255, 255, 255, 0.1); - } - - .btn-primary:hover { - background: rgba(0, 0, 0, 0.9); - backdrop-filter: blur(25px) saturate(180%) brightness(120%); - -webkit-backdrop-filter: blur(25px) saturate(180%) brightness(120%); - border: 1px solid rgba(255, 255, 255, 0.3); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.4), - 0 10px 20px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.3); - } - - .dark .btn-primary { - background: rgba(255, 255, 255, 0.7); - border: 1px solid rgba(0, 0, 0, 0.1); - box-shadow: - 0 20px 40px rgba(0, 0, 0, 0.2), - 0 8px 16px rgba(0, 0, 0, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.8), - 0 0 0 1px rgba(0, 0, 0, 0.05); - } - - .dark .btn-primary:hover { - background: rgba(255, 255, 255, 0.9); - border: 1px solid rgba(0, 0, 0, 0.15); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.3), - 0 10px 20px rgba(0, 0, 0, 0.2), - inset 0 1px 0 rgba(255, 255, 255, 0.9); - } - - .btn-secondary { - @apply text-slate-900 dark:text-white px-4 py-2 rounded-lg transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-slate-500 focus:ring-offset-2 shadow-2xl hover:-translate-y-0.5; - background: rgba(255, 255, 255, 0.3); - backdrop-filter: blur(20px) saturate(150%) brightness(110%); - -webkit-backdrop-filter: blur(20px) saturate(150%) brightness(110%); - border: 1px solid rgba(255, 255, 255, 0.4); - box-shadow: - 0 20px 40px rgba(0, 0, 0, 0.15), - 0 8px 16px rgba(0, 0, 0, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.5), - 0 0 0 1px rgba(255, 255, 255, 0.2); - } - - .btn-secondary:hover { - background: rgba(255, 255, 255, 0.5); - backdrop-filter: blur(25px) saturate(180%) brightness(120%); - -webkit-backdrop-filter: blur(25px) saturate(180%) brightness(120%); - border: 1px solid rgba(255, 255, 255, 0.6); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.2), - 0 10px 20px rgba(0, 0, 0, 0.15), - inset 0 1px 0 rgba(255, 255, 255, 0.7); + @apply bg-white border border-gray-200 text-gray-900; + background: var(--surface); + border-color: var(--border); + color: var(--text); } .dark .btn-secondary { - background: rgba(0, 0, 0, 0.4); - border: 1px solid rgba(255, 255, 255, 0.2); - box-shadow: - 0 20px 40px rgba(0, 0, 0, 0.3), - 0 8px 16px rgba(0, 0, 0, 0.2), - inset 0 1px 0 rgba(255, 255, 255, 0.2), - 0 0 0 1px rgba(255, 255, 255, 0.1); + @apply bg-gray-700 border-gray-600 text-white; } - .dark .btn-secondary:hover { - background: rgba(0, 0, 0, 0.6); - border: 1px solid rgba(255, 255, 255, 0.3); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.4), - 0 10px 20px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.3); - } - - .btn-outline { - @apply border-2 border-black/70 hover:bg-black/70 dark:border-white/70 dark:hover:bg-white/70 text-black hover:text-white dark:text-white dark:hover:text-slate-900 px-4 py-2 rounded-lg transition-all duration-300 focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2 backdrop-blur-lg; - backdrop-filter: blur(16px) saturate(150%); - -webkit-backdrop-filter: blur(16px) saturate(150%); - box-shadow: 0 15px 30px rgba(0, 0, 0, 0.1), 0 0 0 1px rgba(255, 255, 255, 0.05); - } - - /* Glassmorphism Card mit abgerundeten Ecken - Verstärkt */ - .glass-card { - @apply rounded-xl p-6 shadow-2xl transition-all duration-300; - background: rgba(255, 255, 255, 0.15); - backdrop-filter: blur(30px) saturate(200%) brightness(120%) contrast(110%); - -webkit-backdrop-filter: blur(30px) saturate(200%) brightness(120%) contrast(110%); - border: 1px solid rgba(255, 255, 255, 0.3); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.15), - 0 8px 16px rgba(0, 0, 0, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.3), - 0 0 0 1px rgba(255, 255, 255, 0.1); - border-radius: var(--card-radius); + .input { + @apply w-full px-3 py-2 border border-gray-200 rounded-lg; + background: var(--surface); + border-color: var(--border); + color: var(--text); } - .dark .glass-card { - background: rgba(0, 0, 0, 0.3); - backdrop-filter: blur(30px) saturate(180%) brightness(110%) contrast(120%); - -webkit-backdrop-filter: blur(30px) saturate(180%) brightness(110%) contrast(120%); - border: 1px solid rgba(255, 255, 255, 0.15); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.4), - 0 8px 16px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.15), - 0 0 0 1px rgba(255, 255, 255, 0.05); - } - - /* Dashboard Cards mit verstärktem Glassmorphism */ - .dashboard-card { - @apply rounded-xl p-6 shadow-2xl transition-all duration-300 hover:-translate-y-1; - background: rgba(255, 255, 255, 0.12); - backdrop-filter: blur(35px) saturate(200%) brightness(125%) contrast(115%); - -webkit-backdrop-filter: blur(35px) saturate(200%) brightness(125%) contrast(115%); - border: 1px solid rgba(255, 255, 255, 0.25); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.15), - 0 8px 16px rgba(0, 0, 0, 0.08), - inset 0 1px 0 rgba(255, 255, 255, 0.25), - 0 0 0 1px rgba(255, 255, 255, 0.1); - border-radius: var(--card-radius); + .input:focus { + outline: none; + border-color: var(--primary); } - .dark .dashboard-card { - background: rgba(0, 0, 0, 0.35); - backdrop-filter: blur(35px) saturate(180%) brightness(115%) contrast(125%); - -webkit-backdrop-filter: blur(35px) saturate(180%) brightness(115%) contrast(125%); - border: 1px solid rgba(255, 255, 255, 0.12); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.5), - 0 8px 16px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.12), - 0 0 0 1px rgba(255, 255, 255, 0.05); - } - - /* Navigation Styles */ - .nav-link { - @apply flex items-center px-4 py-2 rounded-lg text-sm font-medium transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-slate-500 focus:ring-offset-2 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700/50 hover:shadow-md; - } - - .nav-link.active { - @apply text-slate-900 dark:text-white bg-slate-100 dark:bg-black shadow-sm; + .dark .input { + @apply bg-gray-700 border-gray-600 text-white; } - /* Verbesserte Navbar Styles - Verstärktes Glassmorphism */ - .navbar { - display: flex; - justify-content: space-between; - align-items: center; - padding: 0.5rem 1rem; - background: rgba(255, 255, 255, 0.1); - backdrop-filter: blur(10px); - border-radius: 10px; - box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); - transition: all 0.3s ease; + /* Navigation */ + .nav { + @apply flex gap-4; } - .navbar-button { - padding: 0.25rem 0.5rem; - font-size: 0.875rem; - border-radius: 5px; - transition: background-color 0.3s ease; + .nav-item { + @apply px-4 py-2 rounded-lg text-gray-600 hover:bg-gray-100; + color: var(--text-muted); + transition: background-color 0.1s ease; } - .navbar-button:hover { - background-color: rgba(255, 255, 255, 0.2); + .nav-item:hover { + background: var(--bg); + color: var(--text); } - @media (max-width: 768px) { - .navbar { - flex-direction: column; - padding: 0.25rem; - } - .navbar-button { - margin: 0.25rem 0; - } + .nav-item.active { + background: var(--primary); + color: white; } - .dark .navbar { - background: rgba(0, 0, 0, 0.25); - backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(120%); - -webkit-backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(120%); - box-shadow: - 0 8px 32px rgba(0, 0, 0, 0.6), - 0 2px 8px rgba(0, 0, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.1), - 0 0 0 1px rgba(255, 255, 255, 0.05); - border-bottom: 1px solid rgba(255, 255, 255, 0.1); + .dark .nav-item { + @apply text-gray-300 hover:bg-gray-700; } - .navbar-brand { - @apply flex items-center space-x-2 transition-transform duration-300 hover:scale-105; + /* Status-Badges */ + .status { + @apply inline-block px-3 py-1 rounded-full text-xs font-semibold uppercase; } - .navbar-menu { - @apply flex items-center justify-center space-x-1 md:space-x-3 lg:space-x-6 p-3 mx-4 rounded-2xl border; - background: rgba(255, 255, 255, 0.25); - backdrop-filter: blur(20px) saturate(150%) brightness(110%); - -webkit-backdrop-filter: blur(20px) saturate(150%) brightness(110%); - border: 1px solid rgba(255, 255, 255, 0.3); - box-shadow: - 0 4px 16px rgba(0, 0, 0, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.4), - 0 0 0 1px rgba(255, 255, 255, 0.1); + .status-online { + background: #d1fae5; + color: #065f46; } - .dark .navbar-menu { - background: rgba(0, 0, 0, 0.4); - backdrop-filter: blur(20px) saturate(150%) brightness(110%); - -webkit-backdrop-filter: blur(20px) saturate(150%) brightness(110%); - border: 1px solid rgba(255, 255, 255, 0.15); - box-shadow: - 0 4px 16px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.2), - 0 0 0 1px rgba(255, 255, 255, 0.05); + .status-offline { + background: #fee2e2; + color: #991b1b; } - .navbar-button { - @apply p-2 rounded-full transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-offset-2; + .status-printing { + background: #dbeafe; + color: #1e40af; } - /* User Menu Styles */ - .user-menu-button { - @apply flex items-center space-x-2 rounded-lg p-1 transition-all duration-300 hover:bg-gray-100/80 dark:hover:bg-slate-700/60 focus:outline-none focus:ring-2 focus:ring-slate-500 focus:ring-offset-2; + .dark .status-online { + background: rgba(16, 185, 129, 0.2); + color: #10b981; } - .user-avatar { - @apply w-10 h-10 bg-black dark:bg-white text-white dark:text-slate-900 rounded-full flex items-center justify-center font-bold text-sm shadow-md transition-all duration-300 hover:shadow-lg; - } - - /* Avatar im Dropdown */ - .avatar-large { - @apply w-14 h-14 bg-black dark:bg-white text-white dark:text-slate-900 rounded-full flex items-center justify-center font-bold text-lg shadow-md; + .dark .status-offline { + background: rgba(239, 68, 68, 0.2); + color: #ef4444; } - .user-dropdown-item { - @apply flex items-center px-4 py-3 text-sm text-slate-700 dark:text-slate-300 hover:bg-gray-100/80 dark:hover:bg-slate-700/60 hover:text-slate-900 dark:hover:text-white transition-all duration-300 focus:outline-none focus:bg-gray-100/80 dark:focus:bg-slate-700/60; - } - - .user-dropdown-separator { - @apply border-t border-gray-200/80 dark:border-slate-700/30 my-1; - } - - .menu-item { - @apply flex items-center space-x-2 px-4 py-2.5 text-slate-700 dark:text-slate-300 rounded-xl transition-all duration-300; - background: rgba(255, 255, 255, 0.1); - backdrop-filter: blur(10px); - -webkit-backdrop-filter: blur(10px); - border: 1px solid rgba(255, 255, 255, 0.2); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); - } - - .menu-item:hover { - @apply text-slate-900 dark:text-white; - background: rgba(255, 255, 255, 0.3); - backdrop-filter: blur(15px) saturate(150%); - -webkit-backdrop-filter: blur(15px) saturate(150%); - border: 1px solid rgba(255, 255, 255, 0.4); - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); - transform: translateY(-1px); - } - - .dark .menu-item { - background: rgba(0, 0, 0, 0.2); - border: 1px solid rgba(255, 255, 255, 0.1); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); - } - - .dark .menu-item:hover { - background: rgba(0, 0, 0, 0.4); - border: 1px solid rgba(255, 255, 255, 0.2); - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3); - } - - .menu-item.active { - @apply text-slate-900 dark:text-white font-medium; - background: rgba(255, 255, 255, 0.5); - backdrop-filter: blur(20px) saturate(180%); - -webkit-backdrop-filter: blur(20px) saturate(180%); - border: 1px solid rgba(255, 255, 255, 0.6); - box-shadow: - 0 4px 16px rgba(0, 0, 0, 0.15), - inset 0 1px 0 rgba(255, 255, 255, 0.5); - } - - .dark .menu-item.active { - background: rgba(0, 0, 0, 0.6); - border: 1px solid rgba(255, 255, 255, 0.3); - box-shadow: - 0 4px 16px rgba(0, 0, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.2); - } - - /* Dropdown Styles - Verstärktes Glassmorphism */ - .user-dropdown { - @apply absolute right-0 mt-2 w-64 rounded-xl shadow-2xl z-50 overflow-hidden; - background: rgba(255, 255, 255, 0.1); - backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%); - -webkit-backdrop-filter: blur(40px) saturate(200%) brightness(130%) contrast(110%); - border: 1px solid rgba(255, 255, 255, 0.3); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.25), - 0 8px 16px rgba(0, 0, 0, 0.15), - inset 0 1px 0 rgba(255, 255, 255, 0.4), - 0 0 0 1px rgba(255, 255, 255, 0.1); - animation: fadeIn 0.2s ease-out forwards; - } - - .dark .user-dropdown { - background: rgba(0, 0, 0, 0.4); - backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(120%); - -webkit-backdrop-filter: blur(40px) saturate(180%) brightness(120%) contrast(120%); - border: 1px solid rgba(255, 255, 255, 0.15); - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.6), - 0 8px 16px rgba(0, 0, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.2), - 0 0 0 1px rgba(255, 255, 255, 0.05); - } - - .dropdown-header { - @apply flex items-center p-4 border-b border-gray-200/80 dark:border-slate-700/30; - } - - .dropdown-item { - @apply flex items-center gap-3 px-4 py-3 text-sm text-slate-700 dark:text-slate-300 hover:bg-gray-100/80 dark:hover:bg-slate-700/60 hover:text-slate-900 dark:hover:text-white transition-all duration-300; - } - - .dropdown-divider { - @apply border-t border-gray-200/80 dark:border-slate-700/30; - } - - /* Mercedes-Benz Logo Animation */ - @keyframes mercedes-rotate { - 0% { transform: rotate(0deg); } - 25% { transform: rotate(90deg); } - 50% { transform: rotate(180deg); } - 75% { transform: rotate(270deg); } - 100% { transform: rotate(360deg); } - } - - .navbar-brand:hover svg { - animation: mercedes-rotate 5s infinite linear; - transform-origin: center; + .dark .status-printing { + background: rgba(59, 130, 246, 0.2); + color: #3b82f6; } } - -/* Navbar Sticky Fix - Außerhalb von @layer für höhere Priorität */ -.navbar { - position: -webkit-sticky !important; - position: sticky !important; - top: 0 !important; - z-index: 50 !important; - width: 100% !important; - left: 0 !important; - right: 0 !important; - /* Verstärktes Glassmorphism Design */ - --navbar-blur: 40px; - --navbar-opacity: 0.15; - background: rgba(255, 255, 255, var(--navbar-opacity, 0.15)) !important; - backdrop-filter: blur(var(--navbar-blur, 40px)) saturate(200%) brightness(110%) contrast(105%) !important; - -webkit-backdrop-filter: blur(var(--navbar-blur, 40px)) saturate(200%) brightness(110%) contrast(105%) !important; - box-shadow: - 0 8px 32px rgba(0, 0, 0, 0.12), - 0 2px 8px rgba(0, 0, 0, 0.08), - inset 0 1px 0 rgba(255, 255, 255, 0.3), - 0 0 0 1px rgba(255, 255, 255, 0.15) !important; - border-bottom: 1px solid rgba(255, 255, 255, 0.2) !important; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; -} - -.dark .navbar { - --navbar-dark-opacity: 0.25; - background: rgba(0, 0, 0, var(--navbar-dark-opacity, 0.25)) !important; - backdrop-filter: blur(calc(var(--navbar-blur, 40px) + 5px)) saturate(180%) brightness(120%) contrast(115%) !important; - -webkit-backdrop-filter: blur(calc(var(--navbar-blur, 40px) + 5px)) saturate(180%) brightness(120%) contrast(115%) !important; - box-shadow: - 0 8px 32px rgba(0, 0, 0, 0.4), - 0 2px 8px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.15), - 0 0 0 1px rgba(255, 255, 255, 0.08) !important; - border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important; -} - -/* Navbar Scroll-Effekt */ -.navbar.scrolled { - --navbar-blur: 50px; - --navbar-opacity: 0.25; - background: rgba(255, 255, 255, var(--navbar-opacity, 0.25)) !important; - backdrop-filter: blur(var(--navbar-blur, 50px)) saturate(220%) brightness(115%) contrast(110%) !important; - -webkit-backdrop-filter: blur(var(--navbar-blur, 50px)) saturate(220%) brightness(115%) contrast(110%) !important; - box-shadow: - 0 12px 40px rgba(0, 0, 0, 0.15), - 0 4px 12px rgba(0, 0, 0, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.4), - 0 0 0 1px rgba(255, 255, 255, 0.2) !important; -} - -.dark .navbar.scrolled { - --navbar-dark-opacity: 0.35; - background: rgba(0, 0, 0, var(--navbar-dark-opacity, 0.35)) !important; - backdrop-filter: blur(calc(var(--navbar-blur, 50px) + 5px)) saturate(200%) brightness(125%) contrast(120%) !important; - -webkit-backdrop-filter: blur(calc(var(--navbar-blur, 50px) + 5px)) saturate(200%) brightness(125%) contrast(120%) !important; - box-shadow: - 0 12px 40px rgba(0, 0, 0, 0.5), - 0 4px 12px rgba(0, 0, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.2), - 0 0 0 1px rgba(255, 255, 255, 0.1) !important; -} - -/* Neue Navbar-Komponenten mit verbessertem Glassmorphism */ -.navbar-menu-new { - @apply flex items-center justify-center space-x-0.5 md:space-x-1; - max-width: 100%; - overflow-x: auto; - scrollbar-width: none; - -ms-overflow-style: none; - /* Glassmorphism für das Menü */ - background: rgba(255, 255, 255, 0.1); - backdrop-filter: blur(25px) saturate(170%) brightness(108%); - -webkit-backdrop-filter: blur(25px) saturate(170%) brightness(108%); - border-radius: 16px; - padding: 8px; - margin: 0 16px; - border: 1px solid rgba(255, 255, 255, 0.15); - box-shadow: - 0 6px 20px rgba(0, 0, 0, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.2), - 0 0 0 1px rgba(255, 255, 255, 0.05); - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); -} - -.dark .navbar-menu-new { - background: rgba(0, 0, 0, 0.2); - backdrop-filter: blur(30px) saturate(150%) brightness(115%); - -webkit-backdrop-filter: blur(30px) saturate(150%) brightness(115%); - border: 1px solid rgba(255, 255, 255, 0.1); - box-shadow: - 0 6px 20px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.1), - 0 0 0 1px rgba(255, 255, 255, 0.03); -} - -.navbar-menu-new::-webkit-scrollbar { - display: none; -} - -/* Glassmorphism Hover-Animation für Navbar-Menu */ -.navbar-menu-new:hover { - backdrop-filter: blur(35px) saturate(190%) brightness(112%); - -webkit-backdrop-filter: blur(35px) saturate(190%) brightness(112%); - box-shadow: - 0 8px 25px rgba(0, 0, 0, 0.15), - inset 0 1px 0 rgba(255, 255, 255, 0.3), - 0 0 0 1px rgba(255, 255, 255, 0.1); - transform: translateY(-1px); -} - -.dark .navbar-menu-new:hover { - backdrop-filter: blur(40px) saturate(170%) brightness(120%); - -webkit-backdrop-filter: blur(40px) saturate(170%) brightness(120%); - box-shadow: - 0 8px 25px rgba(0, 0, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.15), - 0 0 0 1px rgba(255, 255, 255, 0.05); -} - -.nav-item { - @apply flex items-center space-x-1.5 px-3 py-2.5 rounded-xl text-sm font-medium transition-all duration-300; - color: rgba(15, 23, 42, 0.85); - /* Gläserner Nav-Item */ - background: rgba(255, 255, 255, 0.08); - backdrop-filter: blur(15px) saturate(140%); - -webkit-backdrop-filter: blur(15px) saturate(140%); - border: 1px solid rgba(255, 255, 255, 0.1); - box-shadow: - 0 4px 12px rgba(0, 0, 0, 0.05), - inset 0 1px 0 rgba(255, 255, 255, 0.15); - position: relative; - overflow: hidden; - animation: nav-item-entrance 0.6s ease-out; -} - -/* Glassmorphism Hover-Effekt */ -.nav-item::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); - transition: left 0.5s; -} - -.nav-item:hover::before { - left: 100%; -} - -/* Zusätzlicher Glitter-Effekt */ -.nav-item::after { - content: ''; - position: absolute; - top: -50%; - left: -50%; - width: 200%; - height: 200%; - background: conic-gradient(from 0deg at 50% 50%, transparent 0deg, rgba(255, 255, 255, 0.1) 30deg, transparent 60deg); - opacity: 0; - transition: opacity 0.3s ease; - pointer-events: none; - animation: rotate 3s linear infinite; -} - -.nav-item:hover::after { - opacity: 1; -} - -.dark .nav-item { - color: rgba(255, 255, 255, 0.85); - background: rgba(0, 0, 0, 0.15); - backdrop-filter: blur(20px) saturate(130%); - -webkit-backdrop-filter: blur(20px) saturate(130%); - border: 1px solid rgba(255, 255, 255, 0.08); - box-shadow: - 0 4px 12px rgba(0, 0, 0, 0.2), - inset 0 1px 0 rgba(255, 255, 255, 0.08); -} - -.nav-item:hover { - color: rgba(15, 23, 42, 1); - background: rgba(255, 255, 255, 0.2); - backdrop-filter: blur(25px) saturate(160%) brightness(110%); - -webkit-backdrop-filter: blur(25px) saturate(160%) brightness(110%); - border: 1px solid rgba(255, 255, 255, 0.25); - box-shadow: - 0 8px 20px rgba(0, 0, 0, 0.12), - inset 0 1px 0 rgba(255, 255, 255, 0.3), - 0 0 0 1px rgba(255, 255, 255, 0.1); - transform: translateY(-2px) scale(1.02); -} - -.dark .nav-item:hover { - color: rgba(255, 255, 255, 1); - background: rgba(0, 0, 0, 0.25); - backdrop-filter: blur(30px) saturate(150%) brightness(120%); - -webkit-backdrop-filter: blur(30px) saturate(150%) brightness(120%); - border: 1px solid rgba(255, 255, 255, 0.15); - box-shadow: - 0 8px 20px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.15), - 0 0 0 1px rgba(255, 255, 255, 0.05); -} - -.nav-item.active { - color: rgba(15, 23, 42, 1); - background: rgba(255, 255, 255, 0.35); - backdrop-filter: blur(35px) saturate(180%) brightness(115%); - -webkit-backdrop-filter: blur(35px) saturate(180%) brightness(115%); - border: 1px solid rgba(255, 255, 255, 0.4); - box-shadow: - 0 12px 24px rgba(0, 0, 0, 0.15), - inset 0 1px 0 rgba(255, 255, 255, 0.5), - 0 0 0 1px rgba(59, 130, 246, 0.3); - transform: translateY(-1px); - animation: nav-item-active-glow 2s ease-in-out infinite alternate; -} - -.dark .nav-item.active { - color: rgba(255, 255, 255, 1); - background: rgba(0, 0, 0, 0.4); - backdrop-filter: blur(40px) saturate(160%) brightness(125%); - -webkit-backdrop-filter: blur(40px) saturate(160%) brightness(125%); - border: 1px solid rgba(255, 255, 255, 0.2); - box-shadow: - 0 12px 24px rgba(0, 0, 0, 0.4), - inset 0 1px 0 rgba(255, 255, 255, 0.2), - 0 0 0 1px rgba(59, 130, 246, 0.2); -} - -/* Animationen für Glassmorphism-Effekte */ -@keyframes nav-item-entrance { - 0% { - opacity: 0; - transform: translateY(10px) scale(0.95); - backdrop-filter: blur(5px); - } - 100% { - opacity: 1; - transform: translateY(0) scale(1); - backdrop-filter: blur(15px) saturate(140%); - } -} - -@keyframes nav-item-active-glow { - 0% { - box-shadow: - 0 12px 24px rgba(0, 0, 0, 0.15), - inset 0 1px 0 rgba(255, 255, 255, 0.5), - 0 0 0 1px rgba(59, 130, 246, 0.3); - } - 100% { - box-shadow: - 0 16px 32px rgba(0, 0, 0, 0.2), - inset 0 1px 0 rgba(255, 255, 255, 0.6), - 0 0 0 2px rgba(59, 130, 246, 0.5); - } -} - -@keyframes rotate { - 0% { - transform: rotate(0deg); - } - 100% { - transform: rotate(360deg); - } -} - -/* Glassmorphism-Partikel-Effekt */ -.navbar::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: - radial-gradient(circle at 20% 50%, rgba(255, 255, 255, 0.1) 1px, transparent 1px), - radial-gradient(circle at 80% 50%, rgba(255, 255, 255, 0.1) 1px, transparent 1px), - radial-gradient(circle at 40% 20%, rgba(255, 255, 255, 0.05) 1px, transparent 1px), - radial-gradient(circle at 60% 80%, rgba(255, 255, 255, 0.05) 1px, transparent 1px); - opacity: 0; - animation: glassmorphism-particles 8s ease-in-out infinite; - pointer-events: none; -} - -.dark .navbar::before { - background: - radial-gradient(circle at 20% 50%, rgba(255, 255, 255, 0.05) 1px, transparent 1px), - radial-gradient(circle at 80% 50%, rgba(255, 255, 255, 0.05) 1px, transparent 1px), - radial-gradient(circle at 40% 20%, rgba(255, 255, 255, 0.03) 1px, transparent 1px), - radial-gradient(circle at 60% 80%, rgba(255, 255, 255, 0.03) 1px, transparent 1px); -} - -@keyframes glassmorphism-particles { - 0%, 100% { - opacity: 0; - transform: scale(1); - } - 50% { - opacity: 1; - transform: scale(1.1); - } -} - -/* Dark Mode Toggle - Kompakteres Design */ -.dark-mode-toggle-new { - @apply relative p-2 rounded-full flex items-center justify-center transition-all duration-300 cursor-pointer; - background: rgba(241, 245, 249, 0.8); - border: 1px solid rgba(255, 255, 255, 0.7); - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.05), - 0 1px 2px rgba(0, 0, 0, 0.04); - color: #334155; - z-index: 100; -} - -.dark-mode-toggle-new:hover { - @apply transform -translate-y-0.5; - background: rgba(241, 245, 249, 0.9); - box-shadow: - 0 8px 16px rgba(0, 0, 0, 0.08), - 0 2px 4px rgba(0, 0, 0, 0.06); -} - -.dark-mode-toggle-new:active { - @apply transform scale-95; - transition: transform 0.1s; -} - -.dark .dark-mode-toggle-new { - background: rgba(30, 41, 59, 0.8); - border: 1px solid rgba(255, 255, 255, 0.1); - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.2), - 0 1px 2px rgba(0, 0, 0, 0.1); - color: #e2e8f0; -} - -.dark .dark-mode-toggle-new:hover { - background: rgba(30, 41, 59, 0.9); - box-shadow: - 0 8px 16px rgba(0, 0, 0, 0.2), - 0 2px 4px rgba(0, 0, 0, 0.15); -} - -/* Icon-Animation */ -.dark-mode-toggle-new .sun-icon, -.dark-mode-toggle-new .moon-icon { - @apply absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transition-all duration-300; -} - -.dark-mode-toggle-new .sun-icon:not(.hidden) { - animation: spin-in 0.5s cubic-bezier(0.25, 1, 0.5, 1) forwards; -} - -.dark-mode-toggle-new .moon-icon:not(.hidden) { - animation: spin-in 0.5s cubic-bezier(0.25, 1, 0.5, 1) forwards; -} - -@keyframes spin-in { - 0% { - opacity: 0; - transform: translateY(10px) scale(0.7) rotate(20deg); - } - 100% { - opacity: 1; - transform: translateY(0) scale(1) rotate(0); - } -} - -.dark .sun-icon { - display: none; -} - -.dark .moon-icon { - display: block; -} - -.sun-icon { - display: block; -} - -.moon-icon { - display: none; -} - -/* User Menu Button - Kompakteres Design */ -.user-menu-button-new { - @apply flex items-center space-x-1.5 rounded-lg p-1 transition-all duration-300; - background: rgba(241, 245, 249, 0.6); - border: 1px solid rgba(255, 255, 255, 0.6); - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.04), - 0 1px 2px rgba(0, 0, 0, 0.02); -} - -.user-menu-button-new:hover { - @apply transform -translate-y-0.5; - background: rgba(241, 245, 249, 0.8); - box-shadow: - 0 8px 16px rgba(0, 0, 0, 0.06), - 0 2px 4px rgba(0, 0, 0, 0.04); -} - -.dark .user-menu-button-new { - background: rgba(30, 41, 59, 0.6); - border: 1px solid rgba(255, 255, 255, 0.08); - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.15), - 0 1px 2px rgba(0, 0, 0, 0.1); -} - -.dark .user-menu-button-new:hover { - background: rgba(30, 41, 59, 0.8); - box-shadow: - 0 8px 16px rgba(0, 0, 0, 0.15), - 0 2px 4px rgba(0, 0, 0, 0.1); -} - -/* User Avatar - Kompakteres Design */ -.user-avatar-new { - @apply h-7 w-7 rounded-full flex items-center justify-center text-white font-semibold text-xs shadow-md transition-all duration-300; - background: linear-gradient(135deg, #000000, #333333); - box-shadow: - 0 2px 4px rgba(0, 0, 0, 0.2), - 0 1px 2px rgba(0, 0, 0, 0.1); -} - -.dark .user-avatar-new { - background: linear-gradient(135deg, #f8fafc, #e2e8f0); - color: #0f172a; - box-shadow: - 0 2px 4px rgba(0, 0, 0, 0.3), - 0 1px 2px rgba(0, 0, 0, 0.2); -} - -/* Login Button - Kompakteres Design */ -.login-button-new { - @apply flex items-center px-3 py-1.5 rounded-lg text-xs font-medium shadow-sm transition-all duration-300; - background: #000000; - color: #ffffff; - border: 1px solid rgba(255, 255, 255, 0.1); - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.1), - 0 1px 2px rgba(0, 0, 0, 0.08); -} - -.login-button-new:hover { - @apply transform -translate-y-0.5; - background: #333333; - box-shadow: - 0 8px 16px rgba(0, 0, 0, 0.15), - 0 3px 4px rgba(0, 0, 0, 0.1); -} - -.dark .login-button-new { - background: #ffffff; - color: #000000; - border: 1px solid rgba(0, 0, 0, 0.1); - box-shadow: - 0 2px 8px rgba(0, 0, 0, 0.2), - 0 1px 2px rgba(0, 0, 0, 0.15); -} - -.dark .login-button-new:hover { - background: #f1f5f9; - box-shadow: - 0 8px 16px rgba(0, 0, 0, 0.25), - 0 3px 4px rgba(0, 0, 0, 0.2); -} - -/* Mobile Menu - Kompakteres Design */ -.mobile-menu-new { - @apply w-full overflow-hidden transition-all duration-300 z-40; - background: rgba(255, 255, 255, 0.8); - backdrop-filter: blur(24px); - -webkit-backdrop-filter: blur(24px); - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.06); - border-bottom: 1px solid rgba(241, 245, 249, 0.8); - max-height: 0; - opacity: 0; -} - -.mobile-menu-new.open { - max-height: 400px; - opacity: 1; - border-bottom: 1px solid rgba(241, 245, 249, 0.8); -} - -.dark .mobile-menu-new { - background: rgba(15, 23, 42, 0.8); - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); - border-bottom: 1px solid rgba(30, 41, 59, 0.8); -} - -.mobile-nav-item { - @apply flex items-center space-x-2.5 px-3 py-2.5 rounded-lg text-sm text-slate-800 dark:text-slate-200 transition-all duration-300; -} - -.mobile-nav-item:hover { - background: rgba(241, 245, 249, 0.8); -} - -.dark .mobile-nav-item:hover { - background: rgba(30, 41, 59, 0.6); -} - -.mobile-nav-item.active { - background: rgba(241, 245, 249, 0.9); - color: #000000; - font-weight: 500; -} - -.dark .mobile-nav-item.active { - background: rgba(30, 41, 59, 0.8); - color: #ffffff; -} - -/* Dashboard Stat Cards mit schwarzem Hintergrund im Dark Mode */ -.mb-stat-card { - background: linear-gradient(135deg, rgba(240, 249, 255, 0.6) 0%, rgba(230, 242, 255, 0.6) 100%); - color: #0f172a; - position: relative; - overflow: hidden; - border: none; - border-radius: var(--card-radius); - backdrop-filter: blur(20px) saturate(180%) brightness(110%); - -webkit-backdrop-filter: blur(20px) saturate(180%) brightness(110%); - box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15), 0 0 0 1px rgba(255, 255, 255, 0.1); - padding: 1.5rem; - margin: 1rem; - transition: transform 0.3s ease, box-shadow 0.3s ease; -} - -.dark .mb-stat-card { - background: linear-gradient(135deg, rgba(0, 0, 0, 0.7) 0%, rgba(10, 10, 10, 0.7) 100%); - color: var(--text-primary, #f8fafc); - box-shadow: 0 25px 50px rgba(0, 0, 0, 0.3), 0 0 0 1px rgba(255, 255, 255, 0.05); -} - -/* Stats und Jobs Page Card Styles */ -.stats-card, .job-card { - @apply bg-white/60 dark:bg-black/80 backdrop-blur-2xl border border-gray-200/70 dark:border-slate-700/20 rounded-xl shadow-2xl transition-all duration-300; - backdrop-filter: blur(24px) saturate(200%) brightness(120%); - -webkit-backdrop-filter: blur(24px) saturate(200%) brightness(120%); - box-shadow: 0 25px 50px rgba(0, 0, 0, 0.2), 0 0 0 1px rgba(255, 255, 255, 0.1); - border-radius: var(--card-radius); -} - -/* Footer Styling - Verstärktes Glassmorphism */ -footer { - @apply transition-all duration-300; - background: rgba(255, 255, 255, 0.1); - backdrop-filter: blur(30px) saturate(180%) brightness(120%); - -webkit-backdrop-filter: blur(30px) saturate(180%) brightness(120%); - border-top: 1px solid rgba(255, 255, 255, 0.2); - box-shadow: - 0 -8px 32px rgba(0, 0, 0, 0.1), - 0 -2px 8px rgba(0, 0, 0, 0.05), - inset 0 1px 0 rgba(255, 255, 255, 0.2), - 0 0 0 1px rgba(255, 255, 255, 0.05); -} - -.dark footer { - background: rgba(0, 0, 0, 0.3); - backdrop-filter: blur(30px) saturate(160%) brightness(110%); - -webkit-backdrop-filter: blur(30px) saturate(160%) brightness(110%); - border-top: 1px solid rgba(255, 255, 255, 0.1); - box-shadow: - 0 -8px 32px rgba(0, 0, 0, 0.3), - 0 -2px 8px rgba(0, 0, 0, 0.2), - inset 0 1px 0 rgba(255, 255, 255, 0.1), - 0 0 0 1px rgba(255, 255, 255, 0.03); -} - -/* Dropdown Pfeil Animation */ -.dropdown-arrow { - @apply transition-transform duration-300; -} - -/* Mercedes-Benz Hintergrund mit Star-Pattern */ -.mercedes-star-bg { - position: relative; -} - -.mercedes-star-bg::after { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 80' width='80' height='80' opacity='0.05' fill='currentColor'%3E%3Cpath d='M58.6,4.5C53,1.6,46.7,0,40,0c-6.7,0-13,1.6-18.6,4.5v0C8.7,11.2,0,24.6,0,40c0,15.4,8.7,28.8,21.5,35.5C27,78.3,33.3,80,40,80c6.7,0,12.9-1.7,18.5-4.6C71.3,68.8,80,55.4,80,40C80,24.6,71.3,11.2,58.6,4.5z M4,40c0-13.1,7-24.5,17.5-30.9v0C26.6,6,32.5,4.2,39,4l-4.5,32.7L21.5,46.8v0L8.3,57.1C5.6,52,4,46.2,4,40z M58.6,70.8C53.1,74.1,46.8,76,40,76c-6.8,0-13.2-1.9-18.6-5.2c-4.9-2.9-8.9-6.9-11.9-11.7l11.9-4.9v0L40,46.6l18.6,7.5v0l12,4.9C67.6,63.9,63.4,67.9,58.6,70.8z M58.6,46.8L58.6,46.8l-12.9-10L41.1,4c6.3,0.2,12.3,2,17.4,5.1v0C69,15.4,76,26.9,76,40c0,6.2-1.5,12-4.3,17.1L58.6,46.8z'/%3E%3C/svg%3E"); - background-position: center; - background-repeat: repeat; - background-size: 40px 40px; - z-index: -1; - opacity: 0.05; -} - -.dark .mercedes-star-bg::after { - opacity: 0.02; - filter: invert(1) brightness(0.4); -} - -/* Zusätzliche Glassmorphism-Verbesserungen */ -.glass-effect { - backdrop-filter: blur(20px) saturate(180%) brightness(110%); - -webkit-backdrop-filter: blur(20px) saturate(180%) brightness(110%); - background: rgba(255, 255, 255, 0.1); - border: 1px solid rgba(255, 255, 255, 0.2); - box-shadow: - 0 8px 32px rgba(0, 0, 0, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.3); -} - -.dark .glass-effect { - background: rgba(0, 0, 0, 0.3); - border: 1px solid rgba(255, 255, 255, 0.1); - box-shadow: - 0 8px 32px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.15); -} - -/* Verbesserte Hover-Effekte für alle interaktiven Elemente */ -.glass-hover { - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); -} - -.glass-hover:hover { - transform: translateY(-2px); - backdrop-filter: blur(25px) saturate(200%) brightness(120%); - -webkit-backdrop-filter: blur(25px) saturate(200%) brightness(120%); - box-shadow: - 0 20px 40px rgba(0, 0, 0, 0.15), - 0 8px 16px rgba(0, 0, 0, 0.1), - inset 0 1px 0 rgba(255, 255, 255, 0.4); -} - -.dark .glass-hover:hover { - box-shadow: - 0 20px 40px rgba(0, 0, 0, 0.4), - 0 8px 16px rgba(0, 0, 0, 0.3), - inset 0 1px 0 rgba(255, 255, 255, 0.2); -} - -/* Neue verbesserte Drucker-Karten für die Drucker-Ansicht */ -.printer-card-new { - @apply bg-gradient-to-br from-white/90 to-white/70 dark:from-slate-800/90 dark:to-slate-900/70 backdrop-blur-2xl rounded-xl border border-gray-200/70 dark:border-slate-700/30 p-5 shadow-2xl transition-all duration-300 hover:-translate-y-1 relative overflow-hidden; - box-shadow: - 0 20px 40px rgba(0, 0, 0, 0.08), - 0 10px 20px rgba(0, 0, 0, 0.06), - 0 0 0 1px rgba(255, 255, 255, 0.1); - border-radius: var(--card-radius, 1rem); -} - -.dark .printer-card-new { - box-shadow: - 0 20px 40px rgba(0, 0, 0, 0.4), - 0 10px 20px rgba(0, 0, 0, 0.3), - 0 0 0 1px rgba(255, 255, 255, 0.05); -} - -/* Online Drucker-Karten mit stärkerem visuellen Unterschied */ -.printer-card-new.online { - @apply bg-gradient-to-br from-green-50/90 to-emerald-50/80 dark:from-green-900/30 dark:to-emerald-900/20 border-green-200 dark:border-green-700/50; - box-shadow: - 0 20px 40px rgba(0, 122, 85, 0.08), - 0 10px 20px rgba(0, 122, 85, 0.06), - 0 0 0 1px rgba(209, 250, 229, 0.4); -} - -.dark .printer-card-new.online { - box-shadow: - 0 20px 40px rgba(0, 0, 0, 0.3), - 0 10px 20px rgba(0, 0, 0, 0.2), - 0 0 0 1px rgba(16, 185, 129, 0.2); -} - -/* Status-Badge mit verbesserten Styles */ -.status-badge-new { - @apply px-2.5 py-1 rounded-full text-xs font-medium inline-flex items-center space-x-1 shadow-sm; - background: rgba(255, 255, 255, 0.9); - backdrop-filter: blur(8px); - -webkit-backdrop-filter: blur(8px); - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); -} - -.dark .status-badge-new { - background: rgba(30, 41, 59, 0.7); - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); -} - -.status-badge-new.online { - @apply bg-green-100/90 text-green-800 dark:bg-green-900/60 dark:text-green-300; -} - -.status-badge-new.offline { - @apply bg-red-100/90 text-red-800 dark:bg-red-900/60 dark:text-red-300; -} - -/* Verbesserte Filterleiste */ -.filter-bar-new { - @apply bg-white/80 dark:bg-slate-800/80 backdrop-blur-xl rounded-lg p-1.5 border border-gray-200/60 dark:border-slate-700/30 shadow-xl; - box-shadow: - 0 10px 25px rgba(0, 0, 0, 0.05), - 0 5px 10px rgba(0, 0, 0, 0.03), - 0 0 0 1px rgba(255, 255, 255, 0.2); -} - -.dark .filter-bar-new { - box-shadow: - 0 10px 25px rgba(0, 0, 0, 0.2), - 0 5px 10px rgba(0, 0, 0, 0.15), - 0 0 0 1px rgba(255, 255, 255, 0.05); -} - -.filter-btn-new { - @apply px-3.5 py-2 text-sm rounded-md transition-all duration-300 font-medium; -} - -.filter-btn-new.active { - @apply bg-black text-white dark:bg-white dark:text-slate-900 shadow-md; - box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); -} - -.dark .filter-btn-new.active { - box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); -} - -/* Verbesserte Aktionsschaltflächen */ -.action-btn-new { - @apply flex items-center justify-center gap-2 px-4 py-2.5 rounded-lg font-medium text-sm transition-all duration-300 shadow-md hover:-translate-y-0.5; - backdrop-filter: blur(8px); - -webkit-backdrop-filter: blur(8px); -} - -.action-btn-new.primary { - @apply bg-indigo-600 hover:bg-indigo-700 text-white dark:bg-indigo-600 dark:hover:bg-indigo-500; - box-shadow: 0 5px 15px rgba(79, 70, 229, 0.2); -} - -.dark .action-btn-new.primary { - box-shadow: 0 5px 15px rgba(79, 70, 229, 0.3); -} - -.action-btn-new.success { - @apply bg-green-600 hover:bg-green-700 text-white dark:bg-green-600 dark:hover:bg-green-500; - box-shadow: 0 5px 15px rgba(16, 185, 129, 0.2); -} - -.dark .action-btn-new.success { - box-shadow: 0 5px 15px rgba(16, 185, 129, 0.3); -} - -.action-btn-new.danger { - @apply bg-red-600 hover:bg-red-700 text-white dark:bg-red-600 dark:hover:bg-red-500; - box-shadow: 0 5px 15px rgba(239, 68, 68, 0.2); -} - -.dark .action-btn-new.danger { - box-shadow: 0 5px 15px rgba(239, 68, 68, 0.3); -} - -/* Informationszeilen in Drucker-Karten */ -.printer-info-row { - @apply flex items-center gap-2 text-xs sm:text-sm text-slate-700 dark:text-slate-300 mb-1.5; -} - -.printer-info-icon { - @apply w-3.5 h-3.5 sm:w-4 sm:h-4 text-slate-500 dark:text-slate-400 flex-shrink-0; -} - -/* Online-Indikator mit Pulseffekt */ -.online-indicator { - @apply absolute top-2.5 right-2.5 w-3 h-3 bg-green-500 rounded-full shadow-lg; - box-shadow: 0 0 0 rgba(16, 185, 129, 0.6); - animation: pulse-ring 2s cubic-bezier(0.455, 0.03, 0.515, 0.955) infinite; -} - -@keyframes pulse-ring { - 0% { - box-shadow: 0 0 0 0 rgba(16, 185, 129, 0.6); - } - 70% { - box-shadow: 0 0 0 6px rgba(16, 185, 129, 0); - } - 100% { - box-shadow: 0 0 0 0 rgba(16, 185, 129, 0); - } -} - -/* Header mit verbesserten Status-Anzeigen */ -.status-overview-new { - @apply flex flex-wrap gap-3 text-xs sm:text-sm p-3 bg-white/60 dark:bg-slate-800/60 backdrop-blur-xl rounded-lg border border-gray-200/60 dark:border-slate-700/30 shadow-lg; - box-shadow: - 0 10px 25px rgba(0, 0, 0, 0.04), - 0 5px 10px rgba(0, 0, 0, 0.02), - 0 0 0 1px rgba(255, 255, 255, 0.1); -} - -.dark .status-overview-new { - box-shadow: - 0 10px 25px rgba(0, 0, 0, 0.15), - 0 5px 10px rgba(0, 0, 0, 0.1), - 0 0 0 1px rgba(255, 255, 255, 0.03); -} - -.status-dot { - @apply w-2.5 h-2.5 rounded-full; -} - -.status-dot.online { - @apply bg-green-500; - animation: pulse-dot 2s cubic-bezier(0.455, 0.03, 0.515, 0.955) infinite; -} - -.status-dot.offline { - @apply bg-red-500; -} - -@keyframes pulse-dot { - 0% { - transform: scale(0.95); - opacity: 1; - } - 50% { - transform: scale(1.1); - opacity: 0.8; - } - 100% { - transform: scale(0.95); - opacity: 1; - } -} - -/* Verbesserte Modals mit Glassmorphism */ -.modal-new { - @apply fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/40 backdrop-blur-sm; -} - -.modal-content-new { - @apply bg-white/90 dark:bg-slate-800/90 backdrop-blur-2xl rounded-2xl p-6 w-full max-w-md shadow-2xl border border-gray-200/60 dark:border-slate-700/30 transform transition-all duration-300; - box-shadow: - 0 25px 50px rgba(0, 0, 0, 0.15), - 0 15px 30px rgba(0, 0, 0, 0.1), - 0 20px 25px -5px rgba(0, 0, 0, 0.5), - 0 10px 10px -5px rgba(0, 0, 0, 0.3); -} - -/* User Dropdown Items */ -.user-dropdown-item { - @apply flex items-center px-4 py-3 text-sm text-slate-700 dark:text-slate-200 hover:bg-slate-50 dark:hover:bg-slate-800 transition-all duration-200 cursor-pointer; -} - -.user-dropdown-item:first-child { - @apply rounded-t-xl; -} - -.user-dropdown-item:last-child { - @apply rounded-b-xl; -} - -.user-dropdown-item:hover { - background: rgba(248, 250, 252, 0.8); - transform: translateX(2px); -} - -.dark .user-dropdown-item:hover { - background: rgba(30, 41, 59, 0.8); -} - -/* User Dropdown Icons */ -.user-dropdown-icon { - @apply w-4 h-4 mr-3 text-slate-500 dark:text-slate-400 transition-colors duration-200; -} - -.user-dropdown-item:hover .user-dropdown-icon { - @apply text-slate-700 dark:text-slate-200; -} - -/* Divider in User Dropdown */ -.user-dropdown-divider { - @apply border-t border-slate-200 dark:border-slate-700 my-1; -} - -/* User Info Section in Dropdown */ -.user-info-section { - @apply px-4 py-3 border-b border-slate-200 dark:border-slate-700; - background: rgba(248, 250, 252, 0.5); -} - -.dark .user-info-section { - background: rgba(30, 41, 59, 0.5); -} - -.user-info-name { - @apply text-sm font-semibold text-slate-900 dark:text-white; -} - -.user-info-role { - @apply text-xs text-slate-500 dark:text-slate-400 mt-1; -} diff --git a/backend/static/css/tailwind.min.css b/backend/static/css/tailwind.min.css index 2366ca7d..f14419d2 100644 --- a/backend/static/css/tailwind.min.css +++ b/backend/static/css/tailwind.min.css @@ -1 +1 @@ -*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:system-ui,-apple-system,sans-serif;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--primary:#0073ce;--primary-dark:#005a9f;--bg:#fafbfc;--surface:#fff;--text:#111827;--text-muted:#6b7280;--border:#e5e7eb;--shadow:0 2px 4px rgba(0,0,0,.05)}.dark{--bg:#1e293b;--surface:#334155;--text:#f8fafc;--text-muted:#94a3b8;--border:#475569;--shadow:0 2px 4px rgba(0,0,0,.3)}body{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}body:is(.dark *){--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}body{font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--text);line-height:1.5;transition:background-color .2s ease,color .2s ease}.\!container{width:100%!important}.container{width:100%}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.card{border-radius:.5rem;border-width:1px;border-color:rgb(229 231 235/var(--tw-border-opacity,1));background-color:rgb(255 255 255/var(--tw-bg-opacity,1));padding:1rem;background:var(--surface);border-color:var(--border);box-shadow:var(--shadow)}.card,.dark .card{--tw-border-opacity:1;--tw-bg-opacity:1}.dark .card{border-color:rgb(75 85 99/var(--tw-border-opacity,1));background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.btn{border-radius:.5rem;padding:.5rem 1rem;font-weight:600;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s;background:var(--primary);color:#fff;border:none}.btn:hover{background:var(--primary-dark)}.btn-secondary{border-width:1px;border-color:rgb(229 231 235/var(--tw-border-opacity,1));background-color:rgb(255 255 255/var(--tw-bg-opacity,1));color:rgb(17 24 39/var(--tw-text-opacity,1));background:var(--surface);border-color:var(--border);color:var(--text)}.btn-secondary,.dark .btn-secondary{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1}.dark .btn-secondary{border-color:rgb(75 85 99/var(--tw-border-opacity,1));background-color:rgb(55 65 81/var(--tw-bg-opacity,1));color:rgb(255 255 255/var(--tw-text-opacity,1))}.input{width:100%;border-radius:.5rem;border-width:1px;--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1));padding:.5rem .75rem;background:var(--surface);border-color:var(--border);color:var(--text)}.input:focus{outline:none;border-color:var(--primary)}.dark .input{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.nav{display:flex;gap:1rem}.nav-item{border-radius:.5rem;padding:.5rem 1rem;--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.nav-item:hover{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.nav-item{color:var(--text-muted);transition:background-color .1s ease}.nav-item:hover{background:var(--bg);color:var(--text)}.nav-item.active{background:var(--primary);color:#fff}.dark .nav-item{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.dark .nav-item:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.status{display:inline-block;border-radius:9999px;padding:.25rem .75rem;font-size:.75rem;line-height:1rem;font-weight:600;text-transform:uppercase}.status-online{background:#d1fae5;color:#065f46}.status-offline{background:#fee2e2;color:#991b1b}.status-printing{background:#dbeafe;color:#1e40af}.dark .status-online{background:rgba(16,185,129,.2);color:#10b981}.dark .status-offline{background:rgba(239,68,68,.2);color:#ef4444}.dark .status-printing{background:rgba(59,130,246,.2);color:#3b82f6}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.-inset-1{inset:-.25rem}.inset-0{inset:0}.inset-y-0{top:0;bottom:0}.-bottom-2{bottom:-.5rem}.-bottom-40{bottom:-10rem}.-bottom-8{bottom:-2rem}.-left-2{left:-.5rem}.-left-32{left:-8rem}.-right-1{right:-.25rem}.-right-2{right:-.5rem}.-right-32{right:-8rem}.-top-1{top:-.25rem}.-top-2{top:-.5rem}.-top-40{top:-10rem}.bottom-0{bottom:0}.bottom-4{bottom:1rem}.bottom-6{bottom:1.5rem}.bottom-8{bottom:2rem}.bottom-full{bottom:100%}.end-1{inset-inline-end:.25rem}.left-0{left:0}.left-1{left:.25rem}.left-1\/2{left:50%}.left-3{left:.75rem}.left-4{left:1rem}.right-0{right:0}.right-2{right:.5rem}.right-2\.5{right:.625rem}.right-3{right:.75rem}.right-4{right:1rem}.right-5{right:1.25rem}.right-6{right:1.5rem}.right-8{right:2rem}.top-0{top:0}.top-1{top:.25rem}.top-1\/2{top:50%}.top-2{top:.5rem}.top-2\.5{top:.625rem}.top-3{top:.75rem}.top-4{top:1rem}.top-5{top:1.25rem}.top-6{top:1.5rem}.top-8{top:2rem}.top-full{top:100%}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.col-span-2{grid-column:span 2/span 2}.col-span-3{grid-column:span 3/span 3}.col-span-full{grid-column:1/-1}.m-1{margin:.25rem}.m-2{margin:.5rem}.m-4{margin:1rem}.mx-0{margin-left:0;margin-right:0}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-8{margin-top:2rem;margin-bottom:2rem}.-ml-1{margin-left:-.25rem}.-mt-8{margin-top:-2rem}.mb-0{margin-bottom:0}.mb-1{margin-bottom:.25rem}.mb-1\.5{margin-bottom:.375rem}.mb-12{margin-bottom:3rem}.mb-16{margin-bottom:4rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.ml-6{margin-left:1.5rem}.ml-8{margin-left:2rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-12{margin-top:3rem}.mt-16{margin-top:4rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.mt-auto{margin-top:auto}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.\!table{display:table!important}.table{display:table}.grid{display:grid}.contents{display:contents}.list-item{display:list-item}.hidden{display:none}.h-0{height:0}.h-1{height:.25rem}.h-10{height:2.5rem}.h-11{height:2.75rem}.h-12{height:3rem}.h-14{height:3.5rem}.h-16{height:4rem}.h-2{height:.5rem}.h-2\.5{height:.625rem}.h-20{height:5rem}.h-24{height:6rem}.h-28{height:7rem}.h-3{height:.75rem}.h-3\.5{height:.875rem}.h-32{height:8rem}.h-4{height:1rem}.h-40{height:10rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-64{height:16rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-96{height:24rem}.h-full{height:100%}.max-h-32{max-height:8rem}.max-h-80{max-height:20rem}.max-h-96{max-height:24rem}.max-h-\[90vh\]{max-height:90vh}.min-h-\[80vh\]{min-height:80vh}.min-h-screen{min-height:100vh}.w-0{width:0}.w-1{width:.25rem}.w-1\/2{width:50%}.w-1\/3{width:33.333333%}.w-10{width:2.5rem}.w-11{width:2.75rem}.w-12{width:3rem}.w-14{width:3.5rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\.5{width:.625rem}.w-2\/3{width:66.666667%}.w-20{width:5rem}.w-24{width:6rem}.w-28{width:7rem}.w-3{width:.75rem}.w-3\.5{width:.875rem}.w-3\/4{width:75%}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-64{width:16rem}.w-7{width:1.75rem}.w-72{width:18rem}.w-8{width:2rem}.w-80{width:20rem}.w-96{width:24rem}.w-auto{width:auto}.w-full{width:100%}.min-w-0{min-width:0}.min-w-\[150px\]{min-width:150px}.min-w-full{min-width:100%}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.max-w-screen-xl{max-width:1280px}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink{flex-shrink:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.flex-grow,.grow{flex-grow:1}.border-collapse{border-collapse:collapse}.origin-top-right{transform-origin:top right}.-translate-x-1{--tw-translate-x:-0.25rem}.-translate-x-1,.-translate-x-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-full{--tw-translate-x:-100%}.-translate-x-full,.-translate-y-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-0{--tw-translate-y:-0px}.-translate-y-1{--tw-translate-y:-0.25rem}.-translate-y-1,.-translate-y-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y:-50%}.translate-x-0{--tw-translate-x:0px}.translate-x-0,.translate-x-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-1{--tw-translate-x:0.25rem}.translate-x-full{--tw-translate-x:100%}.translate-x-full,.translate-y-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-1{--tw-translate-y:0.25rem}.rotate-0{--tw-rotate:0deg}.rotate-0,.rotate-180{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-180{--tw-rotate:180deg}.rotate-90{--tw-rotate:90deg}.rotate-90,.skew-x-12{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.skew-x-12{--tw-skew-x:12deg}.scale-100{--tw-scale-x:1;--tw-scale-y:1}.scale-100,.scale-105{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05}.scale-110{--tw-scale-x:1.1;--tw-scale-y:1.1}.scale-110,.scale-75{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-75{--tw-scale-x:.75;--tw-scale-y:.75}.scale-95{--tw-scale-x:.95;--tw-scale-y:.95}.scale-95,.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes bounce{0%,to{transform:translateY(-25%);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;animation-timing-function:cubic-bezier(0,0,.2,1)}}.animate-bounce{animation:bounce 1s infinite}@keyframes ping{75%,to{transform:scale(2);opacity:0}}.animate-ping{animation:ping 1s cubic-bezier(0,0,.2,1) infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.resize-none{resize:none}.resize{resize:both}.scroll-mt-8{scroll-margin-top:2rem}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-x-0>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(0px*var(--tw-space-x-reverse));margin-left:calc(0px*(1 - var(--tw-space-x-reverse)))}.space-x-0\.5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.125rem*var(--tw-space-x-reverse));margin-left:calc(.125rem*(1 - var(--tw-space-x-reverse)))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)))}.space-x-1\.5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.375rem*var(--tw-space-x-reverse));margin-left:calc(.375rem*(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-2\.5>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.625rem*var(--tw-space-x-reverse));margin-left:calc(.625rem*(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1.5rem*var(--tw-space-x-reverse));margin-left:calc(1.5rem*(1 - var(--tw-space-x-reverse)))}.space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-16>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(4rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(4rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity,1))}.divide-slate-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(226 232 240/var(--tw-divide-opacity,1))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.scroll-smooth{scroll-behavior:smooth}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-3xl{border-radius:1.5rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.rounded-b-3xl{border-bottom-right-radius:1.5rem;border-bottom-left-radius:1.5rem}.rounded-b-xl{border-bottom-right-radius:.75rem;border-bottom-left-radius:.75rem}.rounded-l-md{border-top-left-radius:.375rem;border-bottom-left-radius:.375rem}.rounded-r-lg{border-top-right-radius:.5rem;border-bottom-right-radius:.5rem}.rounded-r-md{border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}.rounded-t-3xl{border-top-left-radius:1.5rem;border-top-right-radius:1.5rem}.rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.rounded-t-xl{border-top-left-radius:.75rem;border-top-right-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-4{border-width:4px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l-2{border-left-width:2px}.border-l-4{border-left-width:4px}.border-r-4{border-right-width:4px}.border-t{border-top-width:1px}.border-t-4{border-top-width:4px}.border-dashed{border-style:dashed}.border-none{border-style:none}.border-amber-200{--tw-border-opacity:1;border-color:rgb(253 230 138/var(--tw-border-opacity,1))}.border-black{--tw-border-opacity:1;border-color:rgb(0 0 0/var(--tw-border-opacity,1))}.border-black\/70{border-color:rgba(0,0,0,.7)}.border-blue-200{--tw-border-opacity:1;border-color:rgb(191 219 254/var(--tw-border-opacity,1))}.border-blue-200\/50{border-color:rgba(191,219,254,.5)}.border-blue-300{--tw-border-opacity:1;border-color:rgb(147 197 253/var(--tw-border-opacity,1))}.border-blue-400{--tw-border-opacity:1;border-color:rgb(96 165 250/var(--tw-border-opacity,1))}.border-blue-500{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity,1))}.border-blue-600{--tw-border-opacity:1;border-color:rgb(37 99 235/var(--tw-border-opacity,1))}.border-blue-700{--tw-border-opacity:1;border-color:rgb(29 78 216/var(--tw-border-opacity,1))}.border-blue-800{--tw-border-opacity:1;border-color:rgb(30 64 175/var(--tw-border-opacity,1))}.border-emerald-200{--tw-border-opacity:1;border-color:rgb(167 243 208/var(--tw-border-opacity,1))}.border-emerald-200\/50{border-color:rgba(167,243,208,.5)}.border-emerald-500{--tw-border-opacity:1;border-color:rgb(16 185 129/var(--tw-border-opacity,1))}.border-emerald-700{--tw-border-opacity:1;border-color:rgb(4 120 87/var(--tw-border-opacity,1))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-gray-200\/50{border-color:rgba(229,231,235,.5)}.border-gray-200\/60{border-color:rgba(229,231,235,.6)}.border-gray-200\/70{border-color:rgba(229,231,235,.7)}.border-gray-200\/80{border-color:rgba(229,231,235,.8)}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.border-gray-300\/60{border-color:rgba(209,213,219,.6)}.border-gray-400{--tw-border-opacity:1;border-color:rgb(156 163 175/var(--tw-border-opacity,1))}.border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity,1))}.border-green-200{--tw-border-opacity:1;border-color:rgb(187 247 208/var(--tw-border-opacity,1))}.border-green-200\/50{border-color:rgba(187,247,208,.5)}.border-green-300{--tw-border-opacity:1;border-color:rgb(134 239 172/var(--tw-border-opacity,1))}.border-green-400{--tw-border-opacity:1;border-color:rgb(74 222 128/var(--tw-border-opacity,1))}.border-green-500{--tw-border-opacity:1;border-color:rgb(34 197 94/var(--tw-border-opacity,1))}.border-green-800{--tw-border-opacity:1;border-color:rgb(22 101 52/var(--tw-border-opacity,1))}.border-indigo-200{--tw-border-opacity:1;border-color:rgb(199 210 254/var(--tw-border-opacity,1))}.border-indigo-200\/50{border-color:rgba(199,210,254,.5)}.border-indigo-600{--tw-border-opacity:1;border-color:rgb(79 70 229/var(--tw-border-opacity,1))}.border-indigo-800{--tw-border-opacity:1;border-color:rgb(55 48 163/var(--tw-border-opacity,1))}.border-orange-200{--tw-border-opacity:1;border-color:rgb(254 215 170/var(--tw-border-opacity,1))}.border-orange-200\/50{border-color:hsla(32,98%,83%,.5)}.border-orange-500{--tw-border-opacity:1;border-color:rgb(249 115 22/var(--tw-border-opacity,1))}.border-orange-800{--tw-border-opacity:1;border-color:rgb(154 52 18/var(--tw-border-opacity,1))}.border-purple-200{--tw-border-opacity:1;border-color:rgb(233 213 255/var(--tw-border-opacity,1))}.border-purple-200\/50{border-color:rgba(233,213,255,.5)}.border-purple-400{--tw-border-opacity:1;border-color:rgb(192 132 252/var(--tw-border-opacity,1))}.border-purple-800{--tw-border-opacity:1;border-color:rgb(107 33 168/var(--tw-border-opacity,1))}.border-red-200{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity,1))}.border-red-200\/50{border-color:hsla(0,96%,89%,.5)}.border-red-300{--tw-border-opacity:1;border-color:rgb(252 165 165/var(--tw-border-opacity,1))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity,1))}.border-red-500{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity,1))}.border-red-800{--tw-border-opacity:1;border-color:rgb(153 27 27/var(--tw-border-opacity,1))}.border-slate-200{--tw-border-opacity:1;border-color:rgb(226 232 240/var(--tw-border-opacity,1))}.border-slate-200\/50{border-color:rgba(226,232,240,.5)}.border-slate-300{--tw-border-opacity:1;border-color:rgb(203 213 225/var(--tw-border-opacity,1))}.border-slate-600{--tw-border-opacity:1;border-color:rgb(71 85 105/var(--tw-border-opacity,1))}.border-slate-700{--tw-border-opacity:1;border-color:rgb(51 65 85/var(--tw-border-opacity,1))}.border-transparent{border-color:transparent}.border-white{--tw-border-opacity:1;border-color:rgb(255 255 255/var(--tw-border-opacity,1))}.border-white\/20{border-color:hsla(0,0%,100%,.2)}.border-white\/30{border-color:hsla(0,0%,100%,.3)}.border-white\/50{border-color:hsla(0,0%,100%,.5)}.border-yellow-200{--tw-border-opacity:1;border-color:rgb(254 240 138/var(--tw-border-opacity,1))}.border-yellow-300{--tw-border-opacity:1;border-color:rgb(253 224 71/var(--tw-border-opacity,1))}.border-yellow-400{--tw-border-opacity:1;border-color:rgb(250 204 21/var(--tw-border-opacity,1))}.border-yellow-500{--tw-border-opacity:1;border-color:rgb(234 179 8/var(--tw-border-opacity,1))}.border-t-slate-800{--tw-border-opacity:1;border-top-color:rgb(30 41 59/var(--tw-border-opacity,1))}.border-t-slate-900{--tw-border-opacity:1;border-top-color:rgb(15 23 42/var(--tw-border-opacity,1))}.border-t-transparent{border-top-color:transparent}.bg-amber-400{--tw-bg-opacity:1;background-color:rgb(251 191 36/var(--tw-bg-opacity,1))}.bg-amber-50{--tw-bg-opacity:1;background-color:rgb(255 251 235/var(--tw-bg-opacity,1))}.bg-amber-500{--tw-bg-opacity:1;background-color:rgb(245 158 11/var(--tw-bg-opacity,1))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.bg-black\/20{background-color:rgba(0,0,0,.2)}.bg-black\/30{background-color:rgba(0,0,0,.3)}.bg-black\/40{background-color:rgba(0,0,0,.4)}.bg-black\/50{background-color:rgba(0,0,0,.5)}.bg-black\/60{background-color:rgba(0,0,0,.6)}.bg-black\/70{background-color:rgba(0,0,0,.7)}.bg-blue-100{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity,1))}.bg-blue-400{--tw-bg-opacity:1;background-color:rgb(96 165 250/var(--tw-bg-opacity,1))}.bg-blue-50{--tw-bg-opacity:1;background-color:rgb(239 246 255/var(--tw-bg-opacity,1))}.bg-blue-50\/50{background-color:rgba(239,246,255,.5)}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-blue-900{--tw-bg-opacity:1;background-color:rgb(30 58 138/var(--tw-bg-opacity,1))}.bg-cyan-100{--tw-bg-opacity:1;background-color:rgb(207 250 254/var(--tw-bg-opacity,1))}.bg-cyan-900{--tw-bg-opacity:1;background-color:rgb(22 78 99/var(--tw-bg-opacity,1))}.bg-emerald-100{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.bg-emerald-600{--tw-bg-opacity:1;background-color:rgb(5 150 105/var(--tw-bg-opacity,1))}.bg-emerald-900{--tw-bg-opacity:1;background-color:rgb(6 78 59/var(--tw-bg-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity,1))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity,1))}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.bg-green-100{--tw-bg-opacity:1;background-color:rgb(220 252 231/var(--tw-bg-opacity,1))}.bg-green-100\/90{background-color:rgba(220,252,231,.9)}.bg-green-300{--tw-bg-opacity:1;background-color:rgb(134 239 172/var(--tw-bg-opacity,1))}.bg-green-400{--tw-bg-opacity:1;background-color:rgb(74 222 128/var(--tw-bg-opacity,1))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity,1))}.bg-green-50\/50{background-color:rgba(240,253,244,.5)}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.bg-green-600{--tw-bg-opacity:1;background-color:rgb(22 163 74/var(--tw-bg-opacity,1))}.bg-green-900{--tw-bg-opacity:1;background-color:rgb(20 83 45/var(--tw-bg-opacity,1))}.bg-indigo-100{--tw-bg-opacity:1;background-color:rgb(224 231 255/var(--tw-bg-opacity,1))}.bg-indigo-50{--tw-bg-opacity:1;background-color:rgb(238 242 255/var(--tw-bg-opacity,1))}.bg-indigo-50\/50{background-color:rgba(238,242,255,.5)}.bg-indigo-500{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity,1))}.bg-indigo-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity,1))}.bg-indigo-900{--tw-bg-opacity:1;background-color:rgb(49 46 129/var(--tw-bg-opacity,1))}.bg-orange-100{--tw-bg-opacity:1;background-color:rgb(255 237 213/var(--tw-bg-opacity,1))}.bg-orange-400{--tw-bg-opacity:1;background-color:rgb(251 146 60/var(--tw-bg-opacity,1))}.bg-orange-50{--tw-bg-opacity:1;background-color:rgb(255 247 237/var(--tw-bg-opacity,1))}.bg-orange-50\/50{background-color:rgba(255,247,237,.5)}.bg-orange-500{--tw-bg-opacity:1;background-color:rgb(249 115 22/var(--tw-bg-opacity,1))}.bg-orange-600{--tw-bg-opacity:1;background-color:rgb(234 88 12/var(--tw-bg-opacity,1))}.bg-orange-900{--tw-bg-opacity:1;background-color:rgb(124 45 18/var(--tw-bg-opacity,1))}.bg-pink-100{--tw-bg-opacity:1;background-color:rgb(252 231 243/var(--tw-bg-opacity,1))}.bg-primary{--tw-bg-opacity:1;background-color:rgb(0 115 206/var(--tw-bg-opacity,1))}.bg-purple-100{--tw-bg-opacity:1;background-color:rgb(243 232 255/var(--tw-bg-opacity,1))}.bg-purple-400{--tw-bg-opacity:1;background-color:rgb(192 132 252/var(--tw-bg-opacity,1))}.bg-purple-50{--tw-bg-opacity:1;background-color:rgb(250 245 255/var(--tw-bg-opacity,1))}.bg-purple-50\/50{background-color:rgba(250,245,255,.5)}.bg-purple-500{--tw-bg-opacity:1;background-color:rgb(168 85 247/var(--tw-bg-opacity,1))}.bg-purple-600{--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity,1))}.bg-purple-900{--tw-bg-opacity:1;background-color:rgb(88 28 135/var(--tw-bg-opacity,1))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity,1))}.bg-red-100\/90{background-color:hsla(0,93%,94%,.9)}.bg-red-300{--tw-bg-opacity:1;background-color:rgb(252 165 165/var(--tw-bg-opacity,1))}.bg-red-400{--tw-bg-opacity:1;background-color:rgb(248 113 113/var(--tw-bg-opacity,1))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.bg-red-50\/50{background-color:hsla(0,86%,97%,.5)}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-red-600{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.bg-red-900{--tw-bg-opacity:1;background-color:rgb(127 29 29/var(--tw-bg-opacity,1))}.bg-slate-100{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity,1))}.bg-slate-200{--tw-bg-opacity:1;background-color:rgb(226 232 240/var(--tw-bg-opacity,1))}.bg-slate-50{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1))}.bg-slate-50\/50{background-color:rgba(248,250,252,.5)}.bg-slate-500{--tw-bg-opacity:1;background-color:rgb(100 116 139/var(--tw-bg-opacity,1))}.bg-slate-600{--tw-bg-opacity:1;background-color:rgb(71 85 105/var(--tw-bg-opacity,1))}.bg-slate-700{--tw-bg-opacity:1;background-color:rgb(51 65 85/var(--tw-bg-opacity,1))}.bg-slate-800{--tw-bg-opacity:1;background-color:rgb(30 41 59/var(--tw-bg-opacity,1))}.bg-slate-900{--tw-bg-opacity:1;background-color:rgb(15 23 42/var(--tw-bg-opacity,1))}.bg-teal-100{--tw-bg-opacity:1;background-color:rgb(204 251 241/var(--tw-bg-opacity,1))}.bg-teal-500{--tw-bg-opacity:1;background-color:rgb(20 184 166/var(--tw-bg-opacity,1))}.bg-teal-900{--tw-bg-opacity:1;background-color:rgb(19 78 74/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-white\/10{background-color:hsla(0,0%,100%,.1)}.bg-white\/15{background-color:hsla(0,0%,100%,.15)}.bg-white\/20{background-color:hsla(0,0%,100%,.2)}.bg-white\/40{background-color:hsla(0,0%,100%,.4)}.bg-white\/60{background-color:hsla(0,0%,100%,.6)}.bg-white\/80{background-color:hsla(0,0%,100%,.8)}.bg-white\/90{background-color:hsla(0,0%,100%,.9)}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgb(254 249 195/var(--tw-bg-opacity,1))}.bg-yellow-300{--tw-bg-opacity:1;background-color:rgb(253 224 71/var(--tw-bg-opacity,1))}.bg-yellow-400{--tw-bg-opacity:1;background-color:rgb(250 204 21/var(--tw-bg-opacity,1))}.bg-yellow-50{--tw-bg-opacity:1;background-color:rgb(254 252 232/var(--tw-bg-opacity,1))}.bg-yellow-500{--tw-bg-opacity:1;background-color:rgb(234 179 8/var(--tw-bg-opacity,1))}.bg-yellow-600{--tw-bg-opacity:1;background-color:rgb(202 138 4/var(--tw-bg-opacity,1))}.bg-yellow-900{--tw-bg-opacity:1;background-color:rgb(113 63 18/var(--tw-bg-opacity,1))}.bg-opacity-50{--tw-bg-opacity:0.5}.bg-opacity-75{--tw-bg-opacity:0.75}.bg-opacity-95{--tw-bg-opacity:0.95}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-tr{background-image:linear-gradient(to top right,var(--tw-gradient-stops))}.from-amber-300{--tw-gradient-from:#fcd34d var(--tw-gradient-from-position);--tw-gradient-to:rgba(252,211,77,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-100{--tw-gradient-from:#dbeafe var(--tw-gradient-from-position);--tw-gradient-to:rgba(219,234,254,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-300{--tw-gradient-from:#93c5fd var(--tw-gradient-from-position);--tw-gradient-to:rgba(147,197,253,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-300\/10{--tw-gradient-from:rgba(147,197,253,.1) var(--tw-gradient-from-position);--tw-gradient-to:rgba(147,197,253,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-400{--tw-gradient-from:#60a5fa var(--tw-gradient-from-position);--tw-gradient-to:rgba(96,165,250,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-400\/20{--tw-gradient-from:rgba(96,165,250,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(96,165,250,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-50{--tw-gradient-from:#eff6ff var(--tw-gradient-from-position);--tw-gradient-to:rgba(239,246,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-500{--tw-gradient-from:#3b82f6 var(--tw-gradient-from-position);--tw-gradient-to:rgba(59,130,246,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-500\/10{--tw-gradient-from:rgba(59,130,246,.1) var(--tw-gradient-from-position);--tw-gradient-to:rgba(59,130,246,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-600{--tw-gradient-from:#2563eb var(--tw-gradient-from-position);--tw-gradient-to:rgba(37,99,235,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-600\/10{--tw-gradient-from:rgba(37,99,235,.1) var(--tw-gradient-from-position);--tw-gradient-to:rgba(37,99,235,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-900{--tw-gradient-from:#1e3a8a var(--tw-gradient-from-position);--tw-gradient-to:rgba(30,58,138,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-emerald-400{--tw-gradient-from:#34d399 var(--tw-gradient-from-position);--tw-gradient-to:rgba(52,211,153,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-emerald-50{--tw-gradient-from:#ecfdf5 var(--tw-gradient-from-position);--tw-gradient-to:rgba(236,253,245,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-emerald-900{--tw-gradient-from:#064e3b var(--tw-gradient-from-position);--tw-gradient-to:rgba(6,78,59,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-100{--tw-gradient-from:#dcfce7 var(--tw-gradient-from-position);--tw-gradient-to:rgba(220,252,231,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-400{--tw-gradient-from:#4ade80 var(--tw-gradient-from-position);--tw-gradient-to:rgba(74,222,128,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-50{--tw-gradient-from:#f0fdf4 var(--tw-gradient-from-position);--tw-gradient-to:rgba(240,253,244,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-50\/90{--tw-gradient-from:rgba(240,253,244,.9) var(--tw-gradient-from-position);--tw-gradient-to:rgba(240,253,244,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-500{--tw-gradient-from:#22c55e var(--tw-gradient-from-position);--tw-gradient-to:rgba(34,197,94,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-500\/10{--tw-gradient-from:rgba(34,197,94,.1) var(--tw-gradient-from-position);--tw-gradient-to:rgba(34,197,94,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-900{--tw-gradient-from:#14532d var(--tw-gradient-from-position);--tw-gradient-to:rgba(20,83,45,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-indigo-500{--tw-gradient-from:#6366f1 var(--tw-gradient-from-position);--tw-gradient-to:rgba(99,102,241,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-400{--tw-gradient-from:#fb923c var(--tw-gradient-from-position);--tw-gradient-to:rgba(251,146,60,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-50{--tw-gradient-from:#fff7ed var(--tw-gradient-from-position);--tw-gradient-to:rgba(255,247,237,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-500{--tw-gradient-from:#f97316 var(--tw-gradient-from-position);--tw-gradient-to:rgba(249,115,22,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-500\/10{--tw-gradient-from:rgba(249,115,22,.1) var(--tw-gradient-from-position);--tw-gradient-to:rgba(249,115,22,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-900{--tw-gradient-from:#7c2d12 var(--tw-gradient-from-position);--tw-gradient-to:rgba(124,45,18,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-100{--tw-gradient-from:#f3e8ff var(--tw-gradient-from-position);--tw-gradient-to:rgba(243,232,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-400{--tw-gradient-from:#c084fc var(--tw-gradient-from-position);--tw-gradient-to:rgba(192,132,252,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-400\/20{--tw-gradient-from:rgba(192,132,252,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(192,132,252,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-50{--tw-gradient-from:#faf5ff var(--tw-gradient-from-position);--tw-gradient-to:rgba(250,245,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-500{--tw-gradient-from:#a855f7 var(--tw-gradient-from-position);--tw-gradient-to:rgba(168,85,247,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-500\/10{--tw-gradient-from:rgba(168,85,247,.1) var(--tw-gradient-from-position);--tw-gradient-to:rgba(168,85,247,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-600{--tw-gradient-from:#9333ea var(--tw-gradient-from-position);--tw-gradient-to:rgba(147,51,234,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-900{--tw-gradient-from:#581c87 var(--tw-gradient-from-position);--tw-gradient-to:rgba(88,28,135,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-red-400{--tw-gradient-from:#f87171 var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,91%,71%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-red-500{--tw-gradient-from:#ef4444 var(--tw-gradient-from-position);--tw-gradient-to:rgba(239,68,68,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-red-500\/10{--tw-gradient-from:rgba(239,68,68,.1) var(--tw-gradient-from-position);--tw-gradient-to:rgba(239,68,68,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-slate-50{--tw-gradient-from:#f8fafc var(--tw-gradient-from-position);--tw-gradient-to:rgba(248,250,252,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-slate-500{--tw-gradient-from:#64748b var(--tw-gradient-from-position);--tw-gradient-to:rgba(100,116,139,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-slate-800{--tw-gradient-from:#1e293b var(--tw-gradient-from-position);--tw-gradient-to:rgba(30,41,59,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-slate-900{--tw-gradient-from:#0f172a var(--tw-gradient-from-position);--tw-gradient-to:rgba(15,23,42,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-transparent{--tw-gradient-from:transparent var(--tw-gradient-from-position);--tw-gradient-to:transparent var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-white{--tw-gradient-from:#fff var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,0%,100%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-white\/90{--tw-gradient-from:hsla(0,0%,100%,.9) var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,0%,100%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-yellow-500{--tw-gradient-from:#eab308 var(--tw-gradient-from-position);--tw-gradient-to:rgba(234,179,8,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.via-blue-100{--tw-gradient-to:rgba(219,234,254,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#dbeafe var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-blue-200{--tw-gradient-to:rgba(191,219,254,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#bfdbfe var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-blue-50{--tw-gradient-to:rgba(239,246,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#eff6ff var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-blue-900{--tw-gradient-to:rgba(30,58,138,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1e3a8a var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-emerald-900{--tw-gradient-to:rgba(6,78,59,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#064e3b var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-green-50{--tw-gradient-to:rgba(240,253,244,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#f0fdf4 var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-green-500{--tw-gradient-to:rgba(34,197,94,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#22c55e var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-indigo-50{--tw-gradient-to:rgba(238,242,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#eef2ff var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-purple-500{--tw-gradient-to:rgba(168,85,247,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#a855f7 var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-red-50{--tw-gradient-to:hsla(0,86%,97%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#fef2f2 var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-red-900{--tw-gradient-to:rgba(127,29,29,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#7f1d1d var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-white{--tw-gradient-to:hsla(0,0%,100%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#fff var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-white\/20{--tw-gradient-to:hsla(0,0%,100%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),hsla(0,0%,100%,.2) var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-white\/5{--tw-gradient-to:hsla(0,0%,100%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),hsla(0,0%,100%,.05) var(--tw-gradient-via-position),var(--tw-gradient-to)}.to-blue-200{--tw-gradient-to:#bfdbfe var(--tw-gradient-to-position)}.to-blue-600{--tw-gradient-to:#2563eb var(--tw-gradient-to-position)}.to-blue-700{--tw-gradient-to:#1d4ed8 var(--tw-gradient-to-position)}.to-blue-800{--tw-gradient-to:#1e40af var(--tw-gradient-to-position)}.to-cyan-50{--tw-gradient-to:#ecfeff var(--tw-gradient-to-position)}.to-cyan-900{--tw-gradient-to:#164e63 var(--tw-gradient-to-position)}.to-emerald-400{--tw-gradient-to:#34d399 var(--tw-gradient-to-position)}.to-emerald-50{--tw-gradient-to:#ecfdf5 var(--tw-gradient-to-position)}.to-emerald-50\/80{--tw-gradient-to:rgba(236,253,245,.8) var(--tw-gradient-to-position)}.to-emerald-500{--tw-gradient-to:#10b981 var(--tw-gradient-to-position)}.to-emerald-500\/10{--tw-gradient-to:rgba(16,185,129,.1) var(--tw-gradient-to-position)}.to-emerald-600{--tw-gradient-to:#059669 var(--tw-gradient-to-position)}.to-emerald-900{--tw-gradient-to:#064e3b var(--tw-gradient-to-position)}.to-green-200{--tw-gradient-to:#bbf7d0 var(--tw-gradient-to-position)}.to-green-50{--tw-gradient-to:#f0fdf4 var(--tw-gradient-to-position)}.to-green-600{--tw-gradient-to:#16a34a var(--tw-gradient-to-position)}.to-green-800{--tw-gradient-to:#166534 var(--tw-gradient-to-position)}.to-green-900{--tw-gradient-to:#14532d var(--tw-gradient-to-position)}.to-indigo-300{--tw-gradient-to:#a5b4fc var(--tw-gradient-to-position)}.to-indigo-300\/10{--tw-gradient-to:rgba(165,180,252,.1) var(--tw-gradient-to-position)}.to-indigo-400{--tw-gradient-to:#818cf8 var(--tw-gradient-to-position)}.to-indigo-50{--tw-gradient-to:#eef2ff var(--tw-gradient-to-position)}.to-indigo-500{--tw-gradient-to:#6366f1 var(--tw-gradient-to-position)}.to-indigo-500\/10{--tw-gradient-to:rgba(99,102,241,.1) var(--tw-gradient-to-position)}.to-indigo-600{--tw-gradient-to:#4f46e5 var(--tw-gradient-to-position)}.to-indigo-600\/20{--tw-gradient-to:rgba(79,70,229,.2) var(--tw-gradient-to-position)}.to-indigo-900{--tw-gradient-to:#312e81 var(--tw-gradient-to-position)}.to-orange-400{--tw-gradient-to:#fb923c var(--tw-gradient-to-position)}.to-orange-50{--tw-gradient-to:#fff7ed var(--tw-gradient-to-position)}.to-orange-500{--tw-gradient-to:#f97316 var(--tw-gradient-to-position)}.to-orange-600{--tw-gradient-to:#ea580c var(--tw-gradient-to-position)}.to-orange-900{--tw-gradient-to:#7c2d12 var(--tw-gradient-to-position)}.to-pink-400{--tw-gradient-to:#f472b6 var(--tw-gradient-to-position)}.to-pink-50{--tw-gradient-to:#fdf2f8 var(--tw-gradient-to-position)}.to-pink-500{--tw-gradient-to:#ec4899 var(--tw-gradient-to-position)}.to-pink-500\/10{--tw-gradient-to:rgba(236,72,153,.1) var(--tw-gradient-to-position)}.to-pink-600{--tw-gradient-to:#db2777 var(--tw-gradient-to-position)}.to-pink-600\/20{--tw-gradient-to:rgba(219,39,119,.2) var(--tw-gradient-to-position)}.to-pink-900{--tw-gradient-to:#831843 var(--tw-gradient-to-position)}.to-purple-200{--tw-gradient-to:#e9d5ff var(--tw-gradient-to-position)}.to-purple-50{--tw-gradient-to:#faf5ff var(--tw-gradient-to-position)}.to-purple-500{--tw-gradient-to:#a855f7 var(--tw-gradient-to-position)}.to-purple-600{--tw-gradient-to:#9333ea var(--tw-gradient-to-position)}.to-purple-600\/10{--tw-gradient-to:rgba(147,51,234,.1) var(--tw-gradient-to-position)}.to-purple-700{--tw-gradient-to:#7e22ce var(--tw-gradient-to-position)}.to-purple-800{--tw-gradient-to:#6b21a8 var(--tw-gradient-to-position)}.to-red-400{--tw-gradient-to:#f87171 var(--tw-gradient-to-position)}.to-red-50{--tw-gradient-to:#fef2f2 var(--tw-gradient-to-position)}.to-red-500{--tw-gradient-to:#ef4444 var(--tw-gradient-to-position)}.to-red-500\/10{--tw-gradient-to:rgba(239,68,68,.1) var(--tw-gradient-to-position)}.to-red-600{--tw-gradient-to:#dc2626 var(--tw-gradient-to-position)}.to-red-900{--tw-gradient-to:#7f1d1d var(--tw-gradient-to-position)}.to-rose-500{--tw-gradient-to:#f43f5e var(--tw-gradient-to-position)}.to-slate-100{--tw-gradient-to:#f1f5f9 var(--tw-gradient-to-position)}.to-slate-600{--tw-gradient-to:#475569 var(--tw-gradient-to-position)}.to-slate-700{--tw-gradient-to:#334155 var(--tw-gradient-to-position)}.to-slate-900{--tw-gradient-to:#0f172a var(--tw-gradient-to-position)}.to-teal-50{--tw-gradient-to:#f0fdfa var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.to-violet-500{--tw-gradient-to:#8b5cf6 var(--tw-gradient-to-position)}.to-violet-500\/10{--tw-gradient-to:rgba(139,92,246,.1) var(--tw-gradient-to-position)}.to-white{--tw-gradient-to:#fff var(--tw-gradient-to-position)}.to-white\/70{--tw-gradient-to:hsla(0,0%,100%,.7) var(--tw-gradient-to-position)}.to-yellow-600{--tw-gradient-to:#ca8a04 var(--tw-gradient-to-position)}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.object-cover{-o-object-fit:cover;object-fit:cover}.p-0{padding:0}.p-1{padding:.25rem}.p-1\.5{padding:.375rem}.p-10{padding:2.5rem}.p-12{padding:3rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-top:0;padding-bottom:0}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-20{padding-top:5rem;padding-bottom:5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pb-20{padding-bottom:5rem}.pb-4{padding-bottom:1rem}.pb-6{padding-bottom:1.5rem}.pb-8{padding-bottom:2rem}.pl-10{padding-left:2.5rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pr-10{padding-right:2.5rem}.pr-12{padding-right:3rem}.pr-20{padding-right:5rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.pt-6{padding-top:1.5rem}.pt-8{padding-top:2rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-middle{vertical-align:middle}.align-bottom{vertical-align:bottom}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.text-6xl{font-size:3.75rem;line-height:1}.text-8xl{font-size:6rem;line-height:1}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-4{line-height:1rem}.leading-5{line-height:1.25rem}.leading-6{line-height:1.5rem}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-amber-500{--tw-text-opacity:1;color:rgb(245 158 11/var(--tw-text-opacity,1))}.text-amber-600{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity,1))}.text-amber-800{--tw-text-opacity:1;color:rgb(146 64 14/var(--tw-text-opacity,1))}.text-amber-900{--tw-text-opacity:1;color:rgb(120 53 15/var(--tw-text-opacity,1))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.text-blue-100{--tw-text-opacity:1;color:rgb(219 234 254/var(--tw-text-opacity,1))}.text-blue-200{--tw-text-opacity:1;color:rgb(191 219 254/var(--tw-text-opacity,1))}.text-blue-300{--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity,1))}.text-blue-400{--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-blue-700{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}.text-blue-800{--tw-text-opacity:1;color:rgb(30 64 175/var(--tw-text-opacity,1))}.text-blue-900{--tw-text-opacity:1;color:rgb(30 58 138/var(--tw-text-opacity,1))}.text-current{color:currentColor}.text-cyan-600{--tw-text-opacity:1;color:rgb(8 145 178/var(--tw-text-opacity,1))}.text-emerald-300{--tw-text-opacity:1;color:rgb(110 231 183/var(--tw-text-opacity,1))}.text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.text-emerald-800{--tw-text-opacity:1;color:rgb(6 95 70/var(--tw-text-opacity,1))}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity,1))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.text-green-300{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.text-green-400{--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity,1))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity,1))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity,1))}.text-green-700{--tw-text-opacity:1;color:rgb(21 128 61/var(--tw-text-opacity,1))}.text-green-800{--tw-text-opacity:1;color:rgb(22 101 52/var(--tw-text-opacity,1))}.text-green-900{--tw-text-opacity:1;color:rgb(20 83 45/var(--tw-text-opacity,1))}.text-indigo-400{--tw-text-opacity:1;color:rgb(129 140 248/var(--tw-text-opacity,1))}.text-indigo-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity,1))}.text-indigo-800{--tw-text-opacity:1;color:rgb(55 48 163/var(--tw-text-opacity,1))}.text-indigo-900{--tw-text-opacity:1;color:rgb(49 46 129/var(--tw-text-opacity,1))}.text-muted{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-orange-500{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity,1))}.text-orange-600{--tw-text-opacity:1;color:rgb(234 88 12/var(--tw-text-opacity,1))}.text-orange-700{--tw-text-opacity:1;color:rgb(194 65 12/var(--tw-text-opacity,1))}.text-orange-800{--tw-text-opacity:1;color:rgb(154 52 18/var(--tw-text-opacity,1))}.text-pink-800{--tw-text-opacity:1;color:rgb(157 23 77/var(--tw-text-opacity,1))}.text-purple-500{--tw-text-opacity:1;color:rgb(168 85 247/var(--tw-text-opacity,1))}.text-purple-600{--tw-text-opacity:1;color:rgb(147 51 234/var(--tw-text-opacity,1))}.text-purple-800{--tw-text-opacity:1;color:rgb(107 33 168/var(--tw-text-opacity,1))}.text-purple-900{--tw-text-opacity:1;color:rgb(88 28 135/var(--tw-text-opacity,1))}.text-red-200{--tw-text-opacity:1;color:rgb(254 202 202/var(--tw-text-opacity,1))}.text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}.text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity,1))}.text-red-900{--tw-text-opacity:1;color:rgb(127 29 29/var(--tw-text-opacity,1))}.text-slate-300{--tw-text-opacity:1;color:rgb(203 213 225/var(--tw-text-opacity,1))}.text-slate-400{--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}.text-slate-500{--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity,1))}.text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}.text-slate-700{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity,1))}.text-slate-800{--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity,1))}.text-slate-900{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity,1))}.text-teal-600{--tw-text-opacity:1;color:rgb(13 148 136/var(--tw-text-opacity,1))}.text-transparent{color:transparent}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.text-yellow-200{--tw-text-opacity:1;color:rgb(254 240 138/var(--tw-text-opacity,1))}.text-yellow-300{--tw-text-opacity:1;color:rgb(253 224 71/var(--tw-text-opacity,1))}.text-yellow-400{--tw-text-opacity:1;color:rgb(250 204 21/var(--tw-text-opacity,1))}.text-yellow-500{--tw-text-opacity:1;color:rgb(234 179 8/var(--tw-text-opacity,1))}.text-yellow-600{--tw-text-opacity:1;color:rgb(202 138 4/var(--tw-text-opacity,1))}.text-yellow-700{--tw-text-opacity:1;color:rgb(161 98 7/var(--tw-text-opacity,1))}.text-yellow-800{--tw-text-opacity:1;color:rgb(133 77 14/var(--tw-text-opacity,1))}.text-yellow-900{--tw-text-opacity:1;color:rgb(113 63 18/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.overline{text-decoration-line:overline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.placeholder-gray-400::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity,1))}.placeholder-gray-400::placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity,1))}.placeholder-slate-500::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(100 116 139/var(--tw-placeholder-opacity,1))}.placeholder-slate-500::placeholder{--tw-placeholder-opacity:1;color:rgb(100 116 139/var(--tw-placeholder-opacity,1))}.opacity-0{opacity:0}.opacity-10{opacity:.1}.opacity-100{opacity:1}.opacity-15{opacity:.15}.opacity-25{opacity:.25}.opacity-30{opacity:.3}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.opacity-90{opacity:.9}.shadow{--tw-shadow:0 1px 3px 0 rgba(0,0,0,.1),0 1px 2px -1px rgba(0,0,0,.1);--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-2xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-md{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-sm,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color)}.shadow-blue-500{--tw-shadow-color:#3b82f6;--tw-shadow:var(--tw-shadow-colored)}.shadow-blue-500\/25{--tw-shadow-color:rgba(59,130,246,.25);--tw-shadow:var(--tw-shadow-colored)}.shadow-green-500{--tw-shadow-color:#22c55e;--tw-shadow:var(--tw-shadow-colored)}.shadow-green-500\/25{--tw-shadow-color:rgba(34,197,94,.25);--tw-shadow:var(--tw-shadow-colored)}.shadow-red-500{--tw-shadow-color:#ef4444;--tw-shadow:var(--tw-shadow-colored)}.shadow-red-500\/25{--tw-shadow-color:rgba(239,68,68,.25);--tw-shadow:var(--tw-shadow-colored)}.shadow-slate-300{--tw-shadow-color:#cbd5e1;--tw-shadow:var(--tw-shadow-colored)}.shadow-slate-900{--tw-shadow-color:#0f172a;--tw-shadow:var(--tw-shadow-colored)}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring,.ring-1{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-black{--tw-ring-opacity:1;--tw-ring-color:rgb(0 0 0/var(--tw-ring-opacity,1))}.ring-blue-400{--tw-ring-opacity:1;--tw-ring-color:rgb(96 165 250/var(--tw-ring-opacity,1))}.ring-blue-500{--tw-ring-opacity:1;--tw-ring-color:rgb(59 130 246/var(--tw-ring-opacity,1))}.ring-green-500{--tw-ring-opacity:1;--tw-ring-color:rgb(34 197 94/var(--tw-ring-opacity,1))}.ring-red-500{--tw-ring-opacity:1;--tw-ring-color:rgb(239 68 68/var(--tw-ring-opacity,1))}.ring-slate-500{--tw-ring-opacity:1;--tw-ring-color:rgb(100 116 139/var(--tw-ring-opacity,1))}.ring-opacity-5{--tw-ring-opacity:0.05}.blur{--tw-blur:blur(8px)}.blur,.blur-2xl{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-2xl{--tw-blur:blur(40px)}.blur-3xl{--tw-blur:blur(64px)}.blur-3xl,.blur-sm{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-sm{--tw-blur:blur(4px)}.drop-shadow{--tw-drop-shadow:drop-shadow(0 1px 2px rgba(0,0,0,.1)) drop-shadow(0 1px 1px rgba(0,0,0,.06))}.drop-shadow,.drop-shadow-sm{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.drop-shadow-sm{--tw-drop-shadow:drop-shadow(0 1px 1px rgba(0,0,0,.05))}.invert{--tw-invert:invert(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.\!filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-2xl{--tw-backdrop-blur:blur(40px)}.backdrop-blur-2xl,.backdrop-blur-md{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.backdrop-blur-sm,.backdrop-blur-xl{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-xl{--tw-backdrop-blur:blur(24px)}.backdrop-filter{-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-shadow{transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.delay-1000{transition-delay:1s}.delay-500{transition-delay:.5s}.duration-1000{transition-duration:1s}.duration-150{transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.content-\[\'\'\]{--tw-content:"";content:var(--tw-content)}.hover\:-translate-y-0:hover{--tw-translate-y:-0px}.hover\:-translate-y-0:hover,.hover\:-translate-y-0\.5:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:-translate-y-0\.5:hover{--tw-translate-y:-0.125rem}.hover\:-translate-y-1:hover{--tw-translate-y:-0.25rem}.hover\:-translate-y-1:hover,.hover\:-translate-y-2:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:-translate-y-2:hover{--tw-translate-y:-0.5rem}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05}.hover\:scale-105:hover,.hover\:scale-110:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-110:hover{--tw-scale-x:1.1;--tw-scale-y:1.1}.hover\:border-blue-300:hover{--tw-border-opacity:1;border-color:rgb(147 197 253/var(--tw-border-opacity,1))}.hover\:border-blue-600:hover{--tw-border-opacity:1;border-color:rgb(37 99 235/var(--tw-border-opacity,1))}.hover\:border-emerald-600:hover{--tw-border-opacity:1;border-color:rgb(5 150 105/var(--tw-border-opacity,1))}.hover\:border-slate-300:hover{--tw-border-opacity:1;border-color:rgb(203 213 225/var(--tw-border-opacity,1))}.hover\:bg-amber-100:hover{--tw-bg-opacity:1;background-color:rgb(254 243 199/var(--tw-bg-opacity,1))}.hover\:bg-black\/5:hover{background-color:rgba(0,0,0,.05)}.hover\:bg-black\/70:hover{background-color:rgba(0,0,0,.7)}.hover\:bg-blue-100:hover{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity,1))}.hover\:bg-blue-200:hover{--tw-bg-opacity:1;background-color:rgb(191 219 254/var(--tw-bg-opacity,1))}.hover\:bg-blue-600:hover{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.hover\:bg-blue-700:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.hover\:bg-emerald-700:hover{--tw-bg-opacity:1;background-color:rgb(4 120 87/var(--tw-bg-opacity,1))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.hover\:bg-gray-100\/80:hover{background-color:rgba(243,244,246,.8)}.hover\:bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.hover\:bg-gray-400:hover{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity,1))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.hover\:bg-gray-600:hover{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.hover\:bg-gray-800:hover{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.hover\:bg-green-100:hover{--tw-bg-opacity:1;background-color:rgb(220 252 231/var(--tw-bg-opacity,1))}.hover\:bg-green-600:hover{--tw-bg-opacity:1;background-color:rgb(22 163 74/var(--tw-bg-opacity,1))}.hover\:bg-green-700:hover{--tw-bg-opacity:1;background-color:rgb(21 128 61/var(--tw-bg-opacity,1))}.hover\:bg-indigo-600:hover{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity,1))}.hover\:bg-indigo-700:hover{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity,1))}.hover\:bg-orange-600:hover{--tw-bg-opacity:1;background-color:rgb(234 88 12/var(--tw-bg-opacity,1))}.hover\:bg-orange-700:hover{--tw-bg-opacity:1;background-color:rgb(194 65 12/var(--tw-bg-opacity,1))}.hover\:bg-purple-600:hover{--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity,1))}.hover\:bg-purple-700:hover{--tw-bg-opacity:1;background-color:rgb(126 34 206/var(--tw-bg-opacity,1))}.hover\:bg-red-100:hover{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity,1))}.hover\:bg-red-50:hover{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.hover\:bg-red-700:hover{--tw-bg-opacity:1;background-color:rgb(185 28 28/var(--tw-bg-opacity,1))}.hover\:bg-slate-100:hover{--tw-bg-opacity:1;background-color:rgb(241 245 249/var(--tw-bg-opacity,1))}.hover\:bg-slate-100\/50:hover{background-color:rgba(241,245,249,.5)}.hover\:bg-slate-100\/80:hover{background-color:rgba(241,245,249,.8)}.hover\:bg-slate-200:hover{--tw-bg-opacity:1;background-color:rgb(226 232 240/var(--tw-bg-opacity,1))}.hover\:bg-slate-300:hover{--tw-bg-opacity:1;background-color:rgb(203 213 225/var(--tw-bg-opacity,1))}.hover\:bg-slate-50:hover{--tw-bg-opacity:1;background-color:rgb(248 250 252/var(--tw-bg-opacity,1))}.hover\:bg-slate-600:hover{--tw-bg-opacity:1;background-color:rgb(71 85 105/var(--tw-bg-opacity,1))}.hover\:bg-slate-700:hover{--tw-bg-opacity:1;background-color:rgb(51 65 85/var(--tw-bg-opacity,1))}.hover\:bg-teal-600:hover{--tw-bg-opacity:1;background-color:rgb(13 148 136/var(--tw-bg-opacity,1))}.hover\:bg-white:hover{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.hover\:bg-white\/20:hover{background-color:hsla(0,0%,100%,.2)}.hover\:bg-white\/25:hover{background-color:hsla(0,0%,100%,.25)}.hover\:bg-yellow-600:hover{--tw-bg-opacity:1;background-color:rgb(202 138 4/var(--tw-bg-opacity,1))}.hover\:bg-yellow-700:hover{--tw-bg-opacity:1;background-color:rgb(161 98 7/var(--tw-bg-opacity,1))}.hover\:from-blue-600:hover{--tw-gradient-from:#2563eb var(--tw-gradient-from-position);--tw-gradient-to:rgba(37,99,235,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:from-blue-700:hover{--tw-gradient-from:#1d4ed8 var(--tw-gradient-from-position);--tw-gradient-to:rgba(29,78,216,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:from-green-600:hover{--tw-gradient-from:#16a34a var(--tw-gradient-from-position);--tw-gradient-to:rgba(22,163,74,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:from-orange-600:hover{--tw-gradient-from:#ea580c var(--tw-gradient-from-position);--tw-gradient-to:rgba(234,88,12,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:from-slate-600:hover{--tw-gradient-from:#475569 var(--tw-gradient-from-position);--tw-gradient-to:rgba(71,85,105,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:to-blue-700:hover{--tw-gradient-to:#1d4ed8 var(--tw-gradient-to-position)}.hover\:to-blue-800:hover{--tw-gradient-to:#1e40af var(--tw-gradient-to-position)}.hover\:to-green-700:hover{--tw-gradient-to:#15803d var(--tw-gradient-to-position)}.hover\:to-red-600:hover{--tw-gradient-to:#dc2626 var(--tw-gradient-to-position)}.hover\:to-slate-700:hover{--tw-gradient-to:#334155 var(--tw-gradient-to-position)}.hover\:text-blue-500:hover{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.hover\:text-blue-600:hover{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.hover\:text-blue-700:hover{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}.hover\:text-blue-800:hover{--tw-text-opacity:1;color:rgb(30 64 175/var(--tw-text-opacity,1))}.hover\:text-blue-900:hover{--tw-text-opacity:1;color:rgb(30 58 138/var(--tw-text-opacity,1))}.hover\:text-emerald-600:hover{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity,1))}.hover\:text-gray-200:hover{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.hover\:text-gray-800:hover{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity,1))}.hover\:text-gray-900:hover{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.hover\:text-green-900:hover{--tw-text-opacity:1;color:rgb(20 83 45/var(--tw-text-opacity,1))}.hover\:text-red-500:hover{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.hover\:text-red-700:hover{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}.hover\:text-red-900:hover{--tw-text-opacity:1;color:rgb(127 29 29/var(--tw-text-opacity,1))}.hover\:text-slate-600:hover{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}.hover\:text-slate-700:hover{--tw-text-opacity:1;color:rgb(51 65 85/var(--tw-text-opacity,1))}.hover\:text-slate-800:hover{--tw-text-opacity:1;color:rgb(30 41 59/var(--tw-text-opacity,1))}.hover\:text-slate-900:hover{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-2xl:hover{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.hover\:shadow-2xl:hover,.hover\:shadow-lg:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.hover\:shadow-md:hover{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.hover\:shadow-md:hover,.hover\:shadow-xl:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.hover\:shadow-xl:hover{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color)}.hover\:shadow-slate-300\/50:hover{--tw-shadow-color:rgba(203,213,225,.5);--tw-shadow:var(--tw-shadow-colored)}.focus\:z-10:focus{z-index:10}.focus\:border-blue-500:focus{--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity,1))}.focus\:border-blue-600:focus{--tw-border-opacity:1;border-color:rgb(37 99 235/var(--tw-border-opacity,1))}.focus\:border-red-500:focus{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity,1))}.focus\:border-transparent:focus{border-color:transparent}.focus\:bg-gray-100\/80:focus{background-color:rgba(243,244,246,.8)}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-1:focus,.focus\:ring-2:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-4:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(59 130 246/var(--tw-ring-opacity,1))}.focus\:ring-blue-500\/20:focus{--tw-ring-color:rgba(59,130,246,.2)}.focus\:ring-blue-500\/50:focus{--tw-ring-color:rgba(59,130,246,.5)}.focus\:ring-blue-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(37 99 235/var(--tw-ring-opacity,1))}.focus\:ring-gray-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(107 114 128/var(--tw-ring-opacity,1))}.focus\:ring-green-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(74 222 128/var(--tw-ring-opacity,1))}.focus\:ring-green-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(34 197 94/var(--tw-ring-opacity,1))}.focus\:ring-green-500\/50:focus{--tw-ring-color:rgba(34,197,94,.5)}.focus\:ring-red-400:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(248 113 113/var(--tw-ring-opacity,1))}.focus\:ring-red-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(239 68 68/var(--tw-ring-opacity,1))}.focus\:ring-red-500\/50:focus{--tw-ring-color:rgba(239,68,68,.5)}.focus\:ring-slate-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(100 116 139/var(--tw-ring-opacity,1))}.focus\:ring-slate-500\/50:focus{--tw-ring-color:rgba(100,116,139,.5)}.focus\:ring-yellow-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(234 179 8/var(--tw-ring-opacity,1))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.active\:scale-95:active{--tw-scale-x:.95;--tw-scale-y:.95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-gray-100:disabled{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.disabled\:opacity-50:disabled{opacity:.5}.group:focus-within .group-focus-within\:text-blue-500{--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.group:hover .group-hover\:-translate-x-1{--tw-translate-x:-0.25rem}.group:hover .group-hover\:-translate-x-1,.group:hover .group-hover\:translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:translate-x-full{--tw-translate-x:100%}.group:hover .group-hover\:rotate-180{--tw-rotate:180deg}.group:hover .group-hover\:rotate-180,.group:hover .group-hover\:scale-105{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05}.group:hover .group-hover\:scale-110{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:text-slate-600{--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}.group:hover .group-hover\:text-slate-900{--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity,1))}.group:hover .group-hover\:opacity-100{opacity:1}.group:active .group-active\:scale-95{--tw-scale-x:.95;--tw-scale-y:.95;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:inline:is(.dark *){display:inline}.dark\:hidden:is(.dark *){display:none}.dark\:rotate-0:is(.dark *){--tw-rotate:0deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:rotate-90:is(.dark *){--tw-rotate:90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:scale-100:is(.dark *){--tw-scale-x:1;--tw-scale-y:1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:scale-75:is(.dark *){--tw-scale-x:.75;--tw-scale-y:.75;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:divide-gray-700:is(.dark *)>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(55 65 81/var(--tw-divide-opacity,1))}.dark\:divide-slate-700:is(.dark *)>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(51 65 85/var(--tw-divide-opacity,1))}.dark\:border-blue-400:is(.dark *){--tw-border-opacity:1;border-color:rgb(96 165 250/var(--tw-border-opacity,1))}.dark\:border-blue-500:is(.dark *){--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity,1))}.dark\:border-blue-600:is(.dark *){--tw-border-opacity:1;border-color:rgb(37 99 235/var(--tw-border-opacity,1))}.dark\:border-blue-700:is(.dark *){--tw-border-opacity:1;border-color:rgb(29 78 216/var(--tw-border-opacity,1))}.dark\:border-blue-700\/30:is(.dark *){border-color:rgba(29,78,216,.3)}.dark\:border-blue-800:is(.dark *){--tw-border-opacity:1;border-color:rgb(30 64 175/var(--tw-border-opacity,1))}.dark\:border-blue-800\/50:is(.dark *){border-color:rgba(30,64,175,.5)}.dark\:border-emerald-700\/30:is(.dark *){border-color:rgba(4,120,87,.3)}.dark\:border-gray-500:is(.dark *){--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity,1))}.dark\:border-gray-600:is(.dark *){--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity,1))}.dark\:border-gray-700:is(.dark *){--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity,1))}.dark\:border-gray-800:is(.dark *){--tw-border-opacity:1;border-color:rgb(31 41 55/var(--tw-border-opacity,1))}.dark\:border-green-600:is(.dark *){--tw-border-opacity:1;border-color:rgb(22 163 74/var(--tw-border-opacity,1))}.dark\:border-green-700:is(.dark *){--tw-border-opacity:1;border-color:rgb(21 128 61/var(--tw-border-opacity,1))}.dark\:border-green-800:is(.dark *){--tw-border-opacity:1;border-color:rgb(22 101 52/var(--tw-border-opacity,1))}.dark\:border-green-800\/50:is(.dark *){border-color:rgba(22,101,52,.5)}.dark\:border-indigo-800:is(.dark *){--tw-border-opacity:1;border-color:rgb(55 48 163/var(--tw-border-opacity,1))}.dark\:border-indigo-800\/50:is(.dark *){border-color:rgba(55,48,163,.5)}.dark\:border-orange-700:is(.dark *){--tw-border-opacity:1;border-color:rgb(194 65 12/var(--tw-border-opacity,1))}.dark\:border-orange-800:is(.dark *){--tw-border-opacity:1;border-color:rgb(154 52 18/var(--tw-border-opacity,1))}.dark\:border-orange-800\/50:is(.dark *){border-color:rgba(154,52,18,.5)}.dark\:border-purple-500:is(.dark *){--tw-border-opacity:1;border-color:rgb(168 85 247/var(--tw-border-opacity,1))}.dark\:border-purple-700:is(.dark *){--tw-border-opacity:1;border-color:rgb(126 34 206/var(--tw-border-opacity,1))}.dark\:border-purple-800\/50:is(.dark *){border-color:rgba(107,33,168,.5)}.dark\:border-red-500:is(.dark *){--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity,1))}.dark\:border-red-600:is(.dark *){--tw-border-opacity:1;border-color:rgb(220 38 38/var(--tw-border-opacity,1))}.dark\:border-red-700:is(.dark *){--tw-border-opacity:1;border-color:rgb(185 28 28/var(--tw-border-opacity,1))}.dark\:border-red-800:is(.dark *){--tw-border-opacity:1;border-color:rgb(153 27 27/var(--tw-border-opacity,1))}.dark\:border-red-800\/50:is(.dark *){border-color:rgba(153,27,27,.5)}.dark\:border-slate-600:is(.dark *){--tw-border-opacity:1;border-color:rgb(71 85 105/var(--tw-border-opacity,1))}.dark\:border-slate-600\/50:is(.dark *){border-color:rgba(71,85,105,.5)}.dark\:border-slate-600\/60:is(.dark *){border-color:rgba(71,85,105,.6)}.dark\:border-slate-700:is(.dark *){--tw-border-opacity:1;border-color:rgb(51 65 85/var(--tw-border-opacity,1))}.dark\:border-slate-700\/20:is(.dark *){border-color:rgba(51,65,85,.2)}.dark\:border-slate-700\/30:is(.dark *){border-color:rgba(51,65,85,.3)}.dark\:border-slate-700\/50:is(.dark *){border-color:rgba(51,65,85,.5)}.dark\:border-white:is(.dark *){--tw-border-opacity:1;border-color:rgb(255 255 255/var(--tw-border-opacity,1))}.dark\:border-white\/20:is(.dark *){border-color:hsla(0,0%,100%,.2)}.dark\:border-white\/70:is(.dark *){border-color:hsla(0,0%,100%,.7)}.dark\:border-yellow-500:is(.dark *){--tw-border-opacity:1;border-color:rgb(234 179 8/var(--tw-border-opacity,1))}.dark\:border-yellow-600:is(.dark *){--tw-border-opacity:1;border-color:rgb(202 138 4/var(--tw-border-opacity,1))}.dark\:border-yellow-700:is(.dark *){--tw-border-opacity:1;border-color:rgb(161 98 7/var(--tw-border-opacity,1))}.dark\:border-yellow-800:is(.dark *){--tw-border-opacity:1;border-color:rgb(133 77 14/var(--tw-border-opacity,1))}.dark\:border-t-slate-700:is(.dark *){--tw-border-opacity:1;border-top-color:rgb(51 65 85/var(--tw-border-opacity,1))}.dark\:bg-amber-600:is(.dark *){--tw-bg-opacity:1;background-color:rgb(217 119 6/var(--tw-bg-opacity,1))}.dark\:bg-black:is(.dark *){--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.dark\:bg-black\/50:is(.dark *){background-color:rgba(0,0,0,.5)}.dark\:bg-black\/70:is(.dark *){background-color:rgba(0,0,0,.7)}.dark\:bg-black\/80:is(.dark *){background-color:rgba(0,0,0,.8)}.dark\:bg-blue-300:is(.dark *){--tw-bg-opacity:1;background-color:rgb(147 197 253/var(--tw-bg-opacity,1))}.dark\:bg-blue-400:is(.dark *){--tw-bg-opacity:1;background-color:rgb(96 165 250/var(--tw-bg-opacity,1))}.dark\:bg-blue-500:is(.dark *){--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1))}.dark\:bg-blue-600:is(.dark *){--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.dark\:bg-blue-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(30 58 138/var(--tw-bg-opacity,1))}.dark\:bg-blue-900\/10:is(.dark *){background-color:rgba(30,58,138,.1)}.dark\:bg-blue-900\/20:is(.dark *){background-color:rgba(30,58,138,.2)}.dark\:bg-blue-900\/30:is(.dark *){background-color:rgba(30,58,138,.3)}.dark\:bg-blue-900\/50:is(.dark *){background-color:rgba(30,58,138,.5)}.dark\:bg-cyan-900\/50:is(.dark *){background-color:rgba(22,78,99,.5)}.dark\:bg-emerald-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(6 78 59/var(--tw-bg-opacity,1))}.dark\:bg-emerald-900\/50:is(.dark *){background-color:rgba(6,78,59,.5)}.dark\:bg-gray-300:is(.dark *){--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.dark\:bg-gray-600:is(.dark *){--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.dark\:bg-gray-700:is(.dark *){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.dark\:bg-gray-800:is(.dark *){--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.dark\:bg-gray-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.dark\:bg-gray-900\/20:is(.dark *){background-color:rgba(17,24,39,.2)}.dark\:bg-gray-900\/30:is(.dark *){background-color:rgba(17,24,39,.3)}.dark\:bg-green-300:is(.dark *){--tw-bg-opacity:1;background-color:rgb(134 239 172/var(--tw-bg-opacity,1))}.dark\:bg-green-400:is(.dark *){--tw-bg-opacity:1;background-color:rgb(74 222 128/var(--tw-bg-opacity,1))}.dark\:bg-green-600:is(.dark *){--tw-bg-opacity:1;background-color:rgb(22 163 74/var(--tw-bg-opacity,1))}.dark\:bg-green-700:is(.dark *){--tw-bg-opacity:1;background-color:rgb(21 128 61/var(--tw-bg-opacity,1))}.dark\:bg-green-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(20 83 45/var(--tw-bg-opacity,1))}.dark\:bg-green-900\/10:is(.dark *){background-color:rgba(20,83,45,.1)}.dark\:bg-green-900\/20:is(.dark *){background-color:rgba(20,83,45,.2)}.dark\:bg-green-900\/30:is(.dark *){background-color:rgba(20,83,45,.3)}.dark\:bg-green-900\/50:is(.dark *){background-color:rgba(20,83,45,.5)}.dark\:bg-green-900\/60:is(.dark *){background-color:rgba(20,83,45,.6)}.dark\:bg-indigo-600:is(.dark *){--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity,1))}.dark\:bg-indigo-700:is(.dark *){--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity,1))}.dark\:bg-indigo-900\/10:is(.dark *){background-color:rgba(49,46,129,.1)}.dark\:bg-indigo-900\/20:is(.dark *){background-color:rgba(49,46,129,.2)}.dark\:bg-indigo-900\/30:is(.dark *){background-color:rgba(49,46,129,.3)}.dark\:bg-indigo-900\/50:is(.dark *){background-color:rgba(49,46,129,.5)}.dark\:bg-orange-300:is(.dark *){--tw-bg-opacity:1;background-color:rgb(253 186 116/var(--tw-bg-opacity,1))}.dark\:bg-orange-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(124 45 18/var(--tw-bg-opacity,1))}.dark\:bg-orange-900\/10:is(.dark *){background-color:rgba(124,45,18,.1)}.dark\:bg-orange-900\/30:is(.dark *){background-color:rgba(124,45,18,.3)}.dark\:bg-orange-900\/50:is(.dark *){background-color:rgba(124,45,18,.5)}.dark\:bg-purple-600:is(.dark *){--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity,1))}.dark\:bg-purple-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(88 28 135/var(--tw-bg-opacity,1))}.dark\:bg-purple-900\/10:is(.dark *){background-color:rgba(88,28,135,.1)}.dark\:bg-purple-900\/30:is(.dark *){background-color:rgba(88,28,135,.3)}.dark\:bg-purple-900\/50:is(.dark *){background-color:rgba(88,28,135,.5)}.dark\:bg-red-300:is(.dark *){--tw-bg-opacity:1;background-color:rgb(252 165 165/var(--tw-bg-opacity,1))}.dark\:bg-red-400:is(.dark *){--tw-bg-opacity:1;background-color:rgb(248 113 113/var(--tw-bg-opacity,1))}.dark\:bg-red-600:is(.dark *){--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.dark\:bg-red-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(127 29 29/var(--tw-bg-opacity,1))}.dark\:bg-red-900\/10:is(.dark *){background-color:rgba(127,29,29,.1)}.dark\:bg-red-900\/20:is(.dark *){background-color:rgba(127,29,29,.2)}.dark\:bg-red-900\/30:is(.dark *){background-color:rgba(127,29,29,.3)}.dark\:bg-red-900\/40:is(.dark *){background-color:rgba(127,29,29,.4)}.dark\:bg-red-900\/50:is(.dark *){background-color:rgba(127,29,29,.5)}.dark\:bg-red-900\/60:is(.dark *){background-color:rgba(127,29,29,.6)}.dark\:bg-slate-600:is(.dark *){--tw-bg-opacity:1;background-color:rgb(71 85 105/var(--tw-bg-opacity,1))}.dark\:bg-slate-700:is(.dark *){--tw-bg-opacity:1;background-color:rgb(51 65 85/var(--tw-bg-opacity,1))}.dark\:bg-slate-700\/30:is(.dark *){background-color:rgba(51,65,85,.3)}.dark\:bg-slate-700\/40:is(.dark *){background-color:rgba(51,65,85,.4)}.dark\:bg-slate-700\/60:is(.dark *){background-color:rgba(51,65,85,.6)}.dark\:bg-slate-800:is(.dark *){--tw-bg-opacity:1;background-color:rgb(30 41 59/var(--tw-bg-opacity,1))}.dark\:bg-slate-800\/50:is(.dark *){background-color:rgba(30,41,59,.5)}.dark\:bg-slate-800\/60:is(.dark *){background-color:rgba(30,41,59,.6)}.dark\:bg-slate-800\/80:is(.dark *){background-color:rgba(30,41,59,.8)}.dark\:bg-slate-800\/90:is(.dark *){background-color:rgba(30,41,59,.9)}.dark\:bg-slate-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(15 23 42/var(--tw-bg-opacity,1))}.dark\:bg-slate-900\/50:is(.dark *){background-color:rgba(15,23,42,.5)}.dark\:bg-slate-900\/60:is(.dark *){background-color:rgba(15,23,42,.6)}.dark\:bg-slate-900\/80:is(.dark *){background-color:rgba(15,23,42,.8)}.dark\:bg-slate-900\/90:is(.dark *){background-color:rgba(15,23,42,.9)}.dark\:bg-teal-900\/50:is(.dark *){background-color:rgba(19,78,74,.5)}.dark\:bg-white:is(.dark *){--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.dark\:bg-white\/10:is(.dark *){background-color:hsla(0,0%,100%,.1)}.dark\:bg-yellow-300:is(.dark *){--tw-bg-opacity:1;background-color:rgb(253 224 71/var(--tw-bg-opacity,1))}.dark\:bg-yellow-400:is(.dark *){--tw-bg-opacity:1;background-color:rgb(250 204 21/var(--tw-bg-opacity,1))}.dark\:bg-yellow-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(113 63 18/var(--tw-bg-opacity,1))}.dark\:bg-yellow-900\/20:is(.dark *){background-color:rgba(113,63,18,.2)}.dark\:bg-yellow-900\/30:is(.dark *){background-color:rgba(113,63,18,.3)}.dark\:bg-yellow-900\/50:is(.dark *){background-color:rgba(113,63,18,.5)}.dark\:bg-opacity-95:is(.dark *){--tw-bg-opacity:0.95}.dark\:from-blue-400:is(.dark *){--tw-gradient-from:#60a5fa var(--tw-gradient-from-position);--tw-gradient-to:rgba(96,165,250,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-blue-400\/20:is(.dark *){--tw-gradient-from:rgba(96,165,250,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(96,165,250,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-blue-900\/10:is(.dark *){--tw-gradient-from:rgba(30,58,138,.1) var(--tw-gradient-from-position);--tw-gradient-to:rgba(30,58,138,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-blue-900\/20:is(.dark *){--tw-gradient-from:rgba(30,58,138,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(30,58,138,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-blue-900\/30:is(.dark *){--tw-gradient-from:rgba(30,58,138,.3) var(--tw-gradient-from-position);--tw-gradient-to:rgba(30,58,138,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-emerald-900\/20:is(.dark *){--tw-gradient-from:rgba(6,78,59,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(6,78,59,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-green-400:is(.dark *){--tw-gradient-from:#4ade80 var(--tw-gradient-from-position);--tw-gradient-to:rgba(74,222,128,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-green-400\/20:is(.dark *){--tw-gradient-from:rgba(74,222,128,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(74,222,128,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-green-900\/10:is(.dark *){--tw-gradient-from:rgba(20,83,45,.1) var(--tw-gradient-from-position);--tw-gradient-to:rgba(20,83,45,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-green-900\/20:is(.dark *){--tw-gradient-from:rgba(20,83,45,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(20,83,45,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-green-900\/30:is(.dark *){--tw-gradient-from:rgba(20,83,45,.3) var(--tw-gradient-from-position);--tw-gradient-to:rgba(20,83,45,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-orange-400:is(.dark *){--tw-gradient-from:#fb923c var(--tw-gradient-from-position);--tw-gradient-to:rgba(251,146,60,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-orange-400\/20:is(.dark *){--tw-gradient-from:rgba(251,146,60,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(251,146,60,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-orange-900\/10:is(.dark *){--tw-gradient-from:rgba(124,45,18,.1) var(--tw-gradient-from-position);--tw-gradient-to:rgba(124,45,18,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-purple-900\/20:is(.dark *){--tw-gradient-from:rgba(88,28,135,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(88,28,135,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-purple-900\/30:is(.dark *){--tw-gradient-from:rgba(88,28,135,.3) var(--tw-gradient-from-position);--tw-gradient-to:rgba(88,28,135,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-red-400:is(.dark *){--tw-gradient-from:#f87171 var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,91%,71%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-red-400\/20:is(.dark *){--tw-gradient-from:hsla(0,91%,71%,.2) var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,91%,71%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-slate-800:is(.dark *){--tw-gradient-from:#1e293b var(--tw-gradient-from-position);--tw-gradient-to:rgba(30,41,59,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-slate-800\/90:is(.dark *){--tw-gradient-from:rgba(30,41,59,.9) var(--tw-gradient-from-position);--tw-gradient-to:rgba(30,41,59,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-slate-900:is(.dark *){--tw-gradient-from:#0f172a var(--tw-gradient-from-position);--tw-gradient-to:rgba(15,23,42,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-slate-950:is(.dark *){--tw-gradient-from:#020617 var(--tw-gradient-from-position);--tw-gradient-to:rgba(2,6,23,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-white:is(.dark *){--tw-gradient-from:#fff var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,0%,100%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:via-blue-200:is(.dark *){--tw-gradient-to:rgba(191,219,254,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#bfdbfe var(--tw-gradient-via-position),var(--tw-gradient-to)}.dark\:via-blue-900:is(.dark *){--tw-gradient-to:rgba(30,58,138,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1e3a8a var(--tw-gradient-via-position),var(--tw-gradient-to)}.dark\:via-blue-900\/20:is(.dark *){--tw-gradient-to:rgba(30,58,138,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),rgba(30,58,138,.2) var(--tw-gradient-via-position),var(--tw-gradient-to)}.dark\:via-blue-950:is(.dark *){--tw-gradient-to:rgba(23,37,84,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#172554 var(--tw-gradient-via-position),var(--tw-gradient-to)}.dark\:via-emerald-900\/20:is(.dark *){--tw-gradient-to:rgba(6,78,59,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),rgba(6,78,59,.2) var(--tw-gradient-via-position),var(--tw-gradient-to)}.dark\:via-red-900\/20:is(.dark *){--tw-gradient-to:rgba(127,29,29,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),rgba(127,29,29,.2) var(--tw-gradient-via-position),var(--tw-gradient-to)}.dark\:via-slate-800:is(.dark *){--tw-gradient-to:rgba(30,41,59,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1e293b var(--tw-gradient-via-position),var(--tw-gradient-to)}.dark\:to-blue-500:is(.dark *){--tw-gradient-to:#3b82f6 var(--tw-gradient-to-position)}.dark\:to-blue-800\/30:is(.dark *){--tw-gradient-to:rgba(30,64,175,.3) var(--tw-gradient-to-position)}.dark\:to-emerald-400\/20:is(.dark *){--tw-gradient-to:rgba(52,211,153,.2) var(--tw-gradient-to-position)}.dark\:to-emerald-900\/10:is(.dark *){--tw-gradient-to:rgba(6,78,59,.1) var(--tw-gradient-to-position)}.dark\:to-emerald-900\/20:is(.dark *){--tw-gradient-to:rgba(6,78,59,.2) var(--tw-gradient-to-position)}.dark\:to-gray-200:is(.dark *){--tw-gradient-to:#e5e7eb var(--tw-gradient-to-position)}.dark\:to-green-500:is(.dark *){--tw-gradient-to:#22c55e var(--tw-gradient-to-position)}.dark\:to-green-800\/30:is(.dark *){--tw-gradient-to:rgba(22,101,52,.3) var(--tw-gradient-to-position)}.dark\:to-green-900\/20:is(.dark *){--tw-gradient-to:rgba(20,83,45,.2) var(--tw-gradient-to-position)}.dark\:to-indigo-400\/20:is(.dark *){--tw-gradient-to:rgba(129,140,248,.2) var(--tw-gradient-to-position)}.dark\:to-indigo-900:is(.dark *){--tw-gradient-to:#312e81 var(--tw-gradient-to-position)}.dark\:to-indigo-900\/10:is(.dark *){--tw-gradient-to:rgba(49,46,129,.1) var(--tw-gradient-to-position)}.dark\:to-indigo-900\/20:is(.dark *){--tw-gradient-to:rgba(49,46,129,.2) var(--tw-gradient-to-position)}.dark\:to-indigo-950:is(.dark *){--tw-gradient-to:#1e1b4b var(--tw-gradient-to-position)}.dark\:to-orange-500:is(.dark *){--tw-gradient-to:#f97316 var(--tw-gradient-to-position)}.dark\:to-orange-900\/20:is(.dark *){--tw-gradient-to:rgba(124,45,18,.2) var(--tw-gradient-to-position)}.dark\:to-pink-400\/20:is(.dark *){--tw-gradient-to:rgba(244,114,182,.2) var(--tw-gradient-to-position)}.dark\:to-pink-900\/20:is(.dark *){--tw-gradient-to:rgba(131,24,67,.2) var(--tw-gradient-to-position)}.dark\:to-purple-500:is(.dark *){--tw-gradient-to:#a855f7 var(--tw-gradient-to-position)}.dark\:to-purple-800\/30:is(.dark *){--tw-gradient-to:rgba(107,33,168,.3) var(--tw-gradient-to-position)}.dark\:to-red-400\/20:is(.dark *){--tw-gradient-to:hsla(0,91%,71%,.2) var(--tw-gradient-to-position)}.dark\:to-red-500:is(.dark *){--tw-gradient-to:#ef4444 var(--tw-gradient-to-position)}.dark\:to-red-900\/10:is(.dark *){--tw-gradient-to:rgba(127,29,29,.1) var(--tw-gradient-to-position)}.dark\:to-slate-200:is(.dark *){--tw-gradient-to:#e2e8f0 var(--tw-gradient-to-position)}.dark\:to-slate-300:is(.dark *){--tw-gradient-to:#cbd5e1 var(--tw-gradient-to-position)}.dark\:to-slate-700:is(.dark *){--tw-gradient-to:#334155 var(--tw-gradient-to-position)}.dark\:to-slate-900:is(.dark *){--tw-gradient-to:#0f172a var(--tw-gradient-to-position)}.dark\:to-slate-900\/70:is(.dark *){--tw-gradient-to:rgba(15,23,42,.7) var(--tw-gradient-to-position)}.dark\:text-amber-400:is(.dark *){--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.dark\:text-blue-100:is(.dark *){--tw-text-opacity:1;color:rgb(219 234 254/var(--tw-text-opacity,1))}.dark\:text-blue-200:is(.dark *){--tw-text-opacity:1;color:rgb(191 219 254/var(--tw-text-opacity,1))}.dark\:text-blue-300:is(.dark *){--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity,1))}.dark\:text-blue-400:is(.dark *){--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.dark\:text-blue-500:is(.dark *){--tw-text-opacity:1;color:rgb(59 130 246/var(--tw-text-opacity,1))}.dark\:text-cyan-400:is(.dark *){--tw-text-opacity:1;color:rgb(34 211 238/var(--tw-text-opacity,1))}.dark\:text-emerald-300:is(.dark *){--tw-text-opacity:1;color:rgb(110 231 183/var(--tw-text-opacity,1))}.dark\:text-emerald-400:is(.dark *){--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.dark\:text-gray-100:is(.dark *){--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity,1))}.dark\:text-gray-200:is(.dark *){--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.dark\:text-gray-300:is(.dark *){--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.dark\:text-gray-400:is(.dark *){--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.dark\:text-gray-500:is(.dark *){--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.dark\:text-gray-600:is(.dark *){--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.dark\:text-green-100:is(.dark *){--tw-text-opacity:1;color:rgb(220 252 231/var(--tw-text-opacity,1))}.dark\:text-green-200:is(.dark *){--tw-text-opacity:1;color:rgb(187 247 208/var(--tw-text-opacity,1))}.dark\:text-green-300:is(.dark *){--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.dark\:text-green-400:is(.dark *){--tw-text-opacity:1;color:rgb(74 222 128/var(--tw-text-opacity,1))}.dark\:text-indigo-200:is(.dark *){--tw-text-opacity:1;color:rgb(199 210 254/var(--tw-text-opacity,1))}.dark\:text-indigo-300:is(.dark *){--tw-text-opacity:1;color:rgb(165 180 252/var(--tw-text-opacity,1))}.dark\:text-indigo-400:is(.dark *){--tw-text-opacity:1;color:rgb(129 140 248/var(--tw-text-opacity,1))}.dark\:text-orange-200:is(.dark *){--tw-text-opacity:1;color:rgb(254 215 170/var(--tw-text-opacity,1))}.dark\:text-orange-300:is(.dark *){--tw-text-opacity:1;color:rgb(253 186 116/var(--tw-text-opacity,1))}.dark\:text-orange-400:is(.dark *){--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity,1))}.dark\:text-purple-200:is(.dark *){--tw-text-opacity:1;color:rgb(233 213 255/var(--tw-text-opacity,1))}.dark\:text-purple-300:is(.dark *){--tw-text-opacity:1;color:rgb(216 180 254/var(--tw-text-opacity,1))}.dark\:text-purple-400:is(.dark *){--tw-text-opacity:1;color:rgb(192 132 252/var(--tw-text-opacity,1))}.dark\:text-red-100:is(.dark *){--tw-text-opacity:1;color:rgb(254 226 226/var(--tw-text-opacity,1))}.dark\:text-red-200:is(.dark *){--tw-text-opacity:1;color:rgb(254 202 202/var(--tw-text-opacity,1))}.dark\:text-red-300:is(.dark *){--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.dark\:text-red-400:is(.dark *){--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.dark\:text-red-600:is(.dark *){--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.dark\:text-slate-100:is(.dark *){--tw-text-opacity:1;color:rgb(241 245 249/var(--tw-text-opacity,1))}.dark\:text-slate-200:is(.dark *){--tw-text-opacity:1;color:rgb(226 232 240/var(--tw-text-opacity,1))}.dark\:text-slate-300:is(.dark *){--tw-text-opacity:1;color:rgb(203 213 225/var(--tw-text-opacity,1))}.dark\:text-slate-400:is(.dark *){--tw-text-opacity:1;color:rgb(148 163 184/var(--tw-text-opacity,1))}.dark\:text-slate-500:is(.dark *){--tw-text-opacity:1;color:rgb(100 116 139/var(--tw-text-opacity,1))}.dark\:text-slate-600:is(.dark *){--tw-text-opacity:1;color:rgb(71 85 105/var(--tw-text-opacity,1))}.dark\:text-slate-900:is(.dark *){--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity,1))}.dark\:text-teal-400:is(.dark *){--tw-text-opacity:1;color:rgb(45 212 191/var(--tw-text-opacity,1))}.dark\:text-white:is(.dark *){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.dark\:text-yellow-200:is(.dark *){--tw-text-opacity:1;color:rgb(254 240 138/var(--tw-text-opacity,1))}.dark\:text-yellow-300:is(.dark *){--tw-text-opacity:1;color:rgb(253 224 71/var(--tw-text-opacity,1))}.dark\:text-yellow-400:is(.dark *){--tw-text-opacity:1;color:rgb(250 204 21/var(--tw-text-opacity,1))}.dark\:placeholder-slate-400:is(.dark *)::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(148 163 184/var(--tw-placeholder-opacity,1))}.dark\:placeholder-slate-400:is(.dark *)::placeholder{--tw-placeholder-opacity:1;color:rgb(148 163 184/var(--tw-placeholder-opacity,1))}.dark\:opacity-0:is(.dark *){opacity:0}.dark\:opacity-100:is(.dark *){opacity:1}.dark\:opacity-5:is(.dark *){opacity:.05}.dark\:shadow-2xl:is(.dark *){--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.dark\:shadow-slate-900\/20:is(.dark *){--tw-shadow-color:rgba(15,23,42,.2);--tw-shadow:var(--tw-shadow-colored)}.dark\:hover\:border-blue-400:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(96 165 250/var(--tw-border-opacity,1))}.dark\:hover\:border-blue-500:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(59 130 246/var(--tw-border-opacity,1))}.dark\:hover\:border-emerald-400:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(52 211 153/var(--tw-border-opacity,1))}.dark\:hover\:border-slate-600:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(71 85 105/var(--tw-border-opacity,1))}.dark\:hover\:bg-blue-500:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(59 130 246/var(--tw-bg-opacity,1))}.dark\:hover\:bg-blue-600:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.dark\:hover\:bg-blue-700:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.dark\:hover\:bg-blue-800:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(30 64 175/var(--tw-bg-opacity,1))}.dark\:hover\:bg-gray-500:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity,1))}.dark\:hover\:bg-gray-600:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.dark\:hover\:bg-gray-700:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.dark\:hover\:bg-green-500:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.dark\:hover\:bg-green-700:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(21 128 61/var(--tw-bg-opacity,1))}.dark\:hover\:bg-purple-700:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(126 34 206/var(--tw-bg-opacity,1))}.dark\:hover\:bg-red-500:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.dark\:hover\:bg-red-700:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(185 28 28/var(--tw-bg-opacity,1))}.dark\:hover\:bg-red-900\/20:hover:is(.dark *){background-color:rgba(127,29,29,.2)}.dark\:hover\:bg-slate-500:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(100 116 139/var(--tw-bg-opacity,1))}.dark\:hover\:bg-slate-600:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(71 85 105/var(--tw-bg-opacity,1))}.dark\:hover\:bg-slate-700:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(51 65 85/var(--tw-bg-opacity,1))}.dark\:hover\:bg-slate-700\/50:hover:is(.dark *){background-color:rgba(51,65,85,.5)}.dark\:hover\:bg-slate-700\/60:hover:is(.dark *){background-color:rgba(51,65,85,.6)}.dark\:hover\:bg-slate-800:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(30 41 59/var(--tw-bg-opacity,1))}.dark\:hover\:bg-slate-800\/50:hover:is(.dark *){background-color:rgba(30,41,59,.5)}.dark\:hover\:bg-white\/15:hover:is(.dark *){background-color:hsla(0,0%,100%,.15)}.dark\:hover\:bg-white\/5:hover:is(.dark *){background-color:hsla(0,0%,100%,.05)}.dark\:hover\:bg-white\/70:hover:is(.dark *){background-color:hsla(0,0%,100%,.7)}.dark\:hover\:text-blue-200:hover:is(.dark *){--tw-text-opacity:1;color:rgb(191 219 254/var(--tw-text-opacity,1))}.dark\:hover\:text-blue-300:hover:is(.dark *){--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity,1))}.dark\:hover\:text-blue-400:hover:is(.dark *){--tw-text-opacity:1;color:rgb(96 165 250/var(--tw-text-opacity,1))}.dark\:hover\:text-emerald-400:hover:is(.dark *){--tw-text-opacity:1;color:rgb(52 211 153/var(--tw-text-opacity,1))}.dark\:hover\:text-gray-200:hover:is(.dark *){--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.dark\:hover\:text-gray-300:hover:is(.dark *){--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.dark\:hover\:text-green-300:hover:is(.dark *){--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.dark\:hover\:text-red-200:hover:is(.dark *){--tw-text-opacity:1;color:rgb(254 202 202/var(--tw-text-opacity,1))}.dark\:hover\:text-red-300:hover:is(.dark *){--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.dark\:hover\:text-slate-200:hover:is(.dark *){--tw-text-opacity:1;color:rgb(226 232 240/var(--tw-text-opacity,1))}.dark\:hover\:text-slate-300:hover:is(.dark *){--tw-text-opacity:1;color:rgb(203 213 225/var(--tw-text-opacity,1))}.dark\:hover\:text-slate-900:hover:is(.dark *){--tw-text-opacity:1;color:rgb(15 23 42/var(--tw-text-opacity,1))}.dark\:hover\:text-white:hover:is(.dark *){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.dark\:hover\:shadow-slate-900\/50:hover:is(.dark *){--tw-shadow-color:rgba(15,23,42,.5);--tw-shadow:var(--tw-shadow-colored)}.dark\:focus\:border-blue-400:focus:is(.dark *){--tw-border-opacity:1;border-color:rgb(96 165 250/var(--tw-border-opacity,1))}.dark\:focus\:bg-slate-700\/60:focus:is(.dark *){background-color:rgba(51,65,85,.6)}.dark\:focus\:ring-blue-400:focus:is(.dark *){--tw-ring-opacity:1;--tw-ring-color:rgb(96 165 250/var(--tw-ring-opacity,1))}.dark\:focus\:ring-blue-400\/20:focus:is(.dark *){--tw-ring-color:rgba(96,165,250,.2)}.dark\:disabled\:bg-slate-800:disabled:is(.dark *){--tw-bg-opacity:1;background-color:rgb(30 41 59/var(--tw-bg-opacity,1))}.group:hover .dark\:group-hover\:text-slate-300:is(.dark *){--tw-text-opacity:1;color:rgb(203 213 225/var(--tw-text-opacity,1))}.group:hover .dark\:group-hover\:text-white:is(.dark *){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}@media (min-width:640px){.sm\:mx-0{margin-left:0;margin-right:0}.sm\:my-8{margin-top:2rem;margin-bottom:2rem}.sm\:ml-3{margin-left:.75rem}.sm\:ml-4{margin-left:1rem}.sm\:mt-0{margin-top:0}.sm\:mt-12{margin-top:3rem}.sm\:block{display:block}.sm\:inline{display:inline}.sm\:flex{display:flex}.sm\:h-10{height:2.5rem}.sm\:h-18{height:4.5rem}.sm\:h-4{height:1rem}.sm\:h-5{height:1.25rem}.sm\:h-6{height:1.5rem}.sm\:w-10{width:2.5rem}.sm\:w-4{width:1rem}.sm\:w-5{width:1.25rem}.sm\:w-6{width:1.5rem}.sm\:w-80{width:20rem}.sm\:w-auto{width:auto}.sm\:w-full{width:100%}.sm\:max-w-lg{max-width:32rem}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:flex-row-reverse{flex-direction:row-reverse}.sm\:items-start{align-items:flex-start}.sm\:justify-center{justify-content:center}.sm\:gap-8{gap:2rem}.sm\:space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.sm\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.sm\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.sm\:p-0{padding:0}.sm\:p-6{padding:1.5rem}.sm\:px-4{padding-left:1rem;padding-right:1rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:py-8{padding-top:2rem;padding-bottom:2rem}.sm\:pb-4{padding-bottom:1rem}.sm\:pt-8{padding-top:2rem}.sm\:text-left{text-align:left}.sm\:align-middle{vertical-align:middle}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:768px){.md\:col-span-2{grid-column:span 2/span 2}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:justify-between{justify-content:space-between}.md\:space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.md\:p-12{padding:3rem}.md\:p-8{padding:2rem}.md\:text-2xl{font-size:1.5rem;line-height:2rem}.md\:text-4xl{font-size:2.25rem;line-height:2.5rem}.md\:text-5xl{font-size:3rem;line-height:1}.md\:text-6xl{font-size:3.75rem;line-height:1}.md\:text-8xl{font-size:6rem;line-height:1}.md\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width:1024px){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:mt-0{margin-top:0}.lg\:block{display:block}.lg\:inline{display:inline}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:h-20{height:5rem}.lg\:h-7{height:1.75rem}.lg\:w-7{width:1.75rem}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:items-center{align-items:center}.lg\:justify-between{justify-content:space-between}.lg\:space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1.5rem*var(--tw-space-x-reverse));margin-left:calc(1.5rem*(1 - var(--tw-space-x-reverse)))}.lg\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.lg\:p-12{padding:3rem}.lg\:px-6{padding-left:1.5rem;padding-right:1.5rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:text-right{text-align:right}.lg\:text-6xl{font-size:3.75rem;line-height:1}.lg\:text-base{font-size:1rem;line-height:1.5rem}}@media (min-width:1280px){.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}} \ No newline at end of file +.-translate-x-1,.-translate-x-1\/2,.-translate-x-full,.-translate-y-1,.-translate-y-1\/2,.dark\:rotate-0:is(.dark *),.dark\:rotate-90:is(.dark *),.group-hover\:rotate-180,.group-hover\:translate-x-full,.rotate-0,.rotate-90,.transform,.translate-x-1,.translate-x-full,.translate-y-1{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1}.group-hover\:-translate-x-1,.hover\:-translate-y-0,.hover\:-translate-y-1,.hover\:-translate-y-2{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1}.dark\:from-white:is(.dark *),.dark\:to-blue-500:is(.dark *),.dark\:to-gray-200:is(.dark *),.from-blue-500,.from-blue-500\/10,.from-blue-600,.from-blue-600\/10,.from-green-100,.from-transparent,.from-white,.hover\:from-blue-600,.hover\:from-green-600,.hover\:to-red-600,.to-blue-600,.to-green-600,.to-red-600,.to-transparent,.to-white,.via-white\/20,.via-white\/5{--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: }.tabular-nums{--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: }.hover\:shadow-lg,.shadow,.shadow-lg,.shadow-sm{--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000}.focus\:ring-1,.focus\:ring-2,.ring,.ring-2{--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(0,115,206,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000}/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--primary:#0073ce;--primary-dark:#005a9f;--bg:#fafbfc;--surface:#fff;--text:#111827;--text-muted:#6b7280;--border:#e5e7eb;--shadow:0 2px 4px rgba(0,0,0,.05)}.dark{--bg:#1e293b;--surface:#334155;--text:#f8fafc;--text-muted:#94a3b8;--border:#475569;--shadow:0 2px 4px rgba(0,0,0,.3)}body{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}body:is(.dark *){--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}body{font-family:system-ui,-apple-system,sans-serif;background:var(--bg);color:var(--text);line-height:1.5;transition:background-color .2s ease,color .2s ease}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.card{border-radius:8px;border-width:1px;border-color:rgb(229 231 235/var(--tw-border-opacity,1));background-color:rgb(255 255 255/var(--tw-bg-opacity,1));padding:1rem;background:var(--surface);border-color:var(--border);box-shadow:var(--shadow)}.card,.dark .card{--tw-border-opacity:1;--tw-bg-opacity:1}.dark .card{border-color:rgb(75 85 99/var(--tw-border-opacity,1));background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.btn{border-radius:8px;padding:.5rem 1rem;font-weight:600;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);background:var(--primary);color:#fff;border:none}.btn:hover{background:var(--primary-dark)}.btn-secondary{border-width:1px;border-color:rgb(229 231 235/var(--tw-border-opacity,1));background-color:rgb(255 255 255/var(--tw-bg-opacity,1));color:rgb(17 24 39/var(--tw-text-opacity,1));background:var(--surface);border-color:var(--border);color:var(--text)}.btn-secondary,.dark .btn-secondary{--tw-border-opacity:1;--tw-bg-opacity:1;--tw-text-opacity:1}.dark .btn-secondary{border-color:rgb(75 85 99/var(--tw-border-opacity,1));background-color:rgb(55 65 81/var(--tw-bg-opacity,1));color:rgb(255 255 255/var(--tw-text-opacity,1))}.input{width:100%;border-radius:8px;border-width:1px;--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1));padding:.5rem .75rem;background:var(--surface);border-color:var(--border);color:var(--text)}.input:focus{outline:none;border-color:var(--primary)}.dark .input{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity,1));--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1));--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.nav{display:flex;gap:1rem}.nav-item{border-radius:8px;padding:.5rem 1rem;--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.nav-item:hover{--tw-bg-opacity:1;background-color:rgb(243 245 247/var(--tw-bg-opacity,1))}.nav-item{color:var(--text-muted);transition:background-color .1s ease}.nav-item:hover{background:var(--bg);color:var(--text)}.nav-item.active{background:var(--primary);color:#fff}.dark .nav-item{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.dark .nav-item:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.status{display:inline-block;border-radius:9999px;padding:.25rem .75rem;font-size:.75rem;font-weight:600;text-transform:uppercase}.status-online{background:#d1fae5;color:#065f46}.status-offline{background:#fee2e2;color:#991b1b}.status-printing{background:#dbeafe;color:#1e40af}.dark .status-online{background:rgba(16,185,129,.2);color:#10b981}.dark .status-offline{background:rgba(239,68,68,.2);color:#ef4444}.dark .status-printing{background:rgba(59,130,246,.2);color:#3b82f6}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.-inset-1{inset:-.25rem}.inset-0{inset:0}.inset-y-0{top:0;bottom:0}.-bottom-2{bottom:-.5rem}.-bottom-8{bottom:-2rem}.-left-2{left:-.5rem}.-right-1{right:-.25rem}.-right-2{right:-.5rem}.-top-1{top:-.25rem}.-top-2{top:-.5rem}.bottom-0{bottom:0}.bottom-4{bottom:1rem}.bottom-6{bottom:1.5rem}.bottom-8{bottom:2rem}.bottom-full{bottom:100%}.end-1{inset-inline-end:.25rem}.left-0{left:0}.left-1\/2{left:50%}.left-3{left:.75rem}.left-4{left:1rem}.right-0{right:0}.right-3{right:.75rem}.right-4{right:1rem}.right-5{right:1.25rem}.right-6{right:1.5rem}.right-8{right:2rem}.top-0{top:0}.top-1\/2{top:50%}.top-3{top:.75rem}.top-4{top:1rem}.top-5{top:1.25rem}.top-6{top:1.5rem}.top-8{top:2rem}.top-full{top:100%}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.col-span-full{grid-column:1/-1}.m-1{margin:.25rem}.m-4{margin:1rem}.mx-2{margin-left:.5rem;margin-right:.5rem}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.-ml-1{margin-left:-.25rem}.-mt-8{margin-top:-2rem}.mb-0{margin-bottom:0}.mb-1{margin-bottom:.25rem}.mb-12{margin-bottom:3rem}.mb-16{margin-bottom:4rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.ml-6{margin-left:1.5rem}.ml-8{margin-left:2rem}.ml-auto{margin-left:auto}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mr-4{margin-right:1rem}.mt-0{margin-top:0}.mt-1{margin-top:.25rem}.mt-12{margin-top:3rem}.mt-16{margin-top:4rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.mt-auto{margin-top:auto}.block{display:block}.inline-block{display:inline-block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.list-item{display:list-item}.hidden{display:none}.h-0{height:0}.h-1{height:.25rem}.h-10{height:2.5rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-20{height:5rem}.h-3{height:.75rem}.h-4{height:1rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-0{width:0}.w-1{width:.25rem}.w-1\/2{width:50%}.w-1\/3{width:33.333333%}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-2{width:.5rem}.w-2\/3{width:66.666667%}.w-20{width:5rem}.w-3{width:.75rem}.w-3\/4{width:75%}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-full{width:100%}.min-w-0{min-width:0}.min-w-full{min-width:100%}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-lg{max-width:32rem}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.max-w-screen-xl{max-width:1280px}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-shrink{flex-shrink:1}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.flex-grow{flex-grow:1}.border-collapse{border-collapse:collapse}.-translate-x-1{--tw-translate-x:-0.25rem}.-translate-x-1,.-translate-x-1\/2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-x-1\/2{--tw-translate-x:-50%}.-translate-x-full{--tw-translate-x:-100%}.-translate-x-full,.-translate-y-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1{--tw-translate-y:-0.25rem}.-translate-y-1\/2{--tw-translate-y:-50%}.-translate-y-1\/2,.translate-x-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-1{--tw-translate-x:0.25rem}.translate-x-full{--tw-translate-x:100%}.translate-x-full,.translate-y-1{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-1{--tw-translate-y:0.25rem}.rotate-0{--tw-rotate:0deg}.rotate-0,.rotate-90{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-90{--tw-rotate:90deg}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-not-allowed{cursor:not-allowed}.cursor-pointer{cursor:pointer}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.select-all{-webkit-user-select:all;-moz-user-select:all;user-select:all}.resize-none{resize:none}.resize{resize:both}.scroll-mt-8{scroll-margin-top:2rem}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.appearance-none{-webkit-appearance:none;-moz-appearance:none;appearance:none}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.grid-cols-7{grid-template-columns:repeat(7,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1.5rem*var(--tw-space-x-reverse));margin-left:calc(1.5rem*(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-16>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(4rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(4rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity,1))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.scroll-smooth{scroll-behavior:smooth}.truncate{overflow:hidden;text-overflow:ellipsis}.truncate,.whitespace-nowrap{white-space:nowrap}.rounded{border-radius:6px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:8px}.rounded-xl{border-radius:12px}.border{border-width:1px}.border-2{border-width:2px}.border-4{border-width:4px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l-2{border-left-width:2px}.border-l-4{border-left-width:4px}.border-r-4{border-right-width:4px}.border-t{border-top-width:1px}.border-t-4{border-top-width:4px}.border-dashed{border-style:dashed}.border-blue-500{--tw-border-opacity:1;border-color:rgb(0 115 206/var(--tw-border-opacity,1))}.border-blue-600{--tw-border-opacity:1;border-color:rgb(0 90 159/var(--tw-border-opacity,1))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-gray-200\/50{border-color:rgba(229,231,235,.5)}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.border-transparent{border-color:transparent}.border-white{--tw-border-opacity:1;border-color:rgb(255 255 255/var(--tw-border-opacity,1))}.border-white\/20{border-color:hsla(0,0%,100%,.2)}.border-white\/30{border-color:hsla(0,0%,100%,.3)}.border-white\/50{border-color:hsla(0,0%,100%,.5)}.border-t-transparent{border-top-color:transparent}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.bg-black\/20{background-color:rgba(0,0,0,.2)}.bg-black\/30{background-color:rgba(0,0,0,.3)}.bg-black\/50{background-color:rgba(0,0,0,.5)}.bg-black\/60{background-color:rgba(0,0,0,.6)}.bg-black\/70{background-color:rgba(0,0,0,.7)}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(0 115 206/var(--tw-bg-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(0 90 159/var(--tw-bg-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 245 247/var(--tw-bg-opacity,1))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.bg-gray-400{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity,1))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(250 251 252/var(--tw-bg-opacity,1))}.bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity,1))}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.bg-green-100{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.bg-green-600{--tw-bg-opacity:1;background-color:rgb(6 95 70/var(--tw-bg-opacity,1))}.bg-primary{--tw-bg-opacity:1;background-color:rgb(0 115 206/var(--tw-bg-opacity,1))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity,1))}.bg-red-600{--tw-bg-opacity:1;background-color:rgb(153 27 27/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-white\/10{background-color:hsla(0,0%,100%,.1)}.bg-white\/15{background-color:hsla(0,0%,100%,.15)}.bg-white\/20{background-color:hsla(0,0%,100%,.2)}.bg-white\/40{background-color:hsla(0,0%,100%,.4)}.bg-white\/60{background-color:hsla(0,0%,100%,.6)}.bg-white\/80{background-color:hsla(0,0%,100%,.8)}.bg-white\/90{background-color:hsla(0,0%,100%,.9)}.bg-opacity-50{--tw-bg-opacity:0.5}.bg-opacity-75{--tw-bg-opacity:0.75}.bg-opacity-95{--tw-bg-opacity:0.95}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.bg-gradient-to-tr{background-image:linear-gradient(to top right,var(--tw-gradient-stops))}.from-blue-500{--tw-gradient-from:#0073ce var(--tw-gradient-from-position);--tw-gradient-to:rgba(0,115,206,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-500\/10{--tw-gradient-from:rgba(0,115,206,.1) var(--tw-gradient-from-position);--tw-gradient-to:rgba(0,115,206,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-600{--tw-gradient-from:#005a9f var(--tw-gradient-from-position);--tw-gradient-to:rgba(0,90,159,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-600\/10{--tw-gradient-from:rgba(0,90,159,.1) var(--tw-gradient-from-position);--tw-gradient-to:rgba(0,90,159,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-100{--tw-gradient-from:#d1fae5 var(--tw-gradient-from-position);--tw-gradient-to:rgba(209,250,229,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-transparent{--tw-gradient-from:transparent var(--tw-gradient-from-position);--tw-gradient-to:transparent var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-white{--tw-gradient-from:#fff var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,0%,100%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.via-white\/20{--tw-gradient-to:hsla(0,0%,100%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),hsla(0,0%,100%,.2) var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-white\/5{--tw-gradient-to:hsla(0,0%,100%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),hsla(0,0%,100%,.05) var(--tw-gradient-via-position),var(--tw-gradient-to)}.to-blue-600{--tw-gradient-to:#005a9f var(--tw-gradient-to-position)}.to-green-600{--tw-gradient-to:#065f46 var(--tw-gradient-to-position)}.to-red-600{--tw-gradient-to:#991b1b var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to:transparent var(--tw-gradient-to-position)}.to-white{--tw-gradient-to:#fff var(--tw-gradient-to-position)}.bg-clip-text{-webkit-background-clip:text;background-clip:text}.object-cover{-o-object-fit:cover;object-fit:cover}.p-1{padding:.25rem}.p-10{padding:2.5rem}.p-12{padding:3rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-5{padding-left:1.25rem;padding-right:1.25rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0{padding-top:0;padding-bottom:0}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-20{padding-top:5rem;padding-bottom:5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pb-20{padding-bottom:5rem}.pb-4{padding-bottom:1rem}.pb-6{padding-bottom:1.5rem}.pb-8{padding-bottom:2rem}.pl-10{padding-left:2.5rem}.pl-3{padding-left:.75rem}.pl-4{padding-left:1rem}.pr-10{padding-right:2.5rem}.pr-12{padding-right:3rem}.pr-20{padding-right:5rem}.pr-3{padding-right:.75rem}.pr-4{padding-right:1rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.pt-6{padding-top:1.5rem}.pt-8{padding-top:2rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-middle{vertical-align:middle}.align-bottom{vertical-align:bottom}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem}.text-3xl{font-size:1.875rem}.text-base{font-size:1rem}.text-lg{font-size:1.125rem}.text-sm{font-size:.875rem}.text-xl{font-size:1.25rem}.text-xs{font-size:.75rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-4{line-height:1rem}.leading-6{line-height:1.5rem}.leading-relaxed{line-height:1.625}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.text-blue-500{--tw-text-opacity:1;color:rgb(0 115 206/var(--tw-text-opacity,1))}.text-blue-600{--tw-text-opacity:1;color:rgb(0 90 159/var(--tw-text-opacity,1))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity,1))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.text-green-600{--tw-text-opacity:1;color:rgb(6 95 70/var(--tw-text-opacity,1))}.text-primary{--tw-text-opacity:1;color:rgb(0 115 206/var(--tw-text-opacity,1))}.text-red-600{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity,1))}.text-transparent{color:transparent}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.underline{text-decoration-line:underline}.overline{text-decoration-line:overline}.opacity-0{opacity:0}.opacity-10{opacity:.1}.opacity-100{opacity:1}.opacity-25{opacity:.25}.opacity-30{opacity:.3}.opacity-50{opacity:.5}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.shadow{--tw-shadow:0 2px 4px rgba(0,0,0,.05);--tw-shadow-colored:0 2px 4px var(--tw-shadow-color)}.shadow,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 4px 8px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 8px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 2px 4px rgba(0,0,0,.05);--tw-shadow-colored:0 2px 4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.ring,.ring-2{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.ring-2{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-shadow{transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1)}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1)}.delay-1000{transition-delay:1s}.delay-500{transition-delay:.5s}.duration-200{transition-duration:.2s}.ease-in{transition-timing-function:cubic-bezier(.4,0,1,1)}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.ease-out{transition-timing-function:cubic-bezier(0,0,.2,1)}.hover\:-translate-y-0:hover{--tw-translate-y:0}.hover\:-translate-y-0:hover,.hover\:-translate-y-1:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:-translate-y-1:hover{--tw-translate-y:-0.25rem}.hover\:-translate-y-2:hover{--tw-translate-y:-0.5rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:border-blue-600:hover{--tw-border-opacity:1;border-color:rgb(0 90 159/var(--tw-border-opacity,1))}.hover\:bg-black\/5:hover{background-color:rgba(0,0,0,.05)}.hover\:bg-blue-600:hover{--tw-bg-opacity:1;background-color:rgb(0 90 159/var(--tw-bg-opacity,1))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(243 245 247/var(--tw-bg-opacity,1))}.hover\:bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.hover\:bg-gray-400:hover{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity,1))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(250 251 252/var(--tw-bg-opacity,1))}.hover\:bg-gray-600:hover{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.hover\:bg-gray-800:hover{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.hover\:bg-green-100:hover{--tw-bg-opacity:1;background-color:rgb(209 250 229/var(--tw-bg-opacity,1))}.hover\:bg-green-600:hover{--tw-bg-opacity:1;background-color:rgb(6 95 70/var(--tw-bg-opacity,1))}.hover\:bg-red-100:hover{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity,1))}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgb(153 27 27/var(--tw-bg-opacity,1))}.hover\:bg-white:hover{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.hover\:bg-white\/20:hover{background-color:hsla(0,0%,100%,.2)}.hover\:bg-white\/25:hover{background-color:hsla(0,0%,100%,.25)}.hover\:from-blue-600:hover{--tw-gradient-from:#005a9f var(--tw-gradient-from-position);--tw-gradient-to:rgba(0,90,159,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:from-green-600:hover{--tw-gradient-from:#065f46 var(--tw-gradient-from-position);--tw-gradient-to:rgba(6,95,70,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:to-red-600:hover{--tw-gradient-to:#991b1b var(--tw-gradient-to-position)}.hover\:text-blue-500:hover{--tw-text-opacity:1;color:rgb(0 115 206/var(--tw-text-opacity,1))}.hover\:text-blue-600:hover{--tw-text-opacity:1;color:rgb(0 90 159/var(--tw-text-opacity,1))}.hover\:text-gray-200:hover{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.hover\:text-gray-800:hover{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity,1))}.hover\:text-gray-900:hover{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:shadow-lg:hover{--tw-shadow:0 4px 8px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 8px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.focus\:z-10:focus{z-index:10}.focus\:border-blue-500:focus{--tw-border-opacity:1;border-color:rgb(0 115 206/var(--tw-border-opacity,1))}.focus\:border-blue-600:focus{--tw-border-opacity:1;border-color:rgb(0 90 159/var(--tw-border-opacity,1))}.focus\:border-transparent:focus{border-color:transparent}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-1:focus,.focus\:ring-2:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-blue-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(0 115 206/var(--tw-ring-opacity,1))}.focus\:ring-blue-600:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(0 90 159/var(--tw-ring-opacity,1))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:bg-gray-100:disabled{--tw-bg-opacity:1;background-color:rgb(243 245 247/var(--tw-bg-opacity,1))}.disabled\:opacity-50:disabled{opacity:.5}.group:focus-within .group-focus-within\:text-blue-500{--tw-text-opacity:1;color:rgb(0 115 206/var(--tw-text-opacity,1))}.group:hover .group-hover\:-translate-x-1{--tw-translate-x:-0.25rem}.group:hover .group-hover\:-translate-x-1,.group:hover .group-hover\:translate-x-full{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:translate-x-full{--tw-translate-x:100%}.group:hover .group-hover\:rotate-180{--tw-rotate:180deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:opacity-100{opacity:1}.dark\:inline:is(.dark *){display:inline}.dark\:hidden:is(.dark *){display:none}.dark\:rotate-0:is(.dark *){--tw-rotate:0deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:rotate-90:is(.dark *){--tw-rotate:90deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.dark\:divide-gray-700:is(.dark *)>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(55 65 81/var(--tw-divide-opacity,1))}.dark\:border-gray-600:is(.dark *){--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity,1))}.dark\:border-gray-700:is(.dark *){--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity,1))}.dark\:border-gray-800:is(.dark *){--tw-border-opacity:1;border-color:rgb(31 41 55/var(--tw-border-opacity,1))}.dark\:border-white\/20:is(.dark *){border-color:hsla(0,0%,100%,.2)}.dark\:bg-black:is(.dark *){--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.dark\:bg-black\/50:is(.dark *){background-color:rgba(0,0,0,.5)}.dark\:bg-black\/80:is(.dark *){background-color:rgba(0,0,0,.8)}.dark\:bg-blue-500:is(.dark *){--tw-bg-opacity:1;background-color:rgb(0 115 206/var(--tw-bg-opacity,1))}.dark\:bg-blue-600:is(.dark *){--tw-bg-opacity:1;background-color:rgb(0 90 159/var(--tw-bg-opacity,1))}.dark\:bg-gray-300:is(.dark *){--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.dark\:bg-gray-600:is(.dark *){--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.dark\:bg-gray-700:is(.dark *){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.dark\:bg-gray-800:is(.dark *){--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.dark\:bg-gray-900:is(.dark *){--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.dark\:bg-gray-900\/20:is(.dark *){background-color:rgba(17,24,39,.2)}.dark\:bg-gray-900\/30:is(.dark *){background-color:rgba(17,24,39,.3)}.dark\:bg-green-600:is(.dark *){--tw-bg-opacity:1;background-color:rgb(6 95 70/var(--tw-bg-opacity,1))}.dark\:bg-red-600:is(.dark *){--tw-bg-opacity:1;background-color:rgb(153 27 27/var(--tw-bg-opacity,1))}.dark\:bg-white\/10:is(.dark *){background-color:hsla(0,0%,100%,.1)}.dark\:bg-opacity-95:is(.dark *){--tw-bg-opacity:0.95}.dark\:from-white:is(.dark *){--tw-gradient-from:#fff var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,0%,100%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:to-blue-500:is(.dark *){--tw-gradient-to:#0073ce var(--tw-gradient-to-position)}.dark\:to-gray-200:is(.dark *){--tw-gradient-to:#e5e7eb var(--tw-gradient-to-position)}.dark\:text-blue-500:is(.dark *){--tw-text-opacity:1;color:rgb(0 115 206/var(--tw-text-opacity,1))}.dark\:text-gray-100:is(.dark *){--tw-text-opacity:1;color:rgb(243 245 247/var(--tw-text-opacity,1))}.dark\:text-gray-200:is(.dark *){--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.dark\:text-gray-300:is(.dark *){--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.dark\:text-gray-400:is(.dark *){--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.dark\:text-gray-500:is(.dark *){--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.dark\:text-gray-600:is(.dark *){--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.dark\:text-green-100:is(.dark *){--tw-text-opacity:1;color:rgb(209 250 229/var(--tw-text-opacity,1))}.dark\:text-red-100:is(.dark *){--tw-text-opacity:1;color:rgb(254 226 226/var(--tw-text-opacity,1))}.dark\:text-red-600:is(.dark *){--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity,1))}.dark\:text-white:is(.dark *){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.dark\:opacity-0:is(.dark *){opacity:0}.dark\:opacity-100:is(.dark *){opacity:1}.dark\:opacity-5:is(.dark *){opacity:.05}.dark\:hover\:border-blue-500:hover:is(.dark *){--tw-border-opacity:1;border-color:rgb(0 115 206/var(--tw-border-opacity,1))}.dark\:hover\:bg-blue-500:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(0 115 206/var(--tw-bg-opacity,1))}.dark\:hover\:bg-blue-600:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(0 90 159/var(--tw-bg-opacity,1))}.dark\:hover\:bg-gray-500:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity,1))}.dark\:hover\:bg-gray-600:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.dark\:hover\:bg-gray-700:hover:is(.dark *){--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.dark\:hover\:bg-white\/15:hover:is(.dark *){background-color:hsla(0,0%,100%,.15)}.dark\:hover\:bg-white\/5:hover:is(.dark *){background-color:hsla(0,0%,100%,.05)}.dark\:hover\:text-gray-200:hover:is(.dark *){--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.dark\:hover\:text-gray-300:hover:is(.dark *){--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.dark\:hover\:text-white:hover:is(.dark *){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.group:hover .dark\:group-hover\:text-white:is(.dark *){--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}@media (min-width:640px){.sm\:mx-0{margin-left:0;margin-right:0}.sm\:my-8{margin-top:2rem;margin-bottom:2rem}.sm\:ml-3{margin-left:.75rem}.sm\:ml-4{margin-left:1rem}.sm\:mt-0{margin-top:0}.sm\:mt-12{margin-top:3rem}.sm\:block{display:block}.sm\:inline{display:inline}.sm\:flex{display:flex}.sm\:h-10{height:2.5rem}.sm\:h-5{height:1.25rem}.sm\:h-6{height:1.5rem}.sm\:w-10{width:2.5rem}.sm\:w-5{width:1.25rem}.sm\:w-6{width:1.5rem}.sm\:w-auto{width:auto}.sm\:w-full{width:100%}.sm\:max-w-lg{max-width:32rem}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:flex-row-reverse{flex-direction:row-reverse}.sm\:items-start{align-items:flex-start}.sm\:justify-center{justify-content:center}.sm\:gap-8{gap:2rem}.sm\:space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.sm\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.sm\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.sm\:p-0{padding:0}.sm\:p-6{padding:1.5rem}.sm\:px-4{padding-left:1rem;padding-right:1rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:py-8{padding-top:2rem;padding-bottom:2rem}.sm\:pb-4{padding-bottom:1rem}.sm\:pt-8{padding-top:2rem}.sm\:text-left{text-align:left}.sm\:align-middle{vertical-align:middle}.sm\:text-sm{font-size:.875rem}}@media (min-width:768px){.md\:col-span-2{grid-column:span 2/span 2}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:justify-between{justify-content:space-between}.md\:p-12{padding:3rem}.md\:text-2xl{font-size:1.5rem}.md\:text-xl{font-size:1.25rem}}@media (min-width:1024px){.lg\:col-span-2{grid-column:span 2/span 2}.lg\:col-span-3{grid-column:span 3/span 3}.lg\:mt-0{margin-top:0}.lg\:block{display:block}.lg\:inline{display:inline}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:h-20{height:5rem}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:items-center{align-items:center}.lg\:justify-between{justify-content:space-between}.lg\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.lg\:p-12{padding:3rem}.lg\:px-6{padding-left:1.5rem;padding-right:1.5rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:text-right{text-align:right}.lg\:text-base{font-size:1rem}}@media (min-width:1280px){.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}} \ No newline at end of file diff --git a/backend/tailwind.config.js b/backend/tailwind.config.js index 1b2148f2..9d99d776 100644 --- a/backend/tailwind.config.js +++ b/backend/tailwind.config.js @@ -11,38 +11,184 @@ module.exports = { ], darkMode: 'class', theme: { - extend: { - colors: { - // Minimale Farbpalette für Kiosk-Modus - primary: '#0073ce', - 'primary-dark': '#005a9f', - surface: '#ffffff', - muted: '#6b7280' + // Reduzierte Farbpalette + colors: { + transparent: 'transparent', + current: 'currentColor', + white: '#ffffff', + black: '#000000', + primary: '#0073ce', + 'primary-dark': '#005a9f', + gray: { + 50: '#fafbfc', + 100: '#f3f5f7', + 200: '#e5e7eb', + 300: '#d1d5db', + 400: '#9ca3af', + 500: '#6b7280', + 600: '#4b5563', + 700: '#374151', + 800: '#1f2937', + 900: '#111827', }, - spacing: { - '18': '4.5rem', - '88': '22rem' + blue: { + 500: '#0073ce', + 600: '#005a9f', }, - fontFamily: { - 'sans': ['system-ui', '-apple-system', 'sans-serif'] + green: { + 100: '#d1fae5', + 600: '#065f46', }, - backdropBlur: { - xs: '2px' - } + red: { + 100: '#fee2e2', + 600: '#991b1b', + }, + }, + + // Reduzierte Spacing-Skala + spacing: { + '0': '0', + '1': '0.25rem', + '2': '0.5rem', + '3': '0.75rem', + '4': '1rem', + '5': '1.25rem', + '6': '1.5rem', + '8': '2rem', + '10': '2.5rem', + '12': '3rem', + '16': '4rem', + '20': '5rem', + }, + + // Reduzierte Font-Größen + fontSize: { + 'xs': '0.75rem', + 'sm': '0.875rem', + 'base': '1rem', + 'lg': '1.125rem', + 'xl': '1.25rem', + '2xl': '1.5rem', + '3xl': '1.875rem', + }, + + // Minimale Border-Radius + borderRadius: { + 'none': '0', + 'sm': '2px', + 'DEFAULT': '6px', + 'lg': '8px', + 'xl': '12px', + 'full': '9999px', + }, + + // Reduzierte Schatten + boxShadow: { + 'sm': '0 2px 4px rgba(0,0,0,0.05)', + 'DEFAULT': '0 2px 4px rgba(0,0,0,0.05)', + 'lg': '0 4px 8px rgba(0,0,0,0.1)', + }, + + // Minimale Transitions + transitionDuration: { + '75': '75ms', + '100': '100ms', + '150': '150ms', + '200': '200ms', + }, + + extend: {} + }, + + // Deaktivierte Utilities für bessere Performance + corePlugins: { + // Nicht benötigte Features deaktivieren + animation: false, // Animationen werden manuell gemacht + backdropBlur: false, // Nicht für Kiosk benötigt + backdropBrightness: false, + backdropContrast: false, + backdropFilter: false, + backdropGrayscale: false, + backdropHueRotate: false, + backdropInvert: false, + backdropOpacity: false, + backdropSaturate: false, + backdropSepia: false, + blur: false, + brightness: false, + contrast: false, + dropShadow: false, + filter: false, + grayscale: false, + hueRotate: false, + invert: false, + saturate: false, + sepia: false, + + // Touch-spezifische Features deaktivieren + touchAction: false, + + // Nicht benötigte Layout-Features + aspectRatio: false, + backdropFilter: false, + + // Reduzierte Transform-Features + scale: false, + skew: false, + transformOrigin: false, + }, + + // Kiosk-spezifische Plugins deaktivieren + plugins: [], + + // Aggressive Purge-Konfiguration + purge: { + enabled: true, + content: [ + './templates/**/*.html', + './static/js/**/*.js', + ], + // Aggressive Purging + options: { + safelist: [ + // Nur essenzielle Klassen behalten + 'container', + 'flex', + 'grid', + 'hidden', + 'block', + 'inline', + 'w-full', + 'h-full', + 'text-center', + 'font-bold', + 'text-primary', + 'bg-white', + 'border', + 'rounded', + 'p-4', + 'm-4', + 'btn', + 'card', + 'nav', + 'header', + 'status-online', + 'status-offline', + 'status-printing', + ], + // Dynamische Klassen-Erkennung + defaultExtractor: content => content.match(/[\w-/:]+(? r.json()).then(console.log) +``` + +## 🔧 Fehlerbehebung + +### SSL-Zertifikat-Probleme + +**Problem**: `SSL_ERROR_SELF_SIGNED_CERT` + +**Lösung**: +1. Backend direkt im Browser öffnen: `https://192.168.0.105:443` +2. Zertifikat manuell akzeptieren +3. "Erweitert" → "Weiter zu 192.168.0.105" + +**Alternative**: +```bash +# curl mit ignoriertem SSL +curl -k https://192.168.0.105:443/health +``` + +### Netzwerk-Verbindungsprobleme + +**Problem**: `Connection refused` oder `Network unreachable` + +**Diagnose**: +```bash +# 1. IP-Erreichbarkeit prüfen +ping 192.168.0.105 + +# 2. Port-Verfügbarkeit prüfen +nmap -p 443 192.168.0.105 + +# 3. Firewall-Status prüfen (auf Backend-Server) +sudo ufw status +``` + +**Lösung**: +```bash +# Auf Backend-Server (192.168.0.105): +sudo ufw allow 443/tcp +sudo ufw allow from 192.168.0.0/24 to any port 443 +``` + +### CORS-Probleme + +**Problem**: `Access-Control-Allow-Origin` Fehler + +**Diagnose**: +```bash +# CORS-Header prüfen +curl -k -H "Origin: http://localhost:3000" \ + -H "Access-Control-Request-Method: GET" \ + -H "Access-Control-Request-Headers: Content-Type" \ + -X OPTIONS \ + https://192.168.0.105:443/api/printers +``` + +**Lösung**: Backend-CORS-Konfiguration prüfen +```python +# Auf Backend-Server: app.py +from flask_cors import CORS + +app = Flask(__name__) +CORS(app, origins=['http://localhost:3000', 'http://192.168.0.*']) +``` + +### Backend-Service-Probleme + +**Problem**: Backend antwortet nicht + +**Diagnose**: +```bash +# Auf Backend-Server (192.168.0.105): +sudo systemctl status myp-backend +sudo journalctl -u myp-backend -f +``` + +**Lösung**: +```bash +# Service neu starten +sudo systemctl restart myp-backend + +# Oder manuell starten +cd /path/to/backend +python app.py --host 0.0.0.0 --port 443 +``` + +## 📊 Monitoring + +### Real-time Verbindungsüberwachung + +```bash +# Kontinuierlicher Health Check +watch -n 5 'curl -k -s https://192.168.0.105:443/health | jq .' + +# Netzwerk-Latenz überwachen +ping -c 10 192.168.0.105 + +# Port-Monitoring +nmap -p 443 192.168.0.105 +``` + +### Frontend-Logs überwachen + +```bash +# Frontend-Logs in Echtzeit +cd frontend +pnpm dev + +# Browser-Konsole (F12) überwachen: +console.log('Backend URL:', API_BASE_URL); +``` + +### Backend-Logs überwachen + +```bash +# Auf Backend-Server: +tail -f /var/log/myp-backend.log +sudo journalctl -u myp-backend -f +``` + +## 🧪 Automatisierte Tests + +### Frontend-Test-Script erstellen + +```javascript +// test-backend-connection.js +const API_BASE_URL = 'https://192.168.0.105:443'; + +async function testConnection() { + try { + // Health Check + const health = await fetch(`${API_BASE_URL}/health`, { + method: 'GET', + }); + console.log('✅ Health Check:', await health.json()); + + // Printers API + const printers = await fetch(`${API_BASE_URL}/api/printers`); + console.log('✅ Printers API:', await printers.json()); + + // Jobs API + const jobs = await fetch(`${API_BASE_URL}/api/jobs`); + console.log('✅ Jobs API:', await jobs.json()); + + } catch (error) { + console.error('❌ Verbindungsfehler:', error); + } +} + +testConnection(); +``` + +```bash +# Test ausführen +node test-backend-connection.js +``` + +### Backend-Test-Script + +```bash +#!/bin/bash +# test-backend.sh + +echo "🔍 Backend-Verbindungstest" +echo "==========================" + +# 1. Ping-Test +echo "1. Ping-Test..." +if ping -c 1 192.168.0.105 > /dev/null; then + echo "✅ Server erreichbar" +else + echo "❌ Server nicht erreichbar" + exit 1 +fi + +# 2. Port-Test +echo "2. Port 443 Test..." +if nc -z 192.168.0.105 443; then + echo "✅ Port 443 offen" +else + echo "❌ Port 443 nicht erreichbar" + exit 1 +fi + +# 3. SSL-Test +echo "3. SSL-Verbindungstest..." +if curl -k -s https://192.168.0.105:443/health > /dev/null; then + echo "✅ SSL-Verbindung erfolgreich" +else + echo "❌ SSL-Verbindung fehlgeschlagen" + exit 1 +fi + +# 4. API-Tests +echo "4. API-Endpunkt-Tests..." +if curl -k -s https://192.168.0.105:443/api/printers > /dev/null; then + echo "✅ Drucker-API verfügbar" +else + echo "❌ Drucker-API nicht verfügbar" +fi + +if curl -k -s https://192.168.0.105:443/api/jobs > /dev/null; then + echo "✅ Jobs-API verfügbar" +else + echo "❌ Jobs-API nicht verfügbar" +fi + +echo "" +echo "🎉 Verbindungstest abgeschlossen!" +``` + +```bash +# Test ausführen +chmod +x test-backend.sh +./test-backend.sh +``` + +## 📋 Checkliste + +### Vor dem ersten Start + +- [ ] Backend-Server unter `192.168.0.105` läuft +- [ ] Port 443 ist geöffnet und erreichbar +- [ ] SSL-Zertifikat ist konfiguriert (selbstsigniert OK) +- [ ] CORS ist für Frontend-Domain konfiguriert +- [ ] API-Endpunkte sind verfügbar + +### Bei Problemen prüfen + +- [ ] Netzwerk-Konnektivität (`ping 192.168.0.105`) +- [ ] Port-Verfügbarkeit (`telnet 192.168.0.105 443`) +- [ ] SSL-Zertifikat manuell akzeptiert +- [ ] Backend-Service läuft (`systemctl status myp-backend`) +- [ ] Firewall-Regeln korrekt konfiguriert +- [ ] Frontend-Konfiguration korrekt (`API_BASE_URL`) + +### Bei erfolgreicher Verbindung + +- [ ] Health Check gibt Status zurück +- [ ] Drucker-API gibt Daten zurück +- [ ] Jobs-API ist funktional +- [ ] Frontend kann Backend-Daten anzeigen +- [ ] Keine CORS-Fehler in Browser-Konsole + +--- + +**Ziel**: Nahtlose Kommunikation zwischen Frontend (`localhost:3000`) und Backend (`https://192.168.0.105:443`) +**Status**: Bereit für Integration +**Support**: Siehe Browser-Konsole und Backend-Logs bei Problemen \ No newline at end of file diff --git a/frontend/setup.sh b/frontend/setup.sh new file mode 100644 index 00000000..4dfb6f5b --- /dev/null +++ b/frontend/setup.sh @@ -0,0 +1,695 @@ +#!/bin/bash + +# =================================================================== +# MYP Frontend - KONSOLIDIERTES SETUP-SKRIPT +# Automatische Installation und Konfiguration des Frontend-Servers +# Optimiert für Debian/Linux mit Docker und Caddy Reverse Proxy +# HTTPS mit Mercedes SSL-Zertifikaten auf m040tbaraspi001.de040.corpintra.net +# Version: 1.0.0 +# =================================================================== + +set -euo pipefail + +# =========================== GLOBALE KONFIGURATION =========================== +readonly APP_NAME="MYP Frontend" +readonly APP_VERSION="1.0.0" +readonly FRONTEND_DIR="/opt/myp-frontend" +readonly SSL_DIR="/etc/ssl/certs/myp" +readonly DOCKER_COMPOSE_SERVICE="myp-frontend" +readonly DOMAIN="m040tbaraspi001.de040.corpintra.net" +readonly CURRENT_DIR="$(pwd)" +readonly INSTALL_LOG="/var/log/myp-frontend-install.log" +readonly CADDY_LOG_DIR="/var/log/caddy" + +# Farben für Ausgabe +readonly RED='\033[0;31m' +readonly GREEN='\033[0;32m' +readonly YELLOW='\033[1;33m' +readonly BLUE='\033[0;34m' +readonly PURPLE='\033[0;35m' +readonly CYAN='\033[0;36m' +readonly NC='\033[0m' + +# =========================== LOGGING-FUNKTIONEN =========================== +log() { + echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}" | tee -a "$INSTALL_LOG" +} + +error() { + echo -e "${RED}[FEHLER] $1${NC}" | tee -a "$INSTALL_LOG" + exit 1 +} + +warning() { + echo -e "${YELLOW}[WARNUNG] $1${NC}" | tee -a "$INSTALL_LOG" +} + +info() { + echo -e "${BLUE}[INFO] $1${NC}" | tee -a "$INSTALL_LOG" +} + +progress() { + echo -e "${PURPLE}[FORTSCHRITT] $1${NC}" | tee -a "$INSTALL_LOG" +} + +success() { + echo -e "${CYAN}[ERFOLG] $1${NC}" | tee -a "$INSTALL_LOG" +} + +# =========================== SYSTEM-VALIDIERUNG =========================== +check_root() { + if [ "$EUID" -ne 0 ]; then + error "Dieses Skript muss als Root ausgeführt werden: sudo $0" + fi + export PATH="/usr/sbin:/sbin:/usr/bin:/bin:/usr/local/bin:$PATH" + log "✅ Root-Berechtigung bestätigt" +} + +check_debian_system() { + if [ ! -f /etc/debian_version ]; then + error "Dieses Skript ist nur für Debian/Raspbian-Systeme geeignet!" + fi + + local debian_version=$(cat /etc/debian_version 2>/dev/null || echo "Unbekannt") + log "✅ Debian/Raspbian-System erkannt (Version: $debian_version)" + + # Prüfe auf Raspberry Pi + if [ -f /proc/device-tree/model ]; then + local pi_model=$(cat /proc/device-tree/model 2>/dev/null || echo "Unbekannt") + info "Raspberry Pi Modell: $pi_model" + fi +} + +check_internet_connection() { + progress "Prüfe Internetverbindung..." + + local test_urls=("8.8.8.8" "1.1.1.1" "google.com") + local connection_ok=false + + for url in "${test_urls[@]}"; do + if ping -c 1 -W 3 "$url" >/dev/null 2>&1; then + connection_ok=true + break + fi + done + + if [ "$connection_ok" = true ]; then + log "✅ Internetverbindung verfügbar" + else + warning "⚠️ Keine Internetverbindung - Installation könnte fehlschlagen" + fi +} + +# =========================== SYSTEM-VORBEREITUNG =========================== +update_system() { + log "=== SYSTEM-UPDATE ===" + + progress "Aktualisiere Paketlisten..." + apt-get update -y || error "APT Update fehlgeschlagen" + + progress "Installiere grundlegende System-Tools..." + apt-get install -y \ + curl \ + wget \ + git \ + nano \ + htop \ + rsync \ + unzip \ + sudo \ + ca-certificates \ + gnupg \ + lsb-release \ + apt-transport-https \ + software-properties-common \ + openssl \ + || error "Grundlegende Tools Installation fehlgeschlagen" + + log "✅ System-Update abgeschlossen" +} + +# =========================== DOCKER INSTALLATION =========================== +install_docker() { + log "=== DOCKER INSTALLATION ===" + + # Prüfe ob Docker bereits installiert ist + if command -v docker >/dev/null 2>&1; then + info "Docker ist bereits installiert" + docker --version + else + progress "Installiere Docker..." + + # Docker GPG-Schlüssel hinzufügen + curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg + + # Docker Repository hinzufügen + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null + + # Paketlisten aktualisieren und Docker installieren + apt-get update -y + apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin + + # Docker-Service aktivieren + systemctl enable docker + systemctl start docker + + log "✅ Docker erfolgreich installiert" + fi + + # Prüfe ob Docker Compose bereits installiert ist + if command -v docker compose >/dev/null 2>&1; then + info "Docker Compose ist bereits installiert" + docker compose version + else + progress "Installiere Docker Compose..." + apt-get install -y docker-compose-plugin || error "Docker Compose Installation fehlgeschlagen" + log "✅ Docker Compose erfolgreich installiert" + fi + + # Docker-Service Status prüfen + if systemctl is-active --quiet docker; then + success "✅ Docker läuft erfolgreich" + else + error "❌ Docker konnte nicht gestartet werden" + fi +} + +# =========================== SSL-ZERTIFIKAT GENERIERUNG =========================== +generate_mercedes_ssl_certificate() { + log "=== MERCEDES SSL-ZERTIFIKAT GENERIERUNG ===" + + progress "Erstelle SSL-Verzeichnis..." + mkdir -p "$SSL_DIR" + + progress "Generiere Mercedes SSL-Zertifikat für $DOMAIN..." + + # Erstelle OpenSSL-Konfigurationsdatei für Subject Alternative Names + cat > "$SSL_DIR/openssl.conf" << EOF +[req] +distinguished_name = req_distinguished_name +req_extensions = v3_req +prompt = no + +[req_distinguished_name] +C = DE +ST = Baden-Wuerttemberg +L = Stuttgart +O = Mercedes-Benz AG +OU = IT-Abteilung +CN = $DOMAIN + +[v3_req] +keyUsage = keyEncipherment, dataEncipherment +extendedKeyUsage = serverAuth +subjectAltName = @alt_names + +[alt_names] +DNS.1 = $DOMAIN +DNS.2 = m040tbaraspi001 +DNS.3 = localhost +DNS.4 = raspberrypi +IP.1 = 127.0.0.1 +IP.2 = 192.168.0.109 +EOF + + # Generiere privaten Schlüssel + progress "Generiere privaten Schlüssel..." + openssl genrsa -out "$SSL_DIR/frontend.key" 4096 || error "Fehler beim Generieren des privaten Schlüssels" + + # Generiere Zertifikat mit SAN (Subject Alternative Names) + progress "Generiere SSL-Zertifikat..." + openssl req -new -x509 -key "$SSL_DIR/frontend.key" -out "$SSL_DIR/frontend.crt" -days 365 \ + -config "$SSL_DIR/openssl.conf" \ + -extensions v3_req \ + || error "Fehler beim Generieren des SSL-Zertifikats" + + # Berechtigungen setzen + chmod 600 "$SSL_DIR/frontend.key" + chmod 644 "$SSL_DIR/frontend.crt" + + # Zertifikat in System-CA-Store hinzufügen + progress "Füge Zertifikat zum System-CA-Store hinzu..." + cp "$SSL_DIR/frontend.crt" "/usr/local/share/ca-certificates/$DOMAIN.crt" + update-ca-certificates + + # Zertifikat-Informationen anzeigen + info "Zertifikat-Details:" + openssl x509 -in "$SSL_DIR/frontend.crt" -text -noout | grep -E "(Subject:|DNS:|IP Address:)" || true + + log "✅ Mercedes SSL-Zertifikat erfolgreich generiert" +} + +# =========================== ANWENDUNG DEPLOYMENT =========================== +deploy_frontend_application() { + log "=== FRONTEND-ANWENDUNG DEPLOYMENT ===" + + progress "Erstelle Frontend-Verzeichnis..." + mkdir -p "$FRONTEND_DIR" + + progress "Kopiere Frontend-Dateien..." + rsync -av --exclude=node_modules --exclude=.git --exclude=ssl "$CURRENT_DIR/" "$FRONTEND_DIR/" + + # Stelle sicher, dass die richtigen Berechtigungen gesetzt sind + chown -R root:root "$FRONTEND_DIR" + chmod -R 755 "$FRONTEND_DIR" + + log "✅ Frontend-Anwendung erfolgreich deployed" +} + +# =========================== CADDY LOG-VERZEICHNIS =========================== +create_caddy_logs() { + log "=== CADDY LOG-VERZEICHNISSE ERSTELLEN ===" + + progress "Erstelle Caddy Log-Verzeichnisse..." + mkdir -p "$CADDY_LOG_DIR" + chmod 755 "$CADDY_LOG_DIR" + + # Erstelle leere Log-Dateien + touch "$CADDY_LOG_DIR/access.log" + touch "$CADDY_LOG_DIR/error.log" + chmod 644 "$CADDY_LOG_DIR"/*.log + + log "✅ Caddy Log-Verzeichnisse erstellt" +} + +# =========================== DOCKER COMPOSE KONFIGURATION =========================== +create_docker_compose_config() { + log "=== DOCKER COMPOSE KONFIGURATION ===" + + progress "Erstelle Docker Compose Konfiguration..." + + cat > "$FRONTEND_DIR/docker-compose.yml" << EOF +version: '3.8' + +services: + # Frontend Next.js Application + frontend-app: + build: + context: . + dockerfile: Dockerfile + container_name: myp-frontend-app + environment: + - NODE_ENV=production + - NEXT_TELEMETRY_DISABLED=1 + networks: + - myp-network + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:3000/health"] + interval: 30s + timeout: 10s + retries: 3 + depends_on: + - db + + # Caddy Reverse Proxy + caddy: + image: caddy:latest + container_name: myp-caddy + ports: + - "80:80" + - "443:443" + volumes: + - ./docker/caddy/Caddyfile:/etc/caddy/Caddyfile:ro + - $SSL_DIR:/etc/ssl/certs/myp:ro + - $CADDY_LOG_DIR:/var/log/caddy + - caddy_data:/data + - caddy_config:/config + networks: + - myp-network + restart: unless-stopped + healthcheck: + test: ["CMD", "caddy", "version"] + interval: 30s + timeout: 10s + retries: 3 + depends_on: + - frontend-app + + # Database (SQLite with volume for persistence) + db: + image: alpine:latest + container_name: myp-db + volumes: + - db_data:/data + command: ["sh", "-c", "mkdir -p /data && tail -f /dev/null"] + networks: + - myp-network + restart: unless-stopped + +volumes: + caddy_data: + driver: local + caddy_config: + driver: local + db_data: + driver: local + +networks: + myp-network: + driver: bridge +EOF + + log "✅ Docker Compose Konfiguration erstellt" +} + +# =========================== SYSTEMD SERVICE =========================== +create_systemd_service() { + log "=== SYSTEMD SERVICE KONFIGURATION ===" + + progress "Erstelle systemd Service für Frontend..." + + cat > "/etc/systemd/system/$DOCKER_COMPOSE_SERVICE.service" << EOF +[Unit] +Description=MYP Frontend Docker Compose Service +Requires=docker.service +After=docker.service +StartLimitIntervalSec=0 + +[Service] +Type=oneshot +RemainAfterExit=yes +WorkingDirectory=$FRONTEND_DIR +ExecStart=/usr/bin/docker compose up -d +ExecStop=/usr/bin/docker compose down +ExecReload=/usr/bin/docker compose restart +TimeoutStartSec=300 +TimeoutStopSec=120 +Restart=on-failure +RestartSec=30 +User=root +Group=root + +# Environment +Environment=COMPOSE_PROJECT_NAME=myp-frontend + +# Sicherheitseinstellungen +NoNewPrivileges=yes +PrivateTmp=yes +ProtectHome=yes +ProtectSystem=strict +ReadWritePaths=$FRONTEND_DIR $SSL_DIR $CADDY_LOG_DIR + +[Install] +WantedBy=multi-user.target +EOF + + # Systemd-Konfiguration neu laden + systemctl daemon-reload + + log "✅ Systemd Service erstellt" +} + +# =========================== SERVICES STARTEN =========================== +start_frontend_services() { + log "=== FRONTEND-SERVICES STARTEN ===" + + progress "Wechsle in Frontend-Verzeichnis..." + cd "$FRONTEND_DIR" + + # Docker Images erstellen + progress "Erstelle Docker Images..." + docker compose build || error "Docker Build fehlgeschlagen" + + # Services aktivieren und starten + progress "Aktiviere und starte Frontend-Service..." + systemctl enable "$DOCKER_COMPOSE_SERVICE" || error "Fehler beim Aktivieren des Frontend-Service" + systemctl start "$DOCKER_COMPOSE_SERVICE" || error "Fehler beim Starten des Frontend-Service" + + # Warte auf Service-Start + progress "Warte auf Service-Start..." + sleep 30 + + # Service-Status prüfen + if systemctl is-active --quiet "$DOCKER_COMPOSE_SERVICE"; then + success "✅ Frontend-Service läuft erfolgreich" + else + error "❌ Frontend-Service konnte nicht gestartet werden" + fi + + # Docker Container-Status prüfen + progress "Prüfe Container-Status..." + docker compose ps + + cd "$CURRENT_DIR" + log "✅ Frontend-Services erfolgreich gestartet" +} + +# =========================== SYSTEM-TEST =========================== +test_frontend_application() { + log "=== FRONTEND-SYSTEM-TEST ===" + + progress "Teste HTTPS-Verbindung zu $DOMAIN..." + + # Warte auf Service-Start + local max_attempts=60 + local attempt=1 + + while [ $attempt -le $max_attempts ]; do + if curl -k -s --connect-timeout 5 "https://$DOMAIN/health" >/dev/null 2>&1; then + success "✅ Frontend erreichbar unter https://$DOMAIN" + break + fi + + # Teste auch localhost + if curl -k -s --connect-timeout 5 "https://localhost/health" >/dev/null 2>&1; then + success "✅ Frontend erreichbar unter https://localhost" + break + fi + + progress "Warte auf Frontend... ($attempt/$max_attempts)" + sleep 5 + ((attempt++)) + done + + if [ $attempt -gt $max_attempts ]; then + error "❌ Frontend nicht erreichbar nach $max_attempts Versuchen" + fi + + # Teste SSL-Zertifikat + progress "Teste SSL-Zertifikat..." + if openssl s_client -connect localhost:443 -servername "$DOMAIN" /dev/null | openssl x509 -noout -text >/dev/null 2>&1; then + success "✅ SSL-Zertifikat gültig" + else + warning "⚠️ SSL-Zertifikat-Test fehlgeschlagen" + fi + + # Teste Container-Gesundheit + progress "Teste Container-Gesundheit..." + cd "$FRONTEND_DIR" + + local unhealthy_containers=$(docker compose ps --format json | jq -r '.[] | select(.Health == "unhealthy") | .Name' 2>/dev/null || echo "") + + if [ -n "$unhealthy_containers" ]; then + warning "⚠️ Ungesunde Container gefunden: $unhealthy_containers" + else + success "✅ Alle Container sind gesund" + fi + + cd "$CURRENT_DIR" + log "✅ System-Test abgeschlossen" +} + +# =========================== FIREWALL-KONFIGURATION =========================== +configure_frontend_firewall() { + log "=== FIREWALL-KONFIGURATION ===" + + progress "Konfiguriere UFW-Firewall für Frontend..." + + # UFW installieren falls nicht vorhanden + if ! command -v ufw >/dev/null 2>&1; then + apt-get install -y ufw || error "UFW Installation fehlgeschlagen" + fi + + # UFW zurücksetzen + ufw --force reset + + # Standard-Richtlinien + ufw default deny incoming + ufw default allow outgoing + + # SSH erlauben (wichtig für Remote-Zugang) + ufw allow ssh + + # HTTP und HTTPS erlauben + ufw allow 80/tcp + ufw allow 443/tcp + + # Lokale Verbindungen erlauben + ufw allow from 127.0.0.1 + ufw allow from ::1 + + # Internes Netzwerk erlauben (Mercedes-Netzwerk) + ufw allow from 192.168.0.0/16 + ufw allow from 10.0.0.0/8 + ufw allow from 172.16.0.0/12 + + # UFW aktivieren + ufw --force enable + + # Firewall-Status anzeigen + ufw status verbose + + log "✅ Firewall für Frontend konfiguriert" +} + +# =========================== HAUPTMENÜ =========================== +show_menu() { + clear + echo -e "${CYAN}=================================================================${NC}" + echo -e "${CYAN} $APP_NAME - Setup-Skript v$APP_VERSION${NC}" + echo -e "${CYAN}=================================================================${NC}" + echo "" + echo -e "${YELLOW}Bitte wählen Sie eine Option:${NC}" + echo "" + echo -e "${GREEN}1)${NC} Vollständige Frontend-Installation" + echo -e " ${BLUE}→ Docker, SSL-Zertifikate, Caddy Reverse Proxy${NC}" + echo -e " ${BLUE}→ Frontend verfügbar unter https://$DOMAIN${NC}" + echo -e " ${BLUE}→ Automatischer Start beim Boot${NC}" + echo "" + echo -e "${GREEN}2)${NC} Nur SSL-Zertifikate neu generieren" + echo -e " ${BLUE}→ Erstellt neue Mercedes SSL-Zertifikate${NC}" + echo -e " ${BLUE}→ Startet Services neu${NC}" + echo "" + echo -e "${GREEN}3)${NC} Service-Status prüfen" + echo -e " ${BLUE}→ Zeigt Status aller Frontend-Services${NC}" + echo -e " ${BLUE}→ Container-Logs und Gesundheitsprüfung${NC}" + echo "" + echo -e "${GREEN}4)${NC} Beenden" + echo "" + echo -e "${CYAN}=================================================================${NC}" + echo -n "Ihre Wahl [1-4]: " +} + +# =========================== INSTALLATIONS-MODI =========================== +install_full_frontend() { + log "=== VOLLSTÄNDIGE FRONTEND-INSTALLATION ===" + + check_root + check_debian_system + check_internet_connection + + update_system + install_docker + generate_mercedes_ssl_certificate + deploy_frontend_application + create_caddy_logs + create_docker_compose_config + create_systemd_service + configure_frontend_firewall + start_frontend_services + test_frontend_application + + success "✅ Vollständige Frontend-Installation abgeschlossen!" + info "Frontend ist verfügbar unter:" + info " 🌐 https://$DOMAIN" + info " 🌐 https://localhost" + info " 🔒 SSL-Zertifikate: $SSL_DIR" + info " 📁 Anwendung: $FRONTEND_DIR" + info " 📋 Logs: $CADDY_LOG_DIR" + info "" + info "Service-Befehle:" + info " systemctl status $DOCKER_COMPOSE_SERVICE" + info " systemctl restart $DOCKER_COMPOSE_SERVICE" + info " docker compose logs -f (in $FRONTEND_DIR)" +} + +regenerate_ssl_certificates() { + log "=== SSL-ZERTIFIKATE NEU GENERIEREN ===" + + check_root + + # Stoppe Services + progress "Stoppe Frontend-Services..." + systemctl stop "$DOCKER_COMPOSE_SERVICE" 2>/dev/null || true + + # Neue Zertifikate generieren + generate_mercedes_ssl_certificate + + # Services neu starten + progress "Starte Frontend-Services neu..." + systemctl start "$DOCKER_COMPOSE_SERVICE" || error "Fehler beim Neustarten der Services" + + # Test + test_frontend_application + + success "✅ SSL-Zertifikate erfolgreich erneuert!" +} + +check_service_status() { + log "=== SERVICE-STATUS PRÜFUNG ===" + + info "Systemd Service Status:" + systemctl status "$DOCKER_COMPOSE_SERVICE" --no-pager || true + + echo "" + info "Docker Container Status:" + if [ -d "$FRONTEND_DIR" ]; then + cd "$FRONTEND_DIR" + docker compose ps 2>/dev/null || true + + echo "" + info "Container-Logs (letzte 20 Zeilen):" + docker compose logs --tail=20 2>/dev/null || true + + cd "$CURRENT_DIR" + else + warning "Frontend-Verzeichnis nicht gefunden: $FRONTEND_DIR" + fi + + echo "" + info "Netzwerk-Tests:" + curl -k -s -I "https://$DOMAIN/health" 2>/dev/null | head -1 || echo "❌ $DOMAIN nicht erreichbar" + curl -k -s -I "https://localhost/health" 2>/dev/null | head -1 || echo "❌ localhost nicht erreichbar" + + echo "" + info "SSL-Zertifikat Info:" + if [ -f "$SSL_DIR/frontend.crt" ]; then + openssl x509 -in "$SSL_DIR/frontend.crt" -noout -dates 2>/dev/null || echo "❌ Zertifikat-Lesefehler" + else + echo "❌ Zertifikat nicht gefunden: $SSL_DIR/frontend.crt" + fi +} + +# =========================== HAUPTPROGRAMM =========================== +main() { + # Erstelle Log-Datei + mkdir -p "$(dirname "$INSTALL_LOG")" + touch "$INSTALL_LOG" + + # Zeige Menü + while true; do + show_menu + read -r choice + + case $choice in + 1) + install_full_frontend + break + ;; + 2) + regenerate_ssl_certificates + break + ;; + 3) + check_service_status + echo "" + echo -e "${YELLOW}Drücken Sie Enter um fortzufahren...${NC}" + read -r + ;; + 4) + log "Setup beendet" + exit 0 + ;; + *) + echo -e "${RED}Ungültige Auswahl. Bitte wählen Sie 1-4.${NC}" + echo "" + echo -e "${YELLOW}Drücken Sie Enter um fortzufahren...${NC}" + read -r + ;; + esac + done +} + +# =========================== SCRIPT AUSFÜHRUNG =========================== +main "$@" \ No newline at end of file