From 1c9ccfb794f257d3df646e27342d692100a54a5c Mon Sep 17 00:00:00 2001 From: Emirhan3 Date: Thu, 24 Sep 2026 16:04:03 +0000 Subject: [PATCH 01/27] feat: Navigationsleiste --- src/components/navigationsbar/navigation.html | 116 +++++++++ src/components/navigationsbar/navigation.js | 51 ++++ src/styles/navigation.css | 223 ++++++++++++++++++ 3 files changed, 390 insertions(+) create mode 100644 src/components/navigationsbar/navigation.html create mode 100644 src/components/navigationsbar/navigation.js create mode 100644 src/styles/navigation.css diff --git a/src/components/navigationsbar/navigation.html b/src/components/navigationsbar/navigation.html new file mode 100644 index 0000000..7fa68c1 --- /dev/null +++ b/src/components/navigationsbar/navigation.html @@ -0,0 +1,116 @@ + + + + + + + TriHub Navigation + + + + + + + +
+

Vorschau der Navigation

+

Dieser Bereich dient nur zum Testen der Navbar.

+
+ + + + diff --git a/src/components/navigationsbar/navigation.js b/src/components/navigationsbar/navigation.js new file mode 100644 index 0000000..eaf7d71 --- /dev/null +++ b/src/components/navigationsbar/navigation.js @@ -0,0 +1,51 @@ +(function () { + var toggle = document.querySelector(".navbar__toggle"); + var nav = document.querySelector(".navbar__nav"); + var dropdownToggle = document.querySelector(".navbar__dropdown-toggle"); + var dropdown = document.querySelector("#dropdown-spektrum"); + + if (!toggle || !nav || !dropdownToggle || !dropdown) return; + + // Mobiles Menü öffnen/schließen + toggle.addEventListener("click", function () { + var isOpen = nav.classList.toggle("is-open"); + toggle.setAttribute("aria-expanded", String(isOpen)); + toggle.setAttribute( + "aria-label", + isOpen ? "Menü schließen" : "Menü öffnen", + ); + }); + + // Dropdown "Triathlon Spektrum" per Klick öffnen/schließen + dropdownToggle.addEventListener("click", function () { + var isOpen = dropdown.classList.toggle("is-open"); + dropdownToggle.setAttribute("aria-expanded", String(isOpen)); + }); + + // Escape schließt Dropdown/Menü, Fokus geht zurück auf den Auslöser + document.addEventListener("keydown", function (e) { + if (e.key !== "Escape") return; + + if (dropdown.classList.contains("is-open")) { + dropdown.classList.remove("is-open"); + dropdownToggle.setAttribute("aria-expanded", "false"); + dropdownToggle.focus(); + } + if (nav.classList.contains("is-open")) { + nav.classList.remove("is-open"); + toggle.setAttribute("aria-expanded", "false"); + toggle.focus(); + } + }); + + // Klick außerhalb schließt das Dropdown + document.addEventListener("click", function (e) { + if ( + !dropdown.contains(e.target) && + !dropdownToggle.contains(e.target) + ) { + dropdown.classList.remove("is-open"); + dropdownToggle.setAttribute("aria-expanded", "false"); + } + }); +})(); diff --git a/src/styles/navigation.css b/src/styles/navigation.css new file mode 100644 index 0000000..e249cf0 --- /dev/null +++ b/src/styles/navigation.css @@ -0,0 +1,223 @@ +@import url("https://fonts.googleapis.com/css2?family=Playfair+Display:wght@700&family=Inter:wght@400;500;600&display=swap"); + +* { + box-sizing: border-box; +} + +body { + margin: 0; + padding-top: 72px; /* Platz für die fixierte Navbar */ + background-color: #0d0d1a; + font-family: "Inter", system-ui, sans-serif; +} + +/* ---------- Kriterium 1: durchgehend fixiert am oberen Bildschirmrand ---------- */ +.navbar { + position: fixed; + top: 0; + left: 0; + width: 100%; + z-index: 1000; + display: flex; + align-items: center; + justify-content: space-between; + height: 72px; + padding: 0 2rem; + background-color: #0d0d1a; + border-bottom: 1px solid #1a1a2e; +} + +/* ---------- Logo ---------- */ +.navbar__logo { + display: flex; + flex-direction: column; + line-height: 1.2; + color: #fff; + text-decoration: none; +} + +.navbar__logo-title { + font-family: "Playfair Display", serif; + font-weight: 700; + font-size: 1.1rem; + letter-spacing: 0.05em; +} + +.navbar__logo-tagline { + font-size: 0.7rem; + color: #94a3b8; +} + +/* ---------- Menü ---------- */ +.navbar__menu { + display: flex; + align-items: center; + gap: 2rem; + list-style: none; + margin: 0; + padding: 0; +} + +.navbar__link { + display: flex; + align-items: center; + gap: 0.35rem; + background: none; + border: none; + color: #f8fafc; + text-decoration: none; + font-size: 0.95rem; + font-family: inherit; + cursor: pointer; + padding: 0.5rem 0.25rem; +} + +.navbar__link:hover { + color: #10b981; +} + +.navbar__link--login { + padding: 0.5rem 1.25rem; + border: 1px solid #10b981; + border-radius: 999px; + color: #10b981; +} + +.navbar__link--login:hover { + background: #10b981; + color: #0d0d1a; +} + +/* ---------- Dropdown ---------- */ +.navbar__item--has-dropdown { + position: relative; +} + +.navbar__chevron { + width: 10px; + height: 6px; + transition: transform 0.15s ease; +} + +.navbar__dropdown-toggle[aria-expanded="true"] .navbar__chevron { + transform: rotate(180deg); +} + +.navbar__dropdown { + display: none; + position: absolute; + top: calc(100% + 0.5rem); + left: 0; + min-width: 180px; + background: #1a1a2e; + border: 1px solid #262640; + border-radius: 8px; + list-style: none; + margin: 0; + padding: 0.5rem; +} + +.navbar__dropdown.is-open { + display: block; +} + +.navbar__dropdown-link { + display: block; + padding: 0.5rem 0.75rem; + color: #f8fafc; + text-decoration: none; + font-size: 0.9rem; + border-radius: 4px; +} + +.navbar__dropdown-link:hover { + background: #0d0d1a; + color: #10b981; +} + +/* ---------- Kriterium 2: barrierefreie Tastatursteuerung ---------- */ +.skip-link { + position: absolute; + top: -40px; + left: 0; + background: #10b981; + color: #0d0d1a; + font-weight: 600; + padding: 0.75rem 1rem; + z-index: 1100; + transition: top 0.2s ease; +} + +.skip-link:focus { + top: 0; +} + +.navbar a:focus-visible, +.navbar button:focus-visible { + outline: 3px solid #10b981; + outline-offset: 2px; + border-radius: 4px; +} + +/* ---------- Mobile Toggle ---------- */ +.navbar__toggle { + display: none; + background: none; + border: none; + cursor: pointer; + padding: 0.5rem; +} + +.navbar__toggle-icon, +.navbar__toggle-icon::before, +.navbar__toggle-icon::after { + display: block; + width: 24px; + height: 2px; + background: #fff; + position: relative; +} + +.navbar__toggle-icon::before, +.navbar__toggle-icon::after { + content: ""; + position: absolute; +} +.navbar__toggle-icon::before { + top: -8px; +} +.navbar__toggle-icon::after { + top: 8px; +} + +@media (max-width: 860px) { + .navbar__toggle { + display: block; + } + + .navbar__nav { + position: absolute; + top: 72px; + left: 0; + right: 0; + background: #0d0d1a; + border-bottom: 1px solid #1a1a2e; + display: none; + } + + .navbar__nav.is-open { + display: block; + } + + .navbar__menu { + flex-direction: column; + align-items: flex-start; + padding: 1rem 2rem; + gap: 1rem; + } + + .navbar__dropdown { + position: static; + margin-top: 0.5rem; + } +} From 6bf2e170151cf609b380a810ed881675042523b1 Mon Sep 17 00:00:00 2001 From: Marvin Gemlin Date: Thu, 24 Sep 2026 16:11:54 +0000 Subject: [PATCH 02/27] feat: Paketbuchung mit CSV, SMTP und fortlaufenden Buchungsnummern --- .env.example | 12 + .gitignore | 6 + .prettierignore | 4 + booking.html | 16 + docs/booking.md | 353 ++++++++++++++++++++ package-lock.json | 72 +++++ package.json | 12 +- playwright.config.js | 20 ++ server/routes/.gitkeep | 0 server/services/.gitkeep | 0 src/features/booking/booking-api.js | 49 +++ src/features/booking/booking-entry.js | 13 + src/features/booking/booking-page.js | 276 ++++++++++++++++ src/features/booking/booking.css | 96 ++++++ src/main.js | 2 +- {server => src/server}/data/.gitkeep | 0 src/server/index.js | 87 +++++ src/server/routes/bookings.js | 75 +++++ src/server/services/booking-service.js | 203 ++++++++++++ src/server/services/csv-storage.js | 88 +++++ src/server/services/email-service.js | 74 +++++ src/shared/booking-copy.js | 4 + src/shared/packages.js | 14 + src/tests/bookings.test.js | 427 +++++++++++++++++++++++++ src/tests/browser/booking.spec.js | 181 +++++++++++ src/tests/helpers/browser-server.js | 23 ++ vite.config.js | 29 ++ 27 files changed, 2134 insertions(+), 2 deletions(-) create mode 100644 .env.example create mode 100644 booking.html create mode 100644 docs/booking.md create mode 100644 playwright.config.js delete mode 100644 server/routes/.gitkeep delete mode 100644 server/services/.gitkeep create mode 100644 src/features/booking/booking-api.js create mode 100644 src/features/booking/booking-entry.js create mode 100644 src/features/booking/booking-page.js create mode 100644 src/features/booking/booking.css rename {server => src/server}/data/.gitkeep (100%) create mode 100644 src/server/index.js create mode 100644 src/server/routes/bookings.js create mode 100644 src/server/services/booking-service.js create mode 100644 src/server/services/csv-storage.js create mode 100644 src/server/services/email-service.js create mode 100644 src/shared/booking-copy.js create mode 100644 src/shared/packages.js create mode 100644 src/tests/bookings.test.js create mode 100644 src/tests/browser/booking.spec.js create mode 100644 src/tests/helpers/browser-server.js create mode 100644 vite.config.js diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..2968da5 --- /dev/null +++ b/.env.example @@ -0,0 +1,12 @@ +# Lokal nur einen Mailfänger verwenden. Keine echten Zugangsdaten eintragen. +HOST=127.0.0.1 +PORT=3000 +BOOKING_DEMO=true +SMTP_HOST=127.0.0.1 +SMTP_PORT=1025 +SMTP_SECURE=false +SMTP_USER= +SMTP_PASS= +SMTP_FROM="Tri-Hub " +# Für echtes SMTP erst nach ausdrücklicher Freigabe ändern. +SMTP_ALLOW_EXTERNAL=false diff --git a/.gitignore b/.gitignore index cc9ffb7..e872025 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,9 @@ dist/ .DS_Store Thumbs.db *.log + +# Private Buchungsdaten und lokale Testergebnisse +src/server/data/* +!src/server/data/.gitkeep +playwright-report/ +test-results/ diff --git a/.prettierignore b/.prettierignore index 320c107..790904b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,7 @@ node_modules/ dist/ package-lock.json + +src/server/data/ +playwright-report/ +test-results/ diff --git a/booking.html b/booking.html new file mode 100644 index 0000000..3a92cba --- /dev/null +++ b/booking.html @@ -0,0 +1,16 @@ + + + + + + + Paket buchen – Tri-Hub + + +
+ + + diff --git a/docs/booking.md b/docs/booking.md new file mode 100644 index 0000000..5f0541d --- /dev/null +++ b/docs/booking.md @@ -0,0 +1,353 @@ +# US2.4 – Paket buchen + +## Stand und Zusammenarbeit + +Entwicklung auf `feature/paket-buchen-backend`, späteres PR-Ziel: `dev`. +Zu Beginn waren HEAD und frisch abgerufenes `origin/dev` identisch. Kein Merge, +Commit oder Push wurde durchgeführt. Es gab keinen Router, keine Paketdaten und +kein Backend. Die Implementierung verwendet die vereinbarte Startstruktur: +Backend unter `src/server/`, gemeinsame Paketdaten unter `src/shared/` und +Tests unter `src/tests/`. Die früheren Doppelordner im Projektwurzelverzeichnis +wurden in diese vorhandenen Ordner überführt. + +## So funktioniert der Ablauf + +1. Der Browser lädt die verfügbaren Pakete über `GET /api/angebote`. +2. Das Formular sendet Paket-ID, Name und E-Mail an `POST /api/bookings`. + Es merkt sich dafür einen zufälligen Anfrageschlüssel (Idempotency-Key). +3. Der Server prüft die Angaben und liest den Paketnamen aus `src/shared/packages.js`. +4. Er speichert Buchungsnummer, Zeitpunkt, Angaben und Schlüssel in der CSV. +5. Erst danach übergibt er die Bestätigung an SMTP und aktualisiert den Versandstatus. +6. Bei einem erneuten Versuch mit denselben Daten und demselben Schlüssel gibt + er die vorhandene Buchung zurück. Es entsteht keine zweite Bestellung. + +## Lokal starten + +Node.js 24 ist im vorhandenen Dev Container vorgesehen. Alle Befehle laufen im +Projektverzeichnis. Vite und Backend benötigen jeweils ein eigenes Terminal. + +```bash +npm ci +cp .env.example .env +``` + +Eine vorhandene `.env` vorher prüfen und nicht überschreiben. Die Beispielwerte +aktivieren das klar gekennzeichnete Testpaket und verwenden nur einen Mailfänger. +Ohne `.env` bleibt der Demo-Modus ausgeschaltet und der echte Paketkatalog leer. + +Einmalig [Mailpit installieren](https://mailpit.axllent.org/docs/install/). +Es läuft als einzelne Binärdatei auch direkt im Dev Container. Kein Docker im +Container erforderlich. Mailpit ohne Weiterleitung an echte SMTP-Server starten: + +```bash +mailpit --listen 127.0.0.1:8025 --smtp 127.0.0.1:1025 --disable-version-check +``` + +In Terminal 2 das Backend starten: + +```bash +npm run dev:server +``` + +In Terminal 3 das Frontend starten: + +```bash +npm run dev +``` + +Öffnen: . +Mailfänger: . Im Dev Container bei Bedarf Port 8025 über +VS Codes Ansicht „Ports“ weiterleiten. Port 5173 ist bereits vorkonfiguriert. +Port 3000 muss für den Browser nicht weitergeleitet werden: Vite vermittelt `/api`. +Diese Beschreibung setzt voraus, dass Mailpit im selben Container läuft. + +`npm run start:server` startet das Backend ohne automatischen Neustart. +Bestehende Befehle `dev`, `build`, `preview`, `format` und `format:check` bleiben erhalten. +`npm run preview` zeigt ausschließlich den gebauten Frontend-Stand. Für eine +vollständige Buchung im Deployment muss der Webserver `/api` separat an Node +weiterleiten; der Vite-Entwicklungsproxy ist kein Produktionsserver. + +## Konfiguration + +Alle SMTP-Einstellungen werden ausschließlich in Node geladen. Niemals mit einem +`VITE_`-Präfix versehen: Solche Variablen könnten im Browser landen. + +| Variable | Lokaler Beispielwert | Bedeutung | +| ------------------------- | -------------------------------- | --------------------------------------------------------------------- | +| `HOST` | `127.0.0.1` | Bind-Adresse des Backends | +| `PORT` | `3000` | Backend-Port; bei Änderung auch Vite-Proxy anpassen | +| `BOOKING_DEMO` | `true` | Fügt ausschließlich lokal das Testpaket hinzu; in Produktion gesperrt | +| `SMTP_HOST` | `127.0.0.1` | Mailfänger im selben Container | +| `SMTP_PORT` | `1025` | SMTP-Port des Mailfängers | +| `SMTP_SECURE` | `false` | Bei echtem SMTP mit direktem TLS üblicherweise `true` auf 465 | +| `SMTP_USER` / `SMTP_PASS` | leer | Beide gemeinsam setzen, falls Authentifizierung benötigt wird | +| `SMTP_FROM` | `Tri-Hub ` | Absender; später abgestimmten echten Absender verwenden | +| `SMTP_ALLOW_EXTERNAL` | `false` | Externes SMTP gesperrt; erst nach ausdrücklicher Freigabe aktivieren | + +Für externe SMTP-Hosts wird TLS verlangt; Zertifikatsprüfung bleibt aktiv. +Für STARTTLS (typischerweise Port 587) bleibt `SMTP_SECURE=false`. +Die `.env.example` enthält nur Testwerte und keine Geheimnisse. +[SMTP-Optionen bei Nodemailer](https://nodemailer.com/smtp). + +## Paketdaten und spätere Router-Anbindung + +Person 3 pflegt `packages` in **`src/shared/packages.js`**. +Das derzeit leere Array enthält später ausschließlich abgestimmte Angebote: + +```js +// Nur Schema-Beispiel, kein verbindliches Angebot: +{ id: "stabile-paket-id", name: "Abgestimmter Paketname", summary: "Kurze Beschreibung" } +``` + +IDs: eindeutig, maximal 80 Zeichen, Kleinbuchstaben/Ziffern mit einzelnen +Bindestrichen. `name` und `summary` sind nicht leere Strings. IDs nach Freigabe +nicht für andere Angebote wiederverwenden. Das Backend prüft den Vertrag beim Start. +Das separate `demoPackages`-Array in derselben Datei enthält nur `demo-booking`, +explizit als Test markiert. Es wird mit `BOOKING_DEMO=true` zugeschaltet. +Keine Preise wurden erfunden. Sobald verbindliche Preisfelder vereinbart sind, +müssen Anzeige und serverseitiger Buchungssnapshot gemeinsam ergänzt werden. +Angaben wie `packageName` oder `price` aus API-Anfragen werden nicht übernommen. + +Die Angebotsseite kann vorerst auf die eigenständige Ansicht verlinken: + +```js +link.href = `/booking.html#/buchen/${encodeURIComponent(paket.id)}`; +``` + +Es wurde kein Router in die gemeinsame Startseite eingebaut. Wenn Person 2 den +Router ergänzt, kann sie bei `/#/buchen/:packageId` diese Funktion aufrufen: + +```js +import { mountBookingPage } from "./features/booking/booking-page.js"; +const cleanup = mountBookingPage(container, { packageId }); +// Beim Verlassen der Route: +cleanup(); +``` + +Das Modul bringt seine begrenzten `.booking`-Styles mit. Farben und Schrift +orientieren sich am vorhandenen Basis-CSS. Gemeinsame Designvariablen und eine +Button-Komponente gibt es bisher nicht. Layout und Navigation bleiben Aufgabe +der anderen Teammitglieder. „Zur Angebotsseite“ zeigt vorerst auf `/#beratung`. + +## API-Beispiele + +`GET /api/angebote` liefert `{ "packages": [...] }` mit `id`, `name`, `summary` +und `testOnly`. Keine personenbezogenen Daten oder SMTP-Einstellungen. + +Für `POST /api/bookings` sind `Content-Type: application/json` und ein +`Idempotency-Key` als UUID v4 erforderlich. Ein Schlüssel gehört genau zu einer +Buchung. Bei Wiederholung denselben Schlüssel und dieselben Angaben verwenden. + +```http +POST /api/bookings +Content-Type: application/json +Idempotency-Key: 36c45d7f-585f-47e9-bf5c-7ea1b7a162ed + +{"packageId":"demo-booking","name":"Test Person","email":"person@example.test"} +``` + +Neue Buchung mit SMTP-Annahme: HTTP `201`; gespeicherte Wiederholung: `200`. + +```json +{ + "bookingId": "TH-000001", + "createdAt": "2026-09-24T12:00:00.000Z", + "packageId": "demo-booking", + "packageName": "1:1-Ernährungsberatung", + "saved": true, + "emailStatus": "accepted", + "replayed": false +} +``` + +Gespeichert, aber E-Mail nicht bestätigt: HTTP `202` mit derselben Struktur und +`emailStatus: "failed"`, `"pending"` oder `"unknown"`. Das bedeutet ausdrücklich +nicht, dass die E-Mail zugestellt wurde. Die Buchungsnummer bleibt gültig. + +| Status/Code | Bedeutung und Verhalten | +| ------------------------------ | ----------------------------------------------------------------------- | +| `400 INVALID_INPUT` | Angaben korrigieren; Feldhinweise stehen in `error.fields` | +| `400 UNKNOWN_PACKAGE` | Paket nicht verfügbar; verfügbares Paket auswählen | +| `400 INVALID_KEY` | UUID-v4-Anfrageschlüssel fehlt oder ist ungültig | +| `400 INVALID_JSON` | Anfrage ist kein gültiges JSON | +| `409 IDEMPOTENCY_CONFLICT` | Derselbe Schlüssel wurde mit anderen Angaben verwendet | +| `413 BODY_TOO_LARGE` | Mehr als 8 KiB Anfrageinhalt | +| `415 UNSUPPORTED_CONTENT_TYPE` | JSON-Content-Type erforderlich | +| `503 BOOKING_NOT_SAVED` | Speicherung fehlgeschlagen; mit gleichem Schlüssel wiederholen | +| `503 STORAGE_UNAVAILABLE` | Vorhandener Status nicht lesbar; mit gleichem Schlüssel wiederholen | +| `500 INTERNAL_ERROR` | Status unklar; keine neue Buchung erzeugen, gleiche Anfrage wiederholen | + +Die E-Mail-Prüfung unterstützt übliche unquotierte ASCII-Adressen. Internationale +Domains können in Punycode angegeben werden; SMTPUTF8-Adressen sind nicht Teil +dieses Sprints. Eine Formatprüfung beweist nicht, dass das Postfach existiert. + +Beispiel für einen Validierungsfehler: + +```json +{ + "error": { + "code": "INVALID_INPUT", + "message": "Bitte die markierten Angaben prüfen.", + "fields": { "email": "Bitte eine gültige E-Mail-Adresse eingeben." } + } +} +``` + +## Simulation und spätere Zahlung + +Das Demonstrationspaket heißt in der Oberfläche „1:1-Ernährungsberatung“. +Die ID `demo-booking` und die Freigabe über `BOOKING_DEMO` bleiben unverändert; +es wurden keine verbindlichen Angebote, Leistungsumfänge oder Preise ergänzt. +Ein gemeinsamer Hinweis aus `src/shared/booking-copy.js` erklärt vor dem Absenden +und in der E-Mail, dass der Ablauf simuliert ist: Der Zahlungsvorgang wird +übersprungen, es wird nichts abgebucht und keine verbindliche Beratung gebucht. +Bei vollständiger Integration kann der Zahlungsschritt ergänzt werden. Dafür +müssen tatsächliche Zahlungsbestätigung und Buchungsstatus serverseitig verbunden +werden; das Entfernen des Hinweises allein aktiviert keine Zahlung. + +## Fortlaufende Buchungsnummern + +Neue Buchungen erhalten `TH-000001`, `TH-000002` usw. Dieselbe Nummer steht in der +Antwort, CSV und E-Mail. Sie wird innerhalb der Warteschlange aus dem höchsten +vorhandenen TH-Wert berechnet und mit der Buchung gespeichert. Ein Neustart setzt +sie nicht zurück; Wiederholungen behalten dieselbe Nummer. Sechs Stellen sind +die Mindestbreite, nach `TH-999999` folgt `TH-1000000`. Es gibt keinen Jahresreset. + +Bestehende UUID-Buchungen und gespeicherte Browserbestätigungen behalten ihre +bisherige Nummer, damit bereits ausgegebene Referenzen gültig bleiben. Nur neue +Buchungen verwenden das neue Format. Der technische Idempotency-Key bleibt eine +zufällige UUID; die lesbare Buchungsnummer ist kein Geheimnis oder Zugriffstoken. + +Der Zähler setzt voraus, dass die CSV vollständig erhalten bleibt. Löschen von +Zeilen, Entfernen der Datei oder Zurückspielen eines älteren Backups kann Nummern +wiederverwendbar machen. Vor einer Archivierungs-/Löschfunktion ist ein separat +persistierter, transaktionaler Zähler nötig. Weiterhin nur ein Backend-Prozess. + +## CSV, Wiederholungen und Fehlergrenzen + +Pfad: `src/server/data/bookings.csv`. Spalten: `bookingId`, `createdAt`, `packageId`, +`packageName`, `name`, `email`, `emailStatus`, `idempotencyKey`, `requestHash`. +`requestHash` verknüpft den Schlüssel mit den normalisierten Anfragedaten, ohne +sie in einer zweiten Datenquelle zu duplizieren. Bereits gespeicherte Buchungen +können auch nach dem Entfernen eines Pakets weiterhin wiederholt abgefragt werden. + +CSV-Bibliotheken behandeln Kommas, Anführungszeichen und Zeilenumbrüche. Zellen, +die wie Tabellenformeln beginnen, erhalten ein Apostroph. Bereits führende +Apostrophe werden ebenfalls maskiert, damit internes Lesen die Originalwerte +wiederherstellen kann. Persönliche Namen dürfen in der API keine Steuerzeichen +enthalten; die Speicherschicht unterstützt Zeilenumbrüche für andere CSV-Felder. +Dateien werden mit Zugriffsmodus `0600` geschrieben. + +Eine Warteschlange führt komplette Buchungen nacheinander aus. Schreiben erfolgt +über eine temporäre Datei und atomisches Umbenennen. **Nur ein laufender +Backend-Prozess auf einem lokalen Dateisystem wird unterstützt.** Keine Cluster, +mehreren Containerinstanzen, manuelle Dateiedits während des Betriebs oder +Netzlaufwerke. Die gesamte CSV wird bei Änderungen gelesen/neu geschrieben; +geeignet für den ersten Sprint mit kleinem Volumen. Es gibt keine verteilte +Transaktion oder vollständige Garantie gegen Hardware-/Stromausfall. + +Versandzustände: + +- `pending`: Buchung gespeichert, Versand noch nicht gestartet. Derselbe Schlüssel + darf den Versand nach einem Schreibfehler fortsetzen. +- `sending`: Versandabsicht dauerhaft gespeichert. Bei Prozessabbruch zeigt die + API diesen Zustand als `unknown`; kein automatischer Neuversand. +- `accepted`: SMTP hat den Empfänger und die Nachricht angenommen. +- `failed`: Der Versand ist fehlgeschlagen bzw. wurde ausdrücklich abgelehnt. +- `unknown`: Verbindung oder Statusspeicherung brach zu einem unklaren Zeitpunkt ab. + +Für `failed` und `unknown` gibt es bewusst keinen automatischen erneuten Versand. +Erst CSV und Mailserver/Mailfänger anhand der Buchungsnummer prüfen. Eine +administrative Wiederholungsfunktion ist offen; keinesfalls eine neue Buchung +anlegen oder `sending` blind zurücksetzen. SMTP und CSV können nicht gemeinsam +atomar abgeschlossen werden, weshalb ein exakt einmaliger Versand nicht garantiert +werden kann. Ein Verbindungsabbruch nach SMTP-Annahme kann doppelte Mails verursachen, +wenn später ohne Prüfung manuell erneut gesendet wird. + +Das Formular speichert Schlüssel und Angaben je Paket in `sessionStorage`, also +für den aktuellen Browser-Tab, auch über Neuladen hinweg. Bei unklarem Ergebnis +bleiben die Angaben schreibgeschützt und können mit demselben Schlüssel erneut +geprüft werden. Nach gespeicherter Buchung bleibt das Formular gesperrt. Ein +bewusst neuer Auftrag benötigt einen neuen Tab bzw. eine neue Sitzung. Das +Schließen des Tabs, gelöschter Sitzungsspeicher, ein anderes Gerät oder ein neuer +Schlüssel liegen außerhalb der Doppelbuchungsabsicherung. Bei unklarem Ausgang +zuerst den bestehenden Status prüfen. Es gibt keine globale Erkennung anhand +von Name/E-Mail, weil zwei bewusst getrennte Buchungen möglich sein müssen. + +`.gitignore` schließt Buchungsdaten einschließlich temporärer Dateien und `.env` +aus. Das Backend liefert ausschließlich API-Antworten aus. Vite blockiert +Serververzeichnis und Umgebungsdateien auch beim direkten Dateizugriff. Im +Deployment nur `dist/` öffentlich bereitstellen, nie den gesamten Projektordner. + +## Prüfen + +Automatisierte Tests erzeugen temporäre CSV-Dateien und simulieren E-Mail-Versand. +Sie schreiben nicht in `src/server/data/bookings.csv` und kontaktieren kein echtes SMTP. +Vor Browsertests lokale Server auf 3000/5173 stoppen; Playwright startet eigene +Testserver und übernimmt keine bereits laufenden Instanzen. + +```bash +npm test +npx playwright install --with-deps chromium +npm run test:browser +npm run format:check +npm run build +``` + +Die einmalige Chromium-Installation benötigt Netzwerk und unter Linux eventuell +Systempakete. Der Dev Container wurde dafür nicht global umkonfiguriert. + +Manuelle Abnahme im lokalen Mailfänger: + +1. Demo-Modus, Mailpit, Backend und Vite wie oben starten. +2. Testpaket mit `person@example.test` buchen. Buchungsnummer notieren. +3. CSV im Editor öffnen: genau eine passende Zeile, Paket, Zeitpunkt und Versandstatus prüfen. +4. In Mailpit die Nachricht öffnen: Empfänger, Paket und Buchungsnummer vergleichen. +5. Seite neu laden: gleiche Buchungsnummer, keine neue CSV-Zeile und keine zweite Mail. +6. Mit Tab/Shift+Tab und Enter bedienen, Feldfehler und sichtbaren Fokus prüfen. + Auf 320 Pixel Breite und mit Browser-Zoom prüfen; ergänzend Screenreader verwenden. +7. Für den Versandfehler einen neuen Test in neuer Sitzung bei gestopptem Mailpit + ausführen: Buchung bleibt gespeichert, Anzeige meldet keine erfolgreiche Zustellung. + +**Das Akzeptanzkriterium „E-Mail zugestellt“ ist mit SMTP-Annahme oder Mailpit allein +noch nicht für einen echten Empfänger nachgewiesen.** Erst nach ausdrücklicher +Freigabe echtes SMTP konfigurieren und an eine vereinbarte Testadresse senden. +Dann im tatsächlichen Empfängerpostfach (auch Spam) Eingang, Buchungsnummer und +Paket kontrollieren und Zeitpunkt sowie Ergebnis im Sprint-Nachweis festhalten. +Es gibt bisher keine automatische Bounce-/Zustellverfolgung. + +## Durchgeführte Prüfungen + +- 17 Backend-Tests: Validierung, CSV-Sonderzeichen/Formelschutz, parallele + Anfragen, Wiederholung mit neuer Serviceinstanz, Speicher- und SMTP-Fehler. +- 6 Chromium-Prüfungen: Tastaturbedienung/Fokus, 320-Pixel-Ansicht, verlorene + Antwort mit Neuladen, Ladezustand, Versandfehler und private Dateipfade. +- Lokaler SMTP-Test mit Mailpit und temporärer CSV: Nachricht tatsächlich im + Mailfänger empfangen, Paket/Buchungsnummer geprüft, wiederholte Anfrage ohne + zusätzliche CSV-Zeile oder E-Mail. Ausschließlich `example.test`-Empfänger. +- Projektweite Formatprüfung, Produktionsbuild und Git-Whitespace-Prüfung. + +Die automatisierten Prüfungen ersetzen keine Screenreader-Abnahme oder Prüfung +in weiteren Browsern. Keine echte E-Mail-Zustellung wurde getestet. Zum Ausführen +der Browserprüfungen wurden Chromium und die benötigten Systembibliotheken in +dieser Arbeitsumgebung installiert; die Dev-Container-Konfigurationsdatei wurde +nicht verändert. Der Mailpit-Test verwendete eine temporär heruntergeladene +Binärdatei außerhalb des Repositorys. + +## Offene Punkte und gemeinsame Dateien + +Offen: echte Paketdaten, Anschluss an den gemeinsamen Router, abschließendes +Teamdesign, reale Zustellprüfung und Screenreader-Abnahme. Vor öffentlichem +Betrieb sind außerdem Betriebsfragen wie Zugriff auf CSV/Backups, Aufbewahrung +und Schutz des öffentlichen Buchungsendpunkts vor automatisiertem Massenversand +zu klären. Keine Zahlungen, Benutzerkonten oder Datenbank implementiert. + +Gemeinsame Änderungen: `package.json` und Lockdatei (Abhängigkeiten/Startbefehle), +`src/main.js` (bestehenden falschen CSS-Import korrigiert), `.gitignore` und +`.prettierignore` (private Daten/Testergebnisse ausschließen). Neu hinzugefügt: +`vite.config.js` (Proxy, Dateizugriffsschutz, zwei HTML-Einstiegspunkte), +`src/shared/packages.js` (gemeinsamer Paketvertrag), `.env.example`, `booking.html` +und `playwright.config.js`. Die Dev-Container-Konfiguration bleibt unverändert. + +Technische Referenzen: [CSV-Parser](https://csv.js.org/parse/api/sync/), +[Playwright-Testserver](https://playwright.dev/docs/test-webserver). diff --git a/package-lock.json b/package-lock.json index d186ecb..7e92738 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,13 @@ "": { "name": "ernaehrungsberatung", "version": "0.1.0", + "dependencies": { + "csv-parse": "^7.0.2", + "csv-stringify": "^6.8.3", + "nodemailer": "^10.0.10" + }, "devDependencies": { + "@playwright/test": "^1.63.0", "prettier": "^3.6.2", "vite": "^7.1.7" }, @@ -477,6 +483,22 @@ "node": "^22.20 || ^24.12 || >=25" } }, + "node_modules/@playwright/test": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.63.0.tgz", + "integrity": "sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.63.5", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.5.tgz", @@ -873,6 +895,18 @@ "dev": true, "license": "MIT" }, + "node_modules/csv-parse": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-7.0.2.tgz", + "integrity": "sha512-uKZghv9UmPkMVLYy//KZ9HFAIJsl7wkhoEdIL0+rhuSY9pZQlhaeGEDPIe+/w7eh81MOql8Q/9+inAGWG6ZHYA==", + "license": "MIT" + }, + "node_modules/csv-stringify": { + "version": "6.8.3", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.8.3.tgz", + "integrity": "sha512-gIeSCvq5F4VtXV3naV3VAewLhBkiZBz+PPhTOA8H3Y8h/ELa+R1ml0GZck/4/Nzo9ep2lvOluilJ6MJlbZsKMA==", + "license": "MIT" + }, "node_modules/esbuild": { "version": "0.28.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", @@ -967,6 +1001,15 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/nodemailer": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-10.0.10.tgz", + "integrity": "sha512-He9XskOFms62SyAKkLu8CcGcYHAo+BSHSsORI8s4PJH8qZgZY4L4lDfvyN0twvmbpyG+eGrxpyBlskj9d9rJ8A==", + "license": "MIT-0", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -987,6 +1030,35 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz", + "integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright-core": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz", + "integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/postcss": { "version": "8.5.28", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.28.tgz", diff --git a/package.json b/package.json index f8e5685..2af1f0a 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,20 @@ "build": "vite build", "preview": "vite preview --host 0.0.0.0 --port 5173 --strictPort", "format": "prettier --write .", - "format:check": "prettier --check ." + "format:check": "prettier --check .", + "dev:server": "node --env-file-if-exists=.env --watch src/server/index.js", + "start:server": "node --env-file-if-exists=.env src/server/index.js", + "test": "node --test src/tests/bookings.test.js", + "test:browser": "playwright test" }, "devDependencies": { + "@playwright/test": "^1.63.0", "prettier": "^3.6.2", "vite": "^7.1.7" + }, + "dependencies": { + "csv-parse": "^7.0.2", + "csv-stringify": "^6.8.3", + "nodemailer": "^10.0.10" } } diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 0000000..892f592 --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,20 @@ +import { defineConfig } from "@playwright/test"; + +export default defineConfig({ + testDir: "./src/tests/browser", + workers: 1, + use: { baseURL: "http://127.0.0.1:5173", browserName: "chromium" }, + webServer: [ + { + command: "node src/tests/helpers/browser-server.js", + url: "http://127.0.0.1:3000/api/angebote", + reuseExistingServer: false, + gracefulShutdown: { signal: "SIGTERM", timeout: 3000 }, + }, + { + command: "npm run dev", + url: "http://127.0.0.1:5173/booking.html", + reuseExistingServer: false, + }, + ], +}); diff --git a/server/routes/.gitkeep b/server/routes/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/server/services/.gitkeep b/server/services/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/src/features/booking/booking-api.js b/src/features/booking/booking-api.js new file mode 100644 index 0000000..65d8613 --- /dev/null +++ b/src/features/booking/booking-api.js @@ -0,0 +1,49 @@ +export class BookingApiError extends Error { + constructor(message, code, fields = {}) { + super(message); + this.code = code; + this.fields = fields; + } +} + +async function request(path, options = {}) { + let response; + let body; + try { + response = await fetch(path, { + ...options, + signal: options.signal ?? AbortSignal.timeout(45000), + }); + body = await response.json(); + } catch (error) { + if (options.signal?.aborted) throw error; + throw new BookingApiError( + "Die Verbindung ist unterbrochen oder der Server nicht erreichbar. Deine Angaben bleiben erhalten. Bitte den Status mit derselben Anfrage erneut prüfen.", + "NETWORK_ERROR", + ); + } + if (!response.ok) { + throw new BookingApiError( + body.error?.message || + "Die Anfrage konnte nicht verarbeitet werden.", + body.error?.code, + body.error?.fields, + ); + } + return body; +} + +export function loadPackages(signal) { + return request("/api/angebote", { signal }); +} + +export function submitBooking(payload, idempotencyKey) { + return request("/api/bookings", { + method: "POST", + headers: { + "Content-Type": "application/json", + "Idempotency-Key": idempotencyKey, + }, + body: JSON.stringify(payload), + }); +} diff --git a/src/features/booking/booking-entry.js b/src/features/booking/booking-entry.js new file mode 100644 index 0000000..81d6433 --- /dev/null +++ b/src/features/booking/booking-entry.js @@ -0,0 +1,13 @@ +import "../../styles/styles.css"; +import { mountBookingPage } from "./booking-page.js"; + +const root = document.querySelector("#booking-root"); +let cleanup; +function render() { + cleanup?.(); + // Eigenständige Testseite, bis Person 2 den gemeinsamen Router bereitstellt. + const match = location.hash.match(/^#\/buchen\/([a-z0-9-]+)$/); + cleanup = mountBookingPage(root, { packageId: match?.[1] }); +} +window.addEventListener("hashchange", render); +render(); diff --git a/src/features/booking/booking-page.js b/src/features/booking/booking-page.js new file mode 100644 index 0000000..e15a656 --- /dev/null +++ b/src/features/booking/booking-page.js @@ -0,0 +1,276 @@ +import { loadPackages, submitBooking } from "./booking-api.js"; +import "./booking.css"; +import { paymentNotice } from "../../shared/booking-copy.js"; + +// Kann später vom gemeinsamen Router aufgerufen werden. cleanup entfernt +// Listener und bricht das Laden beim Verlassen der Ansicht ab. +export function mountBookingPage(root, { packageId } = {}) { + const controller = new AbortController(); + const { signal } = controller; + root.innerHTML = ` +
+ Zur Angebotsseite +

Paket buchen

+

Paket wird geladen …

+
+
`; + const intro = root.querySelector(".booking__intro"); + const content = root.querySelector(".booking__content"); + + async function load() { + if (!packageId) { + intro.textContent = + "Bitte wähle zuerst ein Paket auf der Angebotsseite aus."; + return; + } + try { + const data = await loadPackages(signal); + if (signal.aborted) return; + const selected = data.packages.find( + (entry) => entry.id === packageId, + ); + if (!selected) { + intro.textContent = + "Dieses Paket ist nicht verfügbar. Bitte wähle ein Paket auf der Angebotsseite aus."; + return; + } + intro.remove(); + renderForm(selected); + } catch { + if (signal.aborted) return; + intro.textContent = + "Das Paket konnte nicht geladen werden. Bitte prüfe deine Verbindung und versuche es erneut."; + const retry = document.createElement("button"); + retry.className = "booking__button"; + retry.textContent = "Paket erneut laden"; + retry.addEventListener( + "click", + () => { + retry.remove(); + intro.textContent = "Paket wird geladen …"; + load(); + }, + { once: true, signal }, + ); + content.replaceChildren(retry); + } + } + + function renderForm(selected) { + content.innerHTML = ` +
+

+

+
+ +
+

Alle Felder sind Pflichtfelder.

+ +
+ + +

+
+
+ + +

An diese Adresse senden wir deine Bestätigung.

+

+
+ +

+
+ `; + content.querySelector(".booking__package-name").textContent = + selected.name; + content.querySelector(".booking__summary").textContent = + selected.summary; + content.querySelector(".booking__payment-notice").textContent = + paymentNotice; + const form = content.querySelector("form"); + const name = form.elements.namedItem("name"); + const email = form.elements.namedItem("email"); + const button = form.querySelector("button"); + const errors = form.querySelector(".booking__errors"); + const progress = form.querySelector(".booking__progress"); + const result = content.querySelector(".booking__result"); + const storageKey = `trihub.booking.${selected.id}`; + let attempt = null; + let busy = false; + let finished = false; + + function lockFields(locked) { + name.readOnly = locked; + email.readOnly = locked; + } + function showError(message, fields = {}) { + errors.textContent = message; + errors.hidden = false; + for (const field of [name, email]) { + const message = fields[field.name] || ""; + content.querySelector( + `#booking-${field.name}-error`, + ).textContent = message; + field.setAttribute("aria-invalid", String(Boolean(message))); + } + errors.focus(); + } + function clearErrors() { + errors.hidden = true; + for (const field of [name, email]) { + field.removeAttribute("aria-invalid"); + content.querySelector( + `#booking-${field.name}-error`, + ).textContent = ""; + } + } + function showResult(body, focus = true) { + const messages = { + accepted: + "Buchung gespeichert. Die Bestätigung wurde vom Mailserver zum Versand angenommen. Die Zustellung ist noch nicht bestätigt.", + failed: "Buchung gespeichert, aber der Bestätigungsversand ist fehlgeschlagen. Bitte nicht erneut buchen. Wende dich mit deiner Buchungsnummer an Tri-Hub.", + pending: + "Buchung gespeichert. Der Bestätigungsversand konnte noch nicht gestartet werden. Du kannst den Versand mit der bestehenden Buchung erneut versuchen.", + unknown: + "Buchung gespeichert. Der Versandstatus der Bestätigung ist unklar. Bitte nicht erneut buchen. Wende dich mit deiner Buchungsnummer an Tri-Hub.", + }; + result.textContent = `${messages[body.emailStatus] || messages.unknown} Buchungsnummer: ${body.bookingId}`; + result.hidden = false; + lockFields(true); + finished = body.emailStatus !== "pending"; + button.disabled = finished; + button.textContent = finished + ? "Buchung gespeichert" + : "Bestätigungsversand erneut versuchen"; + if (focus) result.focus(); + } + + try { + const stored = JSON.parse(sessionStorage.getItem(storageKey)); + if (stored?.key && stored.payload?.packageId === selected.id) { + attempt = stored; + name.value = stored.payload.name; + email.value = stored.payload.email; + lockFields(true); + button.textContent = "Status prüfen / erneut versuchen"; + if (stored.result) showResult(stored.result, false); + else + progress.textContent = + "Eine frühere Anfrage ist noch offen. Bitte ihren Status prüfen, bevor du erneut buchst."; + } + } catch { + // Speichern wird vor der ersten Anfrage zwingend erneut geprüft. + } + + form.addEventListener( + "submit", + async (event) => { + event.preventDefault(); + if (busy || finished) return; + clearErrors(); + if (!attempt) { + const fields = {}; + name.value = name.value.trim(); + email.value = email.value.trim(); + if ( + !name.value || + name.value.length > 120 || + /[\x00-\x1f\x7f]/.test(name.value) + ) + fields.name = + "Bitte deinen Namen eingeben (höchstens 120 Zeichen, ohne Zeilenumbrüche)."; + if (!email.validity.valid || email.value.length > 254) + fields.email = + "Bitte eine gültige E-Mail-Adresse eingeben."; + if (Object.keys(fields).length) { + showError( + "Bitte die markierten Angaben prüfen.", + fields, + ); + return; + } + const nextAttempt = { + key: crypto.randomUUID(), + payload: { + packageId: selected.id, + name: name.value, + email: email.value, + }, + }; + try { + sessionStorage.setItem( + storageKey, + JSON.stringify(nextAttempt), + ); + } catch { + showError( + "Der Browser kann die Anfrage nicht für einen sicheren erneuten Versuch merken. Bitte erlaube den Sitzungsspeicher und versuche es erneut.", + ); + return; + } + attempt = nextAttempt; + } + busy = true; + button.disabled = true; + button.textContent = "Buchung wird verarbeitet …"; + form.setAttribute("aria-busy", "true"); + lockFields(true); + progress.textContent = + "Bitte warten. Deine Buchung wird geprüft und gespeichert."; + try { + const body = await submitBooking( + attempt.payload, + attempt.key, + ); + attempt.result = body; + try { + sessionStorage.setItem( + storageKey, + JSON.stringify(attempt), + ); + } catch { + /* Der ursprüngliche Schlüssel bleibt erhalten. */ + } + if (!signal.aborted) showResult(body); + } catch (error) { + // Nur eindeutige Ablehnungen vor dem Speichern erlauben eine + // Korrektur. Bei Verbindungsfehlern bleiben Daten und Key fest. + if ( + [ + "INVALID_INPUT", + "UNKNOWN_PACKAGE", + "INVALID_KEY", + ].includes(error.code) + ) { + attempt = null; + try { + sessionStorage.removeItem(storageKey); + } catch { + /* Keine Anfrage gespeichert. */ + } + lockFields(false); + } + if (!signal.aborted) { + showError(error.message, error.fields); + button.textContent = attempt + ? "Status prüfen / erneut versuchen" + : "Paket buchen"; + } + } finally { + busy = false; + if (!signal.aborted) { + form.removeAttribute("aria-busy"); + progress.textContent = ""; + button.disabled = finished; + } + } + }, + { signal }, + ); + } + load(); + return () => controller.abort(); +} diff --git a/src/features/booking/booking.css b/src/features/booking/booking.css new file mode 100644 index 0000000..0c99b24 --- /dev/null +++ b/src/features/booking/booking.css @@ -0,0 +1,96 @@ +.booking { + max-width: 42rem; + margin-inline: auto; + overflow-wrap: anywhere; +} + +.booking h1 { + max-width: none; +} + +.booking__package, +.booking__result { + padding: 1.25rem; + border: 1px solid currentColor; + border-radius: 0.5rem; + margin-block: 1.5rem; +} + +.booking__package h2 { + margin-top: 0; +} +.booking__payment { + margin-block: 1.5rem; + padding-left: 1rem; + border-left: 3px solid currentColor; +} + +.booking__payment h2 { + font-size: 1.1rem; +} +.booking__field { + margin-block: 1.5rem; +} +.booking label { + display: block; + font-weight: 600; + margin-bottom: 0.35rem; +} +.booking input, +.booking__button { + font: inherit; + border: 1px solid currentColor; + border-radius: 0.25rem; + min-height: 2.75rem; + padding: 0.65rem 0.8rem; +} +.booking input { + width: 100%; + color: inherit; + background: transparent; +} +.booking input[readonly] { + border-style: dashed; +} +.booking__button { + color: #f7faf7; + background: #176448; + cursor: pointer; + max-width: 100%; +} +.booking__button:disabled { + opacity: 0.7; + cursor: default; +} +.booking :focus-visible, +.booking__errors:focus, +.booking__result:focus { + outline: 3px solid currentColor; + outline-offset: 4px; +} +.booking__button:focus-visible { + outline-color: #18352b; +} +.booking__errors, +.booking__field-error { + color: #8b1e1e; +} +.booking__errors { + padding: 0.8rem; + border: 2px solid currentColor; + margin-block: 1rem; +} +.booking__field p { + margin-block: 0.35rem; +} +.booking__field-error:empty { + display: none; +} +.booking [aria-invalid="true"] { + border: 2px solid #8b1e1e; +} +@media (max-width: 30rem) { + .booking__button { + width: 100%; + } +} diff --git a/src/main.js b/src/main.js index cb01bb8..95efe73 100644 --- a/src/main.js +++ b/src/main.js @@ -1,3 +1,3 @@ -import "./styles.css"; +import "./styles/styles.css"; document.querySelector("#year").textContent = new Date().getFullYear(); diff --git a/server/data/.gitkeep b/src/server/data/.gitkeep similarity index 100% rename from server/data/.gitkeep rename to src/server/data/.gitkeep diff --git a/src/server/index.js b/src/server/index.js new file mode 100644 index 0000000..8fb4c7e --- /dev/null +++ b/src/server/index.js @@ -0,0 +1,87 @@ +import { createServer } from "node:http"; +import { fileURLToPath, pathToFileURL } from "node:url"; +import { packages, demoPackages } from "../shared/packages.js"; +import { createBookingService } from "./services/booking-service.js"; +import { createCsvStorage } from "./services/csv-storage.js"; +import { createEmailService } from "./services/email-service.js"; +import { handleBooking, json } from "./routes/bookings.js"; + +export function createApp({ + catalog = packages, + storage = createCsvStorage( + fileURLToPath(new URL("./data/bookings.csv", import.meta.url)), + ), + sendConfirmation, +} = {}) { + const ids = new Set(); + for (const entry of catalog) { + if ( + !entry || + typeof entry.id !== "string" || + !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(entry.id) || + entry.id.length > 80 || + ids.has(entry.id) || + typeof entry.name !== "string" || + !entry.name.trim() || + typeof entry.summary !== "string" || + !entry.summary.trim() + ) { + throw new Error( + "Paketdaten erfüllen den Vertrag in src/shared/packages.js nicht.", + ); + } + ids.add(entry.id); + } + const service = createBookingService({ + storage, + packages: catalog, + sendConfirmation: sendConfirmation ?? createEmailService(), + }); + return createServer(async (request, response) => { + const path = new URL(request.url, "http://localhost").pathname; + if (path === "/api/angebote" && request.method === "GET") { + return json(response, 200, { + packages: catalog.map(({ id, name, summary, testOnly }) => ({ + id, + name, + summary, + testOnly: Boolean(testOnly), + })), + }); + } + if (path === "/api/bookings") { + if (request.method !== "POST") { + response.setHeader("Allow", "POST"); + return json(response, 405, { + error: { + code: "METHOD_NOT_ALLOWED", + message: "Bitte POST verwenden.", + }, + }); + } + return handleBooking(request, response, service); + } + // Ausschließlich API; keine Auslieferung von CSV oder Serverdateien. + return json(response, 404, { + error: { code: "NOT_FOUND", message: "Adresse nicht gefunden." }, + }); + }); +} + +if ( + process.argv[1] && + import.meta.url === pathToFileURL(process.argv[1]).href +) { + const demo = process.env.BOOKING_DEMO === "true"; + if (demo && process.env.NODE_ENV === "production") + throw new Error("Demo-Modus ist in Produktion gesperrt."); + const port = Number(process.env.PORT || 3000); + const server = createApp({ + catalog: demo ? [...packages, ...demoPackages] : packages, + }); + server.listen(port, process.env.HOST || "127.0.0.1", () => { + console.log( + `Buchungs-API auf Port ${port}${demo ? " (Testpaket aktiviert)" : ""}`, + ); + }); +} diff --git a/src/server/routes/bookings.js b/src/server/routes/bookings.js new file mode 100644 index 0000000..f9f80fd --- /dev/null +++ b/src/server/routes/bookings.js @@ -0,0 +1,75 @@ +import { BookingError } from "../services/booking-service.js"; + +export function json(response, status, body) { + response.writeHead(status, { + "Content-Type": "application/json; charset=utf-8", + "Cache-Control": "no-store", + "X-Content-Type-Options": "nosniff", + }); + response.end(JSON.stringify(body)); +} + +async function readJson(request) { + if ( + request.headers["content-type"]?.split(";")[0].trim() !== + "application/json" + ) { + throw new BookingError( + 415, + "UNSUPPORTED_CONTENT_TYPE", + "Bitte JSON senden.", + ); + } + let size = 0; + const chunks = []; + for await (const chunk of request) { + size += chunk.length; + if (size > 8192) + throw new BookingError( + 413, + "BODY_TOO_LARGE", + "Die Anfrage ist zu groß.", + ); + chunks.push(chunk); + } + try { + return JSON.parse(Buffer.concat(chunks).toString("utf8")); + } catch { + throw new BookingError( + 400, + "INVALID_JSON", + "Die Anfrage enthält kein gültiges JSON.", + ); + } +} + +export async function handleBooking(request, response, service) { + try { + const input = await readJson(request); + const result = await service.book( + input, + request.headers["idempotency-key"], + ); + json(response, result.status, result.body); + } catch (error) { + if (error instanceof BookingError) { + json(response, error.status, { + error: { + code: error.code, + message: error.message, + fields: error.fields, + }, + }); + } else { + // Keine persönlichen Daten oder SMTP-Zugangsdaten ins Log schreiben. + console.error("Buchungsanfrage unerwartet fehlgeschlagen."); + json(response, 500, { + error: { + code: "INTERNAL_ERROR", + message: + "Der Status ist unklar. Bitte dieselbe Anfrage erneut versuchen.", + }, + }); + } + } +} diff --git a/src/server/services/booking-service.js b/src/server/services/booking-service.js new file mode 100644 index 0000000..bb32272 --- /dev/null +++ b/src/server/services/booking-service.js @@ -0,0 +1,203 @@ +import { createHash } from "node:crypto"; + +export class BookingError extends Error { + constructor(status, code, message, fields = {}) { + super(message); + this.status = status; + this.code = code; + this.fields = fields; + } +} + +const uuidPattern = + /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; +// Übliche unquotierte E-Mail-Adressen; einzelne Domainlabels maximal 63 Zeichen. +const emailPattern = + /^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/i; + +function validateInput(input, key) { + if (!uuidPattern.test(key ?? "")) { + throw new BookingError( + 400, + "INVALID_KEY", + "Ein gültiger Idempotency-Key (UUID v4) ist erforderlich.", + ); + } + if (!input || typeof input !== "object" || Array.isArray(input)) { + throw new BookingError( + 400, + "INVALID_INPUT", + "Erwartet wird ein JSON-Objekt.", + ); + } + const fields = {}; + const result = {}; + for (const [field, max, message] of [ + ["packageId", 80, "Bitte ein gültiges Paket auswählen."], + ["name", 120, "Bitte einen Namen mit höchstens 120 Zeichen eingeben."], + [ + "email", + 254, + "Bitte eine gültige E-Mail-Adresse eingeben (maximal 254 Zeichen).", + ], + ]) { + const value = input[field]; + if (typeof value !== "string" || !value.trim() || value.length > max) { + fields[field] = message; + } else { + result[field] = value.trim(); + } + } + if ( + result.packageId && + !/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(result.packageId) + ) { + fields.packageId = "Die Paket-ID ist ungültig."; + } + if (result.name && /[\x00-\x1f\x7f]/.test(result.name)) { + fields.name = + "Bitte den Namen ohne Zeilenumbrüche oder Steuerzeichen eingeben."; + } + if ( + result.email && + (!emailPattern.test(result.email) || + result.email.split("@")[0].length > 64) + ) { + fields.email = "Bitte eine gültige E-Mail-Adresse eingeben."; + } + if (Object.keys(fields).length) { + throw new BookingError( + 400, + "INVALID_INPUT", + "Bitte die markierten Angaben prüfen.", + fields, + ); + } + return result; +} + +// Der höchste gespeicherte Wert ist der persistente Zähler. Die bestehende +// Warteschlange verhindert, dass parallele Anfragen dieselbe Nummer erhalten. +function nextBookingId(records) { + let highest = 0n; + for (const record of records) { + const match = /^TH-([0-9]{6,})$/.exec(record.bookingId); + if (match) { + const number = BigInt(match[1]); + if (number > highest) highest = number; + } + } + return `TH-${String(highest + 1n).padStart(6, "0")}`; +} + +function responseFor(record, replayed) { + // Nach Prozessabbruch während SMTP bleibt die tatsächliche Annahme unklar. + const emailStatus = + record.emailStatus === "sending" ? "unknown" : record.emailStatus; + return { + status: emailStatus === "accepted" ? (replayed ? 200 : 201) : 202, + body: { + bookingId: record.bookingId, + createdAt: record.createdAt, + packageId: record.packageId, + packageName: record.packageName, + saved: true, + emailStatus, + replayed, + }, + }; +} + +export function createBookingService({ storage, sendConfirmation, packages }) { + // Eine Warteschlange umfasst Lesen, Schreiben UND Versand. Nur ein Prozess! + let queue = Promise.resolve(); + async function processBooking(input, key) { + const data = validateInput(input, key); + const requestHash = createHash("sha256") + .update(JSON.stringify(data)) + .digest("hex"); + let records; + try { + records = await storage.readAll(); + } catch { + throw new BookingError( + 503, + "STORAGE_UNAVAILABLE", + "Der Buchungsstatus kann gerade nicht geprüft werden. Bitte mit derselben Anfrage erneut versuchen.", + ); + } + let record = records.find((entry) => entry.idempotencyKey === key); + const replayed = Boolean(record); + if (record && record.requestHash !== requestHash) { + throw new BookingError( + 409, + "IDEMPOTENCY_CONFLICT", + "Dieser Anfrageschlüssel gehört zu anderen Buchungsdaten. Bitte die ursprünglichen Angaben verwenden.", + ); + } + if (record && record.emailStatus !== "pending") + return responseFor(record, true); + if (!record) { + const selected = packages.find( + (entry) => entry.id === data.packageId, + ); + if (!selected) { + throw new BookingError( + 400, + "UNKNOWN_PACKAGE", + "Das ausgewählte Paket ist nicht verfügbar.", + { packageId: "Bitte ein verfügbares Paket auswählen." }, + ); + } + record = { + bookingId: nextBookingId(records), + createdAt: new Date().toISOString(), + packageId: selected.id, + packageName: selected.name, + name: data.name, + email: data.email, + emailStatus: "pending", + idempotencyKey: key, + requestHash, + }; + records.push(record); + try { + await storage.writeAll(records); + } catch { + throw new BookingError( + 503, + "BOOKING_NOT_SAVED", + "Die Buchung konnte nicht gespeichert werden. Bitte erneut versuchen.", + ); + } + } + // Versandabsicht zuerst persistieren. Nach einem Absturz niemals blind + // noch einmal senden: SMTP und CSV bilden keine gemeinsame Transaktion. + record.emailStatus = "sending"; + try { + await storage.writeAll(records); + } catch { + record.emailStatus = "pending"; + return responseFor(record, replayed); + } + try { + await sendConfirmation(record); + record.emailStatus = "accepted"; + } catch (error) { + record.emailStatus = error.deliveryUnknown ? "unknown" : "failed"; + } + try { + await storage.writeAll(records); + } catch { + record.emailStatus = "unknown"; + } + return responseFor(record, replayed); + } + return { + book(input, key) { + const task = queue.then(() => processBooking(input, key)); + queue = task.catch(() => {}); + return task; + }, + }; +} diff --git a/src/server/services/csv-storage.js b/src/server/services/csv-storage.js new file mode 100644 index 0000000..978d146 --- /dev/null +++ b/src/server/services/csv-storage.js @@ -0,0 +1,88 @@ +import { mkdir, open, readFile, rename, rm } from "node:fs/promises"; +import { dirname } from "node:path"; +import { randomUUID } from "node:crypto"; +import { parse } from "csv-parse/sync"; +import { stringify } from "csv-stringify/sync"; + +export const columns = [ + "bookingId", + "createdAt", + "packageId", + "packageName", + "name", + "email", + "emailStatus", + "idempotencyKey", + "requestHash", +]; + +// Apostroph-Präfix schützt Tabellenprogramme. Ein vorhandenes Apostroph wird +// ebenfalls maskiert, damit Lesen/Schreiben die Originalwerte exakt erhält. +function protectCell(value) { + const text = String(value ?? ""); + return /^(?:'|[\t\r\n]|\s*[=+@-])/u.test(text) ? `'${text}` : text; +} + +function restoreCell(value) { + return value.startsWith("'") ? value.slice(1) : value; +} + +export function createCsvStorage(filePath) { + return { + async readAll() { + let content; + try { + content = await readFile(filePath, "utf8"); + } catch (error) { + if (error.code === "ENOENT") return []; + throw error; + } + if (!content.trim()) throw new Error("Leere Buchungsdatei"); + return parse(content, { + bom: true, + columns(header) { + if (header.join(",") !== columns.join(",")) { + throw new Error("Unbekanntes CSV-Format"); + } + return header; + }, + skip_empty_lines: true, + }).map((row) => + Object.fromEntries( + Object.entries(row).map(([key, value]) => [ + key, + restoreCell(value), + ]), + ), + ); + }, + async writeAll(records) { + await mkdir(dirname(filePath), { recursive: true }); + const temporary = `${filePath}.${randomUUID()}.tmp`; + const content = stringify( + records.map((row) => + Object.fromEntries( + columns.map((column) => [ + column, + protectCell(row[column]), + ]), + ), + ), + { header: true, columns, record_delimiter: "\r\n" }, + ); + try { + const handle = await open(temporary, "wx", 0o600); + try { + await handle.writeFile(content, "utf8"); + await handle.sync(); + } finally { + await handle.close(); + } + // Atomarer Austausch im selben Verzeichnis: keine halben Zeilen. + await rename(temporary, filePath); + } finally { + await rm(temporary, { force: true }); + } + }, + }; +} diff --git a/src/server/services/email-service.js b/src/server/services/email-service.js new file mode 100644 index 0000000..5b2f00e --- /dev/null +++ b/src/server/services/email-service.js @@ -0,0 +1,74 @@ +import nodemailer from "nodemailer"; +import { paymentNotice } from "../../shared/booking-copy.js"; + +export function createEmailService( + env = process.env, + makeTransport = nodemailer.createTransport, +) { + const localHosts = ["127.0.0.1", "localhost", "::1", "mailpit"]; + const host = env.SMTP_HOST || "127.0.0.1"; + const local = localHosts.includes(host); + if (!local && env.SMTP_ALLOW_EXTERNAL !== "true") { + throw new Error( + "Externes SMTP ist gesperrt. Erst nach ausdrücklicher Freigabe SMTP_ALLOW_EXTERNAL=true setzen.", + ); + } + const port = Number(env.SMTP_PORT || (local ? 1025 : 587)); + if (!Number.isInteger(port) || port < 1 || port > 65535) + throw new Error("Ungültiger SMTP_PORT"); + if (Boolean(env.SMTP_USER) !== Boolean(env.SMTP_PASS)) + throw new Error("SMTP_USER und SMTP_PASS gemeinsam setzen"); + const transport = makeTransport({ + host, + port, + secure: env.SMTP_SECURE === "true", + requireTLS: !local, + auth: env.SMTP_USER + ? { user: env.SMTP_USER, pass: env.SMTP_PASS } + : undefined, + connectionTimeout: 10000, + greetingTimeout: 10000, + socketTimeout: 20000, + disableFileAccess: true, + disableUrlAccess: true, + }); + const from = env.SMTP_FROM || "Tri-Hub "; + return async function sendConfirmation(booking) { + let info; + try { + info = await transport.sendMail({ + from, + to: { address: booking.email, name: booking.name }, + subject: `Buchungsbestätigung ${booking.bookingId}`, + text: [ + `Hallo ${booking.name},`, + "", + "vielen Dank für deine Buchung. Wir haben deine Angaben erhalten.", + `Buchungsnummer: ${booking.bookingId}`, + `Paket: ${booking.packageName}`, + "", + paymentNotice, + "", + "Dein Tri-Hub-Team", + ].join("\n"), + }); + } catch (error) { + // Bei Verbindungsabbruch kann SMTP die Nachricht schon angenommen + // haben. Nur ausdrückliche Ablehnungen gelten sicher als Fehler. + const explicitRejection = + ["EAUTH", "EENVELOPE", "ECONNECTION", "EDNS"].includes( + error.code, + ) || Number(error.responseCode) >= 400; + error.deliveryUnknown = !explicitRejection; + throw error; + } + if ( + !info.accepted?.some( + (address) => + address.toLowerCase() === booking.email.toLowerCase(), + ) + ) { + throw new Error("Empfänger vom Mailserver nicht angenommen"); + } + }; +} diff --git a/src/shared/booking-copy.js b/src/shared/booking-copy.js new file mode 100644 index 0000000..3fdac03 --- /dev/null +++ b/src/shared/booking-copy.js @@ -0,0 +1,4 @@ +// Gemeinsamer Hinweis für Buchungsseite und Bestätigungs-E-Mail. +// Bei der späteren Integration echter Zahlungen zusammen mit dem Ablauf ändern. +export const paymentNotice = + "Dieser Buchungsablauf ist eine Simulation. Der Zahlungsvorgang wird übersprungen: Es erfolgt keine Abbuchung und keine verbindliche Beratungsbuchung. Bei der vollständigen Integration des Bestellprozesses kann die Zahlung ergänzt werden."; diff --git a/src/shared/packages.js b/src/shared/packages.js new file mode 100644 index 0000000..1305cad --- /dev/null +++ b/src/shared/packages.js @@ -0,0 +1,14 @@ +// Gemeinsamer Vertrag mit Person 3: eindeutige id, name und summary (Strings). +// Hier ausschließlich die vom Team freigegebenen Angebote eintragen. +export const packages = []; + +// Ausschließlich für lokale Entwicklung; kein echtes Angebot, kein Preis. +export const demoPackages = [ + { + id: "demo-booking", + name: "1:1-Ernährungsberatung", + summary: + "Persönliche Begleitung rund um deine Ernährung im Alltag und beim Sport.", + testOnly: true, + }, +]; diff --git a/src/tests/bookings.test.js b/src/tests/bookings.test.js new file mode 100644 index 0000000..7f3af02 --- /dev/null +++ b/src/tests/bookings.test.js @@ -0,0 +1,427 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { randomUUID } from "node:crypto"; +import { createApp } from "../server/index.js"; +import { createCsvStorage } from "../server/services/csv-storage.js"; +import { createBookingService } from "../server/services/booking-service.js"; +import { createEmailService } from "../server/services/email-service.js"; +import { demoPackages } from "../shared/packages.js"; + +const input = { + packageId: demoPackages[0].id, + name: 'Test, "Person"', + email: "person@example.test", +}; + +async function setup(t, options = {}) { + const directory = await mkdtemp(join(tmpdir(), "trihub-booking-")); + const file = join(directory, "bookings.csv"); + const storage = createCsvStorage(file); + const sent = []; + const sendConfirmation = + options.sendConfirmation ?? + (async (booking) => { + sent.push({ ...booking }); + }); + const dependencies = { + catalog: demoPackages, + storage, + sendConfirmation, + ...options, + }; + const app = createApp(dependencies); + await new Promise((resolve) => app.listen(0, "127.0.0.1", resolve)); + t.after(async () => { + app.closeAllConnections(); + await new Promise((resolve) => app.close(resolve)); + await rm(directory, { recursive: true, force: true }); + }); + const url = `http://127.0.0.1:${app.address().port}`; + async function post(body = input, key = randomUUID()) { + const response = await fetch(`${url}/api/bookings`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "Idempotency-Key": key, + }, + body: JSON.stringify(body), + }); + return { status: response.status, body: await response.json() }; + } + return { post, url, file, storage, sent, dependencies }; +} + +test("Buchung steht vor Versand in CSV; Paketname kommt vom Server", async (t) => { + const fixture = await setup(t); + const result = await fixture.post({ + ...input, + packageName: "Manipuliert", + price: 0, + }); + assert.equal(result.status, 201); + assert.equal(result.body.emailStatus, "accepted"); + assert.equal(result.body.packageName, demoPackages[0].name); + assert.equal(result.body.bookingId, "TH-000001"); + assert.ok(!Number.isNaN(Date.parse(result.body.createdAt))); + const records = await fixture.storage.readAll(); + assert.equal(records.length, 1); + assert.equal(records[0].name, input.name); + assert.equal(records[0].emailStatus, "accepted"); + assert.equal(fixture.sent.length, 1); + assert.equal(fixture.sent[0].emailStatus, "sending"); + assert.ok(!("email" in result.body)); +}); + +test("Pflichtfelder, Typen, Längen, E-Mail und unbekannte IDs werden abgelehnt", async (t) => { + const { post, storage, sent } = await setup(t); + for (const body of [ + null, + [], + {}, + { ...input, name: " " }, + { ...input, name: 42 }, + { ...input, name: "a".repeat(121) }, + { ...input, name: "Test\nBcc: fremd" }, + { ...input, email: "keine-adresse" }, + { ...input, email: "a..b@example.test" }, + { ...input, email: ".person@example.test" }, + { ...input, email: "person.@example.test" }, + { ...input, email: "person@example..test" }, + { ...input, email: "person@-example.test" }, + { ...input, email: `person@${"a".repeat(64)}.test` }, + { ...input, email: "a\r\nb@example.test" }, + { ...input, email: false }, + { ...input, email: "a".repeat(255) }, + { ...input, packageId: "unknown" }, + { ...input, packageId: "../demo" }, + { ...input, packageId: 1 }, + { ...input, packageId: "a".repeat(81) }, + ]) { + const result = await post(body); + assert.equal(result.status, 400, JSON.stringify(body)); + } + assert.equal((await post(input, "invalid")).status, 400); + assert.deepEqual(await storage.readAll(), []); + assert.equal(sent.length, 0); +}); + +test("Parallele Wiederholungen und Neustart erzeugen nur eine Buchung und Mail", async (t) => { + const { post, storage, sent, dependencies } = await setup(t); + const key = randomUUID(); + const results = await Promise.all( + Array.from({ length: 8 }, () => post(input, key)), + ); + assert.equal(new Set(results.map((r) => r.body.bookingId)).size, 1); + assert.equal(results.filter((r) => r.status === 201).length, 1); + assert.equal(sent.length, 1); + assert.equal((await storage.readAll()).length, 1); + // Neue Serviceinstanz liest ausschließlich den persistenten CSV-Zustand. + const restarted = createBookingService({ ...dependencies, packages: [] }); + const replay = await restarted.book(input, key); + assert.equal(replay.status, 200); + assert.equal(replay.body.bookingId, results[0].body.bookingId); + assert.equal(sent.length, 1); + const conflict = await post({ ...input, name: "Andere Person" }, key); + assert.equal(conflict.status, 409); +}); + +test("Verschiedene gleichzeitige Buchungen behalten alle CSV-Zeilen", async (t) => { + const { post, storage } = await setup(t); + const results = await Promise.all( + Array.from({ length: 12 }, (_, i) => + post({ ...input, name: `Person ${i}` }), + ), + ); + assert.ok(results.every((r) => r.status === 201)); + const records = await storage.readAll(); + assert.equal(records.length, 12); + assert.deepEqual( + records.map((record) => record.bookingId), + Array.from( + { length: 12 }, + (_, i) => `TH-${String(i + 1).padStart(6, "0")}`, + ), + ); +}); + +test("CSV schützt Formeln und erhält Kommas, Anführungszeichen, Apostrophe und Zeilenumbrüche", async (t) => { + const { storage, file } = await setup(t); + const values = [ + '=HYPERLINK("evil")', + "+1", + "-1", + "@SUM(A1)", + "\t=1", + "\r=1", + "\n=1", + " =1", + "'Original", + 'Test, "Name"\nzweite Zeile', + ]; + const rows = values.map((name) => ({ name, packageName: name })); + await storage.writeAll(rows); + assert.deepEqual( + (await storage.readAll()).map((r) => r.name), + values, + ); + const text = await readFile(file, "utf8"); + assert.ok(text.includes("'=HYPERLINK")); + assert.ok(text.includes("''Original")); + assert.ok(text.includes('"Test, ""Name""\nzweite Zeile"')); + await storage.writeAll(await storage.readAll()); + assert.deepEqual( + (await storage.readAll()).map((r) => r.name), + values, + ); +}); + +test("Fehlgeschlagenes erstes Schreiben verhindert Versand", async (t) => { + let sent = 0; + const { post } = await setup(t, { + storage: { + readAll: async () => [], + writeAll: async () => { + throw new Error("Disk full"); + }, + }, + sendConfirmation: async () => { + sent++; + }, + }); + const result = await post(); + assert.equal(result.status, 503); + assert.equal(result.body.error.code, "BOOKING_NOT_SAVED"); + assert.equal(sent, 0); +}); + +test("Beschädigte CSV wird nicht überschrieben", async (t) => { + const { post, file, sent } = await setup(t); + await writeFile(file, "falscher,header\n1,2\n"); + const result = await post(); + assert.equal(result.status, 503); + assert.equal(result.body.error.code, "STORAGE_UNAVAILABLE"); + assert.equal(await readFile(file, "utf8"), "falscher,header\n1,2\n"); + assert.equal(sent.length, 0); +}); + +test("Mailfehler speichert Buchung; Wiederholung sendet nicht erneut", async (t) => { + let calls = 0; + const { post, storage } = await setup(t, { + sendConfirmation: async () => { + calls++; + throw new Error("SMTP rejected"); + }, + }); + const key = randomUUID(); + const first = await post(input, key); + assert.equal(first.status, 202); + assert.equal(first.body.saved, true); + assert.equal(first.body.emailStatus, "failed"); + assert.equal((await storage.readAll())[0].emailStatus, "failed"); + assert.equal((await post(input, key)).body.bookingId, first.body.bookingId); + assert.equal(calls, 1); +}); + +test("Ausstehender Versand kann nach Schreibfehler sicher fortgesetzt werden", async (t) => { + const { storage } = await setup(t); + let writes = 0; + let sends = 0; + const service = createBookingService({ + packages: demoPackages, + storage: { + readAll: () => storage.readAll(), + writeAll: async (rows) => { + if (++writes === 2) throw new Error("Disk full"); + return storage.writeAll(rows); + }, + }, + sendConfirmation: async () => { + sends++; + }, + }); + const key = randomUUID(); + const first = await service.book(input, key); + assert.equal(first.body.emailStatus, "pending"); + assert.equal(sends, 0); + const retry = await service.book(input, key); + assert.equal(retry.body.emailStatus, "accepted"); + assert.equal(retry.body.bookingId, first.body.bookingId); + assert.equal(sends, 1); +}); + +test("Status-Schreibfehler nach SMTP bleibt unklar und löst keinen Doppelversand aus", async (t) => { + const { storage } = await setup(t); + let writes = 0; + let sends = 0; + const service = createBookingService({ + packages: demoPackages, + storage: { + readAll: () => storage.readAll(), + writeAll: async (rows) => { + if (++writes === 3) throw new Error("Disk full"); + return storage.writeAll(rows); + }, + }, + sendConfirmation: async () => { + sends++; + }, + }); + const key = randomUUID(); + assert.equal((await service.book(input, key)).body.emailStatus, "unknown"); + assert.equal((await storage.readAll())[0].emailStatus, "sending"); + const restarted = createBookingService({ + storage, + packages: demoPackages, + sendConfirmation: async () => { + sends++; + }, + }); + assert.equal( + (await restarted.book(input, key)).body.emailStatus, + "unknown", + ); + assert.equal(sends, 1); +}); + +test("SMTP-Nachricht enthält Buchungsnummer und Paket; externe Hosts sind standardmäßig gesperrt", async () => { + let message; + const send = createEmailService({}, () => ({ + sendMail: async (mail) => { + message = mail; + return { accepted: [input.email] }; + }, + })); + await send({ + ...input, + bookingId: "TH-000001", + packageName: demoPackages[0].name, + }); + assert.ok(message.text.includes("TH-000001")); + assert.ok(message.text.includes(demoPackages[0].name)); + assert.equal(message.to.address, input.email); + assert.ok(message.text.includes("Zahlungsvorgang wird übersprungen")); + assert.ok(message.text.includes("keine Abbuchung")); + assert.throws( + () => createEmailService({ SMTP_HOST: "smtp.example.org" }), + /gesperrt/, + ); + const rejected = createEmailService({}, () => ({ + sendMail: async () => ({ accepted: [] }), + })); + await assert.rejects(() => rejected(input)); +}); + +test("SMTP-Verbindungsabbruch unterscheidet sich von expliziter Ablehnung", async () => { + for (const [code, expected] of [ + ["ETIMEDOUT", true], + ["EAUTH", false], + ]) { + const send = createEmailService({}, () => ({ + sendMail: async () => { + throw Object.assign(new Error("SMTP"), { code }); + }, + })); + await assert.rejects( + () => send(input), + (error) => error.deliveryUnknown === expected, + ); + } +}); + +test("HTTP-Vertrag: JSON, Größenlimit, Methoden und private Dateien", async (t) => { + const { url } = await setup(t); + const response = await fetch(`${url}/api/bookings`); + assert.equal(response.status, 405); + for (const path of [ + "/src/server/data/bookings.csv", + "/.env", + "/data/bookings.csv", + ]) { + assert.equal((await fetch(`${url}${path}`)).status, 404); + } + assert.equal( + (await fetch(`${url}/api/bookings`, { method: "POST", body: "{}" })) + .status, + 415, + ); + assert.equal( + ( + await fetch(`${url}/api/bookings`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: "{", + }) + ).status, + 400, + ); + assert.equal( + ( + await fetch(`${url}/api/bookings`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ name: "a".repeat(9000) }), + }) + ).status, + 413, + ); + const catalog = await (await fetch(`${url}/api/angebote`)).json(); + assert.deepEqual(catalog.packages, demoPackages); + assert.equal((await fetch(`${url}/api/packages`)).status, 404); +}); + +test("Paketvertrag lehnt fehlende, numerische und doppelte IDs ab", () => { + for (const catalog of [ + [null], + [{ ...demoPackages[0], id: 123 }], + [{ ...demoPackages[0], id: undefined }], + [demoPackages[0], demoPackages[0]], + ]) { + assert.throws( + () => createApp({ catalog, sendConfirmation: async () => {} }), + /Paketdaten erfüllen/, + ); + } +}); + +test("Fortlaufende Nummern bleiben nach Neustart erhalten und Wiederholungen verbrauchen keine Nummer", async (t) => { + const { storage, post } = await setup(t); + const key = randomUUID(); + assert.equal((await post(input, key)).body.bookingId, "TH-000001"); + assert.equal((await post(input, key)).body.bookingId, "TH-000001"); + const restarted = createBookingService({ + storage, + packages: demoPackages, + sendConfirmation: async () => {}, + }); + assert.equal( + (await restarted.book(input, randomUUID())).body.bookingId, + "TH-000002", + ); + assert.equal((await storage.readAll()).length, 2); +}); + +test("Bestehende UUID-Buchungen bleiben unverändert und weiterhin abrufbar", async (t) => { + const { storage, post } = await setup(t); + const key = randomUUID(); + await post(input, key); + const records = await storage.readAll(); + const legacyId = randomUUID(); + records[0].bookingId = legacyId; + await storage.writeAll(records); + assert.equal((await post(input, key)).body.bookingId, legacyId); + assert.equal((await post()).body.bookingId, "TH-000001"); + assert.equal((await storage.readAll())[0].bookingId, legacyId); +}); + +test("Nummernvergabe verwendet den höchsten CSV-Wert und wächst über sechs Stellen hinaus", async (t) => { + const { storage, post } = await setup(t); + await post(); + await post(); + const records = await storage.readAll(); + records[0].bookingId = "TH-999999"; + await storage.writeAll(records); + assert.equal((await post()).body.bookingId, "TH-1000000"); +}); diff --git a/src/tests/browser/booking.spec.js b/src/tests/browser/booking.spec.js new file mode 100644 index 0000000..c9d584d --- /dev/null +++ b/src/tests/browser/booking.spec.js @@ -0,0 +1,181 @@ +import { test, expect } from "@playwright/test"; + +const pageUrl = "/booking.html#/buchen/demo-booking"; +async function fill(page) { + await page + .getByLabel("Name (Pflichtfeld)", { exact: true }) + .fill("Test Person"); + await page + .getByLabel("E-Mail-Adresse (Pflichtfeld)") + .fill("person@example.test"); +} + +test("Tastaturbedienung, Validierung und vollständige Buchung über Vite-Proxy", async ({ + page, +}) => { + await page.goto(pageUrl); + await expect( + page.getByRole("heading", { name: "1:1-Ernährungsberatung" }), + ).toBeVisible(); + await expect(page.locator(".booking__payment-notice")).toContainText( + "Zahlungsvorgang wird übersprungen", + ); + await expect(page.locator(".booking__payment-notice")).toContainText( + "keine Abbuchung", + ); + await page.keyboard.press("Tab"); + await expect( + page.getByRole("link", { name: "Zur Angebotsseite" }), + ).toBeFocused(); + await page.keyboard.press("Tab"); + await expect( + page.getByLabel("Name (Pflichtfeld)", { exact: true }), + ).toBeFocused(); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Enter"); + await expect(page.getByRole("alert")).toBeFocused(); + await expect(page.locator('[aria-invalid="true"]')).toHaveCount(2); + await page.keyboard.press("Tab"); + await page.keyboard.type("Test Person"); + await page.keyboard.press("Tab"); + await page.keyboard.type("person@example.test"); + await page.keyboard.press("Tab"); + const outline = await page + .getByRole("button", { name: "Paket buchen", exact: true }) + .evaluate((element) => getComputedStyle(element).outlineStyle); + expect(outline).toBe("solid"); + await page.keyboard.press("Enter"); + await expect(page.locator(".booking__result")).toContainText( + /Buchungsnummer: TH-\d{6,}/, + ); + await expect(page.locator(".booking__result")).toContainText( + "Zustellung ist noch nicht bestätigt", + ); + await expect( + page.getByRole("button", { name: "Buchung gespeichert" }), + ).toBeDisabled(); + await page.reload(); + await expect(page.locator(".booking__result")).toContainText( + /Buchungsnummer: TH-\d{6,}/, + ); +}); + +test("Schmales Display: Formular ohne horizontalen Überlauf", async ({ + page, +}) => { + await page.setViewportSize({ width: 320, height: 640 }); + await page.goto(pageUrl); + await fill(page); + expect( + await page.evaluate( + () => document.documentElement.scrollWidth <= window.innerWidth, + ), + ).toBe(true); + await expect( + page.getByRole("button", { name: "Paket buchen", exact: true }), + ).toBeVisible(); +}); + +test("Verlorene Antwort: Wiederholung nach Neuladen behält Schlüssel und Buchungsnummer", async ({ + page, +}) => { + let firstKey; + let firstBooking; + let requests = 0; + await page.route("**/api/bookings", async (route) => { + requests++; + if (requests === 1) { + firstKey = route.request().headers()["idempotency-key"]; + const response = await route.fetch(); + firstBooking = (await response.json()).bookingId; + await route.abort("failed"); + } else { + expect(route.request().headers()["idempotency-key"]).toBe(firstKey); + await route.continue(); + } + }); + await page.goto(pageUrl); + await fill(page); + await page + .getByRole("button", { name: "Paket buchen", exact: true }) + .click(); + await expect(page.getByRole("alert")).toBeVisible(); + await page.reload(); + await expect( + page.getByLabel("Name (Pflichtfeld)", { exact: true }), + ).toHaveValue("Test Person"); + await expect( + page.getByLabel("Name (Pflichtfeld)", { exact: true }), + ).toHaveAttribute("readonly", ""); + await page + .getByRole("button", { name: "Status prüfen / erneut versuchen" }) + .click(); + await expect(page.locator(".booking__result")).toContainText(firstBooking); + expect(requests).toBe(2); +}); + +test("Ladezustand verhindert doppelte Klicks; Mailfehler behauptet keinen vollständigen Erfolg", async ({ + page, +}) => { + let release; + const gate = new Promise((resolve) => { + release = resolve; + }); + await page.route("**/api/bookings", async (route) => { + await gate; + await route.fulfill({ + status: 202, + contentType: "application/json", + body: JSON.stringify({ + saved: true, + bookingId: "test-mailfehler", + emailStatus: "failed", + }), + }); + }); + await page.goto(pageUrl); + await fill(page); + await page + .getByRole("button", { name: "Paket buchen", exact: true }) + .click(); + await expect( + page.getByRole("button", { name: "Buchung wird verarbeitet …" }), + ).toBeDisabled(); + release(); + await expect(page.locator(".booking__result")).toContainText( + "Bestätigungsversand ist fehlgeschlagen", + ); + await expect(page.locator(".booking__result")).toContainText( + "Bitte nicht erneut buchen", + ); + await expect( + page.getByRole("button", { name: "Buchung gespeichert" }), + ).toBeDisabled(); +}); + +test("Fehlende und unbekannte Paket-ID zeigen kein Formular", async ({ + page, +}) => { + await page.goto("/booking.html"); + await expect(page.getByText("Bitte wähle zuerst ein Paket")).toBeVisible(); + await expect(page.locator("form")).toHaveCount(0); + await page.goto("/booking.html#/buchen/unbekannt"); + await expect( + page.getByText("Dieses Paket ist nicht verfügbar"), + ).toBeVisible(); + await expect(page.locator("form")).toHaveCount(0); +}); + +test("Vite liefert Serverdateien und Umgebungsdateien nicht aus", async ({ + request, +}) => { + for (const path of [ + "/src/server/index.js", + "/src/server/data/.gitkeep", + "/.env.example", + ]) { + const response = await request.get(path); + expect([403, 404]).toContain(response.status()); + } +}); diff --git a/src/tests/helpers/browser-server.js b/src/tests/helpers/browser-server.js new file mode 100644 index 0000000..f9f02d8 --- /dev/null +++ b/src/tests/helpers/browser-server.js @@ -0,0 +1,23 @@ +// Ausschließlich Testserver: temporäre CSV und simulierter Versand. +import { mkdtemp, rm } from "node:fs/promises"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { createApp } from "../../server/index.js"; +import { createCsvStorage } from "../../server/services/csv-storage.js"; +import { demoPackages } from "../../shared/packages.js"; + +const directory = await mkdtemp(join(tmpdir(), "trihub-browser-")); +const server = createApp({ + catalog: demoPackages, + storage: createCsvStorage(join(directory, "bookings.csv")), + sendConfirmation: async () => {}, +}); +server.listen(3000, "127.0.0.1"); +async function stop() { + server.closeAllConnections(); + await new Promise((resolve) => server.close(resolve)); + await rm(directory, { recursive: true, force: true }); + process.exit(0); +} +process.on("SIGTERM", stop); +process.on("SIGINT", stop); diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..665b58f --- /dev/null +++ b/vite.config.js @@ -0,0 +1,29 @@ +import { defineConfig } from "vite"; +import { fileURLToPath } from "node:url"; + +export default defineConfig({ + server: { + proxy: { "/api": "http://127.0.0.1:3000" }, + fs: { + // Auch Vites direkten Dateizugriff auf das Projekt absichern. + deny: [ + ".env", + ".env.*", + "*.{crt,pem}", + "**/.git/**", + "**/server/**", + "**/tests/**", + ], + }, + }, + build: { + rollupOptions: { + input: { + main: fileURLToPath(new URL("./index.html", import.meta.url)), + booking: fileURLToPath( + new URL("./booking.html", import.meta.url), + ), + }, + }, + }, +}); From e09fef8c96125658249b003d0e99d9aa8f5d0206 Mon Sep 17 00:00:00 2001 From: Niels Yannick Tietze <3020513@stud.hs-mannheim.de> Date: Thu, 24 Sep 2026 18:18:07 +0200 Subject: [PATCH 03/27] feat: Gestaltung --- src/design/styles.css | 9135 ++++++++++++++++++ src/design/tri-hub-ernaehrungsberatung.html | 321 + src/styles.css | 9158 ++++++++++++++++++- 3 files changed, 18583 insertions(+), 31 deletions(-) create mode 100644 src/design/styles.css create mode 100644 src/design/tri-hub-ernaehrungsberatung.html diff --git a/src/design/styles.css b/src/design/styles.css new file mode 100644 index 0000000..697e903 --- /dev/null +++ b/src/design/styles.css @@ -0,0 +1,9135 @@ +/*! tailwindcss v4.1.14 | MIT License | https://tailwindcss.com */ +@layer properties { + @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or + ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { + *, + :before, + :after, + ::backdrop { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-translate-z: 0; + --tw-rotate-x: initial; + --tw-rotate-y: initial; + --tw-rotate-z: initial; + --tw-skew-x: initial; + --tw-skew-y: initial; + --tw-space-y-reverse: 0; + --tw-space-x-reverse: 0; + --tw-divide-y-reverse: 0; + --tw-border-style: solid; + --tw-gradient-position: initial; + --tw-gradient-from: #0000; + --tw-gradient-via: #0000; + --tw-gradient-to: #0000; + --tw-gradient-stops: initial; + --tw-gradient-via-stops: initial; + --tw-gradient-from-position: 0%; + --tw-gradient-via-position: 50%; + --tw-gradient-to-position: 100%; + --tw-leading: initial; + --tw-font-weight: initial; + --tw-tracking: initial; + --tw-ordinal: initial; + --tw-slashed-zero: initial; + --tw-numeric-figure: initial; + --tw-numeric-spacing: initial; + --tw-numeric-fraction: initial; + --tw-shadow: 0 0 #0000; + --tw-shadow-color: initial; + --tw-shadow-alpha: 100%; + --tw-inset-shadow: 0 0 #0000; + --tw-inset-shadow-color: initial; + --tw-inset-shadow-alpha: 100%; + --tw-ring-color: initial; + --tw-ring-shadow: 0 0 #0000; + --tw-inset-ring-color: initial; + --tw-inset-ring-shadow: 0 0 #0000; + --tw-ring-inset: initial; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-offset-shadow: 0 0 #0000; + --tw-outline-style: solid; + --tw-blur: initial; + --tw-brightness: initial; + --tw-contrast: initial; + --tw-grayscale: initial; + --tw-hue-rotate: initial; + --tw-invert: initial; + --tw-opacity: initial; + --tw-saturate: initial; + --tw-sepia: initial; + --tw-drop-shadow: initial; + --tw-drop-shadow-color: initial; + --tw-drop-shadow-alpha: 100%; + --tw-drop-shadow-size: initial; + --tw-backdrop-blur: initial; + --tw-backdrop-brightness: initial; + --tw-backdrop-contrast: initial; + --tw-backdrop-grayscale: initial; + --tw-backdrop-hue-rotate: initial; + --tw-backdrop-invert: initial; + --tw-backdrop-opacity: initial; + --tw-backdrop-saturate: initial; + --tw-backdrop-sepia: initial; + --tw-duration: initial; + --tw-ease: initial; + --tw-content: ""; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-scale-z: 1; + --tw-divide-x-reverse: 0; + --tw-animation-delay: 0s; + --tw-animation-direction: normal; + --tw-animation-duration: initial; + --tw-animation-fill-mode: none; + --tw-animation-iteration-count: 1; + --tw-enter-blur: 0; + --tw-enter-opacity: 1; + --tw-enter-rotate: 0; + --tw-enter-scale: 1; + --tw-enter-translate-x: 0; + --tw-enter-translate-y: 0; + --tw-exit-blur: 0; + --tw-exit-opacity: 1; + --tw-exit-rotate: 0; + --tw-exit-scale: 1; + --tw-exit-translate-x: 0; + --tw-exit-translate-y: 0; + } + } +} +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 400; + src: + url(/assets/dm-sans-latin-ext-400-normal-BtiwyxMk.woff2) format("woff2"), + url(/assets/dm-sans-latin-ext-400-normal-BjWJ59Pq.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 400; + src: + url(/assets/dm-sans-latin-400-normal-CW0RaeGs.woff2) format("woff2"), + url(/assets/dm-sans-latin-400-normal-BwCSEQnW.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 500; + src: + url(/assets/dm-sans-latin-ext-500-normal-BJfUCQsA.woff2) format("woff2"), + url(/assets/dm-sans-latin-ext-500-normal-DR84L5F-.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 500; + src: + url(/assets/dm-sans-latin-500-normal-B9HHJjqV.woff2) format("woff2"), + url(/assets/dm-sans-latin-500-normal-Dr3UlScf.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 600; + src: + url(/assets/dm-sans-latin-ext-600-normal-4vooXBpG.woff2) format("woff2"), + url(/assets/dm-sans-latin-ext-600-normal-DRtaDpgU.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 600; + src: + url(/assets/dm-sans-latin-600-normal-Aqo67rzb.woff2) format("woff2"), + url(/assets/dm-sans-latin-600-normal-BmdmIIQ2.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 700; + src: + url(/assets/dm-sans-latin-ext-700-normal-CJIcxD6K.woff2) format("woff2"), + url(/assets/dm-sans-latin-ext-700-normal-BLI3TTWz.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 700; + src: + url(/assets/dm-sans-latin-700-normal-DvUfVpUG.woff2) format("woff2"), + url(/assets/dm-sans-latin-700-normal-CUSSCpQX.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 500; + src: + url(/assets/sora-latin-ext-500-normal-B5KKQIFO.woff2) format("woff2"), + url(/assets/sora-latin-ext-500-normal-DwxUJRqY.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 500; + src: + url(/assets/sora-latin-500-normal-01eiPEn0.woff2) format("woff2"), + url(/assets/sora-latin-500-normal-w58xtEt9.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 600; + src: + url(/assets/sora-latin-ext-600-normal-Cue1zdhl.woff2) format("woff2"), + url(/assets/sora-latin-ext-600-normal-DLOJK0Ta.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 600; + src: + url(/assets/sora-latin-600-normal-Cdg4DaK0.woff2) format("woff2"), + url(/assets/sora-latin-600-normal-1_7fyUAY.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 700; + src: + url(/assets/sora-latin-ext-700-normal-DM0oy5s8.woff2) format("woff2"), + url(/assets/sora-latin-ext-700-normal-Oc7uZIYt.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 700; + src: + url(/assets/sora-latin-700-normal-9waGdLWo.woff2) format("woff2"), + url(/assets/sora-latin-700-normal-BKPfQAnC.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 800; + src: + url(/assets/sora-latin-ext-800-normal-YDFYE6t9.woff2) format("woff2"), + url(/assets/sora-latin-ext-800-normal-BvAfeed7.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 800; + src: + url(/assets/sora-latin-800-normal-2tKLL3qT.woff2) format("woff2"), + url(/assets/sora-latin-800-normal-c3Huklug.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} +@layer theme { + :root, + :host { + --font-sans: "DM Sans", sans-serif; + --font-mono: + ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New", monospace; + --color-red-50: oklch(97.1% 0.013 17.38); + --color-red-100: oklch(93.6% 0.032 17.717); + --color-red-200: oklch(88.5% 0.062 18.334); + --color-red-400: oklch(70.4% 0.191 22.216); + --color-red-500: oklch(63.7% 0.237 25.331); + --color-red-600: oklch(57.7% 0.245 27.325); + --color-red-700: oklch(50.5% 0.213 27.518); + --color-red-800: oklch(44.4% 0.177 26.899); + --color-red-900: oklch(39.6% 0.141 25.723); + --color-orange-100: oklch(95.4% 0.038 75.164); + --color-orange-500: oklch(70.5% 0.213 47.604); + --color-orange-600: oklch(64.6% 0.222 41.116); + --color-orange-800: oklch(47% 0.157 37.304); + --color-amber-100: oklch(96.2% 0.059 95.617); + --color-amber-400: oklch(82.8% 0.189 84.429); + --color-yellow-200: oklch(94.5% 0.129 101.54); + --color-yellow-300: oklch(90.5% 0.182 98.111); + --color-yellow-500: oklch(79.5% 0.184 86.047); + --color-yellow-700: oklch(55.4% 0.135 66.442); + --color-green-100: oklch(96.2% 0.044 156.743); + --color-green-200: oklch(92.5% 0.084 155.995); + --color-green-300: oklch(87.1% 0.15 154.449); + --color-green-400: oklch(79.2% 0.209 151.711); + --color-green-500: oklch(72.3% 0.219 149.579); + --color-green-600: oklch(62.7% 0.194 149.214); + --color-green-700: oklch(52.7% 0.154 150.069); + --color-green-800: oklch(44.8% 0.119 151.328); + --color-emerald-100: oklch(95% 0.052 163.051); + --color-emerald-400: oklch(76.5% 0.177 163.223); + --color-teal-500: oklch(70.4% 0.14 182.503); + --color-teal-600: oklch(60% 0.118 184.704); + --color-cyan-100: oklch(95.6% 0.045 203.388); + --color-cyan-300: oklch(86.5% 0.127 207.078); + --color-cyan-400: oklch(78.9% 0.154 211.53); + --color-cyan-500: oklch(71.5% 0.143 215.221); + --color-cyan-600: oklch(60.9% 0.126 221.723); + --color-blue-100: oklch(93.2% 0.032 255.585); + --color-blue-200: oklch(88.2% 0.059 254.128); + --color-blue-300: oklch(80.9% 0.105 251.813); + --color-blue-400: oklch(70.7% 0.165 254.624); + --color-blue-500: oklch(62.3% 0.214 259.815); + --color-blue-600: oklch(54.6% 0.245 262.881); + --color-blue-700: oklch(48.8% 0.243 264.376); + --color-blue-800: oklch(42.4% 0.199 265.638); + --color-indigo-500: oklch(58.5% 0.233 277.117); + --color-indigo-600: oklch(51.1% 0.262 276.966); + --color-purple-100: oklch(94.6% 0.033 307.174); + --color-purple-300: oklch(82.7% 0.119 306.383); + --color-purple-400: oklch(71.4% 0.203 305.504); + --color-purple-500: oklch(62.7% 0.265 303.9); + --color-purple-600: oklch(55.8% 0.288 302.321); + --color-purple-700: oklch(49.6% 0.265 301.924); + --color-purple-800: oklch(43.8% 0.218 303.724); + --color-rose-100: oklch(94.1% 0.03 12.58); + --color-rose-400: oklch(71.2% 0.194 13.428); + --color-slate-50: oklch(98.4% 0.003 247.858); + --color-slate-100: oklch(96.8% 0.007 247.896); + --color-slate-200: oklch(92.9% 0.013 255.508); + --color-slate-300: oklch(86.9% 0.022 252.894); + --color-slate-500: oklch(55.4% 0.046 257.417); + --color-slate-600: oklch(44.6% 0.043 257.281); + --color-slate-700: oklch(37.2% 0.044 257.287); + --color-slate-900: oklch(20.8% 0.042 265.755); + --color-slate-950: oklch(12.9% 0.042 264.695); + --color-gray-100: oklch(96.7% 0.003 264.542); + --color-gray-200: oklch(92.8% 0.006 264.531); + --color-gray-300: oklch(87.2% 0.01 258.338); + --color-gray-500: oklch(55.1% 0.027 264.364); + --color-gray-600: oklch(44.6% 0.03 256.802); + --color-gray-700: oklch(37.3% 0.034 259.733); + --color-gray-800: oklch(27.8% 0.033 256.848); + --color-black: #000; + --color-white: #fff; + --spacing: 0.25rem; + --container-xs: 20rem; + --container-sm: 24rem; + --container-md: 28rem; + --container-lg: 32rem; + --container-xl: 36rem; + --container-2xl: 42rem; + --container-3xl: 48rem; + --container-4xl: 56rem; + --container-5xl: 64rem; + --container-6xl: 72rem; + --container-7xl: 80rem; + --text-xs: 0.75rem; + --text-xs--line-height: calc(1/0.75); + --text-sm: 0.875rem; + --text-sm--line-height: calc(1.25/0.875); + --text-base: 1rem; + --text-base--line-height: 1.5; + --text-lg: 1.125rem; + --text-lg--line-height: calc(1.75/1.125); + --text-xl: 1.25rem; + --text-xl--line-height: calc(1.75/1.25); + --text-2xl: 1.5rem; + --text-2xl--line-height: calc(2/1.5); + --text-3xl: 1.875rem; + --text-3xl--line-height: 1.2; + --text-4xl: 2.25rem; + --text-4xl--line-height: calc(2.5/2.25); + --text-5xl: 3rem; + --text-5xl--line-height: 1; + --text-6xl: 3.75rem; + --text-6xl--line-height: 1; + --text-7xl: 4.5rem; + --text-7xl--line-height: 1; + --font-weight-normal: 400; + --font-weight-medium: 500; + --font-weight-semibold: 600; + --font-weight-bold: 700; + --tracking-tight: -0.025em; + --tracking-wide: 0.025em; + --tracking-widest: 0.1em; + --leading-tight: 1.25; + --leading-snug: 1.375; + --leading-normal: 1.5; + --leading-relaxed: 1.625; + --radius-xs: 0.125rem; + --radius-2xl: 1rem; + --ease-out: cubic-bezier(0, 0, 0.2, 1); + --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + --animate-spin: spin 1s linear infinite; + --animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; + --blur-sm: 8px; + --blur-md: 12px; + --blur-xl: 24px; + --blur-2xl: 40px; + --blur-3xl: 64px; + --aspect-video: 16/9; + --default-transition-duration: 0.15s; + --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + --default-font-family: "DM Sans", sans-serif; + --default-mono-font-family: var(--font-mono); + --font-display: "Sora", sans-serif; + --color-border: var(--border); + } +} +@layer base { + *, + :after, + :before, + ::backdrop { + box-sizing: border-box; + border: 0 solid; + margin: 0; + padding: 0; + } + ::file-selector-button { + box-sizing: border-box; + border: 0 solid; + margin: 0; + padding: 0; + } + html, + :host { + -webkit-text-size-adjust: 100%; + tab-size: 4; + line-height: 1.5; + font-family: var( + --default-font-family, + ui-sans-serif, + system-ui, + sans-serif, + "Apple Color Emoji", + "Segoe UI Emoji", + "Segoe UI Symbol", + "Noto Color Emoji" + ); + font-feature-settings: var(--default-font-feature-settings, normal); + font-variation-settings: var(--default-font-variation-settings, normal); + -webkit-tap-highlight-color: transparent; + } + 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; + -webkit-text-decoration: inherit; + text-decoration: inherit; + } + b, + strong { + font-weight: bolder; + } + code, + kbd, + samp, + pre { + font-family: var( + --default-mono-font-family, + ui-monospace, + SFMono-Regular, + Menlo, + Monaco, + Consolas, + "Liberation Mono", + "Courier New", + monospace + ); + font-feature-settings: var( + --default-mono-font-feature-settings, + normal + ); + font-variation-settings: var( + --default-mono-font-variation-settings, + normal + ); + font-size: 1em; + } + small { + font-size: 80%; + } + sub, + sup { + vertical-align: baseline; + font-size: 75%; + line-height: 0; + position: relative; + } + sub { + bottom: -0.25em; + } + sup { + top: -0.5em; + } + table { + text-indent: 0; + border-color: inherit; + border-collapse: collapse; + } + :-moz-focusring { + outline: auto; + } + progress { + vertical-align: baseline; + } + summary { + display: list-item; + } + ol, + ul, + menu { + list-style: none; + } + img, + svg, + video, + canvas, + audio, + iframe, + embed, + object { + vertical-align: middle; + display: block; + } + img, + video { + max-width: 100%; + height: auto; + } + button, + input, + select, + optgroup, + textarea { + font: inherit; + font-feature-settings: inherit; + font-variation-settings: inherit; + letter-spacing: inherit; + color: inherit; + opacity: 1; + background-color: #0000; + border-radius: 0; + } + ::file-selector-button { + font: inherit; + font-feature-settings: inherit; + font-variation-settings: inherit; + letter-spacing: inherit; + color: inherit; + opacity: 1; + background-color: #0000; + border-radius: 0; + } + :where(select:is([multiple], [size])) optgroup { + font-weight: bolder; + } + :where(select:is([multiple], [size])) optgroup option { + padding-inline-start: 20px; + } + ::file-selector-button { + margin-inline-end: 4px; + } + ::placeholder { + opacity: 1; + } + @supports (not ((-webkit-appearance: -apple-pay-button))) or + (contain-intrinsic-size: 1px) { + ::placeholder { + color: currentColor; + } + @supports (color: color-mix(in lab, red, red)) { + ::placeholder { + color: color-mix(in oklab, currentcolor 50%, transparent); + } + } + } + textarea { + resize: vertical; + } + ::-webkit-search-decoration { + -webkit-appearance: none; + } + ::-webkit-date-and-time-value { + min-height: 1lh; + text-align: inherit; + } + ::-webkit-datetime-edit { + display: inline-flex; + } + ::-webkit-datetime-edit-fields-wrapper { + padding: 0; + } + ::-webkit-datetime-edit { + padding-block: 0; + } + ::-webkit-datetime-edit-year-field { + padding-block: 0; + } + ::-webkit-datetime-edit-month-field { + padding-block: 0; + } + ::-webkit-datetime-edit-day-field { + padding-block: 0; + } + ::-webkit-datetime-edit-hour-field { + padding-block: 0; + } + ::-webkit-datetime-edit-minute-field { + padding-block: 0; + } + ::-webkit-datetime-edit-second-field { + padding-block: 0; + } + ::-webkit-datetime-edit-millisecond-field { + padding-block: 0; + } + ::-webkit-datetime-edit-meridiem-field { + padding-block: 0; + } + ::-webkit-calendar-picker-indicator { + line-height: 1; + } + :-moz-ui-invalid { + box-shadow: none; + } + button, + input:where([type="button"], [type="reset"], [type="submit"]) { + appearance: button; + } + ::file-selector-button { + appearance: button; + } + ::-webkit-inner-spin-button { + height: auto; + } + ::-webkit-outer-spin-button { + height: auto; + } + [hidden]:where(:not([hidden="until-found"])) { + display: none !important; + } + * { + border-color: var(--border); + outline-color: var(--ring); + } + @supports (color: color-mix(in lab, red, red)) { + * { + outline-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + html { + scroll-behavior: smooth; + } + body { + font-family: var(--font-sans); + background: + radial-gradient(circle at 0 0, #2eb6c21f, #0000 28%), + radial-gradient(circle at 85% 10%, #ffbe2814, #0000 16%), + linear-gradient(#051018, #071720 46%, #050e16); + background-color: var(--background); + color: var(--foreground); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + h1, + h2, + h3, + .font-display { + font-family: var(--font-display); + letter-spacing: -0.04em; + font-weight: 700; + } + button:not(:disabled), + [role="button"]:not([aria-disabled="true"]), + [type="button"]:not(:disabled), + [type="submit"]:not(:disabled), + [type="reset"]:not(:disabled), + a[href], + select:not(:disabled), + input[type="checkbox"]:not(:disabled), + input[type="radio"]:not(:disabled) { + cursor: pointer; + } + ::selection { + color: #fff; + background: #2fb7c24d; + } +} +@layer components { + .container { + width: 100%; + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; + } + .flex { + min-width: 0; + min-height: 0; + } + .eyebrow { + color: #fff9; + letter-spacing: 0.24em; + text-transform: uppercase; + font-size: 0.72rem; + font-weight: 600; + line-height: 1; + } + .panel-glass { + -webkit-backdrop-filter: blur(16px); + backdrop-filter: blur(16px); + background: linear-gradient(#ffffff14, #ffffff08); + border: 1px solid #ffffff1a; + box-shadow: 0 10px 26px #00000029; + } + .card-premium { + -webkit-backdrop-filter: blur(18px); + backdrop-filter: blur(18px); + background: + linear-gradient(#ffffff0f, #ffffff05), + linear-gradient(#08151ee6, #091822fa); + border: 1px solid #ffffff1a; + transition: + transform 0.32s cubic-bezier(0.22, 1, 0.36, 1), + border-color 0.32s, + box-shadow 0.32s, + background 0.32s; + position: relative; + overflow: hidden; + box-shadow: 0 18px 48px #00000038; + } + .card-premium:before { + content: ""; + pointer-events: none; + background: linear-gradient( + 120deg, + #2db6c214, + #0000 46% 60%, + #ffc12b08 + ); + position: absolute; + inset: 0; + } + .card-premium:hover { + border-color: #2fb7c23d; + transform: translateY(-4px); + box-shadow: 0 24px 62px #0000004d; + } + .hero-shell:after { + content: ""; + pointer-events: none; + background: linear-gradient(#06101400, #061014f0); + height: 160px; + position: absolute; + inset: auto 0 0; + } + .hero-visual-frame:after { + content: ""; + pointer-events: none; + border: 1px solid #ffffff14; + border-radius: 1.4rem; + position: absolute; + inset: 0; + } + @media (min-width: 640px) { + .container { + padding-left: 1.5rem; + padding-right: 1.5rem; + } + } + @media (min-width: 1024px) { + .container { + max-width: 1280px; + padding-left: 2rem; + padding-right: 2rem; + } + } +} +@layer utilities { + .\@container\/card-header { + container: card-header/inline-size; + } + .\@container\/field-group { + container: field-group/inline-size; + } + .pointer-events-none { + pointer-events: none; + } + .invisible { + visibility: hidden; + } + .sr-only { + clip-path: inset(50%); + white-space: nowrap; + border-width: 0; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + position: absolute; + overflow: hidden; + } + .absolute { + position: absolute; + } + .fixed { + position: fixed; + } + .relative { + position: relative; + } + .static { + position: static; + } + .sticky { + position: sticky; + } + .inset-0 { + inset: calc(var(--spacing) * 0); + } + .inset-x-0 { + inset-inline: calc(var(--spacing) * 0); + } + .inset-x-6 { + inset-inline: calc(var(--spacing) * 6); + } + .inset-x-7 { + inset-inline: calc(var(--spacing) * 7); + } + .inset-x-8 { + inset-inline: calc(var(--spacing) * 8); + } + .inset-y-0 { + inset-block: calc(var(--spacing) * 0); + } + .-top-12 { + top: calc(var(--spacing) * -12); + } + .top-0 { + top: calc(var(--spacing) * 0); + } + .top-1\.5 { + top: calc(var(--spacing) * 1.5); + } + .top-1\/2 { + top: 50%; + } + .top-3\.5 { + top: calc(var(--spacing) * 3.5); + } + .top-4 { + top: calc(var(--spacing) * 4); + } + .top-6 { + top: calc(var(--spacing) * 6); + } + .top-16 { + top: calc(var(--spacing) * 16); + } + .top-\[1px\] { + top: 1px; + } + .top-\[50\%\] { + top: 50%; + } + .top-\[60\%\] { + top: 60%; + } + .top-full { + top: 100%; + } + .-right-12 { + right: calc(var(--spacing) * -12); + } + .right-0 { + right: calc(var(--spacing) * 0); + } + .right-1 { + right: calc(var(--spacing) * 1); + } + .right-2 { + right: calc(var(--spacing) * 2); + } + .right-3 { + right: calc(var(--spacing) * 3); + } + .right-4 { + right: calc(var(--spacing) * 4); + } + .-bottom-12 { + bottom: calc(var(--spacing) * -12); + } + .bottom-0 { + bottom: calc(var(--spacing) * 0); + } + .bottom-4 { + bottom: calc(var(--spacing) * 4); + } + .bottom-6 { + bottom: calc(var(--spacing) * 6); + } + .bottom-7 { + bottom: calc(var(--spacing) * 7); + } + .bottom-8 { + bottom: calc(var(--spacing) * 8); + } + .-left-12 { + left: calc(var(--spacing) * -12); + } + .left-0 { + left: calc(var(--spacing) * 0); + } + .left-1\/2 { + left: 50%; + } + .left-2 { + left: calc(var(--spacing) * 2); + } + .left-4 { + left: calc(var(--spacing) * 4); + } + .left-\[1\.25rem\] { + left: 1.25rem; + } + .left-\[50\%\] { + left: 50%; + } + .isolate { + isolation: isolate; + } + .z-10 { + z-index: 10; + } + .z-20 { + z-index: 20; + } + .z-40 { + z-index: 40; + } + .z-50 { + z-index: 50; + } + .z-\[1\] { + z-index: 1; + } + .z-\[120\] { + z-index: 120; + } + .order-first { + order: -9999; + } + .order-last { + order: 9999; + } + .col-start-2 { + grid-column-start: 2; + } + .row-span-2 { + grid-row: span 2 / span 2; + } + .row-start-1 { + grid-row-start: 1; + } + .container { + width: 100%; + } + @media (min-width: 40rem) { + .container { + max-width: 40rem; + } + } + @media (min-width: 48rem) { + .container { + max-width: 48rem; + } + } + @media (min-width: 64rem) { + .container { + max-width: 64rem; + } + } + @media (min-width: 80rem) { + .container { + max-width: 80rem; + } + } + @media (min-width: 96rem) { + .container { + max-width: 96rem; + } + } + .\!m-0 { + margin: calc(var(--spacing) * 0) !important; + } + .-mx-1 { + margin-inline: calc(var(--spacing) * -1); + } + .mx-2 { + margin-inline: calc(var(--spacing) * 2); + } + .mx-3\.5 { + margin-inline: calc(var(--spacing) * 3.5); + } + .mx-4 { + margin-inline: calc(var(--spacing) * 4); + } + .mx-auto { + margin-inline: auto; + } + .-my-2 { + margin-block: calc(var(--spacing) * -2); + } + .my-0 { + margin-block: calc(var(--spacing) * 0); + } + .my-0\.5 { + margin-block: calc(var(--spacing) * 0.5); + } + .my-1 { + margin-block: calc(var(--spacing) * 1); + } + .-mt-4 { + margin-top: calc(var(--spacing) * -4); + } + .mt-0\.5 { + margin-top: calc(var(--spacing) * 0.5); + } + .mt-1 { + margin-top: calc(var(--spacing) * 1); + } + .mt-1\.5 { + margin-top: calc(var(--spacing) * 1.5); + } + .mt-2 { + margin-top: calc(var(--spacing) * 2); + } + .mt-3 { + margin-top: calc(var(--spacing) * 3); + } + .mt-4 { + margin-top: calc(var(--spacing) * 4); + } + .mt-5 { + margin-top: calc(var(--spacing) * 5); + } + .mt-6 { + margin-top: calc(var(--spacing) * 6); + } + .mt-7 { + margin-top: calc(var(--spacing) * 7); + } + .mt-8 { + margin-top: calc(var(--spacing) * 8); + } + .mt-10 { + margin-top: calc(var(--spacing) * 10); + } + .mt-12 { + margin-top: calc(var(--spacing) * 12); + } + .mt-14 { + margin-top: calc(var(--spacing) * 14); + } + .mt-16 { + margin-top: calc(var(--spacing) * 16); + } + .mt-auto { + margin-top: auto; + } + .mr-1 { + margin-right: calc(var(--spacing) * 1); + } + .mr-2 { + margin-right: calc(var(--spacing) * 2); + } + .mb-1 { + margin-bottom: calc(var(--spacing) * 1); + } + .mb-2 { + margin-bottom: calc(var(--spacing) * 2); + } + .mb-3 { + margin-bottom: calc(var(--spacing) * 3); + } + .mb-4 { + margin-bottom: calc(var(--spacing) * 4); + } + .mb-5 { + margin-bottom: calc(var(--spacing) * 5); + } + .mb-6 { + margin-bottom: calc(var(--spacing) * 6); + } + .mb-8 { + margin-bottom: calc(var(--spacing) * 8); + } + .mb-10 { + margin-bottom: calc(var(--spacing) * 10); + } + .mb-12 { + margin-bottom: calc(var(--spacing) * 12); + } + .mb-14 { + margin-bottom: calc(var(--spacing) * 14); + } + .mb-16 { + margin-bottom: calc(var(--spacing) * 16); + } + .mb-20 { + margin-bottom: calc(var(--spacing) * 20); + } + .-ml-4 { + margin-left: calc(var(--spacing) * -4); + } + .ml-1 { + margin-left: calc(var(--spacing) * 1); + } + .ml-2 { + margin-left: calc(var(--spacing) * 2); + } + .ml-3 { + margin-left: calc(var(--spacing) * 3); + } + .ml-4 { + margin-left: calc(var(--spacing) * 4); + } + .ml-auto { + margin-left: auto; + } + .line-clamp-1 { + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + display: -webkit-box; + overflow: hidden; + } + .line-clamp-2 { + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + display: -webkit-box; + overflow: hidden; + } + .line-clamp-3 { + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + display: -webkit-box; + overflow: hidden; + } + .block { + display: block; + } + .flex { + display: flex; + } + .grid { + display: grid; + } + .hidden { + display: none; + } + .inline { + display: inline; + } + .inline-block { + display: inline-block; + } + .inline-flex { + display: inline-flex; + } + .table { + display: table; + } + .table-caption { + display: table-caption; + } + .table-cell { + display: table-cell; + } + .table-row { + display: table-row; + } + .field-sizing-content { + field-sizing: content; + } + .aspect-\[4\/3\] { + aspect-ratio: 4/3; + } + .aspect-\[4\/5\] { + aspect-ratio: 4/5; + } + .aspect-square { + aspect-ratio: 1; + } + .aspect-video { + aspect-ratio: var(--aspect-video); + } + .size-\(--cell-size\) { + width: var(--cell-size); + height: var(--cell-size); + } + .size-2 { + width: calc(var(--spacing) * 2); + height: calc(var(--spacing) * 2); + } + .size-2\.5 { + width: calc(var(--spacing) * 2.5); + height: calc(var(--spacing) * 2.5); + } + .size-3 { + width: calc(var(--spacing) * 3); + height: calc(var(--spacing) * 3); + } + .size-3\.5 { + width: calc(var(--spacing) * 3.5); + height: calc(var(--spacing) * 3.5); + } + .size-4 { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + .size-6 { + width: calc(var(--spacing) * 6); + height: calc(var(--spacing) * 6); + } + .size-7 { + width: calc(var(--spacing) * 7); + height: calc(var(--spacing) * 7); + } + .size-8 { + width: calc(var(--spacing) * 8); + height: calc(var(--spacing) * 8); + } + .size-9 { + width: calc(var(--spacing) * 9); + height: calc(var(--spacing) * 9); + } + .size-10 { + width: calc(var(--spacing) * 10); + height: calc(var(--spacing) * 10); + } + .size-12 { + width: calc(var(--spacing) * 12); + height: calc(var(--spacing) * 12); + } + .size-auto { + width: auto; + height: auto; + } + .size-full { + width: 100%; + height: 100%; + } + .h-\(--cell-size\) { + height: var(--cell-size); + } + .h-1\.5 { + height: calc(var(--spacing) * 1.5); + } + .h-2 { + height: calc(var(--spacing) * 2); + } + .h-2\.5 { + height: calc(var(--spacing) * 2.5); + } + .h-3 { + height: calc(var(--spacing) * 3); + } + .h-4 { + height: calc(var(--spacing) * 4); + } + .h-5 { + height: calc(var(--spacing) * 5); + } + .h-6 { + height: calc(var(--spacing) * 6); + } + .h-7 { + height: calc(var(--spacing) * 7); + } + .h-8 { + height: calc(var(--spacing) * 8); + } + .h-9 { + height: calc(var(--spacing) * 9); + } + .h-10 { + height: calc(var(--spacing) * 10); + } + .h-11 { + height: calc(var(--spacing) * 11); + } + .h-12 { + height: calc(var(--spacing) * 12); + } + .h-14 { + height: calc(var(--spacing) * 14); + } + .h-16 { + height: calc(var(--spacing) * 16); + } + .h-24 { + height: calc(var(--spacing) * 24); + } + .h-28 { + height: calc(var(--spacing) * 28); + } + .h-32 { + height: calc(var(--spacing) * 32); + } + .h-48 { + height: calc(var(--spacing) * 48); + } + .h-64 { + height: calc(var(--spacing) * 64); + } + .h-72 { + height: calc(var(--spacing) * 72); + } + .h-\[1\.15rem\] { + height: 1.15rem; + } + .h-\[38px\] { + height: 38px; + } + .h-\[200px\] { + height: 200px; + } + .h-\[320px\] { + height: 320px; + } + .h-\[500px\] { + height: 500px; + } + .h-\[560px\] { + height: 560px; + } + .h-\[calc\(100\%-1px\)\] { + height: calc(100% - 1px); + } + .h-\[var\(--radix-navigation-menu-viewport-height\)\] { + height: var(--radix-navigation-menu-viewport-height); + } + .h-\[var\(--radix-select-trigger-height\)\] { + height: var(--radix-select-trigger-height); + } + .h-auto { + height: auto; + } + .h-full { + height: 100%; + } + .h-px { + height: 1px; + } + .h-svh { + height: 100svh; + } + .max-h-\(--radix-context-menu-content-available-height\) { + max-height: var(--radix-context-menu-content-available-height); + } + .max-h-\(--radix-dropdown-menu-content-available-height\) { + max-height: var(--radix-dropdown-menu-content-available-height); + } + .max-h-\(--radix-select-content-available-height\) { + max-height: var(--radix-select-content-available-height); + } + .max-h-32 { + max-height: calc(var(--spacing) * 32); + } + .max-h-96 { + max-height: calc(var(--spacing) * 96); + } + .max-h-\[60vh\] { + max-height: 60vh; + } + .max-h-\[70vh\] { + max-height: 70vh; + } + .max-h-\[90vh\] { + max-height: 90vh; + } + .max-h-\[300px\] { + max-height: 300px; + } + .min-h-0 { + min-height: calc(var(--spacing) * 0); + } + .min-h-4 { + min-height: calc(var(--spacing) * 4); + } + .min-h-9 { + min-height: calc(var(--spacing) * 9); + } + .min-h-16 { + min-height: calc(var(--spacing) * 16); + } + .min-h-20 { + min-height: calc(var(--spacing) * 20); + } + .min-h-24 { + min-height: calc(var(--spacing) * 24); + } + .min-h-\[2\.75rem\] { + min-height: 2.75rem; + } + .min-h-\[80px\] { + min-height: 80px; + } + .min-h-\[100px\] { + min-height: 100px; + } + .min-h-\[120px\] { + min-height: 120px; + } + .min-h-\[200px\] { + min-height: 200px; + } + .min-h-\[280px\] { + min-height: 280px; + } + .min-h-\[300px\] { + min-height: 300px; + } + .min-h-\[320px\] { + min-height: 320px; + } + .min-h-screen { + min-height: 100vh; + } + .min-h-svh { + min-height: 100svh; + } + .w-\(--cell-size\) { + width: var(--cell-size); + } + .w-\(--sidebar-width\) { + width: var(--sidebar-width); + } + .w-0 { + width: calc(var(--spacing) * 0); + } + .w-0\.5 { + width: calc(var(--spacing) * 0.5); + } + .w-1 { + width: calc(var(--spacing) * 1); + } + .w-1\.5 { + width: calc(var(--spacing) * 1.5); + } + .w-1\/2 { + width: 50%; + } + .w-2 { + width: calc(var(--spacing) * 2); + } + .w-2\.5 { + width: calc(var(--spacing) * 2.5); + } + .w-3 { + width: calc(var(--spacing) * 3); + } + .w-3\/4 { + width: 75%; + } + .w-4 { + width: calc(var(--spacing) * 4); + } + .w-5 { + width: calc(var(--spacing) * 5); + } + .w-6 { + width: calc(var(--spacing) * 6); + } + .w-8 { + width: calc(var(--spacing) * 8); + } + .w-9 { + width: calc(var(--spacing) * 9); + } + .w-10 { + width: calc(var(--spacing) * 10); + } + .w-11 { + width: calc(var(--spacing) * 11); + } + .w-12 { + width: calc(var(--spacing) * 12); + } + .w-14 { + width: calc(var(--spacing) * 14); + } + .w-16 { + width: calc(var(--spacing) * 16); + } + .w-20 { + width: calc(var(--spacing) * 20); + } + .w-24 { + width: calc(var(--spacing) * 24); + } + .w-32 { + width: calc(var(--spacing) * 32); + } + .w-48 { + width: calc(var(--spacing) * 48); + } + .w-64 { + width: calc(var(--spacing) * 64); + } + .w-72 { + width: calc(var(--spacing) * 72); + } + .w-\[38px\] { + width: 38px; + } + .w-\[46\%\] { + width: 46%; + } + .w-\[100px\] { + width: 100px; + } + .w-\[280px\] { + width: 280px; + } + .w-\[400px\] { + width: 400px; + } + .w-auto { + width: auto; + } + .w-fit { + width: fit-content; + } + .w-full { + width: 100%; + } + .w-max { + width: max-content; + } + .w-px { + width: 1px; + } + .max-w-\(--skeleton-width\) { + max-width: var(--skeleton-width); + } + .max-w-2xl { + max-width: var(--container-2xl); + } + .max-w-3xl { + max-width: var(--container-3xl); + } + .max-w-4xl { + max-width: var(--container-4xl); + } + .max-w-5xl { + max-width: var(--container-5xl); + } + .max-w-6xl { + max-width: var(--container-6xl); + } + .max-w-7xl { + max-width: var(--container-7xl); + } + .max-w-\[18rem\] { + max-width: 18rem; + } + .max-w-\[40rem\] { + max-width: 40rem; + } + .max-w-\[42rem\] { + max-width: 42rem; + } + .max-w-\[52rem\] { + max-width: 52rem; + } + .max-w-\[58rem\] { + max-width: 58rem; + } + .max-w-\[80\%\] { + max-width: 80%; + } + .max-w-\[calc\(100\%-2rem\)\] { + max-width: calc(100% - 2rem); + } + .max-w-lg { + max-width: var(--container-lg); + } + .max-w-max { + max-width: max-content; + } + .max-w-md { + max-width: var(--container-md); + } + .max-w-none { + max-width: none; + } + .max-w-sm { + max-width: var(--container-sm); + } + .max-w-xl { + max-width: var(--container-xl); + } + .max-w-xs { + max-width: var(--container-xs); + } + .min-w-\(--cell-size\) { + min-width: var(--cell-size); + } + .min-w-0 { + min-width: calc(var(--spacing) * 0); + } + .min-w-5 { + min-width: calc(var(--spacing) * 5); + } + .min-w-8 { + min-width: calc(var(--spacing) * 8); + } + .min-w-9 { + min-width: calc(var(--spacing) * 9); + } + .min-w-10 { + min-width: calc(var(--spacing) * 10); + } + .min-w-\[8rem\] { + min-width: 8rem; + } + .min-w-\[12rem\] { + min-width: 12rem; + } + .min-w-\[200px\] { + min-width: 200px; + } + .min-w-\[var\(--radix-select-trigger-width\)\] { + min-width: var(--radix-select-trigger-width); + } + .min-w-fit { + min-width: fit-content; + } + .min-w-full { + min-width: 100%; + } + .flex-1 { + flex: 1; + } + .flex-shrink-0, + .shrink-0 { + flex-shrink: 0; + } + .grow { + flex-grow: 1; + } + .grow-0 { + flex-grow: 0; + } + .basis-full { + flex-basis: 100%; + } + .caption-bottom { + caption-side: bottom; + } + .border-collapse { + border-collapse: collapse; + } + .origin-\(--radix-context-menu-content-transform-origin\) { + transform-origin: var(--radix-context-menu-content-transform-origin); + } + .origin-\(--radix-dropdown-menu-content-transform-origin\) { + transform-origin: var(--radix-dropdown-menu-content-transform-origin); + } + .origin-\(--radix-hover-card-content-transform-origin\) { + transform-origin: var(--radix-hover-card-content-transform-origin); + } + .origin-\(--radix-menubar-content-transform-origin\) { + transform-origin: var(--radix-menubar-content-transform-origin); + } + .origin-\(--radix-popover-content-transform-origin\) { + transform-origin: var(--radix-popover-content-transform-origin); + } + .origin-\(--radix-select-content-transform-origin\) { + transform-origin: var(--radix-select-content-transform-origin); + } + .origin-\(--radix-tooltip-content-transform-origin\) { + transform-origin: var(--radix-tooltip-content-transform-origin); + } + .-translate-x-1\/2 { + --tw-translate-x: -50%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .-translate-x-px { + --tw-translate-x: -1px; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-x-0 { + --tw-translate-x: calc(var(--spacing) * 0); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-x-0\.5 { + --tw-translate-x: calc(var(--spacing) * 0.5); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-x-5 { + --tw-translate-x: calc(var(--spacing) * 5); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-x-\[-50\%\] { + --tw-translate-x: -50%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-x-full { + --tw-translate-x: 100%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-x-px { + --tw-translate-x: 1px; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .-translate-y-1\/2 { + --tw-translate-y: -50%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-y-0\.5 { + --tw-translate-y: calc(var(--spacing) * 0.5); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-y-\[-50\%\] { + --tw-translate-y: -50%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-y-\[calc\(-50\%_-_2px\)\] { + --tw-translate-y: calc(-50% - 2px); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .rotate-45 { + rotate: 45deg; + } + .rotate-90 { + rotate: 90deg; + } + .rotate-180 { + rotate: 180deg; + } + .transform { + transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) + var(--tw-skew-x,) var(--tw-skew-y,); + } + .animate-caret-blink { + animation: 1.25s ease-out infinite caret-blink; + } + .animate-in { + animation: enter var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .animate-pulse { + animation: var(--animate-pulse); + } + .animate-spin { + animation: var(--animate-spin); + } + .cursor-col-resize { + cursor: col-resize; + } + .cursor-default { + cursor: default; + } + .cursor-not-allowed { + cursor: not-allowed; + } + .cursor-pointer { + cursor: pointer; + } + .cursor-text { + cursor: text; + } + .touch-none { + touch-action: none; + } + .resize-none { + resize: none; + } + .scroll-my-1 { + scroll-margin-block: calc(var(--spacing) * 1); + } + .scroll-py-1 { + scroll-padding-block: calc(var(--spacing) * 1); + } + .list-inside { + list-style-position: inside; + } + .list-disc { + list-style-type: disc; + } + .list-none { + list-style-type: none; + } + .auto-rows-min { + grid-auto-rows: min-content; + } + .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-\[0_1fr\] { + grid-template-columns: 0 1fr; + } + .grid-cols-\[1\.15fr_0\.9fr_0\.9fr_1fr_1fr\] { + grid-template-columns: 1.15fr 0.9fr 0.9fr 1fr 1fr; + } + .grid-rows-\[auto_auto\] { + grid-template-rows: auto auto; + } + .flex-col { + flex-direction: column; + } + .flex-col-reverse { + flex-direction: column-reverse; + } + .flex-row { + flex-direction: row; + } + .flex-wrap { + flex-wrap: wrap; + } + .items-baseline { + align-items: baseline; + } + .items-center { + align-items: center; + } + .items-end { + align-items: flex-end; + } + .items-start { + align-items: flex-start; + } + .items-stretch { + align-items: stretch; + } + .justify-between { + justify-content: space-between; + } + .justify-center { + justify-content: center; + } + .justify-end { + justify-content: flex-end; + } + .justify-start { + justify-content: flex-start; + } + .justify-items-start { + justify-items: start; + } + .gap-0 { + gap: calc(var(--spacing) * 0); + } + .gap-0\.5 { + gap: calc(var(--spacing) * 0.5); + } + .gap-1 { + gap: calc(var(--spacing) * 1); + } + .gap-1\.5 { + gap: calc(var(--spacing) * 1.5); + } + .gap-2 { + gap: calc(var(--spacing) * 2); + } + .gap-2\.5 { + gap: calc(var(--spacing) * 2.5); + } + .gap-3 { + gap: calc(var(--spacing) * 3); + } + .gap-4 { + gap: calc(var(--spacing) * 4); + } + .gap-5 { + gap: calc(var(--spacing) * 5); + } + .gap-6 { + gap: calc(var(--spacing) * 6); + } + .gap-7 { + gap: calc(var(--spacing) * 7); + } + .gap-8 { + gap: calc(var(--spacing) * 8); + } + .gap-10 { + gap: calc(var(--spacing) * 10); + } + .gap-12 { + gap: calc(var(--spacing) * 12); + } + .gap-14 { + gap: calc(var(--spacing) * 14); + } + .gap-16 { + gap: calc(var(--spacing) * 16); + } + :where(.space-y-1 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 1) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-y-2 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 2) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-y-3 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 3) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-y-4 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 4) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-y-5 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 5) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-y-6 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 6) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-y-8 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 8) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-y-12 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 12) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 12) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-x-2 > :not(:last-child)) { + --tw-space-x-reverse: 0; + margin-inline-start: calc( + calc(var(--spacing) * 2) * var(--tw-space-x-reverse) + ); + margin-inline-end: calc( + calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)) + ); + } + .gap-y-0\.5 { + row-gap: calc(var(--spacing) * 0.5); + } + :where(.divide-y > :not(:last-child)) { + --tw-divide-y-reverse: 0; + border-bottom-style: var(--tw-border-style); + border-top-style: var(--tw-border-style); + border-top-width: calc(1px * var(--tw-divide-y-reverse)); + border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + } + :where(.divide-white\/8 > :not(:last-child)) { + border-color: #ffffff14; + } + @supports (color: color-mix(in lab, red, red)) { + :where(.divide-white\/8 > :not(:last-child)) { + border-color: color-mix( + in oklab, + var(--color-white) 8%, + transparent + ); + } + } + :where(.divide-white\/10 > :not(:last-child)) { + border-color: #ffffff1a; + } + @supports (color: color-mix(in lab, red, red)) { + :where(.divide-white\/10 > :not(:last-child)) { + border-color: color-mix( + in oklab, + var(--color-white) 10%, + transparent + ); + } + } + .self-start { + align-self: flex-start; + } + .self-stretch { + align-self: stretch; + } + .justify-self-end { + justify-self: flex-end; + } + .truncate { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + .overflow-auto { + overflow: auto; + } + .overflow-hidden { + overflow: hidden; + } + .overflow-x-auto { + overflow-x: auto; + } + .overflow-x-hidden { + overflow-x: hidden; + } + .overflow-y-auto { + overflow-y: auto; + } + .rounded { + border-radius: 0.25rem; + } + .rounded-2xl { + border-radius: var(--radius-2xl); + } + .rounded-\[1\.1rem\] { + border-radius: 1.1rem; + } + .rounded-\[1\.2rem\] { + border-radius: 1.2rem; + } + .rounded-\[1\.3rem\] { + border-radius: 1.3rem; + } + .rounded-\[1\.4rem\] { + border-radius: 1.4rem; + } + .rounded-\[1\.5rem\] { + border-radius: 1.5rem; + } + .rounded-\[1\.6rem\] { + border-radius: 1.6rem; + } + .rounded-\[1\.7rem\] { + border-radius: 1.7rem; + } + .rounded-\[1\.8rem\] { + border-radius: 1.8rem; + } + .rounded-\[1\.9rem\] { + border-radius: 1.9rem; + } + .rounded-\[1\.25rem\] { + border-radius: 1.25rem; + } + .rounded-\[1\.35rem\] { + border-radius: 1.35rem; + } + .rounded-\[1\.45rem\] { + border-radius: 1.45rem; + } + .rounded-\[1\.65rem\] { + border-radius: 1.65rem; + } + .rounded-\[1rem\] { + border-radius: 1rem; + } + .rounded-\[2\.2rem\] { + border-radius: 2.2rem; + } + .rounded-\[2\.3rem\] { + border-radius: 2.3rem; + } + .rounded-\[2px\] { + border-radius: 2px; + } + .rounded-\[2rem\] { + border-radius: 2rem; + } + .rounded-\[4px\] { + border-radius: 4px; + } + .rounded-\[10px\] { + border-radius: 10px; + } + .rounded-\[20px\] { + border-radius: 20px; + } + .rounded-\[calc\(var\(--radius\)-5px\)\] { + border-radius: calc(var(--radius) - 5px); + } + .rounded-\[inherit\] { + border-radius: inherit; + } + .rounded-full { + border-radius: 3.40282e38px; + } + .rounded-lg { + border-radius: var(--radius); + } + .rounded-md { + border-radius: calc(var(--radius) - 3px); + } + .rounded-none { + border-radius: 0; + } + .rounded-sm { + border-radius: calc(var(--radius) - 6px); + } + .rounded-xl { + border-radius: calc(var(--radius) + 8px); + } + .rounded-xs { + border-radius: var(--radius-xs); + } + .rounded-l-md { + border-top-left-radius: calc(var(--radius) - 3px); + border-bottom-left-radius: calc(var(--radius) - 3px); + } + .rounded-tl-sm { + border-top-left-radius: calc(var(--radius) - 6px); + } + .rounded-r-md { + border-top-right-radius: calc(var(--radius) - 3px); + border-bottom-right-radius: calc(var(--radius) - 3px); + } + .border { + border-style: var(--tw-border-style); + border-width: 1px; + } + .border-0 { + border-style: var(--tw-border-style); + border-width: 0; + } + .border-2 { + border-style: var(--tw-border-style); + border-width: 2px; + } + .border-\[1\.5px\] { + border-style: var(--tw-border-style); + border-width: 1.5px; + } + .border-y { + border-block-style: var(--tw-border-style); + border-block-width: 1px; + } + .border-t { + border-top-style: var(--tw-border-style); + border-top-width: 1px; + } + .border-r { + border-right-style: var(--tw-border-style); + border-right-width: 1px; + } + .border-r-0 { + border-right-style: var(--tw-border-style); + border-right-width: 0; + } + .border-b { + border-bottom-style: var(--tw-border-style); + border-bottom-width: 1px; + } + .border-b-2 { + border-bottom-style: var(--tw-border-style); + border-bottom-width: 2px; + } + .border-l { + border-left-style: var(--tw-border-style); + border-left-width: 1px; + } + .border-dashed { + --tw-border-style: dashed; + border-style: dashed; + } + .border-\(--color-border\) { + border-color: var(--color-border); + } + .border-\[\#0A66C2\]\/30 { + border-color: #0a66c24d; + } + .border-\[\#1877F2\]\/30 { + border-color: #1877f24d; + } + .border-\[rgba\(0\,0\,0\,0\.08\)\] { + border-color: #00000014; + } + .border-\[var\(--accent-strong\)\], + .border-\[var\(--accent-strong\)\]\/18 { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-strong\)\]\/18 { + border-color: color-mix( + in oklab, + var(--accent-strong) 18%, + transparent + ); + } + } + .border-\[var\(--accent-strong\)\]\/20 { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-strong\)\]\/20 { + border-color: color-mix( + in oklab, + var(--accent-strong) 20%, + transparent + ); + } + } + .border-\[var\(--accent-strong\)\]\/30 { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-strong\)\]\/30 { + border-color: color-mix( + in oklab, + var(--accent-strong) 30%, + transparent + ); + } + } + .border-\[var\(--accent-strong\)\]\/35 { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-strong\)\]\/35 { + border-color: color-mix( + in oklab, + var(--accent-strong) 35%, + transparent + ); + } + } + .border-\[var\(--accent-strong\)\]\/45 { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-strong\)\]\/45 { + border-color: color-mix( + in oklab, + var(--accent-strong) 45%, + transparent + ); + } + } + .border-\[var\(--accent-strong\)\]\/50 { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-strong\)\]\/50 { + border-color: color-mix( + in oklab, + var(--accent-strong) 50%, + transparent + ); + } + } + .border-\[var\(--accent-warm\)\]\/20 { + border-color: var(--accent-warm); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-warm\)\]\/20 { + border-color: color-mix( + in oklab, + var(--accent-warm) 20%, + transparent + ); + } + } + .border-\[var\(--accent-warm\)\]\/30 { + border-color: var(--accent-warm); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-warm\)\]\/30 { + border-color: color-mix( + in oklab, + var(--accent-warm) 30%, + transparent + ); + } + } + .border-accent\/20 { + border-color: var(--accent); + } + @supports (color: color-mix(in lab, red, red)) { + .border-accent\/20 { + border-color: color-mix(in oklab, var(--accent) 20%, transparent); + } + } + .border-amber-400\/30 { + border-color: #fcbb004d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-amber-400\/30 { + border-color: color-mix( + in oklab, + var(--color-amber-400) 30%, + transparent + ); + } + } + .border-black\/10 { + border-color: #0000001a; + } + @supports (color: color-mix(in lab, red, red)) { + .border-black\/10 { + border-color: color-mix( + in oklab, + var(--color-black) 10%, + transparent + ); + } + } + .border-blue-200 { + border-color: var(--color-blue-200); + } + .border-blue-400\/30 { + border-color: #54a2ff4d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-blue-400\/30 { + border-color: color-mix( + in oklab, + var(--color-blue-400) 30%, + transparent + ); + } + } + .border-blue-500\/20 { + border-color: #3080ff33; + } + @supports (color: color-mix(in lab, red, red)) { + .border-blue-500\/20 { + border-color: color-mix( + in oklab, + var(--color-blue-500) 20%, + transparent + ); + } + } + .border-blue-500\/30 { + border-color: #3080ff4d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-blue-500\/30 { + border-color: color-mix( + in oklab, + var(--color-blue-500) 30%, + transparent + ); + } + } + .border-border, + .border-border\/50 { + border-color: var(--border); + } + @supports (color: color-mix(in lab, red, red)) { + .border-border\/50 { + border-color: color-mix(in oklab, var(--border) 50%, transparent); + } + } + .border-cyan-400\/30 { + border-color: #00d2ef4d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-cyan-400\/30 { + border-color: color-mix( + in oklab, + var(--color-cyan-400) 30%, + transparent + ); + } + } + .border-destructive\/20 { + border-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .border-destructive\/20 { + border-color: color-mix( + in oklab, + var(--destructive) 20%, + transparent + ); + } + } + .border-emerald-400\/20 { + border-color: #00d29433; + } + @supports (color: color-mix(in lab, red, red)) { + .border-emerald-400\/20 { + border-color: color-mix( + in oklab, + var(--color-emerald-400) 20%, + transparent + ); + } + } + .border-emerald-400\/30 { + border-color: #00d2944d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-emerald-400\/30 { + border-color: color-mix( + in oklab, + var(--color-emerald-400) 30%, + transparent + ); + } + } + .border-foreground\/10 { + border-color: var(--foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .border-foreground\/10 { + border-color: color-mix( + in oklab, + var(--foreground) 10%, + transparent + ); + } + } + .border-gray-200 { + border-color: var(--color-gray-200); + } + .border-gray-500\/30 { + border-color: #6a72824d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-gray-500\/30 { + border-color: color-mix( + in oklab, + var(--color-gray-500) 30%, + transparent + ); + } + } + .border-green-200 { + border-color: var(--color-green-200); + } + .border-green-500 { + border-color: var(--color-green-500); + } + .border-green-500\/20 { + border-color: #00c75833; + } + @supports (color: color-mix(in lab, red, red)) { + .border-green-500\/20 { + border-color: color-mix( + in oklab, + var(--color-green-500) 20%, + transparent + ); + } + } + .border-green-500\/30 { + border-color: #00c7584d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-green-500\/30 { + border-color: color-mix( + in oklab, + var(--color-green-500) 30%, + transparent + ); + } + } + .border-input { + border-color: var(--input); + } + .border-muted-foreground\/30 { + border-color: var(--muted-foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .border-muted-foreground\/30 { + border-color: color-mix( + in oklab, + var(--muted-foreground) 30%, + transparent + ); + } + } + .border-primary { + border-color: var(--primary); + } + .border-red-200 { + border-color: var(--color-red-200); + } + .border-red-500 { + border-color: var(--color-red-500); + } + .border-red-500\/20 { + border-color: #fb2c3633; + } + @supports (color: color-mix(in lab, red, red)) { + .border-red-500\/20 { + border-color: color-mix( + in oklab, + var(--color-red-500) 20%, + transparent + ); + } + } + .border-rose-400\/20 { + border-color: #ff667f33; + } + @supports (color: color-mix(in lab, red, red)) { + .border-rose-400\/20 { + border-color: color-mix( + in oklab, + var(--color-rose-400) 20%, + transparent + ); + } + } + .border-rose-400\/30 { + border-color: #ff667f4d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-rose-400\/30 { + border-color: color-mix( + in oklab, + var(--color-rose-400) 30%, + transparent + ); + } + } + .border-sidebar-border { + border-color: var(--sidebar-border); + } + .border-slate-200 { + border-color: var(--color-slate-200); + } + .border-slate-300 { + border-color: var(--color-slate-300); + } + .border-transparent { + border-color: #0000; + } + .border-white\/5 { + border-color: #ffffff0d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/5 { + border-color: color-mix( + in oklab, + var(--color-white) 5%, + transparent + ); + } + } + .border-white\/6 { + border-color: #ffffff0f; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/6 { + border-color: color-mix( + in oklab, + var(--color-white) 6%, + transparent + ); + } + } + .border-white\/8 { + border-color: #ffffff14; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/8 { + border-color: color-mix( + in oklab, + var(--color-white) 8%, + transparent + ); + } + } + .border-white\/10 { + border-color: #ffffff1a; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/10 { + border-color: color-mix( + in oklab, + var(--color-white) 10%, + transparent + ); + } + } + .border-white\/12 { + border-color: #ffffff1f; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/12 { + border-color: color-mix( + in oklab, + var(--color-white) 12%, + transparent + ); + } + } + .border-white\/16 { + border-color: #ffffff29; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/16 { + border-color: color-mix( + in oklab, + var(--color-white) 16%, + transparent + ); + } + } + .border-white\/18 { + border-color: #ffffff2e; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/18 { + border-color: color-mix( + in oklab, + var(--color-white) 18%, + transparent + ); + } + } + .border-white\/20 { + border-color: #fff3; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/20 { + border-color: color-mix( + in oklab, + var(--color-white) 20%, + transparent + ); + } + } + .border-yellow-200 { + border-color: var(--color-yellow-200); + } + .border-yellow-500\/20 { + border-color: #edb20033; + } + @supports (color: color-mix(in lab, red, red)) { + .border-yellow-500\/20 { + border-color: color-mix( + in oklab, + var(--color-yellow-500) 20%, + transparent + ); + } + } + .border-yellow-500\/30 { + border-color: #edb2004d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-yellow-500\/30 { + border-color: color-mix( + in oklab, + var(--color-yellow-500) 30%, + transparent + ); + } + } + .border-t-transparent { + border-top-color: #0000; + } + .border-l-transparent { + border-left-color: #0000; + } + .bg-\(--color-bg\) { + background-color: var(--color-bg); + } + .bg-\[\#0A66C2\]\/20 { + background-color: #0a66c233; + } + .bg-\[\#1a1a19\] { + background-color: #1a1a19; + } + .bg-\[\#1877F2\]\/20 { + background-color: #1877f233; + } + .bg-\[\#f4f1ea\] { + background-color: #f4f1ea; + } + .bg-\[\#f8f8f7\] { + background-color: #f8f8f7; + } + .bg-\[rgba\(7\,18\,22\,0\.8\)\] { + background-color: #071216cc; + } + .bg-\[rgba\(7\,18\,22\,0\.78\)\] { + background-color: #071216c7; + } + .bg-\[rgba\(7\,18\,22\,0\.84\)\] { + background-color: #071216d6; + } + .bg-\[rgba\(7\,18\,22\,0\.95\)\] { + background-color: #071216f2; + } + .bg-\[rgba\(7\,18\,24\,0\.96\)\] { + background-color: #071218f5; + } + .bg-\[rgba\(7\,18\,24\,0\.98\)\] { + background-color: #071218fa; + } + .bg-\[rgba\(8\,20\,25\,0\.76\)\] { + background-color: #081419c2; + } + .bg-\[rgba\(8\,20\,25\,0\.78\)\] { + background-color: #081419c7; + } + .bg-\[rgba\(8\,20\,25\,0\.95\)\] { + background-color: #081419f2; + } + .bg-\[rgba\(8\,21\,30\,0\.88\)\] { + background-color: #08151ee0; + } + .bg-\[rgba\(8\,23\,28\,0\.78\)\] { + background-color: #08171cc7; + } + .bg-\[rgba\(9\,20\,24\,0\.82\)\] { + background-color: #091418d1; + } + .bg-\[rgba\(9\,20\,24\,0\.95\)\] { + background-color: #091418f2; + } + .bg-\[rgba\(9\,20\,24\,0\.98\)\] { + background-color: #091418fa; + } + .bg-\[rgba\(11\,34\,42\,0\.95\)\] { + background-color: #0b222af2; + } + .bg-\[rgba\(46\,182\,194\,0\.12\)\] { + background-color: #2eb6c21f; + } + .bg-\[rgba\(83\,213\,205\,0\.12\)\] { + background-color: #53d5cd1f; + } + .bg-\[rgba\(255\,255\,255\,0\.04\)\] { + background-color: #ffffff0a; + } + .bg-\[var\(--accent-strong\)\], + .bg-\[var\(--accent-strong\)\]\/5 { + background-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-strong\)\]\/5 { + background-color: color-mix( + in oklab, + var(--accent-strong) 5%, + transparent + ); + } + } + .bg-\[var\(--accent-strong\)\]\/8 { + background-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-strong\)\]\/8 { + background-color: color-mix( + in oklab, + var(--accent-strong) 8%, + transparent + ); + } + } + .bg-\[var\(--accent-strong\)\]\/10 { + background-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-strong\)\]\/10 { + background-color: color-mix( + in oklab, + var(--accent-strong) 10%, + transparent + ); + } + } + .bg-\[var\(--accent-strong\)\]\/12 { + background-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-strong\)\]\/12 { + background-color: color-mix( + in oklab, + var(--accent-strong) 12%, + transparent + ); + } + } + .bg-\[var\(--accent-strong\)\]\/20 { + background-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-strong\)\]\/20 { + background-color: color-mix( + in oklab, + var(--accent-strong) 20%, + transparent + ); + } + } + .bg-\[var\(--accent-warm\)\], + .bg-\[var\(--accent-warm\)\]\/5 { + background-color: var(--accent-warm); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-warm\)\]\/5 { + background-color: color-mix( + in oklab, + var(--accent-warm) 5%, + transparent + ); + } + } + .bg-\[var\(--accent-warm\)\]\/10 { + background-color: var(--accent-warm); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-warm\)\]\/10 { + background-color: color-mix( + in oklab, + var(--accent-warm) 10%, + transparent + ); + } + } + .bg-\[var\(--accent-warm\)\]\/20 { + background-color: var(--accent-warm); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-warm\)\]\/20 { + background-color: color-mix( + in oklab, + var(--accent-warm) 20%, + transparent + ); + } + } + .bg-\[var\(--surface-elevated\)\] { + background-color: var(--surface-elevated); + } + .bg-accent, + .bg-accent\/10 { + background-color: var(--accent); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-accent\/10 { + background-color: color-mix( + in oklab, + var(--accent) 10%, + transparent + ); + } + } + .bg-amber-400\/10 { + background-color: #fcbb001a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-amber-400\/10 { + background-color: color-mix( + in oklab, + var(--color-amber-400) 10%, + transparent + ); + } + } + .bg-background, + .bg-background\/50 { + background-color: var(--background); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-background\/50 { + background-color: color-mix( + in oklab, + var(--background) 50%, + transparent + ); + } + } + .bg-background\/95 { + background-color: var(--background); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-background\/95 { + background-color: color-mix( + in oklab, + var(--background) 95%, + transparent + ); + } + } + .bg-black\/20 { + background-color: #0003; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-black\/20 { + background-color: color-mix( + in oklab, + var(--color-black) 20%, + transparent + ); + } + } + .bg-black\/40 { + background-color: #0006; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-black\/40 { + background-color: color-mix( + in oklab, + var(--color-black) 40%, + transparent + ); + } + } + .bg-black\/50 { + background-color: #00000080; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-black\/50 { + background-color: color-mix( + in oklab, + var(--color-black) 50%, + transparent + ); + } + } + .bg-blue-100 { + background-color: var(--color-blue-100); + } + .bg-blue-400\/10 { + background-color: #54a2ff1a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-blue-400\/10 { + background-color: color-mix( + in oklab, + var(--color-blue-400) 10%, + transparent + ); + } + } + .bg-blue-500\/10 { + background-color: #3080ff1a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-blue-500\/10 { + background-color: color-mix( + in oklab, + var(--color-blue-500) 10%, + transparent + ); + } + } + .bg-blue-500\/20 { + background-color: #3080ff33; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-blue-500\/20 { + background-color: color-mix( + in oklab, + var(--color-blue-500) 20%, + transparent + ); + } + } + .bg-blue-600 { + background-color: var(--color-blue-600); + } + .bg-border { + background-color: var(--border); + } + .bg-card { + background-color: var(--card); + } + .bg-cyan-400\/10 { + background-color: #00d2ef1a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-cyan-400\/10 { + background-color: color-mix( + in oklab, + var(--color-cyan-400) 10%, + transparent + ); + } + } + .bg-cyan-500\/20 { + background-color: #00b7d733; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-cyan-500\/20 { + background-color: color-mix( + in oklab, + var(--color-cyan-500) 20%, + transparent + ); + } + } + .bg-destructive, + .bg-destructive\/10 { + background-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-destructive\/10 { + background-color: color-mix( + in oklab, + var(--destructive) 10%, + transparent + ); + } + } + .bg-emerald-400\/10 { + background-color: #00d2941a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-emerald-400\/10 { + background-color: color-mix( + in oklab, + var(--color-emerald-400) 10%, + transparent + ); + } + } + .bg-foreground, + .bg-foreground\/5 { + background-color: var(--foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-foreground\/5 { + background-color: color-mix( + in oklab, + var(--foreground) 5%, + transparent + ); + } + } + .bg-gray-100 { + background-color: var(--color-gray-100); + } + .bg-gray-500\/10 { + background-color: #6a72821a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-gray-500\/10 { + background-color: color-mix( + in oklab, + var(--color-gray-500) 10%, + transparent + ); + } + } + .bg-gray-500\/20 { + background-color: #6a728233; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-gray-500\/20 { + background-color: color-mix( + in oklab, + var(--color-gray-500) 20%, + transparent + ); + } + } + .bg-green-100 { + background-color: var(--color-green-100); + } + .bg-green-500\/10 { + background-color: #00c7581a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-green-500\/10 { + background-color: color-mix( + in oklab, + var(--color-green-500) 10%, + transparent + ); + } + } + .bg-green-500\/20 { + background-color: #00c75833; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-green-500\/20 { + background-color: color-mix( + in oklab, + var(--color-green-500) 20%, + transparent + ); + } + } + .bg-input { + background-color: var(--input); + } + .bg-muted, + .bg-muted\/50 { + background-color: var(--muted); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-muted\/50 { + background-color: color-mix( + in oklab, + var(--muted) 50%, + transparent + ); + } + } + .bg-orange-100 { + background-color: var(--color-orange-100); + } + .bg-popover { + background-color: var(--popover); + } + .bg-primary, + .bg-primary\/10 { + background-color: var(--primary); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-primary\/10 { + background-color: color-mix( + in oklab, + var(--primary) 10%, + transparent + ); + } + } + .bg-primary\/20 { + background-color: var(--primary); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-primary\/20 { + background-color: color-mix( + in oklab, + var(--primary) 20%, + transparent + ); + } + } + .bg-purple-100 { + background-color: var(--color-purple-100); + } + .bg-purple-500\/10 { + background-color: #ac4bff1a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-purple-500\/10 { + background-color: color-mix( + in oklab, + var(--color-purple-500) 10%, + transparent + ); + } + } + .bg-purple-500\/20 { + background-color: #ac4bff33; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-purple-500\/20 { + background-color: color-mix( + in oklab, + var(--color-purple-500) 20%, + transparent + ); + } + } + .bg-red-50 { + background-color: var(--color-red-50); + } + .bg-red-100 { + background-color: var(--color-red-100); + } + .bg-red-500\/10 { + background-color: #fb2c361a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-red-500\/10 { + background-color: color-mix( + in oklab, + var(--color-red-500) 10%, + transparent + ); + } + } + .bg-red-600\/10 { + background-color: #e400141a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-red-600\/10 { + background-color: color-mix( + in oklab, + var(--color-red-600) 10%, + transparent + ); + } + } + .bg-rose-400\/10 { + background-color: #ff667f1a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-rose-400\/10 { + background-color: color-mix( + in oklab, + var(--color-rose-400) 10%, + transparent + ); + } + } + .bg-secondary { + background-color: var(--secondary); + } + .bg-sidebar { + background-color: var(--sidebar); + } + .bg-sidebar-border { + background-color: var(--sidebar-border); + } + .bg-slate-50 { + background-color: var(--color-slate-50); + } + .bg-slate-100 { + background-color: var(--color-slate-100); + } + .bg-slate-900 { + background-color: var(--color-slate-900); + } + .bg-slate-950 { + background-color: var(--color-slate-950); + } + .bg-transparent { + background-color: #0000; + } + .bg-white { + background-color: var(--color-white); + } + .bg-white\/3 { + background-color: #ffffff08; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/3 { + background-color: color-mix( + in oklab, + var(--color-white) 3%, + transparent + ); + } + } + .bg-white\/4 { + background-color: #ffffff0a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/4 { + background-color: color-mix( + in oklab, + var(--color-white) 4%, + transparent + ); + } + } + .bg-white\/5 { + background-color: #ffffff0d; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/5 { + background-color: color-mix( + in oklab, + var(--color-white) 5%, + transparent + ); + } + } + .bg-white\/6 { + background-color: #ffffff0f; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/6 { + background-color: color-mix( + in oklab, + var(--color-white) 6%, + transparent + ); + } + } + .bg-white\/7 { + background-color: #ffffff12; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/7 { + background-color: color-mix( + in oklab, + var(--color-white) 7%, + transparent + ); + } + } + .bg-white\/8 { + background-color: #ffffff14; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/8 { + background-color: color-mix( + in oklab, + var(--color-white) 8%, + transparent + ); + } + } + .bg-white\/10 { + background-color: #ffffff1a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/10 { + background-color: color-mix( + in oklab, + var(--color-white) 10%, + transparent + ); + } + } + .bg-white\/20 { + background-color: #fff3; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/20 { + background-color: color-mix( + in oklab, + var(--color-white) 20%, + transparent + ); + } + } + .bg-white\/80 { + background-color: #fffc; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/80 { + background-color: color-mix( + in oklab, + var(--color-white) 80%, + transparent + ); + } + } + .bg-white\/\[0\.02\] { + background-color: #ffffff05; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/\[0\.02\] { + background-color: color-mix( + in oklab, + var(--color-white) 2%, + transparent + ); + } + } + .bg-white\/\[0\.03\] { + background-color: #ffffff08; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/\[0\.03\] { + background-color: color-mix( + in oklab, + var(--color-white) 3%, + transparent + ); + } + } + .bg-yellow-500\/10 { + background-color: #edb2001a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-yellow-500\/10 { + background-color: color-mix( + in oklab, + var(--color-yellow-500) 10%, + transparent + ); + } + } + .bg-yellow-500\/20 { + background-color: #edb20033; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-yellow-500\/20 { + background-color: color-mix( + in oklab, + var(--color-yellow-500) 20%, + transparent + ); + } + } + .bg-gradient-to-b { + --tw-gradient-position: to bottom in oklab; + background-image: linear-gradient(var(--tw-gradient-stops)); + } + .bg-gradient-to-br { + --tw-gradient-position: to bottom right in oklab; + background-image: linear-gradient(var(--tw-gradient-stops)); + } + .bg-gradient-to-r { + --tw-gradient-position: to right in oklab; + background-image: linear-gradient(var(--tw-gradient-stops)); + } + .bg-\[linear-gradient\(90deg\,rgba\(4\,14\,18\,0\)_0\%\,rgba\(4\,14\,18\,0\.01\)_22\%\,rgba\(4\,14\,18\,0\.12\)_100\%\)\] { + background-image: linear-gradient( + 90deg, + #040e1200, + #040e1203 22%, + #040e121f + ); + } + .bg-\[linear-gradient\(90deg\,rgba\(4\,14\,18\,0\.58\)_0\%\,rgba\(4\,14\,18\,0\.24\)_34\%\,rgba\(4\,14\,18\,0\.08\)_58\%\,rgba\(4\,14\,18\,0\.34\)_100\%\)\] { + background-image: linear-gradient( + 90deg, + #040e1294, + #040e123d 34%, + #040e1214 58%, + #040e1257 + ); + } + .bg-\[linear-gradient\(135deg\,rgba\(7\,18\,22\,0\.94\)\,rgba\(11\,27\,32\,0\.92\)\)\] { + background-image: linear-gradient(135deg, #071216f0, #0b1b20eb); + } + .bg-\[linear-gradient\(135deg\,rgba\(46\,182\,194\,0\.22\)\,rgba\(255\,193\,43\,0\.12\)\)\] { + background-image: linear-gradient(135deg, #2eb6c238, #ffc12b1f); + } + .bg-\[linear-gradient\(135deg\,rgba\(83\,213\,205\,0\.24\)\,rgba\(255\,193\,43\,0\.18\)\)\] { + background-image: linear-gradient(135deg, #53d5cd3d, #ffc12b2e); + } + .bg-\[linear-gradient\(135deg\,rgba\(255\,255\,255\,0\.08\)\,rgba\(255\,255\,255\,0\.03\)\)\] { + background-image: linear-gradient(135deg, #ffffff14, #ffffff08); + } + .bg-\[linear-gradient\(180deg\,rgba\(7\,18\,22\,1\)\,rgba\(10\,27\,31\,1\)\)\] { + background-image: linear-gradient(#071216, #0a1b1f); + } + .bg-\[linear-gradient\(180deg\,rgba\(255\,255\,255\,0\.06\)\,rgba\(255\,255\,255\,0\.03\)\)\] { + background-image: linear-gradient(#ffffff0f, #ffffff08); + } + .bg-\[radial-gradient\(circle\,rgba\(77\,201\,196\,0\.18\)\,transparent_70\%\)\] { + background-image: radial-gradient(circle, #4dc9c42e, #0000 70%); + } + .bg-\[radial-gradient\(circle_at_center\,rgba\(46\,182\,194\,0\.1\)\,transparent_58\%\)\] { + background-image: radial-gradient(circle, #2eb6c21a, #0000 58%); + } + .bg-\[radial-gradient\(circle_at_center\,rgba\(83\,213\,205\,0\.1\)\,transparent_58\%\)\] { + background-image: radial-gradient(circle, #53d5cd1a, #0000 58%); + } + .bg-\[radial-gradient\(circle_at_center\,rgba\(255\,193\,43\,0\.11\)\,transparent_60\%\)\] { + background-image: radial-gradient(circle, #ffc12b1c, #0000 60%); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(46\,182\,194\,0\.12\)\,transparent_34\%\)\,radial-gradient\(circle_at_82\%_12\%\,rgba\(255\,193\,43\,0\.1\)\,transparent_18\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,20\,0\.76\)\,rgba\(4\,14\,20\,0\.95\)\)\] { + background-image: + radial-gradient(circle at 0 0, #2eb6c21f, #0000 34%), + radial-gradient(circle at 82% 12%, #ffc12b1a, #0000 18%), + linear-gradient(#040e14c2, #040e14f2); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(46\,182\,194\,0\.12\)\,transparent_34\%\)\,radial-gradient\(circle_at_82\%_12\%\,rgba\(255\,193\,43\,0\.08\)\,transparent_18\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,20\,0\.76\)\,rgba\(4\,14\,20\,0\.95\)\)\] { + background-image: + radial-gradient(circle at 0 0, #2eb6c21f, #0000 34%), + radial-gradient(circle at 82% 12%, #ffc12b14, #0000 18%), + linear-gradient(#040e14c2, #040e14f2); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(46\,182\,194\,0\.13\)\,transparent_34\%\)\,radial-gradient\(circle_at_82\%_12\%\,rgba\(255\,193\,43\,0\.10\)\,transparent_20\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,20\,0\.78\)\,rgba\(4\,14\,20\,0\.96\)\)\] { + background-image: + radial-gradient(circle at 0 0, #2eb6c221, #0000 34%), + radial-gradient(circle at 82% 12%, #ffc12b1a, #0000 20%), + linear-gradient(#040e14c7, #040e14f5); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(46\,182\,194\,0\.13\)\,transparent_34\%\)\,radial-gradient\(circle_at_82\%_12\%\,rgba\(255\,193\,43\,0\.12\)\,transparent_20\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,20\,0\.78\)\,rgba\(4\,14\,20\,0\.96\)\)\] { + background-image: + radial-gradient(circle at 0 0, #2eb6c221, #0000 34%), + radial-gradient(circle at 82% 12%, #ffc12b1f, #0000 20%), + linear-gradient(#040e14c7, #040e14f5); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(46\,182\,194\,0\.14\)\,transparent_34\%\)\,radial-gradient\(circle_at_82\%_12\%\,rgba\(255\,193\,43\,0\.08\)\,transparent_18\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,20\,0\.76\)\,rgba\(4\,14\,20\,0\.95\)\)\] { + background-image: + radial-gradient(circle at 0 0, #2eb6c224, #0000 34%), + radial-gradient(circle at 82% 12%, #ffc12b14, #0000 18%), + linear-gradient(#040e14c2, #040e14f2); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(46\,182\,194\,0\.14\)\,transparent_36\%\)\,radial-gradient\(circle_at_82\%_18\%\,rgba\(255\,193\,43\,0\.12\)\,transparent_22\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,20\,0\.82\)\,rgba\(4\,14\,20\,0\.97\)\)\] { + background-image: + radial-gradient(circle at 0 0, #2eb6c224, #0000 36%), + radial-gradient(circle at 82% 18%, #ffc12b1f, #0000 22%), + linear-gradient(#040e14d1, #040e14f7); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(77\,201\,196\,0\.08\)\,transparent_30\%\)\,radial-gradient\(circle_at_82\%_14\%\,rgba\(255\,255\,255\,0\.05\)\,transparent_18\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,18\,0\.26\)\,rgba\(4\,14\,18\,0\.72\)\)\] { + background-image: + radial-gradient(circle at 0 0, #4dc9c414, #0000 30%), + radial-gradient(circle at 82% 14%, #ffffff0d, #0000 18%), + linear-gradient(#040e1242, #040e12b8); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(77\,201\,196\,0\.08\)\,transparent_34\%\)\,radial-gradient\(circle_at_80\%_15\%\,rgba\(255\,255\,255\,0\.06\)\,transparent_18\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,18\,0\.52\)\,rgba\(4\,14\,18\,0\.76\)\)\] { + background-image: + radial-gradient(circle at 0 0, #4dc9c414, #0000 34%), + radial-gradient(circle at 80% 15%, #ffffff0f, #0000 18%), + linear-gradient(#040e1285, #040e12c2); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(77\,201\,196\,0\.08\)\,transparent_34\%\)\,radial-gradient\(circle_at_80\%_15\%\,rgba\(255\,255\,255\,0\.06\)\,transparent_18\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,18\,0\.78\)\,rgba\(4\,14\,18\,0\.96\)\)\] { + background-image: + radial-gradient(circle at 0 0, #4dc9c414, #0000 34%), + radial-gradient(circle at 80% 15%, #ffffff0f, #0000 18%), + linear-gradient(#040e12c7, #040e12f5); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(77\,201\,196\,0\.08\)\,transparent_34\%\)\,radial-gradient\(circle_at_82\%_18\%\,rgba\(255\,255\,255\,0\.05\)\,transparent_18\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,18\,0\.76\)\,rgba\(4\,14\,18\,0\.97\)\)\] { + background-image: + radial-gradient(circle at 0 0, #4dc9c414, #0000 34%), + radial-gradient(circle at 82% 18%, #ffffff0d, #0000 18%), + linear-gradient(#040e12c2, #040e12f7); + } + .from-\[var\(--accent-strong\)\] { + --tw-gradient-from: var(--accent-strong); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-background\/95 { + --tw-gradient-from: var(--background); + } + @supports (color: color-mix(in lab, red, red)) { + .from-background\/95 { + --tw-gradient-from: color-mix( + in oklab, + var(--background) 95%, + transparent + ); + } + } + .from-background\/95 { + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-blue-500 { + --tw-gradient-from: var(--color-blue-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-cyan-500 { + --tw-gradient-from: var(--color-cyan-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-green-500 { + --tw-gradient-from: var(--color-green-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-indigo-500 { + --tw-gradient-from: var(--color-indigo-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-orange-500 { + --tw-gradient-from: var(--color-orange-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-purple-500 { + --tw-gradient-from: var(--color-purple-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-slate-50 { + --tw-gradient-from: var(--color-slate-50); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-teal-500 { + --tw-gradient-from: var(--color-teal-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-\[var\(--accent-warm\)\] { + --tw-gradient-to: var(--accent-warm); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-background\/80 { + --tw-gradient-to: var(--background); + } + @supports (color: color-mix(in lab, red, red)) { + .to-background\/80 { + --tw-gradient-to: color-mix( + in oklab, + var(--background) 80%, + transparent + ); + } + } + .to-background\/80 { + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-blue-500 { + --tw-gradient-to: var(--color-blue-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-blue-600 { + --tw-gradient-to: var(--color-blue-600); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-cyan-600 { + --tw-gradient-to: var(--color-cyan-600); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-green-600 { + --tw-gradient-to: var(--color-green-600); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-indigo-600 { + --tw-gradient-to: var(--color-indigo-600); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-orange-600 { + --tw-gradient-to: var(--color-orange-600); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-purple-600 { + --tw-gradient-to: var(--color-purple-600); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-slate-100 { + --tw-gradient-to: var(--color-slate-100); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-teal-600 { + --tw-gradient-to: var(--color-teal-600); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .bg-cover { + background-size: cover; + } + .bg-center { + background-position: 50%; + } + .fill-current { + fill: currentColor; + } + .fill-foreground { + fill: var(--foreground); + } + .fill-primary { + fill: var(--primary); + } + .object-cover { + object-fit: cover; + } + .object-center { + object-position: center; + } + .p-0 { + padding: calc(var(--spacing) * 0); + } + .p-1 { + padding: calc(var(--spacing) * 1); + } + .p-2 { + padding: calc(var(--spacing) * 2); + } + .p-3 { + padding: calc(var(--spacing) * 3); + } + .p-4 { + padding: calc(var(--spacing) * 4); + } + .p-5 { + padding: calc(var(--spacing) * 5); + } + .p-6 { + padding: calc(var(--spacing) * 6); + } + .p-7 { + padding: calc(var(--spacing) * 7); + } + .p-8 { + padding: calc(var(--spacing) * 8); + } + .p-10 { + padding: calc(var(--spacing) * 10); + } + .p-12 { + padding: calc(var(--spacing) * 12); + } + .p-\[3px\] { + padding: 3px; + } + .p-px { + padding: 1px; + } + .px-\(--cell-size\) { + padding-inline: var(--cell-size); + } + .px-1 { + padding-inline: calc(var(--spacing) * 1); + } + .px-1\.5 { + padding-inline: calc(var(--spacing) * 1.5); + } + .px-2 { + padding-inline: calc(var(--spacing) * 2); + } + .px-2\.5 { + padding-inline: calc(var(--spacing) * 2.5); + } + .px-3 { + padding-inline: calc(var(--spacing) * 3); + } + .px-4 { + padding-inline: calc(var(--spacing) * 4); + } + .px-5 { + padding-inline: calc(var(--spacing) * 5); + } + .px-6 { + padding-inline: calc(var(--spacing) * 6); + } + .px-7 { + padding-inline: calc(var(--spacing) * 7); + } + .py-0\.5 { + padding-block: calc(var(--spacing) * 0.5); + } + .py-1 { + padding-block: calc(var(--spacing) * 1); + } + .py-1\.5 { + padding-block: calc(var(--spacing) * 1.5); + } + .py-2 { + padding-block: calc(var(--spacing) * 2); + } + .py-2\.5 { + padding-block: calc(var(--spacing) * 2.5); + } + .py-3 { + padding-block: calc(var(--spacing) * 3); + } + .py-4 { + padding-block: calc(var(--spacing) * 4); + } + .py-5 { + padding-block: calc(var(--spacing) * 5); + } + .py-6 { + padding-block: calc(var(--spacing) * 6); + } + .py-7 { + padding-block: calc(var(--spacing) * 7); + } + .py-8 { + padding-block: calc(var(--spacing) * 8); + } + .py-12 { + padding-block: calc(var(--spacing) * 12); + } + .py-14 { + padding-block: calc(var(--spacing) * 14); + } + .py-16 { + padding-block: calc(var(--spacing) * 16); + } + .py-20 { + padding-block: calc(var(--spacing) * 20); + } + .py-24 { + padding-block: calc(var(--spacing) * 24); + } + .pt-0 { + padding-top: calc(var(--spacing) * 0); + } + .pt-2 { + padding-top: calc(var(--spacing) * 2); + } + .pt-3 { + padding-top: calc(var(--spacing) * 3); + } + .pt-4 { + padding-top: calc(var(--spacing) * 4); + } + .pt-6 { + padding-top: calc(var(--spacing) * 6); + } + .pt-8 { + padding-top: calc(var(--spacing) * 8); + } + .pt-12 { + padding-top: calc(var(--spacing) * 12); + } + .pt-24 { + padding-top: calc(var(--spacing) * 24); + } + .pr-1 { + padding-right: calc(var(--spacing) * 1); + } + .pr-2 { + padding-right: calc(var(--spacing) * 2); + } + .pr-2\.5 { + padding-right: calc(var(--spacing) * 2.5); + } + .pr-3 { + padding-right: calc(var(--spacing) * 3); + } + .pr-4 { + padding-right: calc(var(--spacing) * 4); + } + .pr-8 { + padding-right: calc(var(--spacing) * 8); + } + .pb-2 { + padding-bottom: calc(var(--spacing) * 2); + } + .pb-3 { + padding-bottom: calc(var(--spacing) * 3); + } + .pb-4 { + padding-bottom: calc(var(--spacing) * 4); + } + .pb-6 { + padding-bottom: calc(var(--spacing) * 6); + } + .pb-8 { + padding-bottom: calc(var(--spacing) * 8); + } + .pb-12 { + padding-bottom: calc(var(--spacing) * 12); + } + .pl-2 { + padding-left: calc(var(--spacing) * 2); + } + .pl-3 { + padding-left: calc(var(--spacing) * 3); + } + .pl-4 { + padding-left: calc(var(--spacing) * 4); + } + .pl-5 { + padding-left: calc(var(--spacing) * 5); + } + .pl-8 { + padding-left: calc(var(--spacing) * 8); + } + .pl-12 { + padding-left: calc(var(--spacing) * 12); + } + .text-center { + text-align: center; + } + .text-left { + text-align: left; + } + .text-right { + text-align: right; + } + .align-middle { + vertical-align: middle; + } + .align-top { + vertical-align: top; + } + .font-mono { + font-family: var(--font-mono); + } + .font-sans { + font-family: + DM Sans, + sans-serif; + } + .text-2xl { + font-size: var(--text-2xl); + line-height: var(--tw-leading, var(--text-2xl--line-height)); + } + .text-3xl { + font-size: var(--text-3xl); + line-height: var(--tw-leading, var(--text-3xl--line-height)); + } + .text-4xl { + font-size: var(--text-4xl); + line-height: var(--tw-leading, var(--text-4xl--line-height)); + } + .text-5xl { + font-size: var(--text-5xl); + line-height: var(--tw-leading, var(--text-5xl--line-height)); + } + .text-base { + font-size: var(--text-base); + line-height: var(--tw-leading, var(--text-base--line-height)); + } + .text-lg { + font-size: var(--text-lg); + line-height: var(--tw-leading, var(--text-lg--line-height)); + } + .text-sm { + font-size: var(--text-sm); + line-height: var(--tw-leading, var(--text-sm--line-height)); + } + .text-sm\/relaxed { + font-size: var(--text-sm); + line-height: var(--leading-relaxed); + } + .text-xl { + font-size: var(--text-xl); + line-height: var(--tw-leading, var(--text-xl--line-height)); + } + .text-xs { + font-size: var(--text-xs); + line-height: var(--tw-leading, var(--text-xs--line-height)); + } + .text-\[0\.8rem\] { + font-size: 0.8rem; + } + .text-\[0\.68rem\] { + font-size: 0.68rem; + } + .text-\[0\.72rem\] { + font-size: 0.72rem; + } + .leading-5 { + --tw-leading: calc(var(--spacing) * 5); + line-height: calc(var(--spacing) * 5); + } + .leading-6 { + --tw-leading: calc(var(--spacing) * 6); + line-height: calc(var(--spacing) * 6); + } + .leading-7 { + --tw-leading: calc(var(--spacing) * 7); + line-height: calc(var(--spacing) * 7); + } + .leading-8 { + --tw-leading: calc(var(--spacing) * 8); + line-height: calc(var(--spacing) * 8); + } + .leading-\[0\.92\] { + --tw-leading: 0.92; + line-height: 0.92; + } + .leading-\[1\.02\] { + --tw-leading: 1.02; + line-height: 1.02; + } + .leading-\[1\.06\] { + --tw-leading: 1.06; + line-height: 1.06; + } + .leading-\[26px\] { + --tw-leading: 26px; + line-height: 26px; + } + .leading-none { + --tw-leading: 1; + line-height: 1; + } + .leading-normal { + --tw-leading: var(--leading-normal); + line-height: var(--leading-normal); + } + .leading-relaxed { + --tw-leading: var(--leading-relaxed); + line-height: var(--leading-relaxed); + } + .leading-snug { + --tw-leading: var(--leading-snug); + line-height: var(--leading-snug); + } + .leading-tight { + --tw-leading: var(--leading-tight); + line-height: var(--leading-tight); + } + .font-bold { + --tw-font-weight: var(--font-weight-bold); + font-weight: var(--font-weight-bold); + } + .font-medium { + --tw-font-weight: var(--font-weight-medium); + font-weight: var(--font-weight-medium); + } + .font-normal { + --tw-font-weight: var(--font-weight-normal); + font-weight: var(--font-weight-normal); + } + .font-semibold { + --tw-font-weight: var(--font-weight-semibold); + font-weight: var(--font-weight-semibold); + } + .tracking-\[-0\.03em\] { + --tw-tracking: -0.03em; + letter-spacing: -0.03em; + } + .tracking-\[-0\.04em\] { + --tw-tracking: -0.04em; + letter-spacing: -0.04em; + } + .tracking-\[-0\.05em\] { + --tw-tracking: -0.05em; + letter-spacing: -0.05em; + } + .tracking-\[-0\.44px\] { + --tw-tracking: -0.44px; + letter-spacing: -0.44px; + } + .tracking-\[-0\.154px\] { + --tw-tracking: -0.154px; + letter-spacing: -0.154px; + } + .tracking-\[0\.1em\] { + --tw-tracking: 0.1em; + letter-spacing: 0.1em; + } + .tracking-\[0\.2em\] { + --tw-tracking: 0.2em; + letter-spacing: 0.2em; + } + .tracking-\[0\.16em\] { + --tw-tracking: 0.16em; + letter-spacing: 0.16em; + } + .tracking-\[0\.18em\] { + --tw-tracking: 0.18em; + letter-spacing: 0.18em; + } + .tracking-\[0\.22em\] { + --tw-tracking: 0.22em; + letter-spacing: 0.22em; + } + .tracking-\[0\.24em\] { + --tw-tracking: 0.24em; + letter-spacing: 0.24em; + } + .tracking-\[0\.26em\] { + --tw-tracking: 0.26em; + letter-spacing: 0.26em; + } + .tracking-\[0\.28em\] { + --tw-tracking: 0.28em; + letter-spacing: 0.28em; + } + .tracking-tight { + --tw-tracking: var(--tracking-tight); + letter-spacing: var(--tracking-tight); + } + .tracking-wide { + --tw-tracking: var(--tracking-wide); + letter-spacing: var(--tracking-wide); + } + .tracking-widest { + --tw-tracking: var(--tracking-widest); + letter-spacing: var(--tracking-widest); + } + .text-balance { + text-wrap: balance; + } + .break-words { + overflow-wrap: break-word; + } + .break-all { + word-break: break-all; + } + .whitespace-break-spaces { + white-space: break-spaces; + } + .whitespace-nowrap { + white-space: nowrap; + } + .whitespace-pre-wrap { + white-space: pre-wrap; + } + .text-\[\#0A66C2\] { + color: #0a66c2; + } + .text-\[\#1877F2\] { + color: #1877f2; + } + .text-\[\#34322d\] { + color: #34322d; + } + .text-\[\#858481\] { + color: #858481; + } + .text-\[var\(--accent-strong\)\] { + color: var(--accent-strong); + } + .text-\[var\(--accent-warm\)\] { + color: var(--accent-warm); + } + .text-\[var\(--ink-strong\)\] { + color: var(--ink-strong); + } + .text-accent-foreground { + color: var(--accent-foreground); + } + .text-amber-100 { + color: var(--color-amber-100); + } + .text-background { + color: var(--background); + } + .text-blue-100 { + color: var(--color-blue-100); + } + .text-blue-300 { + color: var(--color-blue-300); + } + .text-blue-700 { + color: var(--color-blue-700); + } + .text-blue-800 { + color: var(--color-blue-800); + } + .text-card-foreground { + color: var(--card-foreground); + } + .text-current { + color: currentColor; + } + .text-cyan-100 { + color: var(--color-cyan-100); + } + .text-cyan-300 { + color: var(--color-cyan-300); + } + .text-destructive { + color: var(--destructive); + } + .text-destructive-foreground { + color: var(--destructive-foreground); + } + .text-emerald-100 { + color: var(--color-emerald-100); + } + .text-foreground, + .text-foreground\/40 { + color: var(--foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .text-foreground\/40 { + color: color-mix(in oklab, var(--foreground) 40%, transparent); + } + } + .text-foreground\/50 { + color: var(--foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .text-foreground\/50 { + color: color-mix(in oklab, var(--foreground) 50%, transparent); + } + } + .text-foreground\/60 { + color: var(--foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .text-foreground\/60 { + color: color-mix(in oklab, var(--foreground) 60%, transparent); + } + } + .text-foreground\/70 { + color: var(--foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .text-foreground\/70 { + color: color-mix(in oklab, var(--foreground) 70%, transparent); + } + } + .text-foreground\/80 { + color: var(--foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .text-foreground\/80 { + color: color-mix(in oklab, var(--foreground) 80%, transparent); + } + } + .text-gray-300 { + color: var(--color-gray-300); + } + .text-gray-500 { + color: var(--color-gray-500); + } + .text-gray-600 { + color: var(--color-gray-600); + } + .text-gray-700 { + color: var(--color-gray-700); + } + .text-gray-800 { + color: var(--color-gray-800); + } + .text-green-300 { + color: var(--color-green-300); + } + .text-green-400 { + color: var(--color-green-400); + } + .text-green-600 { + color: var(--color-green-600); + } + .text-green-700 { + color: var(--color-green-700); + } + .text-green-800 { + color: var(--color-green-800); + } + .text-muted-foreground { + color: var(--muted-foreground); + } + .text-orange-800 { + color: var(--color-orange-800); + } + .text-popover-foreground { + color: var(--popover-foreground); + } + .text-primary { + color: var(--primary); + } + .text-primary-foreground { + color: var(--primary-foreground); + } + .text-purple-300 { + color: var(--color-purple-300); + } + .text-purple-700 { + color: var(--color-purple-700); + } + .text-purple-800 { + color: var(--color-purple-800); + } + .text-red-400 { + color: var(--color-red-400); + } + .text-red-500 { + color: var(--color-red-500); + } + .text-red-600 { + color: var(--color-red-600); + } + .text-red-700 { + color: var(--color-red-700); + } + .text-red-800 { + color: var(--color-red-800); + } + .text-red-900 { + color: var(--color-red-900); + } + .text-rose-100 { + color: var(--color-rose-100); + } + .text-rose-100\/85 { + color: #ffe4e6d9; + } + @supports (color: color-mix(in lab, red, red)) { + .text-rose-100\/85 { + color: color-mix(in oklab, var(--color-rose-100) 85%, transparent); + } + } + .text-secondary-foreground { + color: var(--secondary-foreground); + } + .text-sidebar-foreground, + .text-sidebar-foreground\/70 { + color: var(--sidebar-foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .text-sidebar-foreground\/70 { + color: color-mix( + in oklab, + var(--sidebar-foreground) 70%, + transparent + ); + } + } + .text-slate-500 { + color: var(--color-slate-500); + } + .text-slate-600 { + color: var(--color-slate-600); + } + .text-slate-700 { + color: var(--color-slate-700); + } + .text-slate-900 { + color: var(--color-slate-900); + } + .text-slate-950 { + color: var(--color-slate-950); + } + .text-white { + color: var(--color-white); + } + .text-white\/30 { + color: #ffffff4d; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/30 { + color: color-mix(in oklab, var(--color-white) 30%, transparent); + } + } + .text-white\/35 { + color: #ffffff59; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/35 { + color: color-mix(in oklab, var(--color-white) 35%, transparent); + } + } + .text-white\/40 { + color: #fff6; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/40 { + color: color-mix(in oklab, var(--color-white) 40%, transparent); + } + } + .text-white\/45 { + color: #ffffff73; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/45 { + color: color-mix(in oklab, var(--color-white) 45%, transparent); + } + } + .text-white\/46 { + color: #ffffff75; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/46 { + color: color-mix(in oklab, var(--color-white) 46%, transparent); + } + } + .text-white\/48 { + color: #ffffff7a; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/48 { + color: color-mix(in oklab, var(--color-white) 48%, transparent); + } + } + .text-white\/50 { + color: #ffffff80; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/50 { + color: color-mix(in oklab, var(--color-white) 50%, transparent); + } + } + .text-white\/52 { + color: #ffffff85; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/52 { + color: color-mix(in oklab, var(--color-white) 52%, transparent); + } + } + .text-white\/55 { + color: #ffffff8c; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/55 { + color: color-mix(in oklab, var(--color-white) 55%, transparent); + } + } + .text-white\/58 { + color: #ffffff94; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/58 { + color: color-mix(in oklab, var(--color-white) 58%, transparent); + } + } + .text-white\/60 { + color: #fff9; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/60 { + color: color-mix(in oklab, var(--color-white) 60%, transparent); + } + } + .text-white\/62 { + color: #ffffff9e; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/62 { + color: color-mix(in oklab, var(--color-white) 62%, transparent); + } + } + .text-white\/64 { + color: #ffffffa3; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/64 { + color: color-mix(in oklab, var(--color-white) 64%, transparent); + } + } + .text-white\/66 { + color: #ffffffa8; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/66 { + color: color-mix(in oklab, var(--color-white) 66%, transparent); + } + } + .text-white\/68 { + color: #ffffffad; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/68 { + color: color-mix(in oklab, var(--color-white) 68%, transparent); + } + } + .text-white\/70 { + color: #ffffffb3; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/70 { + color: color-mix(in oklab, var(--color-white) 70%, transparent); + } + } + .text-white\/72 { + color: #ffffffb8; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/72 { + color: color-mix(in oklab, var(--color-white) 72%, transparent); + } + } + .text-white\/74 { + color: #ffffffbd; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/74 { + color: color-mix(in oklab, var(--color-white) 74%, transparent); + } + } + .text-white\/76 { + color: #ffffffc2; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/76 { + color: color-mix(in oklab, var(--color-white) 76%, transparent); + } + } + .text-white\/78 { + color: #ffffffc7; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/78 { + color: color-mix(in oklab, var(--color-white) 78%, transparent); + } + } + .text-white\/80 { + color: #fffc; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/80 { + color: color-mix(in oklab, var(--color-white) 80%, transparent); + } + } + .text-white\/82 { + color: #ffffffd1; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/82 { + color: color-mix(in oklab, var(--color-white) 82%, transparent); + } + } + .text-white\/84 { + color: #ffffffd6; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/84 { + color: color-mix(in oklab, var(--color-white) 84%, transparent); + } + } + .text-white\/90 { + color: #ffffffe6; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/90 { + color: color-mix(in oklab, var(--color-white) 90%, transparent); + } + } + .text-yellow-300 { + color: var(--color-yellow-300); + } + .text-yellow-700 { + color: var(--color-yellow-700); + } + .capitalize { + text-transform: capitalize; + } + .uppercase { + text-transform: uppercase; + } + .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,); + } + .underline { + text-decoration-line: underline; + } + .decoration-white\/30 { + text-decoration-color: #ffffff4d; + } + @supports (color: color-mix(in lab, red, red)) { + .decoration-white\/30 { + -webkit-text-decoration-color: color-mix( + in oklab, + var(--color-white) 30%, + transparent + ); + text-decoration-color: color-mix( + in oklab, + var(--color-white) 30%, + transparent + ); + } + } + .underline-offset-4 { + text-underline-offset: 4px; + } + .placeholder-white\/40::placeholder { + color: #fff6; + } + @supports (color: color-mix(in lab, red, red)) { + .placeholder-white\/40::placeholder { + color: color-mix(in oklab, var(--color-white) 40%, transparent); + } + } + .opacity-0 { + opacity: 0; + } + .opacity-20 { + opacity: 0.2; + } + .opacity-50 { + opacity: 0.5; + } + .opacity-70 { + opacity: 0.7; + } + .opacity-90 { + opacity: 0.9; + } + .opacity-100 { + opacity: 1; + } + .opacity-\[0\.04\] { + opacity: 0.04; + } + .opacity-\[0\.20\] { + opacity: 0.2; + } + .opacity-\[0\.52\] { + opacity: 0.52; + } + .shadow { + --tw-shadow: + 0 1px 3px 0 var(--tw-shadow-color, #0000001a), + 0 1px 2px -1px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_0_0_1px_hsl\(var\(--sidebar-border\)\)\] { + --tw-shadow: 0 0 0 1px + var(--tw-shadow-color, hsl(var(--sidebar-border))); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_12px_28px_rgba\(0\,0\,0\,0\.18\)\] { + --tw-shadow: 0 12px 28px var(--tw-shadow-color, #0000002e); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_14px_34px_rgba\(0\,0\,0\,0\.18\)\] { + --tw-shadow: 0 14px 34px var(--tw-shadow-color, #0000002e); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_14px_34px_rgba\(0\,0\,0\,0\.22\)\] { + --tw-shadow: 0 14px 34px var(--tw-shadow-color, #00000038); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_16px_34px_rgba\(16\,122\,132\,0\.2\)\] { + --tw-shadow: 0 16px 34px var(--tw-shadow-color, #107a8433); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_16px_40px_rgba\(0\,0\,0\,0\.34\)\] { + --tw-shadow: 0 16px 40px var(--tw-shadow-color, #00000057); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_16px_40px_rgba\(0\,0\,0\,0\.35\)\] { + --tw-shadow: 0 16px 40px var(--tw-shadow-color, #00000059); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_18px_50px_rgba\(0\,0\,0\,0\.18\)\] { + --tw-shadow: 0 18px 50px var(--tw-shadow-color, #0000002e); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_18px_50px_rgba\(15\,23\,42\,0\.08\)\] { + --tw-shadow: 0 18px 50px var(--tw-shadow-color, #0f172a14); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_20px_70px_rgba\(0\,0\,0\,0\.25\)\] { + --tw-shadow: 0 20px 70px var(--tw-shadow-color, #00000040); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_22px_60px_rgba\(0\,0\,0\,0\.24\)\] { + --tw-shadow: 0 22px 60px var(--tw-shadow-color, #0000003d); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_22px_70px_rgba\(0\,0\,0\,0\.2\)\] { + --tw-shadow: 0 22px 70px var(--tw-shadow-color, #0003); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_24px_80px_rgba\(0\,0\,0\,0\.24\)\] { + --tw-shadow: 0 24px 80px var(--tw-shadow-color, #0000003d); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_26px_70px_rgba\(0\,0\,0\,0\.28\)\] { + --tw-shadow: 0 26px 70px var(--tw-shadow-color, #00000047); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_28px_80px_rgba\(0\,0\,0\,0\.45\)\] { + --tw-shadow: 0 28px 80px var(--tw-shadow-color, #00000073); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_30px_90px_rgba\(0\,0\,0\,0\.35\)\] { + --tw-shadow: 0 30px 90px var(--tw-shadow-color, #00000059); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0px_4px_11px_0px_rgba\(0\,0\,0\,0\.08\)\] { + --tw-shadow: 0px 4px 11px 0px var(--tw-shadow-color, #00000014); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-lg { + --tw-shadow: + 0 10px 15px -3px var(--tw-shadow-color, #0000001a), + 0 4px 6px -4px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-md { + --tw-shadow: + 0 4px 6px -1px var(--tw-shadow-color, #0000001a), + 0 2px 4px -2px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-none { + --tw-shadow: 0 0 #0000; + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-sm { + --tw-shadow: + 0 1px 3px 0 var(--tw-shadow-color, #0000001a), + 0 1px 2px -1px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-xl { + --tw-shadow: + 0 20px 25px -5px var(--tw-shadow-color, #0000001a), + 0 8px 10px -6px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-xs { + --tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, #0000000d); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .ring-0 { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(0px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .ring-ring\/50 { + --tw-ring-color: var(--ring); + } + @supports (color: color-mix(in lab, red, red)) { + .ring-ring\/50 { + --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + .ring-sidebar-ring { + --tw-ring-color: var(--sidebar-ring); + } + .ring-offset-background { + --tw-ring-offset-color: var(--background); + } + .outline-hidden { + --tw-outline-style: none; + outline-style: none; + } + @media (forced-colors: active) { + .outline-hidden { + outline-offset: 2px; + outline: 2px solid #0000; + } + } + .outline { + outline-style: var(--tw-outline-style); + outline-width: 1px; + } + .blur-3xl { + --tw-blur: blur(var(--blur-3xl)); + 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,); + } + .backdrop-blur { + --tw-backdrop-blur: blur(8px); + -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-2xl { + --tw-backdrop-blur: blur(var(--blur-2xl)); + -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(var(--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-sm { + --tw-backdrop-blur: blur(var(--blur-sm)); + -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(var(--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,); + } + .transition { + transition-property: + color, + background-color, + border-color, + outline-color, + text-decoration-color, + fill, + stroke, + --tw-gradient-from, + --tw-gradient-via, + --tw-gradient-to, + opacity, + box-shadow, + transform, + translate, + scale, + rotate, + filter, + -webkit-backdrop-filter, + backdrop-filter, + display, + content-visibility, + overlay, + pointer-events; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-\[color\,box-shadow\] { + transition-property: color, box-shadow; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-\[left\,right\,width\] { + transition-property: left, right, width; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-\[margin\,opacity\] { + transition-property: margin, opacity; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-\[width\,height\,padding\] { + transition-property: width, height, padding; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-\[width\] { + transition-property: width; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-all { + transition-property: all; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-colors { + transition-property: + color, background-color, border-color, outline-color, + text-decoration-color, fill, stroke, --tw-gradient-from, + --tw-gradient-via, --tw-gradient-to; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-opacity { + transition-property: opacity; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-shadow { + transition-property: box-shadow; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-transform { + transition-property: transform, translate, scale, rotate; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-none { + transition-property: none; + } + .duration-100 { + --tw-duration: 0.1s; + transition-duration: 0.1s; + } + .duration-200 { + --tw-duration: 0.2s; + transition-duration: 0.2s; + } + .duration-300 { + --tw-duration: 0.3s; + transition-duration: 0.3s; + } + .duration-500 { + --tw-duration: 0.5s; + transition-duration: 0.5s; + } + .duration-1000 { + --tw-duration: 1s; + transition-duration: 1s; + } + .ease-in-out { + --tw-ease: var(--ease-in-out); + transition-timing-function: var(--ease-in-out); + } + .ease-linear { + --tw-ease: linear; + transition-timing-function: linear; + } + .ease-out { + --tw-ease: var(--ease-out); + transition-timing-function: var(--ease-out); + } + .fade-in-0 { + --tw-enter-opacity: 0; + } + .outline-none { + --tw-outline-style: none; + outline-style: none; + } + .select-none { + -webkit-user-select: none; + user-select: none; + } + .zoom-in-95 { + --tw-enter-scale: 0.95; + } + .\[--cell-size\:--spacing\(8\)\] { + --cell-size: calc(var(--spacing) * 8); + } + .group-focus-within\/menu-item\:opacity-100:is( + :where(.group\/menu-item):focus-within * + ) { + opacity: 1; + } + @media (hover: hover) { + .group-hover\:block:is(:where(.group):hover *) { + display: block; + } + .group-hover\:translate-x-1:is(:where(.group):hover *) { + --tw-translate-x: calc(var(--spacing) * 1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .group-hover\:translate-y-\[-1px\]:is(:where(.group):hover *) { + --tw-translate-y: -1px; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .group-hover\:rotate-180:is(:where(.group):hover *) { + rotate: 180deg; + } + .group-hover\:bg-white\/20:is(:where(.group):hover *) { + background-color: #fff3; + } + @supports (color: color-mix(in lab, red, red)) { + .group-hover\:bg-white\/20:is(:where(.group):hover *) { + background-color: color-mix( + in oklab, + var(--color-white) 20%, + transparent + ); + } + } + .group-hover\:text-\[var\(--accent-strong\)\]:is( + :where(.group):hover * + ) { + color: var(--accent-strong); + } + .group-hover\:opacity-100:is(:where(.group):hover *), + .group-hover\/menu-item\:opacity-100:is( + :where(.group\/menu-item):hover * + ) { + opacity: 1; + } + } + .group-has-data-\[sidebar\=menu-action\]\/menu-item\:pr-8:is( + :where(.group\/menu-item):has([data-sidebar="menu-action"]) * + ) { + padding-right: calc(var(--spacing) * 8); + } + .group-has-\[\[data-orientation\=horizontal\]\]\/field\:text-balance:is( + :where(.group\/field):has([data-orientation="horizontal"]) * + ) { + text-wrap: balance; + } + .group-has-\[\[data-slot\=item-description\]\]\/item\:translate-y-0\.5:is( + :where(.group\/item):has([data-slot="item-description"]) * + ) { + --tw-translate-y: calc(var(--spacing) * 0.5); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .group-has-\[\[data-slot\=item-description\]\]\/item\:self-start:is( + :where(.group\/item):has([data-slot="item-description"]) * + ) { + align-self: flex-start; + } + .group-has-\[\>input\]\/input-group\:pt-2\.5:is( + :where(.group\/input-group):has(> input) * + ) { + padding-top: calc(var(--spacing) * 2.5); + } + .group-has-\[\>input\]\/input-group\:pb-2\.5:is( + :where(.group\/input-group):has(> input) * + ) { + padding-bottom: calc(var(--spacing) * 2.5); + } + .group-data-\[collapsible\=icon\]\:-mt-8:is( + :where(.group)[data-collapsible="icon"] * + ) { + margin-top: calc(var(--spacing) * -8); + } + .group-data-\[collapsible\=icon\]\:hidden:is( + :where(.group)[data-collapsible="icon"] * + ) { + display: none; + } + .group-data-\[collapsible\=icon\]\:size-8\!:is( + :where(.group)[data-collapsible="icon"] * + ) { + width: calc(var(--spacing) * 8) !important; + height: calc(var(--spacing) * 8) !important; + } + .group-data-\[collapsible\=icon\]\:w-\(--sidebar-width-icon\):is( + :where(.group)[data-collapsible="icon"] * + ) { + width: var(--sidebar-width-icon); + } + .group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\)\]:is( + :where(.group)[data-collapsible="icon"] * + ) { + width: calc(var(--sidebar-width-icon) + (calc(var(--spacing) * 4))); + } + .group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\+2px\)\]:is( + :where(.group)[data-collapsible="icon"] * + ) { + width: calc( + var(--sidebar-width-icon) + (calc(var(--spacing) * 4)) + 2px + ); + } + .group-data-\[collapsible\=icon\]\:justify-center:is( + :where(.group)[data-collapsible="icon"] * + ) { + justify-content: center; + } + .group-data-\[collapsible\=icon\]\:overflow-hidden:is( + :where(.group)[data-collapsible="icon"] * + ) { + overflow: hidden; + } + .group-data-\[collapsible\=icon\]\:p-0\!:is( + :where(.group)[data-collapsible="icon"] * + ) { + padding: calc(var(--spacing) * 0) !important; + } + .group-data-\[collapsible\=icon\]\:p-2\!:is( + :where(.group)[data-collapsible="icon"] * + ) { + padding: calc(var(--spacing) * 2) !important; + } + .group-data-\[collapsible\=icon\]\:opacity-0:is( + :where(.group)[data-collapsible="icon"] * + ) { + opacity: 0; + } + .group-data-\[collapsible\=offcanvas\]\:right-\[calc\(var\(--sidebar-width\)\*-1\)\]:is( + :where(.group)[data-collapsible="offcanvas"] * + ) { + right: calc(var(--sidebar-width) * -1); + } + .group-data-\[collapsible\=offcanvas\]\:left-\[calc\(var\(--sidebar-width\)\*-1\)\]:is( + :where(.group)[data-collapsible="offcanvas"] * + ) { + left: calc(var(--sidebar-width) * -1); + } + .group-data-\[collapsible\=offcanvas\]\:w-0:is( + :where(.group)[data-collapsible="offcanvas"] * + ) { + width: calc(var(--spacing) * 0); + } + .group-data-\[collapsible\=offcanvas\]\:translate-x-0:is( + :where(.group)[data-collapsible="offcanvas"] * + ) { + --tw-translate-x: calc(var(--spacing) * 0); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .group-data-\[disabled\=true\]\:pointer-events-none:is( + :where(.group)[data-disabled="true"] * + ) { + pointer-events: none; + } + .group-data-\[disabled\=true\]\:opacity-50:is( + :where(.group)[data-disabled="true"] * + ), + .group-data-\[disabled\=true\]\/field\:opacity-50:is( + :where(.group\/field)[data-disabled="true"] * + ), + .group-data-\[disabled\=true\]\/input-group\:opacity-50:is( + :where(.group\/input-group)[data-disabled="true"] * + ) { + opacity: 0.5; + } + .group-data-\[focused\=true\]\/day\:relative:is( + :where(.group\/day)[data-focused="true"] * + ) { + position: relative; + } + .group-data-\[focused\=true\]\/day\:z-10:is( + :where(.group\/day)[data-focused="true"] * + ) { + z-index: 10; + } + .group-data-\[focused\=true\]\/day\:border-ring:is( + :where(.group\/day)[data-focused="true"] * + ) { + border-color: var(--ring); + } + .group-data-\[focused\=true\]\/day\:ring-\[3px\]:is( + :where(.group\/day)[data-focused="true"] * + ) { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .group-data-\[focused\=true\]\/day\:ring-ring\/50:is( + :where(.group\/day)[data-focused="true"] * + ) { + --tw-ring-color: var(--ring); + } + @supports (color: color-mix(in lab, red, red)) { + .group-data-\[focused\=true\]\/day\:ring-ring\/50:is( + :where(.group\/day)[data-focused="true"] * + ) { + --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + .group-data-\[side\=left\]\:-right-4:is( + :where(.group)[data-side="left"] * + ) { + right: calc(var(--spacing) * -4); + } + .group-data-\[side\=left\]\:border-r:is( + :where(.group)[data-side="left"] * + ) { + border-right-style: var(--tw-border-style); + border-right-width: 1px; + } + .group-data-\[side\=right\]\:left-0:is( + :where(.group)[data-side="right"] * + ) { + left: calc(var(--spacing) * 0); + } + .group-data-\[side\=right\]\:rotate-180:is( + :where(.group)[data-side="right"] * + ) { + rotate: 180deg; + } + .group-data-\[side\=right\]\:border-l:is( + :where(.group)[data-side="right"] * + ) { + border-left-style: var(--tw-border-style); + border-left-width: 1px; + } + .group-data-\[state\=open\]\:rotate-180:is( + :where(.group)[data-state="open"] * + ) { + rotate: 180deg; + } + .group-data-\[variant\=floating\]\:rounded-lg:is( + :where(.group)[data-variant="floating"] * + ) { + border-radius: var(--radius); + } + .group-data-\[variant\=floating\]\:border:is( + :where(.group)[data-variant="floating"] * + ) { + border-style: var(--tw-border-style); + border-width: 1px; + } + .group-data-\[variant\=floating\]\:border-sidebar-border:is( + :where(.group)[data-variant="floating"] * + ) { + border-color: var(--sidebar-border); + } + .group-data-\[variant\=floating\]\:shadow-sm:is( + :where(.group)[data-variant="floating"] * + ) { + --tw-shadow: + 0 1px 3px 0 var(--tw-shadow-color, #0000001a), + 0 1px 2px -1px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .group-data-\[variant\=outline\]\/field-group\:-mb-2:is( + :where(.group\/field-group)[data-variant="outline"] * + ) { + margin-bottom: calc(var(--spacing) * -2); + } + .group-data-\[vaul-drawer-direction\=bottom\]\/drawer-content\:block:is( + :where(.group\/drawer-content)[data-vaul-drawer-direction="bottom"] * + ) { + display: block; + } + .group-data-\[vaul-drawer-direction\=bottom\]\/drawer-content\:text-center:is( + :where(.group\/drawer-content)[data-vaul-drawer-direction="bottom"] * + ), + .group-data-\[vaul-drawer-direction\=top\]\/drawer-content\:text-center:is( + :where(.group\/drawer-content)[data-vaul-drawer-direction="top"] * + ) { + text-align: center; + } + .group-data-\[viewport\=false\]\/navigation-menu\:top-full:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + top: 100%; + } + .group-data-\[viewport\=false\]\/navigation-menu\:mt-1\.5:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + margin-top: calc(var(--spacing) * 1.5); + } + .group-data-\[viewport\=false\]\/navigation-menu\:overflow-hidden:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + overflow: hidden; + } + .group-data-\[viewport\=false\]\/navigation-menu\:rounded-md:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + border-radius: calc(var(--radius) - 3px); + } + .group-data-\[viewport\=false\]\/navigation-menu\:border:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + border-style: var(--tw-border-style); + border-width: 1px; + } + .group-data-\[viewport\=false\]\/navigation-menu\:bg-popover:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + background-color: var(--popover); + } + .group-data-\[viewport\=false\]\/navigation-menu\:text-popover-foreground:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + color: var(--popover-foreground); + } + .group-data-\[viewport\=false\]\/navigation-menu\:shadow:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + --tw-shadow: + 0 1px 3px 0 var(--tw-shadow-color, #0000001a), + 0 1px 2px -1px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .group-data-\[viewport\=false\]\/navigation-menu\:duration-200:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + --tw-duration: 0.2s; + transition-duration: 0.2s; + } + @media (hover: hover) { + .peer-hover\/menu-button\:text-sidebar-accent-foreground:is( + :where(.peer\/menu-button):hover ~ * + ) { + color: var(--sidebar-accent-foreground); + } + } + .peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled ~ *) { + cursor: not-allowed; + } + .peer-disabled\:opacity-50:is(:where(.peer):disabled ~ *) { + opacity: 0.5; + } + .peer-data-\[active\=true\]\/menu-button\:text-sidebar-accent-foreground:is( + :where(.peer\/menu-button)[data-active="true"] ~ * + ) { + color: var(--sidebar-accent-foreground); + } + .peer-data-\[size\=default\]\/menu-button\:top-1\.5:is( + :where(.peer\/menu-button)[data-size="default"] ~ * + ) { + top: calc(var(--spacing) * 1.5); + } + .peer-data-\[size\=lg\]\/menu-button\:top-2\.5:is( + :where(.peer\/menu-button)[data-size="lg"] ~ * + ) { + top: calc(var(--spacing) * 2.5); + } + .peer-data-\[size\=sm\]\/menu-button\:top-1:is( + :where(.peer\/menu-button)[data-size="sm"] ~ * + ) { + top: calc(var(--spacing) * 1); + } + .marker\:text-\[var\(--accent-warm\)\] ::marker { + color: var(--accent-warm); + } + .marker\:text-\[var\(--accent-warm\)\]::marker { + color: var(--accent-warm); + } + .marker\:text-\[var\(--accent-warm\)\] ::-webkit-details-marker { + color: var(--accent-warm); + } + .marker\:text-\[var\(--accent-warm\)\]::-webkit-details-marker { + color: var(--accent-warm); + } + .selection\:bg-primary ::selection { + background-color: var(--primary); + } + .selection\:bg-primary::selection { + background-color: var(--primary); + } + .selection\:text-primary-foreground ::selection { + color: var(--primary-foreground); + } + .selection\:text-primary-foreground::selection { + color: var(--primary-foreground); + } + .file\:mr-4::file-selector-button { + margin-right: calc(var(--spacing) * 4); + } + .file\:inline-flex::file-selector-button { + display: inline-flex; + } + .file\:h-7::file-selector-button { + height: calc(var(--spacing) * 7); + } + .file\:rounded-full::file-selector-button { + border-radius: 3.40282e38px; + } + .file\:border-0::file-selector-button { + border-style: var(--tw-border-style); + border-width: 0; + } + .file\:bg-transparent::file-selector-button { + background-color: #0000; + } + .file\:bg-white::file-selector-button { + background-color: var(--color-white); + } + .file\:px-4::file-selector-button { + padding-inline: calc(var(--spacing) * 4); + } + .file\:py-2::file-selector-button { + padding-block: calc(var(--spacing) * 2); + } + .file\:text-sm::file-selector-button { + font-size: var(--text-sm); + line-height: var(--tw-leading, var(--text-sm--line-height)); + } + .file\:font-medium::file-selector-button { + --tw-font-weight: var(--font-weight-medium); + font-weight: var(--font-weight-medium); + } + .file\:font-semibold::file-selector-button { + --tw-font-weight: var(--font-weight-semibold); + font-weight: var(--font-weight-semibold); + } + .file\:text-\[var\(--ink-strong\)\]::file-selector-button { + color: var(--ink-strong); + } + .file\:text-foreground::file-selector-button { + color: var(--foreground); + } + .placeholder\:text-muted-foreground::placeholder { + color: var(--muted-foreground); + } + .placeholder\:text-white\/36::placeholder { + color: #ffffff5c; + } + @supports (color: color-mix(in lab, red, red)) { + .placeholder\:text-white\/36::placeholder { + color: color-mix(in oklab, var(--color-white) 36%, transparent); + } + } + .placeholder\:text-white\/40::placeholder { + color: #fff6; + } + @supports (color: color-mix(in lab, red, red)) { + .placeholder\:text-white\/40::placeholder { + color: color-mix(in oklab, var(--color-white) 40%, transparent); + } + } + .before\:absolute:before { + content: var(--tw-content); + position: absolute; + } + .before\:bottom-2:before { + content: var(--tw-content); + bottom: calc(var(--spacing) * 2); + } + .before\:left-1\/2:before { + content: var(--tw-content); + left: 50%; + } + .before\:h-1\.5:before { + content: var(--tw-content); + height: calc(var(--spacing) * 1.5); + } + .before\:w-1\.5:before { + content: var(--tw-content); + width: calc(var(--spacing) * 1.5); + } + .before\:-translate-x-1\/2:before { + content: var(--tw-content); + --tw-translate-x: -50%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .before\:rounded-full:before { + content: var(--tw-content); + border-radius: 3.40282e38px; + } + .before\:bg-\[var\(--accent-warm\)\]:before { + content: var(--tw-content); + background-color: var(--accent-warm); + } + .after\:absolute:after { + content: var(--tw-content); + position: absolute; + } + .after\:-inset-2:after { + content: var(--tw-content); + inset: calc(var(--spacing) * -2); + } + .after\:inset-y-0:after { + content: var(--tw-content); + inset-block: calc(var(--spacing) * 0); + } + .after\:left-1\/2:after { + content: var(--tw-content); + left: 50%; + } + .after\:w-1:after { + content: var(--tw-content); + width: calc(var(--spacing) * 1); + } + .after\:w-\[2px\]:after { + content: var(--tw-content); + width: 2px; + } + .after\:-translate-x-1\/2:after { + content: var(--tw-content); + --tw-translate-x: -50%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .group-data-\[collapsible\=offcanvas\]\:after\:left-full:is( + :where(.group)[data-collapsible="offcanvas"] * + ):after { + content: var(--tw-content); + left: 100%; + } + .first\:rounded-t-lg:first-child { + border-top-left-radius: var(--radius); + border-top-right-radius: var(--radius); + } + .first\:rounded-l-md:first-child { + border-top-left-radius: calc(var(--radius) - 3px); + border-bottom-left-radius: calc(var(--radius) - 3px); + } + .first\:border-l:first-child { + border-left-style: var(--tw-border-style); + border-left-width: 1px; + } + .last\:mt-0:last-child { + margin-top: calc(var(--spacing) * 0); + } + .last\:rounded-r-md:last-child { + border-top-right-radius: calc(var(--radius) - 3px); + border-bottom-right-radius: calc(var(--radius) - 3px); + } + .last\:rounded-b-lg:last-child { + border-bottom-right-radius: var(--radius); + border-bottom-left-radius: var(--radius); + } + .last\:border-b-0:last-child { + border-bottom-style: var(--tw-border-style); + border-bottom-width: 0; + } + @media (hover: hover) { + .hover\:-translate-y-0\.5:hover { + --tw-translate-y: calc(var(--spacing) * -0.5); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .hover\:-translate-y-1:hover { + --tw-translate-y: calc(var(--spacing) * -1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .hover\:-translate-y-2:hover { + --tw-translate-y: calc(var(--spacing) * -2); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .hover\:scale-105:hover { + --tw-scale-x: 105%; + --tw-scale-y: 105%; + --tw-scale-z: 105%; + scale: var(--tw-scale-x) var(--tw-scale-y); + } + .hover\:scale-\[1\.03\]:hover { + scale: 1.03; + } + .hover\:border-\[var\(--accent-strong\)\]\/30:hover { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:border-\[var\(--accent-strong\)\]\/30:hover { + border-color: color-mix( + in oklab, + var(--accent-strong) 30%, + transparent + ); + } + } + .hover\:border-\[var\(--accent-strong\)\]\/60:hover { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:border-\[var\(--accent-strong\)\]\/60:hover { + border-color: color-mix( + in oklab, + var(--accent-strong) 60%, + transparent + ); + } + } + .hover\:border-\[var\(--accent-warm\)\]\/60:hover { + border-color: var(--accent-warm); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:border-\[var\(--accent-warm\)\]\/60:hover { + border-color: color-mix( + in oklab, + var(--accent-warm) 60%, + transparent + ); + } + } + .hover\:border-\[var\(--accent-warm\)\]\/75:hover { + border-color: var(--accent-warm); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:border-\[var\(--accent-warm\)\]\/75:hover { + border-color: color-mix( + in oklab, + var(--accent-warm) 75%, + transparent + ); + } + } + .hover\:border-white\/20:hover { + border-color: #fff3; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:border-white\/20:hover { + border-color: color-mix( + in oklab, + var(--color-white) 20%, + transparent + ); + } + } + .hover\:border-white\/40:hover { + border-color: #fff6; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:border-white\/40:hover { + border-color: color-mix( + in oklab, + var(--color-white) 40%, + transparent + ); + } + } + .hover\:bg-\[\#0A66C2\]\/30:hover { + background-color: #0a66c24d; + } + .hover\:bg-\[\#1a1a19\]\/90:hover { + background-color: #1a1a19e6; + } + .hover\:bg-\[\#1877F2\]\/30:hover { + background-color: #1877f24d; + } + .hover\:bg-\[rgba\(83\,213\,205\,0\.2\)\]:hover { + background-color: #53d5cd33; + } + .hover\:bg-\[var\(--accent-strong\)\]:hover, + .hover\:bg-\[var\(--accent-strong\)\]\/30:hover { + background-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-\[var\(--accent-strong\)\]\/30:hover { + background-color: color-mix( + in oklab, + var(--accent-strong) 30%, + transparent + ); + } + } + .hover\:bg-\[var\(--accent-strong\)\]\/90:hover { + background-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-\[var\(--accent-strong\)\]\/90:hover { + background-color: color-mix( + in oklab, + var(--accent-strong) 90%, + transparent + ); + } + } + .hover\:bg-\[var\(--accent-warm\)\]:hover { + background-color: var(--accent-warm); + } + .hover\:bg-accent:hover, + .hover\:bg-accent\/50:hover { + background-color: var(--accent); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-accent\/50:hover { + background-color: color-mix( + in oklab, + var(--accent) 50%, + transparent + ); + } + } + .hover\:bg-blue-700:hover { + background-color: var(--color-blue-700); + } + .hover\:bg-destructive\/90:hover { + background-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-destructive\/90:hover { + background-color: color-mix( + in oklab, + var(--destructive) 90%, + transparent + ); + } + } + .hover\:bg-muted:hover, + .hover\:bg-muted\/50:hover { + background-color: var(--muted); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-muted\/50:hover { + background-color: color-mix( + in oklab, + var(--muted) 50%, + transparent + ); + } + } + .hover\:bg-primary\/20:hover { + background-color: var(--primary); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-primary\/20:hover { + background-color: color-mix( + in oklab, + var(--primary) 20%, + transparent + ); + } + } + .hover\:bg-primary\/90:hover { + background-color: var(--primary); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-primary\/90:hover { + background-color: color-mix( + in oklab, + var(--primary) 90%, + transparent + ); + } + } + .hover\:bg-red-600\/20:hover { + background-color: #e4001433; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-red-600\/20:hover { + background-color: color-mix( + in oklab, + var(--color-red-600) 20%, + transparent + ); + } + } + .hover\:bg-secondary\/80:hover { + background-color: var(--secondary); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-secondary\/80:hover { + background-color: color-mix( + in oklab, + var(--secondary) 80%, + transparent + ); + } + } + .hover\:bg-sidebar-accent:hover { + background-color: var(--sidebar-accent); + } + .hover\:bg-slate-100:hover { + background-color: var(--color-slate-100); + } + .hover\:bg-white:hover { + background-color: var(--color-white); + } + .hover\:bg-white\/5:hover { + background-color: #ffffff0d; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-white\/5:hover { + background-color: color-mix( + in oklab, + var(--color-white) 5%, + transparent + ); + } + } + .hover\:bg-white\/6:hover { + background-color: #ffffff0f; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-white\/6:hover { + background-color: color-mix( + in oklab, + var(--color-white) 6%, + transparent + ); + } + } + .hover\:bg-white\/8:hover { + background-color: #ffffff14; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-white\/8:hover { + background-color: color-mix( + in oklab, + var(--color-white) 8%, + transparent + ); + } + } + .hover\:bg-white\/10:hover { + background-color: #ffffff1a; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-white\/10:hover { + background-color: color-mix( + in oklab, + var(--color-white) 10%, + transparent + ); + } + } + .hover\:bg-white\/20:hover { + background-color: #fff3; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-white\/20:hover { + background-color: color-mix( + in oklab, + var(--color-white) 20%, + transparent + ); + } + } + .hover\:bg-\[linear-gradient\(135deg\,rgba\(83\,213\,205\,0\.42\)\,rgba\(255\,193\,43\,0\.3\)\)\]:hover { + background-image: linear-gradient(135deg, #53d5cd6b, #ffc12b4d); + } + .hover\:text-\[var\(--accent-strong\)\]:hover { + color: var(--accent-strong); + } + .hover\:text-\[var\(--accent-warm\)\]:hover { + color: var(--accent-warm); + } + .hover\:text-\[var\(--ink-strong\)\]:hover { + color: var(--ink-strong); + } + .hover\:text-accent-foreground:hover { + color: var(--accent-foreground); + } + .hover\:text-foreground:hover { + color: var(--foreground); + } + .hover\:text-muted-foreground:hover { + color: var(--muted-foreground); + } + .hover\:text-sidebar-accent-foreground:hover { + color: var(--sidebar-accent-foreground); + } + .hover\:text-slate-900:hover { + color: var(--color-slate-900); + } + .hover\:text-white:hover { + color: var(--color-white); + } + .hover\:text-white\/60:hover { + color: #fff9; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:text-white\/60:hover { + color: color-mix(in oklab, var(--color-white) 60%, transparent); + } + } + .hover\:text-white\/80:hover { + color: #fffc; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:text-white\/80:hover { + color: color-mix(in oklab, var(--color-white) 80%, transparent); + } + } + .hover\:underline:hover { + text-decoration-line: underline; + } + .hover\:opacity-80:hover { + opacity: 0.8; + } + .hover\:opacity-90:hover { + opacity: 0.9; + } + .hover\:opacity-100:hover { + opacity: 1; + } + .hover\:shadow-\[var\(--accent-strong\)\]\/30:hover { + --tw-shadow-alpha: 30%; + --tw-shadow: var(--accent-strong); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:shadow-\[0_0_0_1px_hsl\(var\(--sidebar-accent\)\)\]:hover { + --tw-shadow: 0 0 0 1px + var(--tw-shadow-color, hsl(var(--sidebar-accent))); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:shadow-\[0_20px_40px_rgba\(77\,201\,196\,0\.15\)\]:hover { + --tw-shadow: 0 20px 40px var(--tw-shadow-color, #4dc9c426); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:shadow-\[0_20px_60px_rgba\(77\,201\,196\,0\.15\)\]:hover { + --tw-shadow: 0 20px 60px var(--tw-shadow-color, #4dc9c426); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:shadow-\[0_24px_48px_rgba\(16\,122\,132\,0\.34\)\]:hover { + --tw-shadow: 0 24px 48px var(--tw-shadow-color, #107a8457); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:shadow-lg:hover { + --tw-shadow: + 0 10px 15px -3px var(--tw-shadow-color, #0000001a), + 0 4px 6px -4px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:shadow-md:hover { + --tw-shadow: + 0 4px 6px -1px var(--tw-shadow-color, #0000001a), + 0 2px 4px -2px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:shadow-xl:hover { + --tw-shadow: + 0 20px 25px -5px var(--tw-shadow-color, #0000001a), + 0 8px 10px -6px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:ring-4:hover { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(4px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:brightness-110:hover { + --tw-brightness: brightness(110%); + 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,); + } + .hover\:group-data-\[collapsible\=offcanvas\]\:bg-sidebar:hover:is( + :where(.group)[data-collapsible="offcanvas"] * + ) { + background-color: var(--sidebar); + } + .hover\:after\:bg-sidebar-border:hover:after { + content: var(--tw-content); + background-color: var(--sidebar-border); + } + } + .focus\:z-10:focus { + z-index: 10; + } + .focus\:border-\[var\(--accent-strong\)\]:focus { + border-color: var(--accent-strong); + } + .focus\:border-white\/30:focus { + border-color: #ffffff4d; + } + @supports (color: color-mix(in lab, red, red)) { + .focus\:border-white\/30:focus { + border-color: color-mix( + in oklab, + var(--color-white) 30%, + transparent + ); + } + } + .focus\:bg-accent:focus { + background-color: var(--accent); + } + .focus\:text-accent-foreground:focus { + color: var(--accent-foreground); + } + .focus\:text-destructive:focus { + color: var(--destructive); + } + .focus\:ring-1:focus { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(1px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .focus\:ring-2:focus { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(2px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .focus\:ring-ring:focus { + --tw-ring-color: var(--ring); + } + .focus\:ring-white\/20:focus { + --tw-ring-color: #fff3; + } + @supports (color: color-mix(in lab, red, red)) { + .focus\:ring-white\/20:focus { + --tw-ring-color: color-mix( + in oklab, + var(--color-white) 20%, + transparent + ); + } + } + .focus\:ring-offset-2:focus { + --tw-ring-offset-width: 2px; + --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 + var(--tw-ring-offset-width) var(--tw-ring-offset-color); + } + .focus\:outline-hidden:focus { + --tw-outline-style: none; + outline-style: none; + } + @media (forced-colors: active) { + .focus\:outline-hidden:focus { + outline-offset: 2px; + outline: 2px solid #0000; + } + } + .focus\:outline-none:focus { + --tw-outline-style: none; + outline-style: none; + } + .focus-visible\:z-10:focus-visible { + z-index: 10; + } + .focus-visible\:border-ring:focus-visible { + border-color: var(--ring); + } + .focus-visible\:ring-0:focus-visible { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(0px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .focus-visible\:ring-1:focus-visible { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(1px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .focus-visible\:ring-2:focus-visible { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(2px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .focus-visible\:ring-4:focus-visible { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(4px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .focus-visible\:ring-\[3px\]:focus-visible { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .focus-visible\:ring-destructive\/20:focus-visible { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .focus-visible\:ring-destructive\/20:focus-visible { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 20%, + transparent + ); + } + } + .focus-visible\:ring-ring:focus-visible, + .focus-visible\:ring-ring\/50:focus-visible { + --tw-ring-color: var(--ring); + } + @supports (color: color-mix(in lab, red, red)) { + .focus-visible\:ring-ring\/50:focus-visible { + --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + .focus-visible\:ring-offset-1:focus-visible { + --tw-ring-offset-width: 1px; + --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 + var(--tw-ring-offset-width) var(--tw-ring-offset-color); + } + .focus-visible\:outline-hidden:focus-visible { + --tw-outline-style: none; + outline-style: none; + } + @media (forced-colors: active) { + .focus-visible\:outline-hidden:focus-visible { + outline-offset: 2px; + outline: 2px solid #0000; + } + } + .focus-visible\:outline-1:focus-visible { + outline-style: var(--tw-outline-style); + outline-width: 1px; + } + .focus-visible\:outline-ring:focus-visible { + outline-color: var(--ring); + } + .active\:bg-sidebar-accent:active { + background-color: var(--sidebar-accent); + } + .active\:text-sidebar-accent-foreground:active { + color: var(--sidebar-accent-foreground); + } + .disabled\:pointer-events-none:disabled { + pointer-events: none; + } + .disabled\:cursor-not-allowed:disabled { + cursor: not-allowed; + } + .disabled\:opacity-50:disabled { + opacity: 0.5; + } + .disabled\:opacity-70:disabled { + opacity: 0.7; + } + :where([data-side="left"]) .in-data-\[side\=left\]\:cursor-w-resize { + cursor: w-resize; + } + :where([data-side="right"]) .in-data-\[side\=right\]\:cursor-e-resize { + cursor: e-resize; + } + .has-focus\:border-ring:has(:focus) { + border-color: var(--ring); + } + .has-focus\:ring-\[3px\]:has(:focus) { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .has-focus\:ring-ring\/50:has(:focus) { + --tw-ring-color: var(--ring); + } + @supports (color: color-mix(in lab, red, red)) { + .has-focus\:ring-ring\/50:has(:focus) { + --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + .has-disabled\:opacity-50:has(:disabled) { + opacity: 0.5; + } + .has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has( + [data-slot="card-action"] + ) { + grid-template-columns: 1fr auto; + } + .has-data-\[state\=checked\]\:border-primary:has([data-state="checked"]) { + border-color: var(--primary); + } + .has-data-\[state\=checked\]\:bg-primary\/5:has([data-state="checked"]) { + background-color: var(--primary); + } + @supports (color: color-mix(in lab, red, red)) { + .has-data-\[state\=checked\]\:bg-primary\/5:has( + [data-state="checked"] + ) { + background-color: color-mix( + in oklab, + var(--primary) 5%, + transparent + ); + } + } + .has-data-\[variant\=inset\]\:bg-sidebar:has([data-variant="inset"]) { + background-color: var(--sidebar); + } + .has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:border-ring:has( + [data-slot="input-group-control"]:focus-visible + ) { + border-color: var(--ring); + } + .has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-\[3px\]:has( + [data-slot="input-group-control"]:focus-visible + ) { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-ring\/50:has( + [data-slot="input-group-control"]:focus-visible + ) { + --tw-ring-color: var(--ring); + } + @supports (color: color-mix(in lab, red, red)) { + .has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-ring\/50:has( + [data-slot="input-group-control"]:focus-visible + ) { + --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + .has-\[\[data-slot\]\[aria-invalid\=true\]\]\:border-destructive:has( + [data-slot][aria-invalid="true"] + ) { + border-color: var(--destructive); + } + .has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/20:has( + [data-slot][aria-invalid="true"] + ) { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/20:has( + [data-slot][aria-invalid="true"] + ) { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 20%, + transparent + ); + } + } + .has-\[\>\[data-align\=block-end\]\]\:h-auto:has( + > [data-align="block-end"] + ) { + height: auto; + } + .has-\[\>\[data-align\=block-end\]\]\:flex-col:has( + > [data-align="block-end"] + ) { + flex-direction: column; + } + .has-\[\>\[data-align\=block-start\]\]\:h-auto:has( + > [data-align="block-start"] + ) { + height: auto; + } + .has-\[\>\[data-align\=block-start\]\]\:flex-col:has( + > [data-align="block-start"] + ) { + flex-direction: column; + } + .has-\[\>\[data-slot\=button-group\]\]\:gap-2:has( + > [data-slot="button-group"] + ) { + gap: calc(var(--spacing) * 2); + } + .has-\[\>\[data-slot\=checkbox-group\]\]\:gap-3:has( + > [data-slot="checkbox-group"] + ) { + gap: calc(var(--spacing) * 3); + } + .has-\[\>\[data-slot\=field-content\]\]\:items-start:has( + > [data-slot="field-content"] + ) { + align-items: flex-start; + } + .has-\[\>\[data-slot\=field\]\]\:w-full:has(> [data-slot="field"]) { + width: 100%; + } + .has-\[\>\[data-slot\=field\]\]\:flex-col:has(> [data-slot="field"]) { + flex-direction: column; + } + .has-\[\>\[data-slot\=field\]\]\:rounded-md:has(> [data-slot="field"]) { + border-radius: calc(var(--radius) - 3px); + } + .has-\[\>\[data-slot\=field\]\]\:border:has(> [data-slot="field"]) { + border-style: var(--tw-border-style); + border-width: 1px; + } + .has-\[\>\[data-slot\=radio-group\]\]\:gap-3:has( + > [data-slot="radio-group"] + ) { + gap: calc(var(--spacing) * 3); + } + .has-\[\>button\]\:mr-\[-0\.45rem\]:has(> button) { + margin-right: -0.45rem; + } + .has-\[\>button\]\:ml-\[-0\.45rem\]:has(> button) { + margin-left: -0.45rem; + } + .has-\[\>kbd\]\:mr-\[-0\.35rem\]:has(> kbd) { + margin-right: -0.35rem; + } + .has-\[\>kbd\]\:ml-\[-0\.35rem\]:has(> kbd) { + margin-left: -0.35rem; + } + .has-\[\>svg\]\:grid-cols-\[calc\(var\(--spacing\)\*4\)_1fr\]:has(> svg) { + grid-template-columns: calc(var(--spacing) * 4) 1fr; + } + .has-\[\>svg\]\:gap-x-3:has(> svg) { + column-gap: calc(var(--spacing) * 3); + } + .has-\[\>svg\]\:p-0:has(> svg) { + padding: calc(var(--spacing) * 0); + } + .has-\[\>svg\]\:px-2:has(> svg) { + padding-inline: calc(var(--spacing) * 2); + } + .has-\[\>svg\]\:px-2\.5:has(> svg) { + padding-inline: calc(var(--spacing) * 2.5); + } + .has-\[\>svg\]\:px-3:has(> svg) { + padding-inline: calc(var(--spacing) * 3); + } + .has-\[\>svg\]\:px-4:has(> svg) { + padding-inline: calc(var(--spacing) * 4); + } + .has-\[\>textarea\]\:h-auto:has(> textarea) { + height: auto; + } + .aria-disabled\:pointer-events-none[aria-disabled="true"] { + pointer-events: none; + } + .aria-disabled\:opacity-50[aria-disabled="true"] { + opacity: 0.5; + } + .aria-invalid\:border-destructive[aria-invalid="true"] { + border-color: var(--destructive); + } + .aria-invalid\:ring-destructive\/20[aria-invalid="true"] { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .aria-invalid\:ring-destructive\/20[aria-invalid="true"] { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 20%, + transparent + ); + } + } + .aria-selected\:text-muted-foreground[aria-selected="true"] { + color: var(--muted-foreground); + } + .data-\[active\=true\]\:z-10[data-active="true"] { + z-index: 10; + } + .data-\[active\=true\]\:border-ring[data-active="true"] { + border-color: var(--ring); + } + .data-\[active\=true\]\:bg-accent\/50[data-active="true"] { + background-color: var(--accent); + } + @supports (color: color-mix(in lab, red, red)) { + .data-\[active\=true\]\:bg-accent\/50[data-active="true"] { + background-color: color-mix( + in oklab, + var(--accent) 50%, + transparent + ); + } + } + .data-\[active\=true\]\:bg-sidebar-accent[data-active="true"] { + background-color: var(--sidebar-accent); + } + .data-\[active\=true\]\:font-medium[data-active="true"] { + --tw-font-weight: var(--font-weight-medium); + font-weight: var(--font-weight-medium); + } + .data-\[active\=true\]\:text-accent-foreground[data-active="true"] { + color: var(--accent-foreground); + } + .data-\[active\=true\]\:text-sidebar-accent-foreground[data-active="true"] { + color: var(--sidebar-accent-foreground); + } + .data-\[active\=true\]\:ring-\[3px\][data-active="true"] { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .data-\[active\=true\]\:ring-ring\/50[data-active="true"] { + --tw-ring-color: var(--ring); + } + @supports (color: color-mix(in lab, red, red)) { + .data-\[active\=true\]\:ring-ring\/50[data-active="true"] { + --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + @media (hover: hover) { + .data-\[active\=true\]\:hover\:bg-accent[data-active="true"]:hover { + background-color: var(--accent); + } + } + .data-\[active\=true\]\:focus\:bg-accent[data-active="true"]:focus { + background-color: var(--accent); + } + .data-\[active\=true\]\:aria-invalid\:border-destructive[data-active="true"][aria-invalid="true"] { + border-color: var(--destructive); + } + .data-\[active\=true\]\:aria-invalid\:ring-destructive\/20[data-active="true"][aria-invalid="true"] { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .data-\[active\=true\]\:aria-invalid\:ring-destructive\/20[data-active="true"][aria-invalid="true"] { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 20%, + transparent + ); + } + } + .data-\[disabled\]\:pointer-events-none[data-disabled] { + pointer-events: none; + } + .data-\[disabled\]\:opacity-50[data-disabled] { + opacity: 0.5; + } + .data-\[disabled\=true\]\:pointer-events-none[data-disabled="true"] { + pointer-events: none; + } + .data-\[disabled\=true\]\:opacity-50[data-disabled="true"] { + opacity: 0.5; + } + .data-\[error\=true\]\:text-destructive[data-error="true"] { + color: var(--destructive); + } + .data-\[inset\]\:pl-8[data-inset] { + padding-left: calc(var(--spacing) * 8); + } + .data-\[invalid\=true\]\:text-destructive[data-invalid="true"] { + color: var(--destructive); + } + .data-\[motion\=from-end\]\:slide-in-from-right-52[data-motion="from-end"] { + --tw-enter-translate-x: calc(52 * var(--spacing)); + } + .data-\[motion\=from-start\]\:slide-in-from-left-52[data-motion="from-start"] { + --tw-enter-translate-x: calc(52 * var(--spacing) * -1); + } + .data-\[motion\=to-end\]\:slide-out-to-right-52[data-motion="to-end"] { + --tw-exit-translate-x: calc(52 * var(--spacing)); + } + .data-\[motion\=to-start\]\:slide-out-to-left-52[data-motion="to-start"] { + --tw-exit-translate-x: calc(52 * var(--spacing) * -1); + } + .data-\[motion\^\=from-\]\:animate-in[data-motion^="from-"] { + animation: enter var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[motion\^\=from-\]\:fade-in[data-motion^="from-"] { + --tw-enter-opacity: 0; + } + .data-\[motion\^\=to-\]\:animate-out[data-motion^="to-"] { + animation: exit var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[motion\^\=to-\]\:fade-out[data-motion^="to-"] { + --tw-exit-opacity: 0; + } + .data-\[orientation\=horizontal\]\:h-1\.5[data-orientation="horizontal"] { + height: calc(var(--spacing) * 1.5); + } + .data-\[orientation\=horizontal\]\:h-full[data-orientation="horizontal"] { + height: 100%; + } + .data-\[orientation\=horizontal\]\:h-px[data-orientation="horizontal"] { + height: 1px; + } + .data-\[orientation\=horizontal\]\:w-full[data-orientation="horizontal"] { + width: 100%; + } + .data-\[orientation\=vertical\]\:h-auto[data-orientation="vertical"] { + height: auto; + } + .data-\[orientation\=vertical\]\:h-full[data-orientation="vertical"] { + height: 100%; + } + .data-\[orientation\=vertical\]\:min-h-44[data-orientation="vertical"] { + min-height: calc(var(--spacing) * 44); + } + .data-\[orientation\=vertical\]\:w-1\.5[data-orientation="vertical"] { + width: calc(var(--spacing) * 1.5); + } + .data-\[orientation\=vertical\]\:w-auto[data-orientation="vertical"] { + width: auto; + } + .data-\[orientation\=vertical\]\:w-full[data-orientation="vertical"] { + width: 100%; + } + .data-\[orientation\=vertical\]\:w-px[data-orientation="vertical"] { + width: 1px; + } + .data-\[orientation\=vertical\]\:flex-col[data-orientation="vertical"] { + flex-direction: column; + } + .data-\[panel-group-direction\=vertical\]\:h-px[data-panel-group-direction="vertical"] { + height: 1px; + } + .data-\[panel-group-direction\=vertical\]\:w-full[data-panel-group-direction="vertical"] { + width: 100%; + } + .data-\[panel-group-direction\=vertical\]\:flex-col[data-panel-group-direction="vertical"] { + flex-direction: column; + } + .data-\[panel-group-direction\=vertical\]\:after\:left-0[data-panel-group-direction="vertical"]:after { + content: var(--tw-content); + left: calc(var(--spacing) * 0); + } + .data-\[panel-group-direction\=vertical\]\:after\:h-1[data-panel-group-direction="vertical"]:after { + content: var(--tw-content); + height: calc(var(--spacing) * 1); + } + .data-\[panel-group-direction\=vertical\]\:after\:w-full[data-panel-group-direction="vertical"]:after { + content: var(--tw-content); + width: 100%; + } + .data-\[panel-group-direction\=vertical\]\:after\:translate-x-0[data-panel-group-direction="vertical"]:after { + content: var(--tw-content); + --tw-translate-x: calc(var(--spacing) * 0); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[panel-group-direction\=vertical\]\:after\:-translate-y-1\/2[data-panel-group-direction="vertical"]:after { + content: var(--tw-content); + --tw-translate-y: -50%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[placeholder\]\:text-muted-foreground[data-placeholder] { + color: var(--muted-foreground); + } + .data-\[range-end\=true\]\:rounded-md[data-range-end="true"] { + border-radius: calc(var(--radius) - 3px); + } + .data-\[range-end\=true\]\:rounded-r-md[data-range-end="true"] { + border-top-right-radius: calc(var(--radius) - 3px); + border-bottom-right-radius: calc(var(--radius) - 3px); + } + .data-\[range-end\=true\]\:bg-primary[data-range-end="true"] { + background-color: var(--primary); + } + .data-\[range-end\=true\]\:text-primary-foreground[data-range-end="true"] { + color: var(--primary-foreground); + } + .data-\[range-middle\=true\]\:rounded-none[data-range-middle="true"] { + border-radius: 0; + } + .data-\[range-middle\=true\]\:bg-accent[data-range-middle="true"] { + background-color: var(--accent); + } + .data-\[range-middle\=true\]\:text-accent-foreground[data-range-middle="true"] { + color: var(--accent-foreground); + } + .data-\[range-start\=true\]\:rounded-md[data-range-start="true"] { + border-radius: calc(var(--radius) - 3px); + } + .data-\[range-start\=true\]\:rounded-l-md[data-range-start="true"] { + border-top-left-radius: calc(var(--radius) - 3px); + border-bottom-left-radius: calc(var(--radius) - 3px); + } + .data-\[range-start\=true\]\:bg-primary[data-range-start="true"] { + background-color: var(--primary); + } + .data-\[range-start\=true\]\:text-primary-foreground[data-range-start="true"] { + color: var(--primary-foreground); + } + .data-\[selected-single\=true\]\:bg-primary[data-selected-single="true"] { + background-color: var(--primary); + } + .data-\[selected-single\=true\]\:text-primary-foreground[data-selected-single="true"] { + color: var(--primary-foreground); + } + .data-\[selected\=true\]\:rounded-none[data-selected="true"] { + border-radius: 0; + } + .data-\[selected\=true\]\:bg-accent[data-selected="true"] { + background-color: var(--accent); + } + .data-\[selected\=true\]\:text-accent-foreground[data-selected="true"] { + color: var(--accent-foreground); + } + .data-\[side\=bottom\]\:translate-y-1[data-side="bottom"] { + --tw-translate-y: calc(var(--spacing) * 1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[side\=bottom\]\:slide-in-from-top-2[data-side="bottom"] { + --tw-enter-translate-y: calc(2 * var(--spacing) * -1); + } + .data-\[side\=left\]\:-translate-x-1[data-side="left"] { + --tw-translate-x: calc(var(--spacing) * -1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[side\=left\]\:slide-in-from-right-2[data-side="left"] { + --tw-enter-translate-x: calc(2 * var(--spacing)); + } + .data-\[side\=right\]\:translate-x-1[data-side="right"] { + --tw-translate-x: calc(var(--spacing) * 1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[side\=right\]\:slide-in-from-left-2[data-side="right"] { + --tw-enter-translate-x: calc(2 * var(--spacing) * -1); + } + .data-\[side\=top\]\:-translate-y-1[data-side="top"] { + --tw-translate-y: calc(var(--spacing) * -1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[side\=top\]\:slide-in-from-bottom-2[data-side="top"] { + --tw-enter-translate-y: calc(2 * var(--spacing)); + } + .data-\[size\=default\]\:h-9[data-size="default"] { + height: calc(var(--spacing) * 9); + } + .data-\[size\=sm\]\:h-8[data-size="sm"] { + height: calc(var(--spacing) * 8); + } + :is( + .\*\:data-\[slot\=alert-description\]\:text-destructive\/90 > * + )[data-slot="alert-description"] { + color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + :is( + .\*\:data-\[slot\=alert-description\]\:text-destructive\/90 > * + )[data-slot="alert-description"] { + color: color-mix(in oklab, var(--destructive) 90%, transparent); + } + } + .data-\[slot\=checkbox-group\]\:gap-3[data-slot="checkbox-group"] { + gap: calc(var(--spacing) * 3); + } + :is( + .\*\*\:data-\[slot\=command-input-wrapper\]\:h-12 * + )[data-slot="command-input-wrapper"] { + height: calc(var(--spacing) * 12); + } + :is( + .\*\*\:data-\[slot\=navigation-menu-link\]\:focus\:ring-0 * + )[data-slot="navigation-menu-link"]:focus { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(0px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + :is( + .\*\*\:data-\[slot\=navigation-menu-link\]\:focus\:outline-none * + )[data-slot="navigation-menu-link"]:focus { + --tw-outline-style: none; + outline-style: none; + } + :is( + .\*\:data-\[slot\=select-value\]\:line-clamp-1 > * + )[data-slot="select-value"] { + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + display: -webkit-box; + overflow: hidden; + } + :is(.\*\:data-\[slot\=select-value\]\:flex > *)[data-slot="select-value"] { + display: flex; + } + :is( + .\*\:data-\[slot\=select-value\]\:items-center > * + )[data-slot="select-value"] { + align-items: center; + } + :is(.\*\:data-\[slot\=select-value\]\:gap-2 > *)[data-slot="select-value"] { + gap: calc(var(--spacing) * 2); + } + .data-\[state\=active\]\:bg-background[data-state="active"] { + background-color: var(--background); + } + .data-\[state\=active\]\:shadow-sm[data-state="active"] { + --tw-shadow: + 0 1px 3px 0 var(--tw-shadow-color, #0000001a), + 0 1px 2px -1px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .data-\[state\=checked\]\:translate-x-\[calc\(100\%-2px\)\][data-state="checked"] { + --tw-translate-x: calc(100% - 2px); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[state\=checked\]\:border-primary[data-state="checked"] { + border-color: var(--primary); + } + .data-\[state\=checked\]\:bg-primary[data-state="checked"] { + background-color: var(--primary); + } + .data-\[state\=checked\]\:text-primary-foreground[data-state="checked"] { + color: var(--primary-foreground); + } + .data-\[state\=closed\]\:animate-accordion-up[data-state="closed"] { + animation: accordion-up + var(--tw-animation-duration, var(--tw-duration, 0.2s)) + var(--tw-ease, ease-out) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[state\=closed\]\:animate-out[data-state="closed"] { + animation: exit var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[state\=closed\]\:duration-300[data-state="closed"] { + --tw-duration: 0.3s; + transition-duration: 0.3s; + } + .data-\[state\=closed\]\:fade-out-0[data-state="closed"] { + --tw-exit-opacity: 0; + } + .data-\[state\=closed\]\:zoom-out-95[data-state="closed"] { + --tw-exit-scale: 0.95; + } + .data-\[state\=closed\]\:slide-out-to-bottom[data-state="closed"] { + --tw-exit-translate-y: 100%; + } + .data-\[state\=closed\]\:slide-out-to-left[data-state="closed"] { + --tw-exit-translate-x: -100%; + } + .data-\[state\=closed\]\:slide-out-to-right[data-state="closed"] { + --tw-exit-translate-x: 100%; + } + .data-\[state\=closed\]\:slide-out-to-top[data-state="closed"] { + --tw-exit-translate-y: -100%; + } + .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=closed\]\:animate-out:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + )[data-state="closed"] { + animation: exit var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=closed\]\:fade-out-0:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + )[data-state="closed"] { + --tw-exit-opacity: 0; + } + .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=closed\]\:zoom-out-95:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + )[data-state="closed"] { + --tw-exit-scale: 0.95; + } + .data-\[state\=hidden\]\:animate-out[data-state="hidden"] { + animation: exit var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[state\=hidden\]\:fade-out[data-state="hidden"] { + --tw-exit-opacity: 0; + } + .data-\[state\=on\]\:bg-accent[data-state="on"] { + background-color: var(--accent); + } + .data-\[state\=on\]\:text-accent-foreground[data-state="on"] { + color: var(--accent-foreground); + } + .data-\[state\=open\]\:animate-accordion-down[data-state="open"] { + animation: accordion-down + var(--tw-animation-duration, var(--tw-duration, 0.2s)) + var(--tw-ease, ease-out) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[state\=open\]\:animate-in[data-state="open"] { + animation: enter var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[state\=open\]\:bg-accent[data-state="open"], + .data-\[state\=open\]\:bg-accent\/50[data-state="open"] { + background-color: var(--accent); + } + @supports (color: color-mix(in lab, red, red)) { + .data-\[state\=open\]\:bg-accent\/50[data-state="open"] { + background-color: color-mix( + in oklab, + var(--accent) 50%, + transparent + ); + } + } + .data-\[state\=open\]\:bg-secondary[data-state="open"] { + background-color: var(--secondary); + } + .data-\[state\=open\]\:text-accent-foreground[data-state="open"] { + color: var(--accent-foreground); + } + .data-\[state\=open\]\:text-muted-foreground[data-state="open"] { + color: var(--muted-foreground); + } + .data-\[state\=open\]\:opacity-100[data-state="open"] { + opacity: 1; + } + .data-\[state\=open\]\:duration-500[data-state="open"] { + --tw-duration: 0.5s; + transition-duration: 0.5s; + } + .data-\[state\=open\]\:fade-in-0[data-state="open"] { + --tw-enter-opacity: 0; + } + .data-\[state\=open\]\:zoom-in-90[data-state="open"] { + --tw-enter-scale: 0.9; + } + .data-\[state\=open\]\:zoom-in-95[data-state="open"] { + --tw-enter-scale: 0.95; + } + .data-\[state\=open\]\:slide-in-from-bottom[data-state="open"] { + --tw-enter-translate-y: 100%; + } + .data-\[state\=open\]\:slide-in-from-left[data-state="open"] { + --tw-enter-translate-x: -100%; + } + .data-\[state\=open\]\:slide-in-from-right[data-state="open"] { + --tw-enter-translate-x: 100%; + } + .data-\[state\=open\]\:slide-in-from-top[data-state="open"] { + --tw-enter-translate-y: -100%; + } + .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=open\]\:animate-in:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + )[data-state="open"] { + animation: enter var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=open\]\:fade-in-0:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + )[data-state="open"] { + --tw-enter-opacity: 0; + } + .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=open\]\:zoom-in-95:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + )[data-state="open"] { + --tw-enter-scale: 0.95; + } + @media (hover: hover) { + .data-\[state\=open\]\:hover\:bg-accent[data-state="open"]:hover { + background-color: var(--accent); + } + .data-\[state\=open\]\:hover\:bg-sidebar-accent[data-state="open"]:hover { + background-color: var(--sidebar-accent); + } + .data-\[state\=open\]\:hover\:text-sidebar-accent-foreground[data-state="open"]:hover { + color: var(--sidebar-accent-foreground); + } + } + .data-\[state\=open\]\:focus\:bg-accent[data-state="open"]:focus { + background-color: var(--accent); + } + .data-\[state\=selected\]\:bg-muted[data-state="selected"] { + background-color: var(--muted); + } + .data-\[state\=unchecked\]\:translate-x-0[data-state="unchecked"] { + --tw-translate-x: calc(var(--spacing) * 0); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[state\=unchecked\]\:bg-input[data-state="unchecked"] { + background-color: var(--input); + } + .data-\[state\=visible\]\:animate-in[data-state="visible"] { + animation: enter var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[state\=visible\]\:fade-in[data-state="visible"] { + --tw-enter-opacity: 0; + } + .data-\[variant\=destructive\]\:text-destructive[data-variant="destructive"] { + color: var(--destructive); + } + .data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant="destructive"]:focus { + background-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant="destructive"]:focus { + background-color: color-mix( + in oklab, + var(--destructive) 10%, + transparent + ); + } + } + .data-\[variant\=destructive\]\:focus\:text-destructive[data-variant="destructive"]:focus { + color: var(--destructive); + } + .data-\[variant\=label\]\:text-sm[data-variant="label"] { + font-size: var(--text-sm); + line-height: var(--tw-leading, var(--text-sm--line-height)); + } + .data-\[variant\=legend\]\:text-base[data-variant="legend"] { + font-size: var(--text-base); + line-height: var(--tw-leading, var(--text-base--line-height)); + } + .data-\[variant\=outline\]\:border-l-0[data-variant="outline"] { + border-left-style: var(--tw-border-style); + border-left-width: 0; + } + .data-\[variant\=outline\]\:shadow-xs[data-variant="outline"] { + --tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, #0000000d); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .data-\[variant\=outline\]\:first\:border-l[data-variant="outline"]:first-child { + border-left-style: var(--tw-border-style); + border-left-width: 1px; + } + .data-\[vaul-drawer-direction\=bottom\]\:inset-x-0[data-vaul-drawer-direction="bottom"] { + inset-inline: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=bottom\]\:bottom-0[data-vaul-drawer-direction="bottom"] { + bottom: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=bottom\]\:mt-24[data-vaul-drawer-direction="bottom"] { + margin-top: calc(var(--spacing) * 24); + } + .data-\[vaul-drawer-direction\=bottom\]\:max-h-\[80vh\][data-vaul-drawer-direction="bottom"] { + max-height: 80vh; + } + .data-\[vaul-drawer-direction\=bottom\]\:rounded-t-lg[data-vaul-drawer-direction="bottom"] { + border-top-left-radius: var(--radius); + border-top-right-radius: var(--radius); + } + .data-\[vaul-drawer-direction\=bottom\]\:border-t[data-vaul-drawer-direction="bottom"] { + border-top-style: var(--tw-border-style); + border-top-width: 1px; + } + .data-\[vaul-drawer-direction\=left\]\:inset-y-0[data-vaul-drawer-direction="left"] { + inset-block: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=left\]\:left-0[data-vaul-drawer-direction="left"] { + left: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=left\]\:w-3\/4[data-vaul-drawer-direction="left"] { + width: 75%; + } + .data-\[vaul-drawer-direction\=left\]\:border-r[data-vaul-drawer-direction="left"] { + border-right-style: var(--tw-border-style); + border-right-width: 1px; + } + .data-\[vaul-drawer-direction\=right\]\:inset-y-0[data-vaul-drawer-direction="right"] { + inset-block: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=right\]\:right-0[data-vaul-drawer-direction="right"] { + right: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=right\]\:w-3\/4[data-vaul-drawer-direction="right"] { + width: 75%; + } + .data-\[vaul-drawer-direction\=right\]\:border-l[data-vaul-drawer-direction="right"] { + border-left-style: var(--tw-border-style); + border-left-width: 1px; + } + .data-\[vaul-drawer-direction\=top\]\:inset-x-0[data-vaul-drawer-direction="top"] { + inset-inline: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=top\]\:top-0[data-vaul-drawer-direction="top"] { + top: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=top\]\:mb-24[data-vaul-drawer-direction="top"] { + margin-bottom: calc(var(--spacing) * 24); + } + .data-\[vaul-drawer-direction\=top\]\:max-h-\[80vh\][data-vaul-drawer-direction="top"] { + max-height: 80vh; + } + .data-\[vaul-drawer-direction\=top\]\:rounded-b-lg[data-vaul-drawer-direction="top"] { + border-bottom-right-radius: var(--radius); + border-bottom-left-radius: var(--radius); + } + .data-\[vaul-drawer-direction\=top\]\:border-b[data-vaul-drawer-direction="top"] { + border-bottom-style: var(--tw-border-style); + border-bottom-width: 1px; + } + .nth-last-2\:-mt-1:nth-last-child(2) { + margin-top: calc(var(--spacing) * -1); + } + @supports ( + (-webkit-backdrop-filter: var(--tw)) or (backdrop-filter: var(--tw)) + ) { + .supports-\[backdrop-filter\]\:backdrop-blur { + --tw-backdrop-blur: blur(8px); + -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,); + } + } + @media (min-width: 40rem) { + .sm\:col-span-2 { + grid-column: span 2 / span 2; + } + .sm\:m-4 { + margin: calc(var(--spacing) * 4); + } + .sm\:block { + display: block; + } + .sm\:flex { + display: flex; + } + .sm\:max-w-\[425px\] { + max-width: 425px; + } + .sm\:max-w-lg { + max-width: var(--container-lg); + } + .sm\:max-w-sm { + max-width: var(--container-sm); + } + .sm\:flex-none { + flex: none; + } + .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\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + .sm\:flex-row { + flex-direction: row; + } + .sm\:flex-wrap { + flex-wrap: wrap; + } + .sm\:items-center { + align-items: center; + } + .sm\:items-start { + align-items: flex-start; + } + .sm\:justify-between { + justify-content: space-between; + } + .sm\:justify-end { + justify-content: flex-end; + } + .sm\:gap-2\.5 { + gap: calc(var(--spacing) * 2.5); + } + .sm\:p-5 { + padding: calc(var(--spacing) * 5); + } + .sm\:p-6 { + padding: calc(var(--spacing) * 6); + } + .sm\:p-8 { + padding: calc(var(--spacing) * 8); + } + .sm\:p-12 { + padding: calc(var(--spacing) * 12); + } + .sm\:px-4 { + padding-inline: calc(var(--spacing) * 4); + } + .sm\:px-6 { + padding-inline: calc(var(--spacing) * 6); + } + .sm\:px-8 { + padding-inline: calc(var(--spacing) * 8); + } + .sm\:py-10 { + padding-block: calc(var(--spacing) * 10); + } + .sm\:py-16 { + padding-block: calc(var(--spacing) * 16); + } + .sm\:pr-2\.5 { + padding-right: calc(var(--spacing) * 2.5); + } + .sm\:pl-2\.5 { + padding-left: calc(var(--spacing) * 2.5); + } + .sm\:text-left { + text-align: left; + } + .sm\:text-3xl { + font-size: var(--text-3xl); + line-height: var(--tw-leading, var(--text-3xl--line-height)); + } + .sm\:text-4xl { + font-size: var(--text-4xl); + line-height: var(--tw-leading, var(--text-4xl--line-height)); + } + .sm\:text-5xl { + font-size: var(--text-5xl); + line-height: var(--tw-leading, var(--text-5xl--line-height)); + } + .sm\:text-6xl { + font-size: var(--text-6xl); + line-height: var(--tw-leading, var(--text-6xl--line-height)); + } + .sm\:text-base { + font-size: var(--text-base); + line-height: var(--tw-leading, var(--text-base--line-height)); + } + .sm\:text-lg { + font-size: var(--text-lg); + line-height: var(--tw-leading, var(--text-lg--line-height)); + } + .sm\:text-xl { + font-size: var(--text-xl); + line-height: var(--tw-leading, var(--text-xl--line-height)); + } + .sm\:text-\[0\.98rem\] { + font-size: 0.98rem; + } + .sm\:text-\[1\.9rem\] { + font-size: 1.9rem; + } + .data-\[vaul-drawer-direction\=left\]\:sm\:max-w-sm[data-vaul-drawer-direction="left"], + .data-\[vaul-drawer-direction\=right\]\:sm\:max-w-sm[data-vaul-drawer-direction="right"] { + max-width: var(--container-sm); + } + } + @media (min-width: 48rem) { + .md\:absolute { + position: absolute; + } + .md\:col-span-2 { + grid-column: span 2 / span 2; + } + .md\:block { + display: block; + } + .md\:flex { + display: flex; + } + .md\:w-\[var\(--radix-navigation-menu-viewport-width\)\] { + width: var(--radix-navigation-menu-viewport-width); + } + .md\:w-auto { + width: auto; + } + .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-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + .md\:grid-cols-\[0\.86fr_1\.14fr\] { + grid-template-columns: 0.86fr 1.14fr; + } + .md\:grid-cols-\[1\.1fr_1\.5fr_1\.2fr_1fr_1\.6fr\] { + grid-template-columns: 1.1fr 1.5fr 1.2fr 1fr 1.6fr; + } + .md\:flex-row { + flex-direction: row; + } + .md\:gap-1\.5 { + gap: calc(var(--spacing) * 1.5); + } + :where(.md\:divide-x > :not(:last-child)) { + --tw-divide-x-reverse: 0; + border-inline-style: var(--tw-border-style); + border-inline-start-width: calc(1px * var(--tw-divide-x-reverse)); + border-inline-end-width: calc( + 1px * calc(1 - var(--tw-divide-x-reverse)) + ); + } + :where(.md\:divide-y-0 > :not(:last-child)) { + --tw-divide-y-reverse: 0; + border-bottom-style: var(--tw-border-style); + border-top-style: var(--tw-border-style); + border-top-width: calc(0px * var(--tw-divide-y-reverse)); + border-bottom-width: calc( + 0px * calc(1 - var(--tw-divide-y-reverse)) + ); + } + .md\:p-5 { + padding: calc(var(--spacing) * 5); + } + .md\:p-7 { + padding: calc(var(--spacing) * 7); + } + .md\:p-12 { + padding: calc(var(--spacing) * 12); + } + .md\:text-left { + text-align: left; + } + .md\:text-sm { + font-size: var(--text-sm); + line-height: var(--tw-leading, var(--text-sm--line-height)); + } + .md\:opacity-0 { + opacity: 0; + } + .md\:peer-data-\[variant\=inset\]\:m-2:is( + :where(.peer)[data-variant="inset"] ~ * + ) { + margin: calc(var(--spacing) * 2); + } + .md\:peer-data-\[variant\=inset\]\:ml-0:is( + :where(.peer)[data-variant="inset"] ~ * + ) { + margin-left: calc(var(--spacing) * 0); + } + .md\:peer-data-\[variant\=inset\]\:rounded-xl:is( + :where(.peer)[data-variant="inset"] ~ * + ) { + border-radius: calc(var(--radius) + 8px); + } + .md\:peer-data-\[variant\=inset\]\:shadow-sm:is( + :where(.peer)[data-variant="inset"] ~ * + ) { + --tw-shadow: + 0 1px 3px 0 var(--tw-shadow-color, #0000001a), + 0 1px 2px -1px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .md\:peer-data-\[variant\=inset\]\:peer-data-\[state\=collapsed\]\:ml-2:is( + :where(.peer)[data-variant="inset"] ~ * + ):is(:where(.peer)[data-state="collapsed"] ~ *) { + margin-left: calc(var(--spacing) * 2); + } + .md\:after\:hidden:after { + content: var(--tw-content); + display: none; + } + } + @media (min-width: 64rem) { + .lg\:col-span-1 { + grid-column: span 1 / span 1; + } + .lg\:col-span-2 { + grid-column: span 2 / span 2; + } + .lg\:block { + display: block; + } + .lg\:flex { + display: flex; + } + .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-\[0\.9fr_1\.1fr\] { + grid-template-columns: 0.9fr 1.1fr; + } + .lg\:grid-cols-\[0\.88fr_1\.12fr\] { + grid-template-columns: 0.88fr 1.12fr; + } + .lg\:grid-cols-\[0\.92fr_1\.08fr\] { + grid-template-columns: 0.92fr 1.08fr; + } + .lg\:grid-cols-\[0\.94fr_1\.06fr\] { + grid-template-columns: 0.94fr 1.06fr; + } + .lg\:grid-cols-\[0\.95fr_1\.05fr\] { + grid-template-columns: 0.95fr 1.05fr; + } + .lg\:grid-cols-\[0\.98fr_1\.02fr\] { + grid-template-columns: 0.98fr 1.02fr; + } + .lg\:grid-cols-\[1\.1fr_0\.9fr\] { + grid-template-columns: 1.1fr 0.9fr; + } + .lg\:grid-cols-\[1\.02fr_0\.98fr\] { + grid-template-columns: 1.02fr 0.98fr; + } + .lg\:grid-cols-\[1\.05fr_0\.95fr\] { + grid-template-columns: 1.05fr 0.95fr; + } + .lg\:grid-cols-\[1\.06fr_0\.94fr\] { + grid-template-columns: 1.06fr 0.94fr; + } + .lg\:grid-cols-\[1\.08fr_0\.92fr\] { + grid-template-columns: 1.08fr 0.92fr; + } + .lg\:grid-cols-\[1\.15fr_0\.85fr_0\.85fr\] { + grid-template-columns: 1.15fr 0.85fr 0.85fr; + } + .lg\:grid-cols-\[1fr_0\.95fr\] { + grid-template-columns: 1fr 0.95fr; + } + .lg\:grid-cols-\[1fr_1\.2fr\] { + grid-template-columns: 1fr 1.2fr; + } + .lg\:grid-cols-\[1fr_1fr\] { + grid-template-columns: 1fr 1fr; + } + .lg\:flex-row { + flex-direction: row; + } + .lg\:flex-row-reverse { + flex-direction: row-reverse; + } + .lg\:items-center { + align-items: center; + } + .lg\:items-end { + align-items: flex-end; + } + .lg\:items-start { + align-items: flex-start; + } + .lg\:justify-between { + justify-content: space-between; + } + .lg\:p-10 { + padding: calc(var(--spacing) * 10); + } + .lg\:px-6 { + padding-inline: calc(var(--spacing) * 6); + } + .lg\:px-8 { + padding-inline: calc(var(--spacing) * 8); + } + .lg\:py-14 { + padding-block: calc(var(--spacing) * 14); + } + .lg\:pl-8 { + padding-left: calc(var(--spacing) * 8); + } + .lg\:text-7xl { + font-size: var(--text-7xl); + line-height: var(--tw-leading, var(--text-7xl--line-height)); + } + } + @media (min-width: 80rem) { + .xl\:col-span-1 { + grid-column: span 1 / span 1; + } + .xl\:col-span-2 { + grid-column: span 2 / span 2; + } + .xl\:flex { + display: flex; + } + .xl\:hidden { + display: none; + } + .xl\:h-full { + height: 100%; + } + .xl\:grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + .xl\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .xl\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + .xl\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + .xl\:grid-cols-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + .xl\:grid-cols-\[0\.95fr_1\.05fr\] { + grid-template-columns: 0.95fr 1.05fr; + } + .xl\:flex-row { + flex-direction: row; + } + .xl\:items-center { + align-items: center; + } + .xl\:justify-between { + justify-content: space-between; + } + } + @media (min-width: 96rem) { + .\32xl\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + } + @container field-group (min-width:28rem) { + .\@md\/field-group\:flex-row { + flex-direction: row; + } + .\@md\/field-group\:items-center { + align-items: center; + } + .\@md\/field-group\:has-\[\>\[data-slot\=field-content\]\]\:items-start:has( + > [data-slot="field-content"] + ) { + align-items: flex-start; + } + } + .dark\:border-blue-800:is(.dark *) { + border-color: var(--color-blue-800); + } + .dark\:border-gray-800:is(.dark *) { + border-color: var(--color-gray-800); + } + .dark\:border-green-800:is(.dark *) { + border-color: var(--color-green-800); + } + .dark\:border-input:is(.dark *) { + border-color: var(--input); + } + .dark\:border-red-800:is(.dark *) { + border-color: var(--color-red-800); + } + .dark\:bg-destructive\/60:is(.dark *) { + background-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:bg-destructive\/60:is(.dark *) { + background-color: color-mix( + in oklab, + var(--destructive) 60%, + transparent + ); + } + } + .dark\:bg-input\/30:is(.dark *) { + background-color: var(--input); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:bg-input\/30:is(.dark *) { + background-color: color-mix( + in oklab, + var(--input) 30%, + transparent + ); + } + } + .dark\:bg-transparent:is(.dark *) { + background-color: #0000; + } + .dark\:text-blue-400:is(.dark *) { + color: var(--color-blue-400); + } + .dark\:text-green-400:is(.dark *) { + color: var(--color-green-400); + } + .dark\:text-muted-foreground:is(.dark *) { + color: var(--muted-foreground); + } + .dark\:text-purple-400:is(.dark *) { + color: var(--color-purple-400); + } + @media (hover: hover) { + .dark\:hover\:bg-accent\/50:is(.dark *):hover { + background-color: var(--accent); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:hover\:bg-accent\/50:is(.dark *):hover { + background-color: color-mix( + in oklab, + var(--accent) 50%, + transparent + ); + } + } + .dark\:hover\:bg-input\/50:is(.dark *):hover { + background-color: var(--input); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:hover\:bg-input\/50:is(.dark *):hover { + background-color: color-mix( + in oklab, + var(--input) 50%, + transparent + ); + } + } + .dark\:hover\:text-accent-foreground:is(.dark *):hover { + color: var(--accent-foreground); + } + } + .dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 40%, + transparent + ); + } + } + .dark\:has-data-\[state\=checked\]\:bg-primary\/10:is(.dark *):has( + [data-state="checked"] + ) { + background-color: var(--primary); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:has-data-\[state\=checked\]\:bg-primary\/10:is(.dark *):has( + [data-state="checked"] + ) { + background-color: color-mix( + in oklab, + var(--primary) 10%, + transparent + ); + } + } + .dark\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/40:is( + .dark * + ):has([data-slot][aria-invalid="true"]) { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/40:is( + .dark * + ):has([data-slot][aria-invalid="true"]) { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 40%, + transparent + ); + } + } + .dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid="true"] { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:aria-invalid\:ring-destructive\/40:is( + .dark * + )[aria-invalid="true"] { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 40%, + transparent + ); + } + } + .dark\:data-\[active\=true\]\:aria-invalid\:ring-destructive\/40:is( + .dark * + )[data-active="true"][aria-invalid="true"] { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:data-\[active\=true\]\:aria-invalid\:ring-destructive\/40:is( + .dark * + )[data-active="true"][aria-invalid="true"] { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 40%, + transparent + ); + } + } + .dark\:data-\[state\=active\]\:border-input:is( + .dark * + )[data-state="active"] { + border-color: var(--input); + } + .dark\:data-\[state\=active\]\:bg-input\/30:is( + .dark * + )[data-state="active"] { + background-color: var(--input); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:data-\[state\=active\]\:bg-input\/30:is( + .dark * + )[data-state="active"] { + background-color: color-mix( + in oklab, + var(--input) 30%, + transparent + ); + } + } + .dark\:data-\[state\=active\]\:text-foreground:is( + .dark * + )[data-state="active"] { + color: var(--foreground); + } + .dark\:data-\[state\=checked\]\:bg-primary:is( + .dark * + )[data-state="checked"] { + background-color: var(--primary); + } + .dark\:data-\[state\=checked\]\:bg-primary-foreground:is( + .dark * + )[data-state="checked"] { + background-color: var(--primary-foreground); + } + .dark\:data-\[state\=unchecked\]\:bg-foreground:is( + .dark * + )[data-state="unchecked"] { + background-color: var(--foreground); + } + .dark\:data-\[state\=unchecked\]\:bg-input\/80:is( + .dark * + )[data-state="unchecked"] { + background-color: var(--input); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:data-\[state\=unchecked\]\:bg-input\/80:is( + .dark * + )[data-state="unchecked"] { + background-color: color-mix( + in oklab, + var(--input) 80%, + transparent + ); + } + } + .dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is( + .dark * + )[data-variant="destructive"]:focus { + background-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is( + .dark * + )[data-variant="destructive"]:focus { + background-color: color-mix( + in oklab, + var(--destructive) 20%, + transparent + ); + } + } + @media print { + .print\:mt-10 { + margin-top: calc(var(--spacing) * 10); + } + .print\:hidden { + display: none; + } + .print\:max-w-none { + max-width: none; + } + .print\:break-inside-avoid-page { + break-inside: avoid-page; + } + .print\:grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + .print\:rounded-none { + border-radius: 0; + } + .print\:border-none { + --tw-border-style: none; + border-style: none; + } + .print\:bg-white { + background-color: var(--color-white); + } + .print\:px-0 { + padding-inline: calc(var(--spacing) * 0); + } + .print\:py-0 { + padding-block: calc(var(--spacing) * 0); + } + .print\:pt-0 { + padding-top: calc(var(--spacing) * 0); + } + .print\:shadow-none { + --tw-shadow: 0 0 #0000; + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + } + .\[\&_\.recharts-cartesian-axis-tick_text\]\:fill-muted-foreground + .recharts-cartesian-axis-tick + text { + fill: var(--muted-foreground); + } + .\[\&_\.recharts-cartesian-grid_line\[stroke\=\'\#ccc\'\]\]\:stroke-border\/50 + .recharts-cartesian-grid + line[stroke="#ccc"] { + stroke: var(--border); + } + @supports (color: color-mix(in lab, red, red)) { + .\[\&_\.recharts-cartesian-grid_line\[stroke\=\'\#ccc\'\]\]\:stroke-border\/50 + .recharts-cartesian-grid + line[stroke="#ccc"] { + stroke: color-mix(in oklab, var(--border) 50%, transparent); + } + } + .\[\&_\.recharts-curve\.recharts-tooltip-cursor\]\:stroke-border + .recharts-curve.recharts-tooltip-cursor { + stroke: var(--border); + } + .\[\&_\.recharts-dot\[stroke\=\'\#fff\'\]\]\:stroke-transparent + .recharts-dot[stroke="#fff"] { + stroke: #0000; + } + .\[\&_\.recharts-layer\]\:outline-hidden .recharts-layer { + --tw-outline-style: none; + outline-style: none; + } + @media (forced-colors: active) { + .\[\&_\.recharts-layer\]\:outline-hidden .recharts-layer { + outline-offset: 2px; + outline: 2px solid #0000; + } + } + .\[\&_\.recharts-polar-grid_\[stroke\=\'\#ccc\'\]\]\:stroke-border + .recharts-polar-grid + [stroke="#ccc"] { + stroke: var(--border); + } + .\[\&_\.recharts-radial-bar-background-sector\]\:fill-muted + .recharts-radial-bar-background-sector, + .\[\&_\.recharts-rectangle\.recharts-tooltip-cursor\]\:fill-muted + .recharts-rectangle.recharts-tooltip-cursor { + fill: var(--muted); + } + .\[\&_\.recharts-reference-line_\[stroke\=\'\#ccc\'\]\]\:stroke-border + .recharts-reference-line + [stroke="#ccc"] { + stroke: var(--border); + } + .\[\&_\.recharts-sector\]\:outline-hidden .recharts-sector { + --tw-outline-style: none; + outline-style: none; + } + @media (forced-colors: active) { + .\[\&_\.recharts-sector\]\:outline-hidden .recharts-sector { + outline-offset: 2px; + outline: 2px solid #0000; + } + } + .\[\&_\.recharts-sector\[stroke\=\'\#fff\'\]\]\:stroke-transparent + .recharts-sector[stroke="#fff"] { + stroke: #0000; + } + .\[\&_\.recharts-surface\]\:outline-hidden .recharts-surface { + --tw-outline-style: none; + outline-style: none; + } + @media (forced-colors: active) { + .\[\&_\.recharts-surface\]\:outline-hidden .recharts-surface { + outline-offset: 2px; + outline: 2px solid #0000; + } + } + .\[\&_\.tiptap\]\:text-white .tiptap { + color: var(--color-white); + } + .\[\&_\.tiptap\]\:outline-none .tiptap { + --tw-outline-style: none; + outline-style: none; + } + .\[\&_\.tiptap_h1\]\:text-white .tiptap h1, + .\[\&_\.tiptap_h2\]\:text-white .tiptap h2 { + color: var(--color-white); + } + .\[\&_\.tiptap_img\]\:max-w-full .tiptap img { + max-width: 100%; + } + .\[\&_\.tiptap_img\]\:cursor-pointer .tiptap img { + cursor: pointer; + } + .\[\&_\.tiptap_img\]\:rounded .tiptap img { + border-radius: 0.25rem; + } + .\[\&_\.tiptap_li\]\:text-white .tiptap li, + .\[\&_\.tiptap_ol\]\:text-white .tiptap ol, + .\[\&_\.tiptap_p\]\:text-white .tiptap p, + .\[\&_\.tiptap_ul\]\:text-white .tiptap ul { + color: var(--color-white); + } + .\[\&_\[cmdk-group-heading\]\]\:px-2 [cmdk-group-heading] { + padding-inline: calc(var(--spacing) * 2); + } + .\[\&_\[cmdk-group-heading\]\]\:py-1\.5 [cmdk-group-heading] { + padding-block: calc(var(--spacing) * 1.5); + } + .\[\&_\[cmdk-group-heading\]\]\:text-xs [cmdk-group-heading] { + font-size: var(--text-xs); + line-height: var(--tw-leading, var(--text-xs--line-height)); + } + .\[\&_\[cmdk-group-heading\]\]\:font-medium [cmdk-group-heading] { + --tw-font-weight: var(--font-weight-medium); + font-weight: var(--font-weight-medium); + } + .\[\&_\[cmdk-group-heading\]\]\:text-muted-foreground [cmdk-group-heading] { + color: var(--muted-foreground); + } + .\[\&_\[cmdk-group\]\]\:px-2 [cmdk-group] { + padding-inline: calc(var(--spacing) * 2); + } + .\[\&_\[cmdk-group\]\:not\(\[hidden\]\)_\~\[cmdk-group\]\]\:pt-0 + [cmdk-group]:not([hidden]) + ~ [cmdk-group] { + padding-top: calc(var(--spacing) * 0); + } + .\[\&_\[cmdk-input-wrapper\]_svg\]\:h-5 [cmdk-input-wrapper] svg { + height: calc(var(--spacing) * 5); + } + .\[\&_\[cmdk-input-wrapper\]_svg\]\:w-5 [cmdk-input-wrapper] svg { + width: calc(var(--spacing) * 5); + } + .\[\&_\[cmdk-input\]\]\:h-12 [cmdk-input] { + height: calc(var(--spacing) * 12); + } + .\[\&_\[cmdk-item\]\]\:px-2 [cmdk-item] { + padding-inline: calc(var(--spacing) * 2); + } + .\[\&_\[cmdk-item\]\]\:py-3 [cmdk-item] { + padding-block: calc(var(--spacing) * 3); + } + .\[\&_\[cmdk-item\]_svg\]\:h-5 [cmdk-item] svg { + height: calc(var(--spacing) * 5); + } + .\[\&_\[cmdk-item\]_svg\]\:w-5 [cmdk-item] svg { + width: calc(var(--spacing) * 5); + } + .\[\&_img\]\:size-full img { + width: 100%; + height: 100%; + } + .\[\&_img\]\:object-cover img { + object-fit: cover; + } + .\[\&_p\]\:leading-relaxed p { + --tw-leading: var(--leading-relaxed); + line-height: var(--leading-relaxed); + } + .\[\&_svg\]\:pointer-events-none svg { + pointer-events: none; + } + .\[\&_svg\]\:shrink-0 svg { + flex-shrink: 0; + } + .\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3 + svg:not([class*="size-"]) { + width: calc(var(--spacing) * 3); + height: calc(var(--spacing) * 3); + } + .\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 + svg:not([class*="size-"]) { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + .\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-6 + svg:not([class*="size-"]) { + width: calc(var(--spacing) * 6); + height: calc(var(--spacing) * 6); + } + .\[\&_svg\:not\(\[class\*\=\'text-\'\]\)\]\:text-muted-foreground + svg:not([class*="text-"]) { + color: var(--muted-foreground); + } + .\[\&_tr\]\:border-b tr { + border-bottom-style: var(--tw-border-style); + border-bottom-width: 1px; + } + .\[\&_tr\:last-child\]\:border-0 tr:last-child { + border-style: var(--tw-border-style); + border-width: 0; + } + .\[\&\+\[data-slot\=item-content\]\]\:flex-none + + [data-slot="item-content"] { + flex: none; + } + .\[\&\:first-child\[data-selected\=true\]_button\]\:rounded-l-md:first-child[data-selected="true"] + button { + border-top-left-radius: calc(var(--radius) - 3px); + border-bottom-left-radius: calc(var(--radius) - 3px); + } + .\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role="checkbox"]) { + padding-right: calc(var(--spacing) * 0); + } + .\[\.border-b\]\:pb-3.border-b { + padding-bottom: calc(var(--spacing) * 3); + } + .\[\.border-b\]\:pb-6.border-b { + padding-bottom: calc(var(--spacing) * 6); + } + .\[\.border-t\]\:pt-3.border-t { + padding-top: calc(var(--spacing) * 3); + } + .\[\.border-t\]\:pt-6.border-t { + padding-top: calc(var(--spacing) * 6); + } + :is( + .rtl\:\*\*\:\[\.rdp-button\\_next\>svg\]\:rotate-180:where( + :dir(rtl), + [dir="rtl"], + [dir="rtl"] * + ) + * + ):is(.rdp-button_next > svg), + :is( + .rtl\:\*\*\:\[\.rdp-button\\_previous\>svg\]\:rotate-180:where( + :dir(rtl), + [dir="rtl"], + [dir="rtl"] * + ) + * + ):is(.rdp-button_previous > svg) { + rotate: 180deg; + } + .\[a\]\:transition-colors:is(a) { + transition-property: + color, background-color, border-color, outline-color, + text-decoration-color, fill, stroke, --tw-gradient-from, + --tw-gradient-via, --tw-gradient-to; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + @media (hover: hover) { + .\[a\]\:hover\:bg-accent\/50:is(a):hover { + background-color: var(--accent); + } + @supports (color: color-mix(in lab, red, red)) { + .\[a\]\:hover\:bg-accent\/50:is(a):hover { + background-color: color-mix( + in oklab, + var(--accent) 50%, + transparent + ); + } + } + } + :is(.\*\:\[span\]\:last\:flex > *):is(span):last-child { + display: flex; + } + :is(.\*\:\[span\]\:last\:items-center > *):is(span):last-child { + align-items: center; + } + :is(.\*\:\[span\]\:last\:gap-2 > *):is(span):last-child { + gap: calc(var(--spacing) * 2); + } + :is( + .data-\[variant\=destructive\]\:\*\:\[svg\]\:\!text-destructive[data-variant="destructive"] + > * + ):is(svg) { + color: var(--destructive) !important; + } + .\[\&\:last-child\[data-selected\=true\]_button\]\:rounded-r-md:last-child[data-selected="true"] + button { + border-top-right-radius: calc(var(--radius) - 3px); + border-bottom-right-radius: calc(var(--radius) - 3px); + } + .\[\&\>\*\]\:w-full > * { + width: 100%; + } + .\[\&\>\*\]\:focus-visible\:relative > :focus-visible { + position: relative; + } + .\[\&\>\*\]\:focus-visible\:z-10 > :focus-visible { + z-index: 10; + } + .\[\&\>\*\]\:data-\[slot\=field\]\:p-4 > [data-slot="field"] { + padding: calc(var(--spacing) * 4); + } + @container field-group (min-width:28rem) { + .\@md\/field-group\:\[\&\>\*\]\:w-auto > * { + width: auto; + } + } + .\[\&\>\*\:not\(\:first-child\)\]\:rounded-t-none > :not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + .\[\&\>\*\:not\(\:first-child\)\]\:rounded-l-none > :not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + .\[\&\>\*\:not\(\:first-child\)\]\:border-t-0 > :not(:first-child) { + border-top-style: var(--tw-border-style); + border-top-width: 0; + } + .\[\&\>\*\:not\(\:first-child\)\]\:border-l-0 > :not(:first-child) { + border-left-style: var(--tw-border-style); + border-left-width: 0; + } + .\[\&\>\*\:not\(\:last-child\)\]\:rounded-r-none > :not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + .\[\&\>\*\:not\(\:last-child\)\]\:rounded-b-none > :not(:last-child) { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + } + .\[\&\>\.sr-only\]\:w-auto > .sr-only { + width: auto; + } + .\[\&\>\[data-slot\=field-group\]\]\:gap-4 > [data-slot="field-group"] { + gap: calc(var(--spacing) * 4); + } + .\[\&\>\[data-slot\=field-label\]\]\:flex-auto > [data-slot="field-label"] { + flex: auto; + } + @container field-group (min-width:28rem) { + .\@md\/field-group\:\[\&\>\[data-slot\=field-label\]\]\:flex-auto + > [data-slot="field-label"] { + flex: auto; + } + } + .has-\[select\[aria-hidden\=true\]\:last-child\]\:\[\&\>\[data-slot\=select-trigger\]\:last-of-type\]\:rounded-r-md:has( + :is(select[aria-hidden="true"]:last-child) + ) + > [data-slot="select-trigger"]:last-of-type { + border-top-right-radius: calc(var(--radius) - 3px); + border-bottom-right-radius: calc(var(--radius) - 3px); + } + .\[\&\>\[data-slot\=select-trigger\]\:not\(\[class\*\=\'w-\'\]\)\]\:w-fit + > [data-slot="select-trigger"]:not([class*="w-"]) { + width: fit-content; + } + .\[\&\>\[role\=checkbox\]\]\:translate-y-\[2px\] > [role="checkbox"] { + --tw-translate-y: 2px; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has( + > [data-slot="field-content"] + ) + > [role="checkbox"], + .has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has( + > [data-slot="field-content"] + ) + [role="radio"] { + margin-top: 1px; + } + @container field-group (min-width:28rem) { + .\@md\/field-group\:has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has( + > [data-slot="field-content"] + ) + > [role="checkbox"], + .\@md\/field-group\:has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has( + > [data-slot="field-content"] + ) + [role="radio"] { + margin-top: 1px; + } + } + .\[\&\>a\]\:underline > a { + text-decoration-line: underline; + } + .\[\&\>a\]\:underline-offset-4 > a { + text-underline-offset: 4px; + } + .\[\&\>a\:hover\]\:text-primary > a:hover { + color: var(--primary); + } + .\[\&\>button\]\:hidden > button { + display: none; + } + .\[\&\>input\]\:flex-1 > input { + flex: 1; + } + .has-\[\>\[data-align\=block-end\]\]\:\[\&\>input\]\:pt-3:has( + > [data-align="block-end"] + ) + > input { + padding-top: calc(var(--spacing) * 3); + } + .has-\[\>\[data-align\=block-start\]\]\:\[\&\>input\]\:pb-3:has( + > [data-align="block-start"] + ) + > input { + padding-bottom: calc(var(--spacing) * 3); + } + .has-\[\>\[data-align\=inline-end\]\]\:\[\&\>input\]\:pr-2:has( + > [data-align="inline-end"] + ) + > input { + padding-right: calc(var(--spacing) * 2); + } + .has-\[\>\[data-align\=inline-start\]\]\:\[\&\>input\]\:pl-2:has( + > [data-align="inline-start"] + ) + > input { + padding-left: calc(var(--spacing) * 2); + } + .\[\&\>kbd\]\:rounded-\[calc\(var\(--radius\)-5px\)\] > kbd { + border-radius: calc(var(--radius) - 5px); + } + .\[\&\>span\]\:text-xs > span { + font-size: var(--text-xs); + line-height: var(--tw-leading, var(--text-xs--line-height)); + } + .\[\&\>span\]\:opacity-70 > span { + opacity: 0.7; + } + .\[\&\>span\:last-child\]\:truncate > span:last-child { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + .\[\&\>svg\]\:pointer-events-none > svg { + pointer-events: none; + } + .\[\&\>svg\]\:size-3 > svg { + width: calc(var(--spacing) * 3); + height: calc(var(--spacing) * 3); + } + .\[\&\>svg\]\:size-3\.5 > svg { + width: calc(var(--spacing) * 3.5); + height: calc(var(--spacing) * 3.5); + } + .\[\&\>svg\]\:size-4 > svg { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + .\[\&\>svg\]\:h-2\.5 > svg { + height: calc(var(--spacing) * 2.5); + } + .\[\&\>svg\]\:h-3 > svg { + height: calc(var(--spacing) * 3); + } + .\[\&\>svg\]\:w-2\.5 > svg { + width: calc(var(--spacing) * 2.5); + } + .\[\&\>svg\]\:w-3 > svg { + width: calc(var(--spacing) * 3); + } + .\[\&\>svg\]\:shrink-0 > svg { + flex-shrink: 0; + } + .\[\&\>svg\]\:translate-y-0\.5 > svg { + --tw-translate-y: calc(var(--spacing) * 0.5); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .\[\&\>svg\]\:text-current > svg { + color: currentColor; + } + .\[\&\>svg\]\:text-muted-foreground > svg { + color: var(--muted-foreground); + } + .\[\&\>svg\]\:text-sidebar-accent-foreground > svg { + color: var(--sidebar-accent-foreground); + } + .\[\&\>svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3\.5 + > svg:not([class*="size-"]) { + width: calc(var(--spacing) * 3.5); + height: calc(var(--spacing) * 3.5); + } + .\[\&\>svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 + > svg:not([class*="size-"]) { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + .\[\&\>tr\]\:last\:border-b-0 > tr:last-child { + border-bottom-style: var(--tw-border-style); + border-bottom-width: 0; + } + .\[\&\[data-panel-group-direction\=vertical\]\>div\]\:rotate-90[data-panel-group-direction="vertical"] + > div { + rotate: 90deg; + } + .\[\&\[data-state\=open\]\>svg\]\:rotate-180[data-state="open"] > svg { + rotate: 180deg; + } + [data-side="left"][data-collapsible="offcanvas"] + .\[\[data-side\=left\]\[data-collapsible\=offcanvas\]_\&\]\:-right-2 { + right: calc(var(--spacing) * -2); + } + [data-side="left"][data-state="collapsed"] + .\[\[data-side\=left\]\[data-state\=collapsed\]_\&\]\:cursor-e-resize { + cursor: e-resize; + } + [data-side="right"][data-collapsible="offcanvas"] + .\[\[data-side\=right\]\[data-collapsible\=offcanvas\]_\&\]\:-left-2 { + left: calc(var(--spacing) * -2); + } + [data-side="right"][data-state="collapsed"] + .\[\[data-side\=right\]\[data-state\=collapsed\]_\&\]\:cursor-w-resize { + cursor: w-resize; + } + [data-slot="card-content"] + .\[\[data-slot\=card-content\]_\&\]\:bg-transparent, + [data-slot="popover-content"] + .\[\[data-slot\=popover-content\]_\&\]\:bg-transparent { + background-color: #0000; + } + [data-slot="tooltip-content"] + .\[\[data-slot\=tooltip-content\]_\&\]\:bg-background\/20 { + background-color: var(--background); + } + @supports (color: color-mix(in lab, red, red)) { + [data-slot="tooltip-content"] + .\[\[data-slot\=tooltip-content\]_\&\]\:bg-background\/20 { + background-color: color-mix( + in oklab, + var(--background) 20%, + transparent + ); + } + } + [data-slot="tooltip-content"] + .\[\[data-slot\=tooltip-content\]_\&\]\:text-background { + color: var(--background); + } + [data-slot="tooltip-content"] + .dark\:\[\[data-slot\=tooltip-content\]_\&\]\:bg-background\/10:is( + .dark * + ) { + background-color: var(--background); + } + @supports (color: color-mix(in lab, red, red)) { + [data-slot="tooltip-content"] + .dark\:\[\[data-slot\=tooltip-content\]_\&\]\:bg-background\/10:is( + .dark * + ) { + background-color: color-mix( + in oklab, + var(--background) 10%, + transparent + ); + } + } + [data-variant="legend"] + .\[\[data-variant\=legend\]\+\&\]\:-mt-1\.5 { + margin-top: calc(var(--spacing) * -1.5); + } + @media (hover: hover) { + a.\[a\&\]\:hover\:bg-accent:hover { + background-color: var(--accent); + } + a.\[a\&\]\:hover\:bg-destructive\/90:hover { + background-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + a.\[a\&\]\:hover\:bg-destructive\/90:hover { + background-color: color-mix( + in oklab, + var(--destructive) 90%, + transparent + ); + } + } + a.\[a\&\]\:hover\:bg-primary\/90:hover { + background-color: var(--primary); + } + @supports (color: color-mix(in lab, red, red)) { + a.\[a\&\]\:hover\:bg-primary\/90:hover { + background-color: color-mix( + in oklab, + var(--primary) 90%, + transparent + ); + } + } + a.\[a\&\]\:hover\:bg-secondary\/90:hover { + background-color: var(--secondary); + } + @supports (color: color-mix(in lab, red, red)) { + a.\[a\&\]\:hover\:bg-secondary\/90:hover { + background-color: color-mix( + in oklab, + var(--secondary) 90%, + transparent + ); + } + } + a.\[a\&\]\:hover\:text-accent-foreground:hover { + color: var(--accent-foreground); + } + } +} +@property --tw-animation-delay { + syntax: "*"; + inherits: false; + initial-value: 0s; +} +@property --tw-animation-direction { + syntax: "*"; + inherits: false; + initial-value: normal; +} +@property --tw-animation-duration { + syntax: "*"; + inherits: false; +} +@property --tw-animation-fill-mode { + syntax: "*"; + inherits: false; + initial-value: none; +} +@property --tw-animation-iteration-count { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-enter-blur { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-enter-opacity { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-enter-rotate { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-enter-scale { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-enter-translate-x { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-enter-translate-y { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-exit-blur { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-exit-opacity { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-exit-rotate { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-exit-scale { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-exit-translate-x { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-exit-translate-y { + syntax: "*"; + inherits: false; + initial-value: 0; +} +:root, +.dark { + --radius: 1.1rem; + --ink-strong: oklch(20% 0.03 220); + --surface-elevated: oklch(18% 0.026 221); + --surface-soft: oklch(23% 0.028 219); + --accent-strong: oklch(70% 0.12 205); + --accent-muted: oklch(58% 0.09 205); + --accent-warm: oklch(84% 0.16 87); + --background: oklch(13% 0.022 223); + --foreground: oklch(96% 0.008 210); + --card: oklch(17% 0.024 221); + --card-foreground: oklch(95% 0.008 210); + --popover: oklch(17% 0.024 221); + --popover-foreground: oklch(95% 0.008 210); + --primary: oklch(70% 0.12 205); + --primary-foreground: oklch(18% 0.02 220); + --secondary: oklch(24% 0.026 220); + --secondary-foreground: oklch(90% 0.01 210); + --muted: oklch(22% 0.022 220); + --muted-foreground: oklch(72% 0.015 214); + --accent: oklch(24% 0.03 209); + --accent-foreground: oklch(95% 0.008 210); + --destructive: oklch(64% 0.21 25); + --destructive-foreground: oklch(98% 0 0); + --border: oklch(100% 0 0/0.1); + --input: oklch(100% 0 0/0.12); + --ring: oklch(70% 0.12 205/0.45); + --chart-1: oklch(70% 0.12 205); + --chart-2: oklch(58% 0.09 206); + --chart-3: oklch(46% 0.07 215); + --chart-4: oklch(84% 0.16 87); + --chart-5: oklch(63% 0.09 177); + --sidebar: oklch(15% 0.02 221); + --sidebar-foreground: oklch(95% 0.008 210); + --sidebar-primary: oklch(70% 0.12 205); + --sidebar-primary-foreground: oklch(18% 0.02 220); + --sidebar-accent: oklch(23% 0.026 219); + --sidebar-accent-foreground: oklch(95% 0.008 210); + --sidebar-border: oklch(100% 0 0/0.1); + --sidebar-ring: oklch(70% 0.12 205/0.45); +} +@property --tw-translate-x { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-translate-y { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-translate-z { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-rotate-x { + syntax: "*"; + inherits: false; +} +@property --tw-rotate-y { + syntax: "*"; + inherits: false; +} +@property --tw-rotate-z { + syntax: "*"; + inherits: false; +} +@property --tw-skew-x { + syntax: "*"; + inherits: false; +} +@property --tw-skew-y { + syntax: "*"; + inherits: false; +} +@property --tw-space-y-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-space-x-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-divide-y-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-border-style { + syntax: "*"; + inherits: false; + initial-value: solid; +} +@property --tw-gradient-position { + syntax: "*"; + inherits: false; +} +@property --tw-gradient-from { + syntax: ""; + inherits: false; + initial-value: #0000; +} +@property --tw-gradient-via { + syntax: ""; + inherits: false; + initial-value: #0000; +} +@property --tw-gradient-to { + syntax: ""; + inherits: false; + initial-value: #0000; +} +@property --tw-gradient-stops { + syntax: "*"; + inherits: false; +} +@property --tw-gradient-via-stops { + syntax: "*"; + inherits: false; +} +@property --tw-gradient-from-position { + syntax: ""; + inherits: false; + initial-value: 0%; +} +@property --tw-gradient-via-position { + syntax: ""; + inherits: false; + initial-value: 50%; +} +@property --tw-gradient-to-position { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-leading { + syntax: "*"; + inherits: false; +} +@property --tw-font-weight { + syntax: "*"; + inherits: false; +} +@property --tw-tracking { + syntax: "*"; + inherits: false; +} +@property --tw-ordinal { + syntax: "*"; + inherits: false; +} +@property --tw-slashed-zero { + syntax: "*"; + inherits: false; +} +@property --tw-numeric-figure { + syntax: "*"; + inherits: false; +} +@property --tw-numeric-spacing { + syntax: "*"; + inherits: false; +} +@property --tw-numeric-fraction { + syntax: "*"; + inherits: false; +} +@property --tw-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-shadow-color { + syntax: "*"; + inherits: false; +} +@property --tw-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-inset-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-inset-shadow-color { + syntax: "*"; + inherits: false; +} +@property --tw-inset-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-ring-color { + syntax: "*"; + inherits: false; +} +@property --tw-ring-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-inset-ring-color { + syntax: "*"; + inherits: false; +} +@property --tw-inset-ring-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-ring-inset { + syntax: "*"; + inherits: false; +} +@property --tw-ring-offset-width { + syntax: ""; + inherits: false; + initial-value: 0; +} +@property --tw-ring-offset-color { + syntax: "*"; + inherits: false; + initial-value: #fff; +} +@property --tw-ring-offset-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-outline-style { + syntax: "*"; + inherits: false; + initial-value: solid; +} +@property --tw-blur { + syntax: "*"; + inherits: false; +} +@property --tw-brightness { + syntax: "*"; + inherits: false; +} +@property --tw-contrast { + syntax: "*"; + inherits: false; +} +@property --tw-grayscale { + syntax: "*"; + inherits: false; +} +@property --tw-hue-rotate { + syntax: "*"; + inherits: false; +} +@property --tw-invert { + syntax: "*"; + inherits: false; +} +@property --tw-opacity { + syntax: "*"; + inherits: false; +} +@property --tw-saturate { + syntax: "*"; + inherits: false; +} +@property --tw-sepia { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow-color { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-drop-shadow-size { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-blur { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-brightness { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-contrast { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-grayscale { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-hue-rotate { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-invert { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-opacity { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-saturate { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-sepia { + syntax: "*"; + inherits: false; +} +@property --tw-duration { + syntax: "*"; + inherits: false; +} +@property --tw-ease { + syntax: "*"; + inherits: false; +} +@property --tw-content { + syntax: "*"; + inherits: false; + initial-value: ""; +} +@property --tw-scale-x { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-scale-y { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-scale-z { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-divide-x-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@keyframes spin { + to { + transform: rotate(360deg); + } +} +@keyframes pulse { + 50% { + opacity: 0.5; + } +} +@keyframes enter { + 0% { + opacity: var(--tw-enter-opacity, 1); + transform: translate3d( + var(--tw-enter-translate-x, 0), + var(--tw-enter-translate-y, 0), + 0 + ) + scale3d( + var(--tw-enter-scale, 1), + var(--tw-enter-scale, 1), + var(--tw-enter-scale, 1) + ) + rotate(var(--tw-enter-rotate, 0)); + filter: blur(var(--tw-enter-blur, 0)); + } +} +@keyframes exit { + to { + opacity: var(--tw-exit-opacity, 1); + transform: translate3d( + var(--tw-exit-translate-x, 0), + var(--tw-exit-translate-y, 0), + 0 + ) + scale3d( + var(--tw-exit-scale, 1), + var(--tw-exit-scale, 1), + var(--tw-exit-scale, 1) + ) + rotate(var(--tw-exit-rotate, 0)); + filter: blur(var(--tw-exit-blur, 0)); + } +} +@keyframes accordion-down { + 0% { + height: 0; + } + to { + height: var( + --radix-accordion-content-height, + var( + --bits-accordion-content-height, + var( + --reka-accordion-content-height, + var( + --kb-accordion-content-height, + var(--ngp-accordion-content-height, auto) + ) + ) + ) + ); + } +} +@keyframes accordion-up { + 0% { + height: var( + --radix-accordion-content-height, + var( + --bits-accordion-content-height, + var( + --reka-accordion-content-height, + var( + --kb-accordion-content-height, + var(--ngp-accordion-content-height, auto) + ) + ) + ) + ); + } + to { + height: 0; + } +} +@keyframes caret-blink { + 0%, + 70%, + to { + opacity: 1; + } + 20%, + 50% { + opacity: 0; + } +} diff --git a/src/design/tri-hub-ernaehrungsberatung.html b/src/design/tri-hub-ernaehrungsberatung.html new file mode 100644 index 0000000..aeecc7d --- /dev/null +++ b/src/design/tri-hub-ernaehrungsberatung.html @@ -0,0 +1,321 @@ + + + + + + TRI-HUB – Ernährungsberatung + + + + + +
+ +
+ + +
+
+
+ Performance & Gesundheit +

+ Maßgeschneiderte Ernährungsberatung für Ausdauerathleten +

+

+ Optimiere deine Energieversorgung im Training und + Wettkampf. Wir entwickeln deine individuelle + Ernährungsstrategie auf wissenschaftlicher Basis. +

+ +
+
+
+ + +
+ +
+
+ Wissenschaftlich & Individuell +

+ Warum Ernährungsberatung bei TRI-HUB? +

+
+ +
+
+
+ 1 +
+

+ Analyse & BIA-Messung +

+

+ Fundierte Bestandsaufnahme von + Körperzusammensetzung, Stoffwechsel und aktuellen + Ernährungsgewohnheiten. +

+
+ +
+
+ 2 +
+

+ Wettkampfstrategie +

+

+ Präzise Verpflegungspläne für Renntage + (Carboloading, Magenverträglichkeit, Kohlenhydrat- & + Flüssigkeitszufuhr). +

+
+ +
+
+ 3 +
+

+ Nachhaltige Integration +

+

+ Praxisnahe Empfehlungen, die sich nahtlos in deinen + Berufs- und Trainingsalltag integrieren lassen. +

+
+
+
+ + +
+
+ Angebote +

+ Unsere Beratungspakete +

+
+ +
+ +
+
+

+ Starter +

+

+ Ideal für den Einstieg und die Optimierung der + Grundernährung. +

+
+ 149 € +
+
    +
  • ✓ Anamnese & Ist-Analyse
  • +
  • ✓ 60 Min. Einzelsession
  • +
  • ✓ Konkrete Handlungsempfehlungen
  • +
+
+ Buchen +
+ + +
+
+ Beliebt +

+ Performance +

+

+ Umfassende Betreuung inkl. Wettkampfstrategie. +

+
+ 349 € +
+
    +
  • ✓ Alle Leistungen aus Starter
  • +
  • + ✓ Detaillierter Wettkampf-Verpflegungsplan +
  • +
  • ✓ 2x Folge-Consulting (je 30 Min.)
  • +
  • ✓ Chat-Support während der Vorbereitung
  • +
+
+ Buchen +
+ + +
+
+

+ Season Pass +

+

+ Kontinuierliche Ernährungsbegleitung über die + gesamte Saison. +

+
+ Individuell +
+
    +
  • + ✓ Regelmäßige Anpassungen je Trainingsphase +
  • +
  • ✓ BIA-Körperanalysen inklusive
  • +
  • + ✓ Maximale Flexibilität & Prioritäts-Support +
  • +
+
+ Anfragen +
+
+
+ + +
+

+ Bereit für den nächsten Schritt? +

+

+ Vereinbare jetzt ein unverbindliches Erstgespräch mit + unseren Ernährungsexperten und erreiche deine Ziele + effizienter. +

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

© 2026 TRI-HUB. Alle Rechte vorbehalten.

+
+
+ + diff --git a/src/styles.css b/src/styles.css index 8917712..697e903 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,39 +1,9135 @@ -:root { - font-family: system-ui, sans-serif; - color: #18352b; - background: #f7faf7; - line-height: 1.6; +/*! tailwindcss v4.1.14 | MIT License | https://tailwindcss.com */ +@layer properties { + @supports (((-webkit-hyphens: none)) and (not (margin-trim: inline))) or + ((-moz-orient: inline) and (not (color: rgb(from red r g b)))) { + *, + :before, + :after, + ::backdrop { + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-translate-z: 0; + --tw-rotate-x: initial; + --tw-rotate-y: initial; + --tw-rotate-z: initial; + --tw-skew-x: initial; + --tw-skew-y: initial; + --tw-space-y-reverse: 0; + --tw-space-x-reverse: 0; + --tw-divide-y-reverse: 0; + --tw-border-style: solid; + --tw-gradient-position: initial; + --tw-gradient-from: #0000; + --tw-gradient-via: #0000; + --tw-gradient-to: #0000; + --tw-gradient-stops: initial; + --tw-gradient-via-stops: initial; + --tw-gradient-from-position: 0%; + --tw-gradient-via-position: 50%; + --tw-gradient-to-position: 100%; + --tw-leading: initial; + --tw-font-weight: initial; + --tw-tracking: initial; + --tw-ordinal: initial; + --tw-slashed-zero: initial; + --tw-numeric-figure: initial; + --tw-numeric-spacing: initial; + --tw-numeric-fraction: initial; + --tw-shadow: 0 0 #0000; + --tw-shadow-color: initial; + --tw-shadow-alpha: 100%; + --tw-inset-shadow: 0 0 #0000; + --tw-inset-shadow-color: initial; + --tw-inset-shadow-alpha: 100%; + --tw-ring-color: initial; + --tw-ring-shadow: 0 0 #0000; + --tw-inset-ring-color: initial; + --tw-inset-ring-shadow: 0 0 #0000; + --tw-ring-inset: initial; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-offset-shadow: 0 0 #0000; + --tw-outline-style: solid; + --tw-blur: initial; + --tw-brightness: initial; + --tw-contrast: initial; + --tw-grayscale: initial; + --tw-hue-rotate: initial; + --tw-invert: initial; + --tw-opacity: initial; + --tw-saturate: initial; + --tw-sepia: initial; + --tw-drop-shadow: initial; + --tw-drop-shadow-color: initial; + --tw-drop-shadow-alpha: 100%; + --tw-drop-shadow-size: initial; + --tw-backdrop-blur: initial; + --tw-backdrop-brightness: initial; + --tw-backdrop-contrast: initial; + --tw-backdrop-grayscale: initial; + --tw-backdrop-hue-rotate: initial; + --tw-backdrop-invert: initial; + --tw-backdrop-opacity: initial; + --tw-backdrop-saturate: initial; + --tw-backdrop-sepia: initial; + --tw-duration: initial; + --tw-ease: initial; + --tw-content: ""; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-scale-z: 1; + --tw-divide-x-reverse: 0; + --tw-animation-delay: 0s; + --tw-animation-direction: normal; + --tw-animation-duration: initial; + --tw-animation-fill-mode: none; + --tw-animation-iteration-count: 1; + --tw-enter-blur: 0; + --tw-enter-opacity: 1; + --tw-enter-rotate: 0; + --tw-enter-scale: 1; + --tw-enter-translate-x: 0; + --tw-enter-translate-y: 0; + --tw-exit-blur: 0; + --tw-exit-opacity: 1; + --tw-exit-rotate: 0; + --tw-exit-scale: 1; + --tw-exit-translate-x: 0; + --tw-exit-translate-y: 0; + } + } } - -* { - box-sizing: border-box; +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 400; + src: + url(/assets/dm-sans-latin-ext-400-normal-BtiwyxMk.woff2) format("woff2"), + url(/assets/dm-sans-latin-ext-400-normal-BjWJ59Pq.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; } - -body { - margin: 0; +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 400; + src: + url(/assets/dm-sans-latin-400-normal-CW0RaeGs.woff2) format("woff2"), + url(/assets/dm-sans-latin-400-normal-BwCSEQnW.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; } - -.container { - width: min(100% - 2rem, 64rem); - margin-inline: auto; - padding-block: 1.5rem; +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 500; + src: + url(/assets/dm-sans-latin-ext-500-normal-BJfUCQsA.woff2) format("woff2"), + url(/assets/dm-sans-latin-ext-500-normal-DR84L5F-.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; } - -h1 { - max-width: 18ch; - font-size: clamp(2rem, 6vw, 4rem); - line-height: 1.1; +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 500; + src: + url(/assets/dm-sans-latin-500-normal-B9HHJjqV.woff2) format("woff2"), + url(/assets/dm-sans-latin-500-normal-Dr3UlScf.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; } - -a { - color: #176448; +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 600; + src: + url(/assets/dm-sans-latin-ext-600-normal-4vooXBpG.woff2) format("woff2"), + url(/assets/dm-sans-latin-ext-600-normal-DRtaDpgU.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; } - -a:focus-visible { - outline: 3px solid currentColor; - outline-offset: 4px; +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 600; + src: + url(/assets/dm-sans-latin-600-normal-Aqo67rzb.woff2) format("woff2"), + url(/assets/dm-sans-latin-600-normal-BmdmIIQ2.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; } - -section { - margin-block: 4rem; +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 700; + src: + url(/assets/dm-sans-latin-ext-700-normal-CJIcxD6K.woff2) format("woff2"), + url(/assets/dm-sans-latin-ext-700-normal-BLI3TTWz.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: DM Sans; + font-style: normal; + font-display: swap; + font-weight: 700; + src: + url(/assets/dm-sans-latin-700-normal-DvUfVpUG.woff2) format("woff2"), + url(/assets/dm-sans-latin-700-normal-CUSSCpQX.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 500; + src: + url(/assets/sora-latin-ext-500-normal-B5KKQIFO.woff2) format("woff2"), + url(/assets/sora-latin-ext-500-normal-DwxUJRqY.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 500; + src: + url(/assets/sora-latin-500-normal-01eiPEn0.woff2) format("woff2"), + url(/assets/sora-latin-500-normal-w58xtEt9.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 600; + src: + url(/assets/sora-latin-ext-600-normal-Cue1zdhl.woff2) format("woff2"), + url(/assets/sora-latin-ext-600-normal-DLOJK0Ta.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 600; + src: + url(/assets/sora-latin-600-normal-Cdg4DaK0.woff2) format("woff2"), + url(/assets/sora-latin-600-normal-1_7fyUAY.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 700; + src: + url(/assets/sora-latin-ext-700-normal-DM0oy5s8.woff2) format("woff2"), + url(/assets/sora-latin-ext-700-normal-Oc7uZIYt.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 700; + src: + url(/assets/sora-latin-700-normal-9waGdLWo.woff2) format("woff2"), + url(/assets/sora-latin-700-normal-BKPfQAnC.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 800; + src: + url(/assets/sora-latin-ext-800-normal-YDFYE6t9.woff2) format("woff2"), + url(/assets/sora-latin-ext-800-normal-BvAfeed7.woff) format("woff"); + unicode-range: + U+100-2BA, U+2BD-2C5, U+2C7-2CC, U+2CE-2D7, U+2DD-2FF, U+304, U+308, + U+329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, + U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +@font-face { + font-family: Sora; + font-style: normal; + font-display: swap; + font-weight: 800; + src: + url(/assets/sora-latin-800-normal-2tKLL3qT.woff2) format("woff2"), + url(/assets/sora-latin-800-normal-c3Huklug.woff) format("woff"); + unicode-range: + U+??, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+304, U+308, + U+329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; +} +@layer theme { + :root, + :host { + --font-sans: "DM Sans", sans-serif; + --font-mono: + ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New", monospace; + --color-red-50: oklch(97.1% 0.013 17.38); + --color-red-100: oklch(93.6% 0.032 17.717); + --color-red-200: oklch(88.5% 0.062 18.334); + --color-red-400: oklch(70.4% 0.191 22.216); + --color-red-500: oklch(63.7% 0.237 25.331); + --color-red-600: oklch(57.7% 0.245 27.325); + --color-red-700: oklch(50.5% 0.213 27.518); + --color-red-800: oklch(44.4% 0.177 26.899); + --color-red-900: oklch(39.6% 0.141 25.723); + --color-orange-100: oklch(95.4% 0.038 75.164); + --color-orange-500: oklch(70.5% 0.213 47.604); + --color-orange-600: oklch(64.6% 0.222 41.116); + --color-orange-800: oklch(47% 0.157 37.304); + --color-amber-100: oklch(96.2% 0.059 95.617); + --color-amber-400: oklch(82.8% 0.189 84.429); + --color-yellow-200: oklch(94.5% 0.129 101.54); + --color-yellow-300: oklch(90.5% 0.182 98.111); + --color-yellow-500: oklch(79.5% 0.184 86.047); + --color-yellow-700: oklch(55.4% 0.135 66.442); + --color-green-100: oklch(96.2% 0.044 156.743); + --color-green-200: oklch(92.5% 0.084 155.995); + --color-green-300: oklch(87.1% 0.15 154.449); + --color-green-400: oklch(79.2% 0.209 151.711); + --color-green-500: oklch(72.3% 0.219 149.579); + --color-green-600: oklch(62.7% 0.194 149.214); + --color-green-700: oklch(52.7% 0.154 150.069); + --color-green-800: oklch(44.8% 0.119 151.328); + --color-emerald-100: oklch(95% 0.052 163.051); + --color-emerald-400: oklch(76.5% 0.177 163.223); + --color-teal-500: oklch(70.4% 0.14 182.503); + --color-teal-600: oklch(60% 0.118 184.704); + --color-cyan-100: oklch(95.6% 0.045 203.388); + --color-cyan-300: oklch(86.5% 0.127 207.078); + --color-cyan-400: oklch(78.9% 0.154 211.53); + --color-cyan-500: oklch(71.5% 0.143 215.221); + --color-cyan-600: oklch(60.9% 0.126 221.723); + --color-blue-100: oklch(93.2% 0.032 255.585); + --color-blue-200: oklch(88.2% 0.059 254.128); + --color-blue-300: oklch(80.9% 0.105 251.813); + --color-blue-400: oklch(70.7% 0.165 254.624); + --color-blue-500: oklch(62.3% 0.214 259.815); + --color-blue-600: oklch(54.6% 0.245 262.881); + --color-blue-700: oklch(48.8% 0.243 264.376); + --color-blue-800: oklch(42.4% 0.199 265.638); + --color-indigo-500: oklch(58.5% 0.233 277.117); + --color-indigo-600: oklch(51.1% 0.262 276.966); + --color-purple-100: oklch(94.6% 0.033 307.174); + --color-purple-300: oklch(82.7% 0.119 306.383); + --color-purple-400: oklch(71.4% 0.203 305.504); + --color-purple-500: oklch(62.7% 0.265 303.9); + --color-purple-600: oklch(55.8% 0.288 302.321); + --color-purple-700: oklch(49.6% 0.265 301.924); + --color-purple-800: oklch(43.8% 0.218 303.724); + --color-rose-100: oklch(94.1% 0.03 12.58); + --color-rose-400: oklch(71.2% 0.194 13.428); + --color-slate-50: oklch(98.4% 0.003 247.858); + --color-slate-100: oklch(96.8% 0.007 247.896); + --color-slate-200: oklch(92.9% 0.013 255.508); + --color-slate-300: oklch(86.9% 0.022 252.894); + --color-slate-500: oklch(55.4% 0.046 257.417); + --color-slate-600: oklch(44.6% 0.043 257.281); + --color-slate-700: oklch(37.2% 0.044 257.287); + --color-slate-900: oklch(20.8% 0.042 265.755); + --color-slate-950: oklch(12.9% 0.042 264.695); + --color-gray-100: oklch(96.7% 0.003 264.542); + --color-gray-200: oklch(92.8% 0.006 264.531); + --color-gray-300: oklch(87.2% 0.01 258.338); + --color-gray-500: oklch(55.1% 0.027 264.364); + --color-gray-600: oklch(44.6% 0.03 256.802); + --color-gray-700: oklch(37.3% 0.034 259.733); + --color-gray-800: oklch(27.8% 0.033 256.848); + --color-black: #000; + --color-white: #fff; + --spacing: 0.25rem; + --container-xs: 20rem; + --container-sm: 24rem; + --container-md: 28rem; + --container-lg: 32rem; + --container-xl: 36rem; + --container-2xl: 42rem; + --container-3xl: 48rem; + --container-4xl: 56rem; + --container-5xl: 64rem; + --container-6xl: 72rem; + --container-7xl: 80rem; + --text-xs: 0.75rem; + --text-xs--line-height: calc(1/0.75); + --text-sm: 0.875rem; + --text-sm--line-height: calc(1.25/0.875); + --text-base: 1rem; + --text-base--line-height: 1.5; + --text-lg: 1.125rem; + --text-lg--line-height: calc(1.75/1.125); + --text-xl: 1.25rem; + --text-xl--line-height: calc(1.75/1.25); + --text-2xl: 1.5rem; + --text-2xl--line-height: calc(2/1.5); + --text-3xl: 1.875rem; + --text-3xl--line-height: 1.2; + --text-4xl: 2.25rem; + --text-4xl--line-height: calc(2.5/2.25); + --text-5xl: 3rem; + --text-5xl--line-height: 1; + --text-6xl: 3.75rem; + --text-6xl--line-height: 1; + --text-7xl: 4.5rem; + --text-7xl--line-height: 1; + --font-weight-normal: 400; + --font-weight-medium: 500; + --font-weight-semibold: 600; + --font-weight-bold: 700; + --tracking-tight: -0.025em; + --tracking-wide: 0.025em; + --tracking-widest: 0.1em; + --leading-tight: 1.25; + --leading-snug: 1.375; + --leading-normal: 1.5; + --leading-relaxed: 1.625; + --radius-xs: 0.125rem; + --radius-2xl: 1rem; + --ease-out: cubic-bezier(0, 0, 0.2, 1); + --ease-in-out: cubic-bezier(0.4, 0, 0.2, 1); + --animate-spin: spin 1s linear infinite; + --animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; + --blur-sm: 8px; + --blur-md: 12px; + --blur-xl: 24px; + --blur-2xl: 40px; + --blur-3xl: 64px; + --aspect-video: 16/9; + --default-transition-duration: 0.15s; + --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + --default-font-family: "DM Sans", sans-serif; + --default-mono-font-family: var(--font-mono); + --font-display: "Sora", sans-serif; + --color-border: var(--border); + } +} +@layer base { + *, + :after, + :before, + ::backdrop { + box-sizing: border-box; + border: 0 solid; + margin: 0; + padding: 0; + } + ::file-selector-button { + box-sizing: border-box; + border: 0 solid; + margin: 0; + padding: 0; + } + html, + :host { + -webkit-text-size-adjust: 100%; + tab-size: 4; + line-height: 1.5; + font-family: var( + --default-font-family, + ui-sans-serif, + system-ui, + sans-serif, + "Apple Color Emoji", + "Segoe UI Emoji", + "Segoe UI Symbol", + "Noto Color Emoji" + ); + font-feature-settings: var(--default-font-feature-settings, normal); + font-variation-settings: var(--default-font-variation-settings, normal); + -webkit-tap-highlight-color: transparent; + } + 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; + -webkit-text-decoration: inherit; + text-decoration: inherit; + } + b, + strong { + font-weight: bolder; + } + code, + kbd, + samp, + pre { + font-family: var( + --default-mono-font-family, + ui-monospace, + SFMono-Regular, + Menlo, + Monaco, + Consolas, + "Liberation Mono", + "Courier New", + monospace + ); + font-feature-settings: var( + --default-mono-font-feature-settings, + normal + ); + font-variation-settings: var( + --default-mono-font-variation-settings, + normal + ); + font-size: 1em; + } + small { + font-size: 80%; + } + sub, + sup { + vertical-align: baseline; + font-size: 75%; + line-height: 0; + position: relative; + } + sub { + bottom: -0.25em; + } + sup { + top: -0.5em; + } + table { + text-indent: 0; + border-color: inherit; + border-collapse: collapse; + } + :-moz-focusring { + outline: auto; + } + progress { + vertical-align: baseline; + } + summary { + display: list-item; + } + ol, + ul, + menu { + list-style: none; + } + img, + svg, + video, + canvas, + audio, + iframe, + embed, + object { + vertical-align: middle; + display: block; + } + img, + video { + max-width: 100%; + height: auto; + } + button, + input, + select, + optgroup, + textarea { + font: inherit; + font-feature-settings: inherit; + font-variation-settings: inherit; + letter-spacing: inherit; + color: inherit; + opacity: 1; + background-color: #0000; + border-radius: 0; + } + ::file-selector-button { + font: inherit; + font-feature-settings: inherit; + font-variation-settings: inherit; + letter-spacing: inherit; + color: inherit; + opacity: 1; + background-color: #0000; + border-radius: 0; + } + :where(select:is([multiple], [size])) optgroup { + font-weight: bolder; + } + :where(select:is([multiple], [size])) optgroup option { + padding-inline-start: 20px; + } + ::file-selector-button { + margin-inline-end: 4px; + } + ::placeholder { + opacity: 1; + } + @supports (not ((-webkit-appearance: -apple-pay-button))) or + (contain-intrinsic-size: 1px) { + ::placeholder { + color: currentColor; + } + @supports (color: color-mix(in lab, red, red)) { + ::placeholder { + color: color-mix(in oklab, currentcolor 50%, transparent); + } + } + } + textarea { + resize: vertical; + } + ::-webkit-search-decoration { + -webkit-appearance: none; + } + ::-webkit-date-and-time-value { + min-height: 1lh; + text-align: inherit; + } + ::-webkit-datetime-edit { + display: inline-flex; + } + ::-webkit-datetime-edit-fields-wrapper { + padding: 0; + } + ::-webkit-datetime-edit { + padding-block: 0; + } + ::-webkit-datetime-edit-year-field { + padding-block: 0; + } + ::-webkit-datetime-edit-month-field { + padding-block: 0; + } + ::-webkit-datetime-edit-day-field { + padding-block: 0; + } + ::-webkit-datetime-edit-hour-field { + padding-block: 0; + } + ::-webkit-datetime-edit-minute-field { + padding-block: 0; + } + ::-webkit-datetime-edit-second-field { + padding-block: 0; + } + ::-webkit-datetime-edit-millisecond-field { + padding-block: 0; + } + ::-webkit-datetime-edit-meridiem-field { + padding-block: 0; + } + ::-webkit-calendar-picker-indicator { + line-height: 1; + } + :-moz-ui-invalid { + box-shadow: none; + } + button, + input:where([type="button"], [type="reset"], [type="submit"]) { + appearance: button; + } + ::file-selector-button { + appearance: button; + } + ::-webkit-inner-spin-button { + height: auto; + } + ::-webkit-outer-spin-button { + height: auto; + } + [hidden]:where(:not([hidden="until-found"])) { + display: none !important; + } + * { + border-color: var(--border); + outline-color: var(--ring); + } + @supports (color: color-mix(in lab, red, red)) { + * { + outline-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + html { + scroll-behavior: smooth; + } + body { + font-family: var(--font-sans); + background: + radial-gradient(circle at 0 0, #2eb6c21f, #0000 28%), + radial-gradient(circle at 85% 10%, #ffbe2814, #0000 16%), + linear-gradient(#051018, #071720 46%, #050e16); + background-color: var(--background); + color: var(--foreground); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + h1, + h2, + h3, + .font-display { + font-family: var(--font-display); + letter-spacing: -0.04em; + font-weight: 700; + } + button:not(:disabled), + [role="button"]:not([aria-disabled="true"]), + [type="button"]:not(:disabled), + [type="submit"]:not(:disabled), + [type="reset"]:not(:disabled), + a[href], + select:not(:disabled), + input[type="checkbox"]:not(:disabled), + input[type="radio"]:not(:disabled) { + cursor: pointer; + } + ::selection { + color: #fff; + background: #2fb7c24d; + } +} +@layer components { + .container { + width: 100%; + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; + } + .flex { + min-width: 0; + min-height: 0; + } + .eyebrow { + color: #fff9; + letter-spacing: 0.24em; + text-transform: uppercase; + font-size: 0.72rem; + font-weight: 600; + line-height: 1; + } + .panel-glass { + -webkit-backdrop-filter: blur(16px); + backdrop-filter: blur(16px); + background: linear-gradient(#ffffff14, #ffffff08); + border: 1px solid #ffffff1a; + box-shadow: 0 10px 26px #00000029; + } + .card-premium { + -webkit-backdrop-filter: blur(18px); + backdrop-filter: blur(18px); + background: + linear-gradient(#ffffff0f, #ffffff05), + linear-gradient(#08151ee6, #091822fa); + border: 1px solid #ffffff1a; + transition: + transform 0.32s cubic-bezier(0.22, 1, 0.36, 1), + border-color 0.32s, + box-shadow 0.32s, + background 0.32s; + position: relative; + overflow: hidden; + box-shadow: 0 18px 48px #00000038; + } + .card-premium:before { + content: ""; + pointer-events: none; + background: linear-gradient( + 120deg, + #2db6c214, + #0000 46% 60%, + #ffc12b08 + ); + position: absolute; + inset: 0; + } + .card-premium:hover { + border-color: #2fb7c23d; + transform: translateY(-4px); + box-shadow: 0 24px 62px #0000004d; + } + .hero-shell:after { + content: ""; + pointer-events: none; + background: linear-gradient(#06101400, #061014f0); + height: 160px; + position: absolute; + inset: auto 0 0; + } + .hero-visual-frame:after { + content: ""; + pointer-events: none; + border: 1px solid #ffffff14; + border-radius: 1.4rem; + position: absolute; + inset: 0; + } + @media (min-width: 640px) { + .container { + padding-left: 1.5rem; + padding-right: 1.5rem; + } + } + @media (min-width: 1024px) { + .container { + max-width: 1280px; + padding-left: 2rem; + padding-right: 2rem; + } + } +} +@layer utilities { + .\@container\/card-header { + container: card-header/inline-size; + } + .\@container\/field-group { + container: field-group/inline-size; + } + .pointer-events-none { + pointer-events: none; + } + .invisible { + visibility: hidden; + } + .sr-only { + clip-path: inset(50%); + white-space: nowrap; + border-width: 0; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + position: absolute; + overflow: hidden; + } + .absolute { + position: absolute; + } + .fixed { + position: fixed; + } + .relative { + position: relative; + } + .static { + position: static; + } + .sticky { + position: sticky; + } + .inset-0 { + inset: calc(var(--spacing) * 0); + } + .inset-x-0 { + inset-inline: calc(var(--spacing) * 0); + } + .inset-x-6 { + inset-inline: calc(var(--spacing) * 6); + } + .inset-x-7 { + inset-inline: calc(var(--spacing) * 7); + } + .inset-x-8 { + inset-inline: calc(var(--spacing) * 8); + } + .inset-y-0 { + inset-block: calc(var(--spacing) * 0); + } + .-top-12 { + top: calc(var(--spacing) * -12); + } + .top-0 { + top: calc(var(--spacing) * 0); + } + .top-1\.5 { + top: calc(var(--spacing) * 1.5); + } + .top-1\/2 { + top: 50%; + } + .top-3\.5 { + top: calc(var(--spacing) * 3.5); + } + .top-4 { + top: calc(var(--spacing) * 4); + } + .top-6 { + top: calc(var(--spacing) * 6); + } + .top-16 { + top: calc(var(--spacing) * 16); + } + .top-\[1px\] { + top: 1px; + } + .top-\[50\%\] { + top: 50%; + } + .top-\[60\%\] { + top: 60%; + } + .top-full { + top: 100%; + } + .-right-12 { + right: calc(var(--spacing) * -12); + } + .right-0 { + right: calc(var(--spacing) * 0); + } + .right-1 { + right: calc(var(--spacing) * 1); + } + .right-2 { + right: calc(var(--spacing) * 2); + } + .right-3 { + right: calc(var(--spacing) * 3); + } + .right-4 { + right: calc(var(--spacing) * 4); + } + .-bottom-12 { + bottom: calc(var(--spacing) * -12); + } + .bottom-0 { + bottom: calc(var(--spacing) * 0); + } + .bottom-4 { + bottom: calc(var(--spacing) * 4); + } + .bottom-6 { + bottom: calc(var(--spacing) * 6); + } + .bottom-7 { + bottom: calc(var(--spacing) * 7); + } + .bottom-8 { + bottom: calc(var(--spacing) * 8); + } + .-left-12 { + left: calc(var(--spacing) * -12); + } + .left-0 { + left: calc(var(--spacing) * 0); + } + .left-1\/2 { + left: 50%; + } + .left-2 { + left: calc(var(--spacing) * 2); + } + .left-4 { + left: calc(var(--spacing) * 4); + } + .left-\[1\.25rem\] { + left: 1.25rem; + } + .left-\[50\%\] { + left: 50%; + } + .isolate { + isolation: isolate; + } + .z-10 { + z-index: 10; + } + .z-20 { + z-index: 20; + } + .z-40 { + z-index: 40; + } + .z-50 { + z-index: 50; + } + .z-\[1\] { + z-index: 1; + } + .z-\[120\] { + z-index: 120; + } + .order-first { + order: -9999; + } + .order-last { + order: 9999; + } + .col-start-2 { + grid-column-start: 2; + } + .row-span-2 { + grid-row: span 2 / span 2; + } + .row-start-1 { + grid-row-start: 1; + } + .container { + width: 100%; + } + @media (min-width: 40rem) { + .container { + max-width: 40rem; + } + } + @media (min-width: 48rem) { + .container { + max-width: 48rem; + } + } + @media (min-width: 64rem) { + .container { + max-width: 64rem; + } + } + @media (min-width: 80rem) { + .container { + max-width: 80rem; + } + } + @media (min-width: 96rem) { + .container { + max-width: 96rem; + } + } + .\!m-0 { + margin: calc(var(--spacing) * 0) !important; + } + .-mx-1 { + margin-inline: calc(var(--spacing) * -1); + } + .mx-2 { + margin-inline: calc(var(--spacing) * 2); + } + .mx-3\.5 { + margin-inline: calc(var(--spacing) * 3.5); + } + .mx-4 { + margin-inline: calc(var(--spacing) * 4); + } + .mx-auto { + margin-inline: auto; + } + .-my-2 { + margin-block: calc(var(--spacing) * -2); + } + .my-0 { + margin-block: calc(var(--spacing) * 0); + } + .my-0\.5 { + margin-block: calc(var(--spacing) * 0.5); + } + .my-1 { + margin-block: calc(var(--spacing) * 1); + } + .-mt-4 { + margin-top: calc(var(--spacing) * -4); + } + .mt-0\.5 { + margin-top: calc(var(--spacing) * 0.5); + } + .mt-1 { + margin-top: calc(var(--spacing) * 1); + } + .mt-1\.5 { + margin-top: calc(var(--spacing) * 1.5); + } + .mt-2 { + margin-top: calc(var(--spacing) * 2); + } + .mt-3 { + margin-top: calc(var(--spacing) * 3); + } + .mt-4 { + margin-top: calc(var(--spacing) * 4); + } + .mt-5 { + margin-top: calc(var(--spacing) * 5); + } + .mt-6 { + margin-top: calc(var(--spacing) * 6); + } + .mt-7 { + margin-top: calc(var(--spacing) * 7); + } + .mt-8 { + margin-top: calc(var(--spacing) * 8); + } + .mt-10 { + margin-top: calc(var(--spacing) * 10); + } + .mt-12 { + margin-top: calc(var(--spacing) * 12); + } + .mt-14 { + margin-top: calc(var(--spacing) * 14); + } + .mt-16 { + margin-top: calc(var(--spacing) * 16); + } + .mt-auto { + margin-top: auto; + } + .mr-1 { + margin-right: calc(var(--spacing) * 1); + } + .mr-2 { + margin-right: calc(var(--spacing) * 2); + } + .mb-1 { + margin-bottom: calc(var(--spacing) * 1); + } + .mb-2 { + margin-bottom: calc(var(--spacing) * 2); + } + .mb-3 { + margin-bottom: calc(var(--spacing) * 3); + } + .mb-4 { + margin-bottom: calc(var(--spacing) * 4); + } + .mb-5 { + margin-bottom: calc(var(--spacing) * 5); + } + .mb-6 { + margin-bottom: calc(var(--spacing) * 6); + } + .mb-8 { + margin-bottom: calc(var(--spacing) * 8); + } + .mb-10 { + margin-bottom: calc(var(--spacing) * 10); + } + .mb-12 { + margin-bottom: calc(var(--spacing) * 12); + } + .mb-14 { + margin-bottom: calc(var(--spacing) * 14); + } + .mb-16 { + margin-bottom: calc(var(--spacing) * 16); + } + .mb-20 { + margin-bottom: calc(var(--spacing) * 20); + } + .-ml-4 { + margin-left: calc(var(--spacing) * -4); + } + .ml-1 { + margin-left: calc(var(--spacing) * 1); + } + .ml-2 { + margin-left: calc(var(--spacing) * 2); + } + .ml-3 { + margin-left: calc(var(--spacing) * 3); + } + .ml-4 { + margin-left: calc(var(--spacing) * 4); + } + .ml-auto { + margin-left: auto; + } + .line-clamp-1 { + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + display: -webkit-box; + overflow: hidden; + } + .line-clamp-2 { + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + display: -webkit-box; + overflow: hidden; + } + .line-clamp-3 { + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + display: -webkit-box; + overflow: hidden; + } + .block { + display: block; + } + .flex { + display: flex; + } + .grid { + display: grid; + } + .hidden { + display: none; + } + .inline { + display: inline; + } + .inline-block { + display: inline-block; + } + .inline-flex { + display: inline-flex; + } + .table { + display: table; + } + .table-caption { + display: table-caption; + } + .table-cell { + display: table-cell; + } + .table-row { + display: table-row; + } + .field-sizing-content { + field-sizing: content; + } + .aspect-\[4\/3\] { + aspect-ratio: 4/3; + } + .aspect-\[4\/5\] { + aspect-ratio: 4/5; + } + .aspect-square { + aspect-ratio: 1; + } + .aspect-video { + aspect-ratio: var(--aspect-video); + } + .size-\(--cell-size\) { + width: var(--cell-size); + height: var(--cell-size); + } + .size-2 { + width: calc(var(--spacing) * 2); + height: calc(var(--spacing) * 2); + } + .size-2\.5 { + width: calc(var(--spacing) * 2.5); + height: calc(var(--spacing) * 2.5); + } + .size-3 { + width: calc(var(--spacing) * 3); + height: calc(var(--spacing) * 3); + } + .size-3\.5 { + width: calc(var(--spacing) * 3.5); + height: calc(var(--spacing) * 3.5); + } + .size-4 { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + .size-6 { + width: calc(var(--spacing) * 6); + height: calc(var(--spacing) * 6); + } + .size-7 { + width: calc(var(--spacing) * 7); + height: calc(var(--spacing) * 7); + } + .size-8 { + width: calc(var(--spacing) * 8); + height: calc(var(--spacing) * 8); + } + .size-9 { + width: calc(var(--spacing) * 9); + height: calc(var(--spacing) * 9); + } + .size-10 { + width: calc(var(--spacing) * 10); + height: calc(var(--spacing) * 10); + } + .size-12 { + width: calc(var(--spacing) * 12); + height: calc(var(--spacing) * 12); + } + .size-auto { + width: auto; + height: auto; + } + .size-full { + width: 100%; + height: 100%; + } + .h-\(--cell-size\) { + height: var(--cell-size); + } + .h-1\.5 { + height: calc(var(--spacing) * 1.5); + } + .h-2 { + height: calc(var(--spacing) * 2); + } + .h-2\.5 { + height: calc(var(--spacing) * 2.5); + } + .h-3 { + height: calc(var(--spacing) * 3); + } + .h-4 { + height: calc(var(--spacing) * 4); + } + .h-5 { + height: calc(var(--spacing) * 5); + } + .h-6 { + height: calc(var(--spacing) * 6); + } + .h-7 { + height: calc(var(--spacing) * 7); + } + .h-8 { + height: calc(var(--spacing) * 8); + } + .h-9 { + height: calc(var(--spacing) * 9); + } + .h-10 { + height: calc(var(--spacing) * 10); + } + .h-11 { + height: calc(var(--spacing) * 11); + } + .h-12 { + height: calc(var(--spacing) * 12); + } + .h-14 { + height: calc(var(--spacing) * 14); + } + .h-16 { + height: calc(var(--spacing) * 16); + } + .h-24 { + height: calc(var(--spacing) * 24); + } + .h-28 { + height: calc(var(--spacing) * 28); + } + .h-32 { + height: calc(var(--spacing) * 32); + } + .h-48 { + height: calc(var(--spacing) * 48); + } + .h-64 { + height: calc(var(--spacing) * 64); + } + .h-72 { + height: calc(var(--spacing) * 72); + } + .h-\[1\.15rem\] { + height: 1.15rem; + } + .h-\[38px\] { + height: 38px; + } + .h-\[200px\] { + height: 200px; + } + .h-\[320px\] { + height: 320px; + } + .h-\[500px\] { + height: 500px; + } + .h-\[560px\] { + height: 560px; + } + .h-\[calc\(100\%-1px\)\] { + height: calc(100% - 1px); + } + .h-\[var\(--radix-navigation-menu-viewport-height\)\] { + height: var(--radix-navigation-menu-viewport-height); + } + .h-\[var\(--radix-select-trigger-height\)\] { + height: var(--radix-select-trigger-height); + } + .h-auto { + height: auto; + } + .h-full { + height: 100%; + } + .h-px { + height: 1px; + } + .h-svh { + height: 100svh; + } + .max-h-\(--radix-context-menu-content-available-height\) { + max-height: var(--radix-context-menu-content-available-height); + } + .max-h-\(--radix-dropdown-menu-content-available-height\) { + max-height: var(--radix-dropdown-menu-content-available-height); + } + .max-h-\(--radix-select-content-available-height\) { + max-height: var(--radix-select-content-available-height); + } + .max-h-32 { + max-height: calc(var(--spacing) * 32); + } + .max-h-96 { + max-height: calc(var(--spacing) * 96); + } + .max-h-\[60vh\] { + max-height: 60vh; + } + .max-h-\[70vh\] { + max-height: 70vh; + } + .max-h-\[90vh\] { + max-height: 90vh; + } + .max-h-\[300px\] { + max-height: 300px; + } + .min-h-0 { + min-height: calc(var(--spacing) * 0); + } + .min-h-4 { + min-height: calc(var(--spacing) * 4); + } + .min-h-9 { + min-height: calc(var(--spacing) * 9); + } + .min-h-16 { + min-height: calc(var(--spacing) * 16); + } + .min-h-20 { + min-height: calc(var(--spacing) * 20); + } + .min-h-24 { + min-height: calc(var(--spacing) * 24); + } + .min-h-\[2\.75rem\] { + min-height: 2.75rem; + } + .min-h-\[80px\] { + min-height: 80px; + } + .min-h-\[100px\] { + min-height: 100px; + } + .min-h-\[120px\] { + min-height: 120px; + } + .min-h-\[200px\] { + min-height: 200px; + } + .min-h-\[280px\] { + min-height: 280px; + } + .min-h-\[300px\] { + min-height: 300px; + } + .min-h-\[320px\] { + min-height: 320px; + } + .min-h-screen { + min-height: 100vh; + } + .min-h-svh { + min-height: 100svh; + } + .w-\(--cell-size\) { + width: var(--cell-size); + } + .w-\(--sidebar-width\) { + width: var(--sidebar-width); + } + .w-0 { + width: calc(var(--spacing) * 0); + } + .w-0\.5 { + width: calc(var(--spacing) * 0.5); + } + .w-1 { + width: calc(var(--spacing) * 1); + } + .w-1\.5 { + width: calc(var(--spacing) * 1.5); + } + .w-1\/2 { + width: 50%; + } + .w-2 { + width: calc(var(--spacing) * 2); + } + .w-2\.5 { + width: calc(var(--spacing) * 2.5); + } + .w-3 { + width: calc(var(--spacing) * 3); + } + .w-3\/4 { + width: 75%; + } + .w-4 { + width: calc(var(--spacing) * 4); + } + .w-5 { + width: calc(var(--spacing) * 5); + } + .w-6 { + width: calc(var(--spacing) * 6); + } + .w-8 { + width: calc(var(--spacing) * 8); + } + .w-9 { + width: calc(var(--spacing) * 9); + } + .w-10 { + width: calc(var(--spacing) * 10); + } + .w-11 { + width: calc(var(--spacing) * 11); + } + .w-12 { + width: calc(var(--spacing) * 12); + } + .w-14 { + width: calc(var(--spacing) * 14); + } + .w-16 { + width: calc(var(--spacing) * 16); + } + .w-20 { + width: calc(var(--spacing) * 20); + } + .w-24 { + width: calc(var(--spacing) * 24); + } + .w-32 { + width: calc(var(--spacing) * 32); + } + .w-48 { + width: calc(var(--spacing) * 48); + } + .w-64 { + width: calc(var(--spacing) * 64); + } + .w-72 { + width: calc(var(--spacing) * 72); + } + .w-\[38px\] { + width: 38px; + } + .w-\[46\%\] { + width: 46%; + } + .w-\[100px\] { + width: 100px; + } + .w-\[280px\] { + width: 280px; + } + .w-\[400px\] { + width: 400px; + } + .w-auto { + width: auto; + } + .w-fit { + width: fit-content; + } + .w-full { + width: 100%; + } + .w-max { + width: max-content; + } + .w-px { + width: 1px; + } + .max-w-\(--skeleton-width\) { + max-width: var(--skeleton-width); + } + .max-w-2xl { + max-width: var(--container-2xl); + } + .max-w-3xl { + max-width: var(--container-3xl); + } + .max-w-4xl { + max-width: var(--container-4xl); + } + .max-w-5xl { + max-width: var(--container-5xl); + } + .max-w-6xl { + max-width: var(--container-6xl); + } + .max-w-7xl { + max-width: var(--container-7xl); + } + .max-w-\[18rem\] { + max-width: 18rem; + } + .max-w-\[40rem\] { + max-width: 40rem; + } + .max-w-\[42rem\] { + max-width: 42rem; + } + .max-w-\[52rem\] { + max-width: 52rem; + } + .max-w-\[58rem\] { + max-width: 58rem; + } + .max-w-\[80\%\] { + max-width: 80%; + } + .max-w-\[calc\(100\%-2rem\)\] { + max-width: calc(100% - 2rem); + } + .max-w-lg { + max-width: var(--container-lg); + } + .max-w-max { + max-width: max-content; + } + .max-w-md { + max-width: var(--container-md); + } + .max-w-none { + max-width: none; + } + .max-w-sm { + max-width: var(--container-sm); + } + .max-w-xl { + max-width: var(--container-xl); + } + .max-w-xs { + max-width: var(--container-xs); + } + .min-w-\(--cell-size\) { + min-width: var(--cell-size); + } + .min-w-0 { + min-width: calc(var(--spacing) * 0); + } + .min-w-5 { + min-width: calc(var(--spacing) * 5); + } + .min-w-8 { + min-width: calc(var(--spacing) * 8); + } + .min-w-9 { + min-width: calc(var(--spacing) * 9); + } + .min-w-10 { + min-width: calc(var(--spacing) * 10); + } + .min-w-\[8rem\] { + min-width: 8rem; + } + .min-w-\[12rem\] { + min-width: 12rem; + } + .min-w-\[200px\] { + min-width: 200px; + } + .min-w-\[var\(--radix-select-trigger-width\)\] { + min-width: var(--radix-select-trigger-width); + } + .min-w-fit { + min-width: fit-content; + } + .min-w-full { + min-width: 100%; + } + .flex-1 { + flex: 1; + } + .flex-shrink-0, + .shrink-0 { + flex-shrink: 0; + } + .grow { + flex-grow: 1; + } + .grow-0 { + flex-grow: 0; + } + .basis-full { + flex-basis: 100%; + } + .caption-bottom { + caption-side: bottom; + } + .border-collapse { + border-collapse: collapse; + } + .origin-\(--radix-context-menu-content-transform-origin\) { + transform-origin: var(--radix-context-menu-content-transform-origin); + } + .origin-\(--radix-dropdown-menu-content-transform-origin\) { + transform-origin: var(--radix-dropdown-menu-content-transform-origin); + } + .origin-\(--radix-hover-card-content-transform-origin\) { + transform-origin: var(--radix-hover-card-content-transform-origin); + } + .origin-\(--radix-menubar-content-transform-origin\) { + transform-origin: var(--radix-menubar-content-transform-origin); + } + .origin-\(--radix-popover-content-transform-origin\) { + transform-origin: var(--radix-popover-content-transform-origin); + } + .origin-\(--radix-select-content-transform-origin\) { + transform-origin: var(--radix-select-content-transform-origin); + } + .origin-\(--radix-tooltip-content-transform-origin\) { + transform-origin: var(--radix-tooltip-content-transform-origin); + } + .-translate-x-1\/2 { + --tw-translate-x: -50%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .-translate-x-px { + --tw-translate-x: -1px; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-x-0 { + --tw-translate-x: calc(var(--spacing) * 0); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-x-0\.5 { + --tw-translate-x: calc(var(--spacing) * 0.5); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-x-5 { + --tw-translate-x: calc(var(--spacing) * 5); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-x-\[-50\%\] { + --tw-translate-x: -50%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-x-full { + --tw-translate-x: 100%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-x-px { + --tw-translate-x: 1px; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .-translate-y-1\/2 { + --tw-translate-y: -50%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-y-0\.5 { + --tw-translate-y: calc(var(--spacing) * 0.5); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-y-\[-50\%\] { + --tw-translate-y: -50%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .translate-y-\[calc\(-50\%_-_2px\)\] { + --tw-translate-y: calc(-50% - 2px); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .rotate-45 { + rotate: 45deg; + } + .rotate-90 { + rotate: 90deg; + } + .rotate-180 { + rotate: 180deg; + } + .transform { + transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) + var(--tw-skew-x,) var(--tw-skew-y,); + } + .animate-caret-blink { + animation: 1.25s ease-out infinite caret-blink; + } + .animate-in { + animation: enter var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .animate-pulse { + animation: var(--animate-pulse); + } + .animate-spin { + animation: var(--animate-spin); + } + .cursor-col-resize { + cursor: col-resize; + } + .cursor-default { + cursor: default; + } + .cursor-not-allowed { + cursor: not-allowed; + } + .cursor-pointer { + cursor: pointer; + } + .cursor-text { + cursor: text; + } + .touch-none { + touch-action: none; + } + .resize-none { + resize: none; + } + .scroll-my-1 { + scroll-margin-block: calc(var(--spacing) * 1); + } + .scroll-py-1 { + scroll-padding-block: calc(var(--spacing) * 1); + } + .list-inside { + list-style-position: inside; + } + .list-disc { + list-style-type: disc; + } + .list-none { + list-style-type: none; + } + .auto-rows-min { + grid-auto-rows: min-content; + } + .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-\[0_1fr\] { + grid-template-columns: 0 1fr; + } + .grid-cols-\[1\.15fr_0\.9fr_0\.9fr_1fr_1fr\] { + grid-template-columns: 1.15fr 0.9fr 0.9fr 1fr 1fr; + } + .grid-rows-\[auto_auto\] { + grid-template-rows: auto auto; + } + .flex-col { + flex-direction: column; + } + .flex-col-reverse { + flex-direction: column-reverse; + } + .flex-row { + flex-direction: row; + } + .flex-wrap { + flex-wrap: wrap; + } + .items-baseline { + align-items: baseline; + } + .items-center { + align-items: center; + } + .items-end { + align-items: flex-end; + } + .items-start { + align-items: flex-start; + } + .items-stretch { + align-items: stretch; + } + .justify-between { + justify-content: space-between; + } + .justify-center { + justify-content: center; + } + .justify-end { + justify-content: flex-end; + } + .justify-start { + justify-content: flex-start; + } + .justify-items-start { + justify-items: start; + } + .gap-0 { + gap: calc(var(--spacing) * 0); + } + .gap-0\.5 { + gap: calc(var(--spacing) * 0.5); + } + .gap-1 { + gap: calc(var(--spacing) * 1); + } + .gap-1\.5 { + gap: calc(var(--spacing) * 1.5); + } + .gap-2 { + gap: calc(var(--spacing) * 2); + } + .gap-2\.5 { + gap: calc(var(--spacing) * 2.5); + } + .gap-3 { + gap: calc(var(--spacing) * 3); + } + .gap-4 { + gap: calc(var(--spacing) * 4); + } + .gap-5 { + gap: calc(var(--spacing) * 5); + } + .gap-6 { + gap: calc(var(--spacing) * 6); + } + .gap-7 { + gap: calc(var(--spacing) * 7); + } + .gap-8 { + gap: calc(var(--spacing) * 8); + } + .gap-10 { + gap: calc(var(--spacing) * 10); + } + .gap-12 { + gap: calc(var(--spacing) * 12); + } + .gap-14 { + gap: calc(var(--spacing) * 14); + } + .gap-16 { + gap: calc(var(--spacing) * 16); + } + :where(.space-y-1 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 1) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-y-2 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 2) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-y-3 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 3) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-y-4 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 4) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-y-5 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 5) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-y-6 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 6) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-y-8 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 8) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-y-12 > :not(:last-child)) { + --tw-space-y-reverse: 0; + margin-block-start: calc( + calc(var(--spacing) * 12) * var(--tw-space-y-reverse) + ); + margin-block-end: calc( + calc(var(--spacing) * 12) * calc(1 - var(--tw-space-y-reverse)) + ); + } + :where(.space-x-2 > :not(:last-child)) { + --tw-space-x-reverse: 0; + margin-inline-start: calc( + calc(var(--spacing) * 2) * var(--tw-space-x-reverse) + ); + margin-inline-end: calc( + calc(var(--spacing) * 2) * calc(1 - var(--tw-space-x-reverse)) + ); + } + .gap-y-0\.5 { + row-gap: calc(var(--spacing) * 0.5); + } + :where(.divide-y > :not(:last-child)) { + --tw-divide-y-reverse: 0; + border-bottom-style: var(--tw-border-style); + border-top-style: var(--tw-border-style); + border-top-width: calc(1px * var(--tw-divide-y-reverse)); + border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + } + :where(.divide-white\/8 > :not(:last-child)) { + border-color: #ffffff14; + } + @supports (color: color-mix(in lab, red, red)) { + :where(.divide-white\/8 > :not(:last-child)) { + border-color: color-mix( + in oklab, + var(--color-white) 8%, + transparent + ); + } + } + :where(.divide-white\/10 > :not(:last-child)) { + border-color: #ffffff1a; + } + @supports (color: color-mix(in lab, red, red)) { + :where(.divide-white\/10 > :not(:last-child)) { + border-color: color-mix( + in oklab, + var(--color-white) 10%, + transparent + ); + } + } + .self-start { + align-self: flex-start; + } + .self-stretch { + align-self: stretch; + } + .justify-self-end { + justify-self: flex-end; + } + .truncate { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + .overflow-auto { + overflow: auto; + } + .overflow-hidden { + overflow: hidden; + } + .overflow-x-auto { + overflow-x: auto; + } + .overflow-x-hidden { + overflow-x: hidden; + } + .overflow-y-auto { + overflow-y: auto; + } + .rounded { + border-radius: 0.25rem; + } + .rounded-2xl { + border-radius: var(--radius-2xl); + } + .rounded-\[1\.1rem\] { + border-radius: 1.1rem; + } + .rounded-\[1\.2rem\] { + border-radius: 1.2rem; + } + .rounded-\[1\.3rem\] { + border-radius: 1.3rem; + } + .rounded-\[1\.4rem\] { + border-radius: 1.4rem; + } + .rounded-\[1\.5rem\] { + border-radius: 1.5rem; + } + .rounded-\[1\.6rem\] { + border-radius: 1.6rem; + } + .rounded-\[1\.7rem\] { + border-radius: 1.7rem; + } + .rounded-\[1\.8rem\] { + border-radius: 1.8rem; + } + .rounded-\[1\.9rem\] { + border-radius: 1.9rem; + } + .rounded-\[1\.25rem\] { + border-radius: 1.25rem; + } + .rounded-\[1\.35rem\] { + border-radius: 1.35rem; + } + .rounded-\[1\.45rem\] { + border-radius: 1.45rem; + } + .rounded-\[1\.65rem\] { + border-radius: 1.65rem; + } + .rounded-\[1rem\] { + border-radius: 1rem; + } + .rounded-\[2\.2rem\] { + border-radius: 2.2rem; + } + .rounded-\[2\.3rem\] { + border-radius: 2.3rem; + } + .rounded-\[2px\] { + border-radius: 2px; + } + .rounded-\[2rem\] { + border-radius: 2rem; + } + .rounded-\[4px\] { + border-radius: 4px; + } + .rounded-\[10px\] { + border-radius: 10px; + } + .rounded-\[20px\] { + border-radius: 20px; + } + .rounded-\[calc\(var\(--radius\)-5px\)\] { + border-radius: calc(var(--radius) - 5px); + } + .rounded-\[inherit\] { + border-radius: inherit; + } + .rounded-full { + border-radius: 3.40282e38px; + } + .rounded-lg { + border-radius: var(--radius); + } + .rounded-md { + border-radius: calc(var(--radius) - 3px); + } + .rounded-none { + border-radius: 0; + } + .rounded-sm { + border-radius: calc(var(--radius) - 6px); + } + .rounded-xl { + border-radius: calc(var(--radius) + 8px); + } + .rounded-xs { + border-radius: var(--radius-xs); + } + .rounded-l-md { + border-top-left-radius: calc(var(--radius) - 3px); + border-bottom-left-radius: calc(var(--radius) - 3px); + } + .rounded-tl-sm { + border-top-left-radius: calc(var(--radius) - 6px); + } + .rounded-r-md { + border-top-right-radius: calc(var(--radius) - 3px); + border-bottom-right-radius: calc(var(--radius) - 3px); + } + .border { + border-style: var(--tw-border-style); + border-width: 1px; + } + .border-0 { + border-style: var(--tw-border-style); + border-width: 0; + } + .border-2 { + border-style: var(--tw-border-style); + border-width: 2px; + } + .border-\[1\.5px\] { + border-style: var(--tw-border-style); + border-width: 1.5px; + } + .border-y { + border-block-style: var(--tw-border-style); + border-block-width: 1px; + } + .border-t { + border-top-style: var(--tw-border-style); + border-top-width: 1px; + } + .border-r { + border-right-style: var(--tw-border-style); + border-right-width: 1px; + } + .border-r-0 { + border-right-style: var(--tw-border-style); + border-right-width: 0; + } + .border-b { + border-bottom-style: var(--tw-border-style); + border-bottom-width: 1px; + } + .border-b-2 { + border-bottom-style: var(--tw-border-style); + border-bottom-width: 2px; + } + .border-l { + border-left-style: var(--tw-border-style); + border-left-width: 1px; + } + .border-dashed { + --tw-border-style: dashed; + border-style: dashed; + } + .border-\(--color-border\) { + border-color: var(--color-border); + } + .border-\[\#0A66C2\]\/30 { + border-color: #0a66c24d; + } + .border-\[\#1877F2\]\/30 { + border-color: #1877f24d; + } + .border-\[rgba\(0\,0\,0\,0\.08\)\] { + border-color: #00000014; + } + .border-\[var\(--accent-strong\)\], + .border-\[var\(--accent-strong\)\]\/18 { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-strong\)\]\/18 { + border-color: color-mix( + in oklab, + var(--accent-strong) 18%, + transparent + ); + } + } + .border-\[var\(--accent-strong\)\]\/20 { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-strong\)\]\/20 { + border-color: color-mix( + in oklab, + var(--accent-strong) 20%, + transparent + ); + } + } + .border-\[var\(--accent-strong\)\]\/30 { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-strong\)\]\/30 { + border-color: color-mix( + in oklab, + var(--accent-strong) 30%, + transparent + ); + } + } + .border-\[var\(--accent-strong\)\]\/35 { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-strong\)\]\/35 { + border-color: color-mix( + in oklab, + var(--accent-strong) 35%, + transparent + ); + } + } + .border-\[var\(--accent-strong\)\]\/45 { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-strong\)\]\/45 { + border-color: color-mix( + in oklab, + var(--accent-strong) 45%, + transparent + ); + } + } + .border-\[var\(--accent-strong\)\]\/50 { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-strong\)\]\/50 { + border-color: color-mix( + in oklab, + var(--accent-strong) 50%, + transparent + ); + } + } + .border-\[var\(--accent-warm\)\]\/20 { + border-color: var(--accent-warm); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-warm\)\]\/20 { + border-color: color-mix( + in oklab, + var(--accent-warm) 20%, + transparent + ); + } + } + .border-\[var\(--accent-warm\)\]\/30 { + border-color: var(--accent-warm); + } + @supports (color: color-mix(in lab, red, red)) { + .border-\[var\(--accent-warm\)\]\/30 { + border-color: color-mix( + in oklab, + var(--accent-warm) 30%, + transparent + ); + } + } + .border-accent\/20 { + border-color: var(--accent); + } + @supports (color: color-mix(in lab, red, red)) { + .border-accent\/20 { + border-color: color-mix(in oklab, var(--accent) 20%, transparent); + } + } + .border-amber-400\/30 { + border-color: #fcbb004d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-amber-400\/30 { + border-color: color-mix( + in oklab, + var(--color-amber-400) 30%, + transparent + ); + } + } + .border-black\/10 { + border-color: #0000001a; + } + @supports (color: color-mix(in lab, red, red)) { + .border-black\/10 { + border-color: color-mix( + in oklab, + var(--color-black) 10%, + transparent + ); + } + } + .border-blue-200 { + border-color: var(--color-blue-200); + } + .border-blue-400\/30 { + border-color: #54a2ff4d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-blue-400\/30 { + border-color: color-mix( + in oklab, + var(--color-blue-400) 30%, + transparent + ); + } + } + .border-blue-500\/20 { + border-color: #3080ff33; + } + @supports (color: color-mix(in lab, red, red)) { + .border-blue-500\/20 { + border-color: color-mix( + in oklab, + var(--color-blue-500) 20%, + transparent + ); + } + } + .border-blue-500\/30 { + border-color: #3080ff4d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-blue-500\/30 { + border-color: color-mix( + in oklab, + var(--color-blue-500) 30%, + transparent + ); + } + } + .border-border, + .border-border\/50 { + border-color: var(--border); + } + @supports (color: color-mix(in lab, red, red)) { + .border-border\/50 { + border-color: color-mix(in oklab, var(--border) 50%, transparent); + } + } + .border-cyan-400\/30 { + border-color: #00d2ef4d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-cyan-400\/30 { + border-color: color-mix( + in oklab, + var(--color-cyan-400) 30%, + transparent + ); + } + } + .border-destructive\/20 { + border-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .border-destructive\/20 { + border-color: color-mix( + in oklab, + var(--destructive) 20%, + transparent + ); + } + } + .border-emerald-400\/20 { + border-color: #00d29433; + } + @supports (color: color-mix(in lab, red, red)) { + .border-emerald-400\/20 { + border-color: color-mix( + in oklab, + var(--color-emerald-400) 20%, + transparent + ); + } + } + .border-emerald-400\/30 { + border-color: #00d2944d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-emerald-400\/30 { + border-color: color-mix( + in oklab, + var(--color-emerald-400) 30%, + transparent + ); + } + } + .border-foreground\/10 { + border-color: var(--foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .border-foreground\/10 { + border-color: color-mix( + in oklab, + var(--foreground) 10%, + transparent + ); + } + } + .border-gray-200 { + border-color: var(--color-gray-200); + } + .border-gray-500\/30 { + border-color: #6a72824d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-gray-500\/30 { + border-color: color-mix( + in oklab, + var(--color-gray-500) 30%, + transparent + ); + } + } + .border-green-200 { + border-color: var(--color-green-200); + } + .border-green-500 { + border-color: var(--color-green-500); + } + .border-green-500\/20 { + border-color: #00c75833; + } + @supports (color: color-mix(in lab, red, red)) { + .border-green-500\/20 { + border-color: color-mix( + in oklab, + var(--color-green-500) 20%, + transparent + ); + } + } + .border-green-500\/30 { + border-color: #00c7584d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-green-500\/30 { + border-color: color-mix( + in oklab, + var(--color-green-500) 30%, + transparent + ); + } + } + .border-input { + border-color: var(--input); + } + .border-muted-foreground\/30 { + border-color: var(--muted-foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .border-muted-foreground\/30 { + border-color: color-mix( + in oklab, + var(--muted-foreground) 30%, + transparent + ); + } + } + .border-primary { + border-color: var(--primary); + } + .border-red-200 { + border-color: var(--color-red-200); + } + .border-red-500 { + border-color: var(--color-red-500); + } + .border-red-500\/20 { + border-color: #fb2c3633; + } + @supports (color: color-mix(in lab, red, red)) { + .border-red-500\/20 { + border-color: color-mix( + in oklab, + var(--color-red-500) 20%, + transparent + ); + } + } + .border-rose-400\/20 { + border-color: #ff667f33; + } + @supports (color: color-mix(in lab, red, red)) { + .border-rose-400\/20 { + border-color: color-mix( + in oklab, + var(--color-rose-400) 20%, + transparent + ); + } + } + .border-rose-400\/30 { + border-color: #ff667f4d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-rose-400\/30 { + border-color: color-mix( + in oklab, + var(--color-rose-400) 30%, + transparent + ); + } + } + .border-sidebar-border { + border-color: var(--sidebar-border); + } + .border-slate-200 { + border-color: var(--color-slate-200); + } + .border-slate-300 { + border-color: var(--color-slate-300); + } + .border-transparent { + border-color: #0000; + } + .border-white\/5 { + border-color: #ffffff0d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/5 { + border-color: color-mix( + in oklab, + var(--color-white) 5%, + transparent + ); + } + } + .border-white\/6 { + border-color: #ffffff0f; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/6 { + border-color: color-mix( + in oklab, + var(--color-white) 6%, + transparent + ); + } + } + .border-white\/8 { + border-color: #ffffff14; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/8 { + border-color: color-mix( + in oklab, + var(--color-white) 8%, + transparent + ); + } + } + .border-white\/10 { + border-color: #ffffff1a; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/10 { + border-color: color-mix( + in oklab, + var(--color-white) 10%, + transparent + ); + } + } + .border-white\/12 { + border-color: #ffffff1f; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/12 { + border-color: color-mix( + in oklab, + var(--color-white) 12%, + transparent + ); + } + } + .border-white\/16 { + border-color: #ffffff29; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/16 { + border-color: color-mix( + in oklab, + var(--color-white) 16%, + transparent + ); + } + } + .border-white\/18 { + border-color: #ffffff2e; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/18 { + border-color: color-mix( + in oklab, + var(--color-white) 18%, + transparent + ); + } + } + .border-white\/20 { + border-color: #fff3; + } + @supports (color: color-mix(in lab, red, red)) { + .border-white\/20 { + border-color: color-mix( + in oklab, + var(--color-white) 20%, + transparent + ); + } + } + .border-yellow-200 { + border-color: var(--color-yellow-200); + } + .border-yellow-500\/20 { + border-color: #edb20033; + } + @supports (color: color-mix(in lab, red, red)) { + .border-yellow-500\/20 { + border-color: color-mix( + in oklab, + var(--color-yellow-500) 20%, + transparent + ); + } + } + .border-yellow-500\/30 { + border-color: #edb2004d; + } + @supports (color: color-mix(in lab, red, red)) { + .border-yellow-500\/30 { + border-color: color-mix( + in oklab, + var(--color-yellow-500) 30%, + transparent + ); + } + } + .border-t-transparent { + border-top-color: #0000; + } + .border-l-transparent { + border-left-color: #0000; + } + .bg-\(--color-bg\) { + background-color: var(--color-bg); + } + .bg-\[\#0A66C2\]\/20 { + background-color: #0a66c233; + } + .bg-\[\#1a1a19\] { + background-color: #1a1a19; + } + .bg-\[\#1877F2\]\/20 { + background-color: #1877f233; + } + .bg-\[\#f4f1ea\] { + background-color: #f4f1ea; + } + .bg-\[\#f8f8f7\] { + background-color: #f8f8f7; + } + .bg-\[rgba\(7\,18\,22\,0\.8\)\] { + background-color: #071216cc; + } + .bg-\[rgba\(7\,18\,22\,0\.78\)\] { + background-color: #071216c7; + } + .bg-\[rgba\(7\,18\,22\,0\.84\)\] { + background-color: #071216d6; + } + .bg-\[rgba\(7\,18\,22\,0\.95\)\] { + background-color: #071216f2; + } + .bg-\[rgba\(7\,18\,24\,0\.96\)\] { + background-color: #071218f5; + } + .bg-\[rgba\(7\,18\,24\,0\.98\)\] { + background-color: #071218fa; + } + .bg-\[rgba\(8\,20\,25\,0\.76\)\] { + background-color: #081419c2; + } + .bg-\[rgba\(8\,20\,25\,0\.78\)\] { + background-color: #081419c7; + } + .bg-\[rgba\(8\,20\,25\,0\.95\)\] { + background-color: #081419f2; + } + .bg-\[rgba\(8\,21\,30\,0\.88\)\] { + background-color: #08151ee0; + } + .bg-\[rgba\(8\,23\,28\,0\.78\)\] { + background-color: #08171cc7; + } + .bg-\[rgba\(9\,20\,24\,0\.82\)\] { + background-color: #091418d1; + } + .bg-\[rgba\(9\,20\,24\,0\.95\)\] { + background-color: #091418f2; + } + .bg-\[rgba\(9\,20\,24\,0\.98\)\] { + background-color: #091418fa; + } + .bg-\[rgba\(11\,34\,42\,0\.95\)\] { + background-color: #0b222af2; + } + .bg-\[rgba\(46\,182\,194\,0\.12\)\] { + background-color: #2eb6c21f; + } + .bg-\[rgba\(83\,213\,205\,0\.12\)\] { + background-color: #53d5cd1f; + } + .bg-\[rgba\(255\,255\,255\,0\.04\)\] { + background-color: #ffffff0a; + } + .bg-\[var\(--accent-strong\)\], + .bg-\[var\(--accent-strong\)\]\/5 { + background-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-strong\)\]\/5 { + background-color: color-mix( + in oklab, + var(--accent-strong) 5%, + transparent + ); + } + } + .bg-\[var\(--accent-strong\)\]\/8 { + background-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-strong\)\]\/8 { + background-color: color-mix( + in oklab, + var(--accent-strong) 8%, + transparent + ); + } + } + .bg-\[var\(--accent-strong\)\]\/10 { + background-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-strong\)\]\/10 { + background-color: color-mix( + in oklab, + var(--accent-strong) 10%, + transparent + ); + } + } + .bg-\[var\(--accent-strong\)\]\/12 { + background-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-strong\)\]\/12 { + background-color: color-mix( + in oklab, + var(--accent-strong) 12%, + transparent + ); + } + } + .bg-\[var\(--accent-strong\)\]\/20 { + background-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-strong\)\]\/20 { + background-color: color-mix( + in oklab, + var(--accent-strong) 20%, + transparent + ); + } + } + .bg-\[var\(--accent-warm\)\], + .bg-\[var\(--accent-warm\)\]\/5 { + background-color: var(--accent-warm); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-warm\)\]\/5 { + background-color: color-mix( + in oklab, + var(--accent-warm) 5%, + transparent + ); + } + } + .bg-\[var\(--accent-warm\)\]\/10 { + background-color: var(--accent-warm); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-warm\)\]\/10 { + background-color: color-mix( + in oklab, + var(--accent-warm) 10%, + transparent + ); + } + } + .bg-\[var\(--accent-warm\)\]\/20 { + background-color: var(--accent-warm); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-\[var\(--accent-warm\)\]\/20 { + background-color: color-mix( + in oklab, + var(--accent-warm) 20%, + transparent + ); + } + } + .bg-\[var\(--surface-elevated\)\] { + background-color: var(--surface-elevated); + } + .bg-accent, + .bg-accent\/10 { + background-color: var(--accent); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-accent\/10 { + background-color: color-mix( + in oklab, + var(--accent) 10%, + transparent + ); + } + } + .bg-amber-400\/10 { + background-color: #fcbb001a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-amber-400\/10 { + background-color: color-mix( + in oklab, + var(--color-amber-400) 10%, + transparent + ); + } + } + .bg-background, + .bg-background\/50 { + background-color: var(--background); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-background\/50 { + background-color: color-mix( + in oklab, + var(--background) 50%, + transparent + ); + } + } + .bg-background\/95 { + background-color: var(--background); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-background\/95 { + background-color: color-mix( + in oklab, + var(--background) 95%, + transparent + ); + } + } + .bg-black\/20 { + background-color: #0003; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-black\/20 { + background-color: color-mix( + in oklab, + var(--color-black) 20%, + transparent + ); + } + } + .bg-black\/40 { + background-color: #0006; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-black\/40 { + background-color: color-mix( + in oklab, + var(--color-black) 40%, + transparent + ); + } + } + .bg-black\/50 { + background-color: #00000080; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-black\/50 { + background-color: color-mix( + in oklab, + var(--color-black) 50%, + transparent + ); + } + } + .bg-blue-100 { + background-color: var(--color-blue-100); + } + .bg-blue-400\/10 { + background-color: #54a2ff1a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-blue-400\/10 { + background-color: color-mix( + in oklab, + var(--color-blue-400) 10%, + transparent + ); + } + } + .bg-blue-500\/10 { + background-color: #3080ff1a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-blue-500\/10 { + background-color: color-mix( + in oklab, + var(--color-blue-500) 10%, + transparent + ); + } + } + .bg-blue-500\/20 { + background-color: #3080ff33; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-blue-500\/20 { + background-color: color-mix( + in oklab, + var(--color-blue-500) 20%, + transparent + ); + } + } + .bg-blue-600 { + background-color: var(--color-blue-600); + } + .bg-border { + background-color: var(--border); + } + .bg-card { + background-color: var(--card); + } + .bg-cyan-400\/10 { + background-color: #00d2ef1a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-cyan-400\/10 { + background-color: color-mix( + in oklab, + var(--color-cyan-400) 10%, + transparent + ); + } + } + .bg-cyan-500\/20 { + background-color: #00b7d733; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-cyan-500\/20 { + background-color: color-mix( + in oklab, + var(--color-cyan-500) 20%, + transparent + ); + } + } + .bg-destructive, + .bg-destructive\/10 { + background-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-destructive\/10 { + background-color: color-mix( + in oklab, + var(--destructive) 10%, + transparent + ); + } + } + .bg-emerald-400\/10 { + background-color: #00d2941a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-emerald-400\/10 { + background-color: color-mix( + in oklab, + var(--color-emerald-400) 10%, + transparent + ); + } + } + .bg-foreground, + .bg-foreground\/5 { + background-color: var(--foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-foreground\/5 { + background-color: color-mix( + in oklab, + var(--foreground) 5%, + transparent + ); + } + } + .bg-gray-100 { + background-color: var(--color-gray-100); + } + .bg-gray-500\/10 { + background-color: #6a72821a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-gray-500\/10 { + background-color: color-mix( + in oklab, + var(--color-gray-500) 10%, + transparent + ); + } + } + .bg-gray-500\/20 { + background-color: #6a728233; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-gray-500\/20 { + background-color: color-mix( + in oklab, + var(--color-gray-500) 20%, + transparent + ); + } + } + .bg-green-100 { + background-color: var(--color-green-100); + } + .bg-green-500\/10 { + background-color: #00c7581a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-green-500\/10 { + background-color: color-mix( + in oklab, + var(--color-green-500) 10%, + transparent + ); + } + } + .bg-green-500\/20 { + background-color: #00c75833; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-green-500\/20 { + background-color: color-mix( + in oklab, + var(--color-green-500) 20%, + transparent + ); + } + } + .bg-input { + background-color: var(--input); + } + .bg-muted, + .bg-muted\/50 { + background-color: var(--muted); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-muted\/50 { + background-color: color-mix( + in oklab, + var(--muted) 50%, + transparent + ); + } + } + .bg-orange-100 { + background-color: var(--color-orange-100); + } + .bg-popover { + background-color: var(--popover); + } + .bg-primary, + .bg-primary\/10 { + background-color: var(--primary); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-primary\/10 { + background-color: color-mix( + in oklab, + var(--primary) 10%, + transparent + ); + } + } + .bg-primary\/20 { + background-color: var(--primary); + } + @supports (color: color-mix(in lab, red, red)) { + .bg-primary\/20 { + background-color: color-mix( + in oklab, + var(--primary) 20%, + transparent + ); + } + } + .bg-purple-100 { + background-color: var(--color-purple-100); + } + .bg-purple-500\/10 { + background-color: #ac4bff1a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-purple-500\/10 { + background-color: color-mix( + in oklab, + var(--color-purple-500) 10%, + transparent + ); + } + } + .bg-purple-500\/20 { + background-color: #ac4bff33; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-purple-500\/20 { + background-color: color-mix( + in oklab, + var(--color-purple-500) 20%, + transparent + ); + } + } + .bg-red-50 { + background-color: var(--color-red-50); + } + .bg-red-100 { + background-color: var(--color-red-100); + } + .bg-red-500\/10 { + background-color: #fb2c361a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-red-500\/10 { + background-color: color-mix( + in oklab, + var(--color-red-500) 10%, + transparent + ); + } + } + .bg-red-600\/10 { + background-color: #e400141a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-red-600\/10 { + background-color: color-mix( + in oklab, + var(--color-red-600) 10%, + transparent + ); + } + } + .bg-rose-400\/10 { + background-color: #ff667f1a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-rose-400\/10 { + background-color: color-mix( + in oklab, + var(--color-rose-400) 10%, + transparent + ); + } + } + .bg-secondary { + background-color: var(--secondary); + } + .bg-sidebar { + background-color: var(--sidebar); + } + .bg-sidebar-border { + background-color: var(--sidebar-border); + } + .bg-slate-50 { + background-color: var(--color-slate-50); + } + .bg-slate-100 { + background-color: var(--color-slate-100); + } + .bg-slate-900 { + background-color: var(--color-slate-900); + } + .bg-slate-950 { + background-color: var(--color-slate-950); + } + .bg-transparent { + background-color: #0000; + } + .bg-white { + background-color: var(--color-white); + } + .bg-white\/3 { + background-color: #ffffff08; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/3 { + background-color: color-mix( + in oklab, + var(--color-white) 3%, + transparent + ); + } + } + .bg-white\/4 { + background-color: #ffffff0a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/4 { + background-color: color-mix( + in oklab, + var(--color-white) 4%, + transparent + ); + } + } + .bg-white\/5 { + background-color: #ffffff0d; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/5 { + background-color: color-mix( + in oklab, + var(--color-white) 5%, + transparent + ); + } + } + .bg-white\/6 { + background-color: #ffffff0f; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/6 { + background-color: color-mix( + in oklab, + var(--color-white) 6%, + transparent + ); + } + } + .bg-white\/7 { + background-color: #ffffff12; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/7 { + background-color: color-mix( + in oklab, + var(--color-white) 7%, + transparent + ); + } + } + .bg-white\/8 { + background-color: #ffffff14; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/8 { + background-color: color-mix( + in oklab, + var(--color-white) 8%, + transparent + ); + } + } + .bg-white\/10 { + background-color: #ffffff1a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/10 { + background-color: color-mix( + in oklab, + var(--color-white) 10%, + transparent + ); + } + } + .bg-white\/20 { + background-color: #fff3; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/20 { + background-color: color-mix( + in oklab, + var(--color-white) 20%, + transparent + ); + } + } + .bg-white\/80 { + background-color: #fffc; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/80 { + background-color: color-mix( + in oklab, + var(--color-white) 80%, + transparent + ); + } + } + .bg-white\/\[0\.02\] { + background-color: #ffffff05; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/\[0\.02\] { + background-color: color-mix( + in oklab, + var(--color-white) 2%, + transparent + ); + } + } + .bg-white\/\[0\.03\] { + background-color: #ffffff08; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-white\/\[0\.03\] { + background-color: color-mix( + in oklab, + var(--color-white) 3%, + transparent + ); + } + } + .bg-yellow-500\/10 { + background-color: #edb2001a; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-yellow-500\/10 { + background-color: color-mix( + in oklab, + var(--color-yellow-500) 10%, + transparent + ); + } + } + .bg-yellow-500\/20 { + background-color: #edb20033; + } + @supports (color: color-mix(in lab, red, red)) { + .bg-yellow-500\/20 { + background-color: color-mix( + in oklab, + var(--color-yellow-500) 20%, + transparent + ); + } + } + .bg-gradient-to-b { + --tw-gradient-position: to bottom in oklab; + background-image: linear-gradient(var(--tw-gradient-stops)); + } + .bg-gradient-to-br { + --tw-gradient-position: to bottom right in oklab; + background-image: linear-gradient(var(--tw-gradient-stops)); + } + .bg-gradient-to-r { + --tw-gradient-position: to right in oklab; + background-image: linear-gradient(var(--tw-gradient-stops)); + } + .bg-\[linear-gradient\(90deg\,rgba\(4\,14\,18\,0\)_0\%\,rgba\(4\,14\,18\,0\.01\)_22\%\,rgba\(4\,14\,18\,0\.12\)_100\%\)\] { + background-image: linear-gradient( + 90deg, + #040e1200, + #040e1203 22%, + #040e121f + ); + } + .bg-\[linear-gradient\(90deg\,rgba\(4\,14\,18\,0\.58\)_0\%\,rgba\(4\,14\,18\,0\.24\)_34\%\,rgba\(4\,14\,18\,0\.08\)_58\%\,rgba\(4\,14\,18\,0\.34\)_100\%\)\] { + background-image: linear-gradient( + 90deg, + #040e1294, + #040e123d 34%, + #040e1214 58%, + #040e1257 + ); + } + .bg-\[linear-gradient\(135deg\,rgba\(7\,18\,22\,0\.94\)\,rgba\(11\,27\,32\,0\.92\)\)\] { + background-image: linear-gradient(135deg, #071216f0, #0b1b20eb); + } + .bg-\[linear-gradient\(135deg\,rgba\(46\,182\,194\,0\.22\)\,rgba\(255\,193\,43\,0\.12\)\)\] { + background-image: linear-gradient(135deg, #2eb6c238, #ffc12b1f); + } + .bg-\[linear-gradient\(135deg\,rgba\(83\,213\,205\,0\.24\)\,rgba\(255\,193\,43\,0\.18\)\)\] { + background-image: linear-gradient(135deg, #53d5cd3d, #ffc12b2e); + } + .bg-\[linear-gradient\(135deg\,rgba\(255\,255\,255\,0\.08\)\,rgba\(255\,255\,255\,0\.03\)\)\] { + background-image: linear-gradient(135deg, #ffffff14, #ffffff08); + } + .bg-\[linear-gradient\(180deg\,rgba\(7\,18\,22\,1\)\,rgba\(10\,27\,31\,1\)\)\] { + background-image: linear-gradient(#071216, #0a1b1f); + } + .bg-\[linear-gradient\(180deg\,rgba\(255\,255\,255\,0\.06\)\,rgba\(255\,255\,255\,0\.03\)\)\] { + background-image: linear-gradient(#ffffff0f, #ffffff08); + } + .bg-\[radial-gradient\(circle\,rgba\(77\,201\,196\,0\.18\)\,transparent_70\%\)\] { + background-image: radial-gradient(circle, #4dc9c42e, #0000 70%); + } + .bg-\[radial-gradient\(circle_at_center\,rgba\(46\,182\,194\,0\.1\)\,transparent_58\%\)\] { + background-image: radial-gradient(circle, #2eb6c21a, #0000 58%); + } + .bg-\[radial-gradient\(circle_at_center\,rgba\(83\,213\,205\,0\.1\)\,transparent_58\%\)\] { + background-image: radial-gradient(circle, #53d5cd1a, #0000 58%); + } + .bg-\[radial-gradient\(circle_at_center\,rgba\(255\,193\,43\,0\.11\)\,transparent_60\%\)\] { + background-image: radial-gradient(circle, #ffc12b1c, #0000 60%); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(46\,182\,194\,0\.12\)\,transparent_34\%\)\,radial-gradient\(circle_at_82\%_12\%\,rgba\(255\,193\,43\,0\.1\)\,transparent_18\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,20\,0\.76\)\,rgba\(4\,14\,20\,0\.95\)\)\] { + background-image: + radial-gradient(circle at 0 0, #2eb6c21f, #0000 34%), + radial-gradient(circle at 82% 12%, #ffc12b1a, #0000 18%), + linear-gradient(#040e14c2, #040e14f2); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(46\,182\,194\,0\.12\)\,transparent_34\%\)\,radial-gradient\(circle_at_82\%_12\%\,rgba\(255\,193\,43\,0\.08\)\,transparent_18\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,20\,0\.76\)\,rgba\(4\,14\,20\,0\.95\)\)\] { + background-image: + radial-gradient(circle at 0 0, #2eb6c21f, #0000 34%), + radial-gradient(circle at 82% 12%, #ffc12b14, #0000 18%), + linear-gradient(#040e14c2, #040e14f2); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(46\,182\,194\,0\.13\)\,transparent_34\%\)\,radial-gradient\(circle_at_82\%_12\%\,rgba\(255\,193\,43\,0\.10\)\,transparent_20\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,20\,0\.78\)\,rgba\(4\,14\,20\,0\.96\)\)\] { + background-image: + radial-gradient(circle at 0 0, #2eb6c221, #0000 34%), + radial-gradient(circle at 82% 12%, #ffc12b1a, #0000 20%), + linear-gradient(#040e14c7, #040e14f5); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(46\,182\,194\,0\.13\)\,transparent_34\%\)\,radial-gradient\(circle_at_82\%_12\%\,rgba\(255\,193\,43\,0\.12\)\,transparent_20\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,20\,0\.78\)\,rgba\(4\,14\,20\,0\.96\)\)\] { + background-image: + radial-gradient(circle at 0 0, #2eb6c221, #0000 34%), + radial-gradient(circle at 82% 12%, #ffc12b1f, #0000 20%), + linear-gradient(#040e14c7, #040e14f5); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(46\,182\,194\,0\.14\)\,transparent_34\%\)\,radial-gradient\(circle_at_82\%_12\%\,rgba\(255\,193\,43\,0\.08\)\,transparent_18\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,20\,0\.76\)\,rgba\(4\,14\,20\,0\.95\)\)\] { + background-image: + radial-gradient(circle at 0 0, #2eb6c224, #0000 34%), + radial-gradient(circle at 82% 12%, #ffc12b14, #0000 18%), + linear-gradient(#040e14c2, #040e14f2); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(46\,182\,194\,0\.14\)\,transparent_36\%\)\,radial-gradient\(circle_at_82\%_18\%\,rgba\(255\,193\,43\,0\.12\)\,transparent_22\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,20\,0\.82\)\,rgba\(4\,14\,20\,0\.97\)\)\] { + background-image: + radial-gradient(circle at 0 0, #2eb6c224, #0000 36%), + radial-gradient(circle at 82% 18%, #ffc12b1f, #0000 22%), + linear-gradient(#040e14d1, #040e14f7); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(77\,201\,196\,0\.08\)\,transparent_30\%\)\,radial-gradient\(circle_at_82\%_14\%\,rgba\(255\,255\,255\,0\.05\)\,transparent_18\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,18\,0\.26\)\,rgba\(4\,14\,18\,0\.72\)\)\] { + background-image: + radial-gradient(circle at 0 0, #4dc9c414, #0000 30%), + radial-gradient(circle at 82% 14%, #ffffff0d, #0000 18%), + linear-gradient(#040e1242, #040e12b8); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(77\,201\,196\,0\.08\)\,transparent_34\%\)\,radial-gradient\(circle_at_80\%_15\%\,rgba\(255\,255\,255\,0\.06\)\,transparent_18\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,18\,0\.52\)\,rgba\(4\,14\,18\,0\.76\)\)\] { + background-image: + radial-gradient(circle at 0 0, #4dc9c414, #0000 34%), + radial-gradient(circle at 80% 15%, #ffffff0f, #0000 18%), + linear-gradient(#040e1285, #040e12c2); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(77\,201\,196\,0\.08\)\,transparent_34\%\)\,radial-gradient\(circle_at_80\%_15\%\,rgba\(255\,255\,255\,0\.06\)\,transparent_18\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,18\,0\.78\)\,rgba\(4\,14\,18\,0\.96\)\)\] { + background-image: + radial-gradient(circle at 0 0, #4dc9c414, #0000 34%), + radial-gradient(circle at 80% 15%, #ffffff0f, #0000 18%), + linear-gradient(#040e12c7, #040e12f5); + } + .bg-\[radial-gradient\(circle_at_top_left\,rgba\(77\,201\,196\,0\.08\)\,transparent_34\%\)\,radial-gradient\(circle_at_82\%_18\%\,rgba\(255\,255\,255\,0\.05\)\,transparent_18\%\)\,linear-gradient\(180deg\,rgba\(4\,14\,18\,0\.76\)\,rgba\(4\,14\,18\,0\.97\)\)\] { + background-image: + radial-gradient(circle at 0 0, #4dc9c414, #0000 34%), + radial-gradient(circle at 82% 18%, #ffffff0d, #0000 18%), + linear-gradient(#040e12c2, #040e12f7); + } + .from-\[var\(--accent-strong\)\] { + --tw-gradient-from: var(--accent-strong); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-background\/95 { + --tw-gradient-from: var(--background); + } + @supports (color: color-mix(in lab, red, red)) { + .from-background\/95 { + --tw-gradient-from: color-mix( + in oklab, + var(--background) 95%, + transparent + ); + } + } + .from-background\/95 { + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-blue-500 { + --tw-gradient-from: var(--color-blue-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-cyan-500 { + --tw-gradient-from: var(--color-cyan-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-green-500 { + --tw-gradient-from: var(--color-green-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-indigo-500 { + --tw-gradient-from: var(--color-indigo-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-orange-500 { + --tw-gradient-from: var(--color-orange-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-purple-500 { + --tw-gradient-from: var(--color-purple-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-slate-50 { + --tw-gradient-from: var(--color-slate-50); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .from-teal-500 { + --tw-gradient-from: var(--color-teal-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-\[var\(--accent-warm\)\] { + --tw-gradient-to: var(--accent-warm); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-background\/80 { + --tw-gradient-to: var(--background); + } + @supports (color: color-mix(in lab, red, red)) { + .to-background\/80 { + --tw-gradient-to: color-mix( + in oklab, + var(--background) 80%, + transparent + ); + } + } + .to-background\/80 { + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-blue-500 { + --tw-gradient-to: var(--color-blue-500); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-blue-600 { + --tw-gradient-to: var(--color-blue-600); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-cyan-600 { + --tw-gradient-to: var(--color-cyan-600); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-green-600 { + --tw-gradient-to: var(--color-green-600); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-indigo-600 { + --tw-gradient-to: var(--color-indigo-600); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-orange-600 { + --tw-gradient-to: var(--color-orange-600); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-purple-600 { + --tw-gradient-to: var(--color-purple-600); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-slate-100 { + --tw-gradient-to: var(--color-slate-100); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .to-teal-600 { + --tw-gradient-to: var(--color-teal-600); + --tw-gradient-stops: var( + --tw-gradient-via-stops, + var(--tw-gradient-position), + var(--tw-gradient-from) var(--tw-gradient-from-position), + var(--tw-gradient-to) var(--tw-gradient-to-position) + ); + } + .bg-cover { + background-size: cover; + } + .bg-center { + background-position: 50%; + } + .fill-current { + fill: currentColor; + } + .fill-foreground { + fill: var(--foreground); + } + .fill-primary { + fill: var(--primary); + } + .object-cover { + object-fit: cover; + } + .object-center { + object-position: center; + } + .p-0 { + padding: calc(var(--spacing) * 0); + } + .p-1 { + padding: calc(var(--spacing) * 1); + } + .p-2 { + padding: calc(var(--spacing) * 2); + } + .p-3 { + padding: calc(var(--spacing) * 3); + } + .p-4 { + padding: calc(var(--spacing) * 4); + } + .p-5 { + padding: calc(var(--spacing) * 5); + } + .p-6 { + padding: calc(var(--spacing) * 6); + } + .p-7 { + padding: calc(var(--spacing) * 7); + } + .p-8 { + padding: calc(var(--spacing) * 8); + } + .p-10 { + padding: calc(var(--spacing) * 10); + } + .p-12 { + padding: calc(var(--spacing) * 12); + } + .p-\[3px\] { + padding: 3px; + } + .p-px { + padding: 1px; + } + .px-\(--cell-size\) { + padding-inline: var(--cell-size); + } + .px-1 { + padding-inline: calc(var(--spacing) * 1); + } + .px-1\.5 { + padding-inline: calc(var(--spacing) * 1.5); + } + .px-2 { + padding-inline: calc(var(--spacing) * 2); + } + .px-2\.5 { + padding-inline: calc(var(--spacing) * 2.5); + } + .px-3 { + padding-inline: calc(var(--spacing) * 3); + } + .px-4 { + padding-inline: calc(var(--spacing) * 4); + } + .px-5 { + padding-inline: calc(var(--spacing) * 5); + } + .px-6 { + padding-inline: calc(var(--spacing) * 6); + } + .px-7 { + padding-inline: calc(var(--spacing) * 7); + } + .py-0\.5 { + padding-block: calc(var(--spacing) * 0.5); + } + .py-1 { + padding-block: calc(var(--spacing) * 1); + } + .py-1\.5 { + padding-block: calc(var(--spacing) * 1.5); + } + .py-2 { + padding-block: calc(var(--spacing) * 2); + } + .py-2\.5 { + padding-block: calc(var(--spacing) * 2.5); + } + .py-3 { + padding-block: calc(var(--spacing) * 3); + } + .py-4 { + padding-block: calc(var(--spacing) * 4); + } + .py-5 { + padding-block: calc(var(--spacing) * 5); + } + .py-6 { + padding-block: calc(var(--spacing) * 6); + } + .py-7 { + padding-block: calc(var(--spacing) * 7); + } + .py-8 { + padding-block: calc(var(--spacing) * 8); + } + .py-12 { + padding-block: calc(var(--spacing) * 12); + } + .py-14 { + padding-block: calc(var(--spacing) * 14); + } + .py-16 { + padding-block: calc(var(--spacing) * 16); + } + .py-20 { + padding-block: calc(var(--spacing) * 20); + } + .py-24 { + padding-block: calc(var(--spacing) * 24); + } + .pt-0 { + padding-top: calc(var(--spacing) * 0); + } + .pt-2 { + padding-top: calc(var(--spacing) * 2); + } + .pt-3 { + padding-top: calc(var(--spacing) * 3); + } + .pt-4 { + padding-top: calc(var(--spacing) * 4); + } + .pt-6 { + padding-top: calc(var(--spacing) * 6); + } + .pt-8 { + padding-top: calc(var(--spacing) * 8); + } + .pt-12 { + padding-top: calc(var(--spacing) * 12); + } + .pt-24 { + padding-top: calc(var(--spacing) * 24); + } + .pr-1 { + padding-right: calc(var(--spacing) * 1); + } + .pr-2 { + padding-right: calc(var(--spacing) * 2); + } + .pr-2\.5 { + padding-right: calc(var(--spacing) * 2.5); + } + .pr-3 { + padding-right: calc(var(--spacing) * 3); + } + .pr-4 { + padding-right: calc(var(--spacing) * 4); + } + .pr-8 { + padding-right: calc(var(--spacing) * 8); + } + .pb-2 { + padding-bottom: calc(var(--spacing) * 2); + } + .pb-3 { + padding-bottom: calc(var(--spacing) * 3); + } + .pb-4 { + padding-bottom: calc(var(--spacing) * 4); + } + .pb-6 { + padding-bottom: calc(var(--spacing) * 6); + } + .pb-8 { + padding-bottom: calc(var(--spacing) * 8); + } + .pb-12 { + padding-bottom: calc(var(--spacing) * 12); + } + .pl-2 { + padding-left: calc(var(--spacing) * 2); + } + .pl-3 { + padding-left: calc(var(--spacing) * 3); + } + .pl-4 { + padding-left: calc(var(--spacing) * 4); + } + .pl-5 { + padding-left: calc(var(--spacing) * 5); + } + .pl-8 { + padding-left: calc(var(--spacing) * 8); + } + .pl-12 { + padding-left: calc(var(--spacing) * 12); + } + .text-center { + text-align: center; + } + .text-left { + text-align: left; + } + .text-right { + text-align: right; + } + .align-middle { + vertical-align: middle; + } + .align-top { + vertical-align: top; + } + .font-mono { + font-family: var(--font-mono); + } + .font-sans { + font-family: + DM Sans, + sans-serif; + } + .text-2xl { + font-size: var(--text-2xl); + line-height: var(--tw-leading, var(--text-2xl--line-height)); + } + .text-3xl { + font-size: var(--text-3xl); + line-height: var(--tw-leading, var(--text-3xl--line-height)); + } + .text-4xl { + font-size: var(--text-4xl); + line-height: var(--tw-leading, var(--text-4xl--line-height)); + } + .text-5xl { + font-size: var(--text-5xl); + line-height: var(--tw-leading, var(--text-5xl--line-height)); + } + .text-base { + font-size: var(--text-base); + line-height: var(--tw-leading, var(--text-base--line-height)); + } + .text-lg { + font-size: var(--text-lg); + line-height: var(--tw-leading, var(--text-lg--line-height)); + } + .text-sm { + font-size: var(--text-sm); + line-height: var(--tw-leading, var(--text-sm--line-height)); + } + .text-sm\/relaxed { + font-size: var(--text-sm); + line-height: var(--leading-relaxed); + } + .text-xl { + font-size: var(--text-xl); + line-height: var(--tw-leading, var(--text-xl--line-height)); + } + .text-xs { + font-size: var(--text-xs); + line-height: var(--tw-leading, var(--text-xs--line-height)); + } + .text-\[0\.8rem\] { + font-size: 0.8rem; + } + .text-\[0\.68rem\] { + font-size: 0.68rem; + } + .text-\[0\.72rem\] { + font-size: 0.72rem; + } + .leading-5 { + --tw-leading: calc(var(--spacing) * 5); + line-height: calc(var(--spacing) * 5); + } + .leading-6 { + --tw-leading: calc(var(--spacing) * 6); + line-height: calc(var(--spacing) * 6); + } + .leading-7 { + --tw-leading: calc(var(--spacing) * 7); + line-height: calc(var(--spacing) * 7); + } + .leading-8 { + --tw-leading: calc(var(--spacing) * 8); + line-height: calc(var(--spacing) * 8); + } + .leading-\[0\.92\] { + --tw-leading: 0.92; + line-height: 0.92; + } + .leading-\[1\.02\] { + --tw-leading: 1.02; + line-height: 1.02; + } + .leading-\[1\.06\] { + --tw-leading: 1.06; + line-height: 1.06; + } + .leading-\[26px\] { + --tw-leading: 26px; + line-height: 26px; + } + .leading-none { + --tw-leading: 1; + line-height: 1; + } + .leading-normal { + --tw-leading: var(--leading-normal); + line-height: var(--leading-normal); + } + .leading-relaxed { + --tw-leading: var(--leading-relaxed); + line-height: var(--leading-relaxed); + } + .leading-snug { + --tw-leading: var(--leading-snug); + line-height: var(--leading-snug); + } + .leading-tight { + --tw-leading: var(--leading-tight); + line-height: var(--leading-tight); + } + .font-bold { + --tw-font-weight: var(--font-weight-bold); + font-weight: var(--font-weight-bold); + } + .font-medium { + --tw-font-weight: var(--font-weight-medium); + font-weight: var(--font-weight-medium); + } + .font-normal { + --tw-font-weight: var(--font-weight-normal); + font-weight: var(--font-weight-normal); + } + .font-semibold { + --tw-font-weight: var(--font-weight-semibold); + font-weight: var(--font-weight-semibold); + } + .tracking-\[-0\.03em\] { + --tw-tracking: -0.03em; + letter-spacing: -0.03em; + } + .tracking-\[-0\.04em\] { + --tw-tracking: -0.04em; + letter-spacing: -0.04em; + } + .tracking-\[-0\.05em\] { + --tw-tracking: -0.05em; + letter-spacing: -0.05em; + } + .tracking-\[-0\.44px\] { + --tw-tracking: -0.44px; + letter-spacing: -0.44px; + } + .tracking-\[-0\.154px\] { + --tw-tracking: -0.154px; + letter-spacing: -0.154px; + } + .tracking-\[0\.1em\] { + --tw-tracking: 0.1em; + letter-spacing: 0.1em; + } + .tracking-\[0\.2em\] { + --tw-tracking: 0.2em; + letter-spacing: 0.2em; + } + .tracking-\[0\.16em\] { + --tw-tracking: 0.16em; + letter-spacing: 0.16em; + } + .tracking-\[0\.18em\] { + --tw-tracking: 0.18em; + letter-spacing: 0.18em; + } + .tracking-\[0\.22em\] { + --tw-tracking: 0.22em; + letter-spacing: 0.22em; + } + .tracking-\[0\.24em\] { + --tw-tracking: 0.24em; + letter-spacing: 0.24em; + } + .tracking-\[0\.26em\] { + --tw-tracking: 0.26em; + letter-spacing: 0.26em; + } + .tracking-\[0\.28em\] { + --tw-tracking: 0.28em; + letter-spacing: 0.28em; + } + .tracking-tight { + --tw-tracking: var(--tracking-tight); + letter-spacing: var(--tracking-tight); + } + .tracking-wide { + --tw-tracking: var(--tracking-wide); + letter-spacing: var(--tracking-wide); + } + .tracking-widest { + --tw-tracking: var(--tracking-widest); + letter-spacing: var(--tracking-widest); + } + .text-balance { + text-wrap: balance; + } + .break-words { + overflow-wrap: break-word; + } + .break-all { + word-break: break-all; + } + .whitespace-break-spaces { + white-space: break-spaces; + } + .whitespace-nowrap { + white-space: nowrap; + } + .whitespace-pre-wrap { + white-space: pre-wrap; + } + .text-\[\#0A66C2\] { + color: #0a66c2; + } + .text-\[\#1877F2\] { + color: #1877f2; + } + .text-\[\#34322d\] { + color: #34322d; + } + .text-\[\#858481\] { + color: #858481; + } + .text-\[var\(--accent-strong\)\] { + color: var(--accent-strong); + } + .text-\[var\(--accent-warm\)\] { + color: var(--accent-warm); + } + .text-\[var\(--ink-strong\)\] { + color: var(--ink-strong); + } + .text-accent-foreground { + color: var(--accent-foreground); + } + .text-amber-100 { + color: var(--color-amber-100); + } + .text-background { + color: var(--background); + } + .text-blue-100 { + color: var(--color-blue-100); + } + .text-blue-300 { + color: var(--color-blue-300); + } + .text-blue-700 { + color: var(--color-blue-700); + } + .text-blue-800 { + color: var(--color-blue-800); + } + .text-card-foreground { + color: var(--card-foreground); + } + .text-current { + color: currentColor; + } + .text-cyan-100 { + color: var(--color-cyan-100); + } + .text-cyan-300 { + color: var(--color-cyan-300); + } + .text-destructive { + color: var(--destructive); + } + .text-destructive-foreground { + color: var(--destructive-foreground); + } + .text-emerald-100 { + color: var(--color-emerald-100); + } + .text-foreground, + .text-foreground\/40 { + color: var(--foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .text-foreground\/40 { + color: color-mix(in oklab, var(--foreground) 40%, transparent); + } + } + .text-foreground\/50 { + color: var(--foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .text-foreground\/50 { + color: color-mix(in oklab, var(--foreground) 50%, transparent); + } + } + .text-foreground\/60 { + color: var(--foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .text-foreground\/60 { + color: color-mix(in oklab, var(--foreground) 60%, transparent); + } + } + .text-foreground\/70 { + color: var(--foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .text-foreground\/70 { + color: color-mix(in oklab, var(--foreground) 70%, transparent); + } + } + .text-foreground\/80 { + color: var(--foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .text-foreground\/80 { + color: color-mix(in oklab, var(--foreground) 80%, transparent); + } + } + .text-gray-300 { + color: var(--color-gray-300); + } + .text-gray-500 { + color: var(--color-gray-500); + } + .text-gray-600 { + color: var(--color-gray-600); + } + .text-gray-700 { + color: var(--color-gray-700); + } + .text-gray-800 { + color: var(--color-gray-800); + } + .text-green-300 { + color: var(--color-green-300); + } + .text-green-400 { + color: var(--color-green-400); + } + .text-green-600 { + color: var(--color-green-600); + } + .text-green-700 { + color: var(--color-green-700); + } + .text-green-800 { + color: var(--color-green-800); + } + .text-muted-foreground { + color: var(--muted-foreground); + } + .text-orange-800 { + color: var(--color-orange-800); + } + .text-popover-foreground { + color: var(--popover-foreground); + } + .text-primary { + color: var(--primary); + } + .text-primary-foreground { + color: var(--primary-foreground); + } + .text-purple-300 { + color: var(--color-purple-300); + } + .text-purple-700 { + color: var(--color-purple-700); + } + .text-purple-800 { + color: var(--color-purple-800); + } + .text-red-400 { + color: var(--color-red-400); + } + .text-red-500 { + color: var(--color-red-500); + } + .text-red-600 { + color: var(--color-red-600); + } + .text-red-700 { + color: var(--color-red-700); + } + .text-red-800 { + color: var(--color-red-800); + } + .text-red-900 { + color: var(--color-red-900); + } + .text-rose-100 { + color: var(--color-rose-100); + } + .text-rose-100\/85 { + color: #ffe4e6d9; + } + @supports (color: color-mix(in lab, red, red)) { + .text-rose-100\/85 { + color: color-mix(in oklab, var(--color-rose-100) 85%, transparent); + } + } + .text-secondary-foreground { + color: var(--secondary-foreground); + } + .text-sidebar-foreground, + .text-sidebar-foreground\/70 { + color: var(--sidebar-foreground); + } + @supports (color: color-mix(in lab, red, red)) { + .text-sidebar-foreground\/70 { + color: color-mix( + in oklab, + var(--sidebar-foreground) 70%, + transparent + ); + } + } + .text-slate-500 { + color: var(--color-slate-500); + } + .text-slate-600 { + color: var(--color-slate-600); + } + .text-slate-700 { + color: var(--color-slate-700); + } + .text-slate-900 { + color: var(--color-slate-900); + } + .text-slate-950 { + color: var(--color-slate-950); + } + .text-white { + color: var(--color-white); + } + .text-white\/30 { + color: #ffffff4d; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/30 { + color: color-mix(in oklab, var(--color-white) 30%, transparent); + } + } + .text-white\/35 { + color: #ffffff59; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/35 { + color: color-mix(in oklab, var(--color-white) 35%, transparent); + } + } + .text-white\/40 { + color: #fff6; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/40 { + color: color-mix(in oklab, var(--color-white) 40%, transparent); + } + } + .text-white\/45 { + color: #ffffff73; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/45 { + color: color-mix(in oklab, var(--color-white) 45%, transparent); + } + } + .text-white\/46 { + color: #ffffff75; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/46 { + color: color-mix(in oklab, var(--color-white) 46%, transparent); + } + } + .text-white\/48 { + color: #ffffff7a; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/48 { + color: color-mix(in oklab, var(--color-white) 48%, transparent); + } + } + .text-white\/50 { + color: #ffffff80; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/50 { + color: color-mix(in oklab, var(--color-white) 50%, transparent); + } + } + .text-white\/52 { + color: #ffffff85; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/52 { + color: color-mix(in oklab, var(--color-white) 52%, transparent); + } + } + .text-white\/55 { + color: #ffffff8c; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/55 { + color: color-mix(in oklab, var(--color-white) 55%, transparent); + } + } + .text-white\/58 { + color: #ffffff94; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/58 { + color: color-mix(in oklab, var(--color-white) 58%, transparent); + } + } + .text-white\/60 { + color: #fff9; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/60 { + color: color-mix(in oklab, var(--color-white) 60%, transparent); + } + } + .text-white\/62 { + color: #ffffff9e; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/62 { + color: color-mix(in oklab, var(--color-white) 62%, transparent); + } + } + .text-white\/64 { + color: #ffffffa3; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/64 { + color: color-mix(in oklab, var(--color-white) 64%, transparent); + } + } + .text-white\/66 { + color: #ffffffa8; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/66 { + color: color-mix(in oklab, var(--color-white) 66%, transparent); + } + } + .text-white\/68 { + color: #ffffffad; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/68 { + color: color-mix(in oklab, var(--color-white) 68%, transparent); + } + } + .text-white\/70 { + color: #ffffffb3; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/70 { + color: color-mix(in oklab, var(--color-white) 70%, transparent); + } + } + .text-white\/72 { + color: #ffffffb8; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/72 { + color: color-mix(in oklab, var(--color-white) 72%, transparent); + } + } + .text-white\/74 { + color: #ffffffbd; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/74 { + color: color-mix(in oklab, var(--color-white) 74%, transparent); + } + } + .text-white\/76 { + color: #ffffffc2; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/76 { + color: color-mix(in oklab, var(--color-white) 76%, transparent); + } + } + .text-white\/78 { + color: #ffffffc7; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/78 { + color: color-mix(in oklab, var(--color-white) 78%, transparent); + } + } + .text-white\/80 { + color: #fffc; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/80 { + color: color-mix(in oklab, var(--color-white) 80%, transparent); + } + } + .text-white\/82 { + color: #ffffffd1; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/82 { + color: color-mix(in oklab, var(--color-white) 82%, transparent); + } + } + .text-white\/84 { + color: #ffffffd6; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/84 { + color: color-mix(in oklab, var(--color-white) 84%, transparent); + } + } + .text-white\/90 { + color: #ffffffe6; + } + @supports (color: color-mix(in lab, red, red)) { + .text-white\/90 { + color: color-mix(in oklab, var(--color-white) 90%, transparent); + } + } + .text-yellow-300 { + color: var(--color-yellow-300); + } + .text-yellow-700 { + color: var(--color-yellow-700); + } + .capitalize { + text-transform: capitalize; + } + .uppercase { + text-transform: uppercase; + } + .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,); + } + .underline { + text-decoration-line: underline; + } + .decoration-white\/30 { + text-decoration-color: #ffffff4d; + } + @supports (color: color-mix(in lab, red, red)) { + .decoration-white\/30 { + -webkit-text-decoration-color: color-mix( + in oklab, + var(--color-white) 30%, + transparent + ); + text-decoration-color: color-mix( + in oklab, + var(--color-white) 30%, + transparent + ); + } + } + .underline-offset-4 { + text-underline-offset: 4px; + } + .placeholder-white\/40::placeholder { + color: #fff6; + } + @supports (color: color-mix(in lab, red, red)) { + .placeholder-white\/40::placeholder { + color: color-mix(in oklab, var(--color-white) 40%, transparent); + } + } + .opacity-0 { + opacity: 0; + } + .opacity-20 { + opacity: 0.2; + } + .opacity-50 { + opacity: 0.5; + } + .opacity-70 { + opacity: 0.7; + } + .opacity-90 { + opacity: 0.9; + } + .opacity-100 { + opacity: 1; + } + .opacity-\[0\.04\] { + opacity: 0.04; + } + .opacity-\[0\.20\] { + opacity: 0.2; + } + .opacity-\[0\.52\] { + opacity: 0.52; + } + .shadow { + --tw-shadow: + 0 1px 3px 0 var(--tw-shadow-color, #0000001a), + 0 1px 2px -1px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_0_0_1px_hsl\(var\(--sidebar-border\)\)\] { + --tw-shadow: 0 0 0 1px + var(--tw-shadow-color, hsl(var(--sidebar-border))); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_12px_28px_rgba\(0\,0\,0\,0\.18\)\] { + --tw-shadow: 0 12px 28px var(--tw-shadow-color, #0000002e); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_14px_34px_rgba\(0\,0\,0\,0\.18\)\] { + --tw-shadow: 0 14px 34px var(--tw-shadow-color, #0000002e); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_14px_34px_rgba\(0\,0\,0\,0\.22\)\] { + --tw-shadow: 0 14px 34px var(--tw-shadow-color, #00000038); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_16px_34px_rgba\(16\,122\,132\,0\.2\)\] { + --tw-shadow: 0 16px 34px var(--tw-shadow-color, #107a8433); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_16px_40px_rgba\(0\,0\,0\,0\.34\)\] { + --tw-shadow: 0 16px 40px var(--tw-shadow-color, #00000057); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_16px_40px_rgba\(0\,0\,0\,0\.35\)\] { + --tw-shadow: 0 16px 40px var(--tw-shadow-color, #00000059); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_18px_50px_rgba\(0\,0\,0\,0\.18\)\] { + --tw-shadow: 0 18px 50px var(--tw-shadow-color, #0000002e); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_18px_50px_rgba\(15\,23\,42\,0\.08\)\] { + --tw-shadow: 0 18px 50px var(--tw-shadow-color, #0f172a14); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_20px_70px_rgba\(0\,0\,0\,0\.25\)\] { + --tw-shadow: 0 20px 70px var(--tw-shadow-color, #00000040); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_22px_60px_rgba\(0\,0\,0\,0\.24\)\] { + --tw-shadow: 0 22px 60px var(--tw-shadow-color, #0000003d); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_22px_70px_rgba\(0\,0\,0\,0\.2\)\] { + --tw-shadow: 0 22px 70px var(--tw-shadow-color, #0003); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_24px_80px_rgba\(0\,0\,0\,0\.24\)\] { + --tw-shadow: 0 24px 80px var(--tw-shadow-color, #0000003d); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_26px_70px_rgba\(0\,0\,0\,0\.28\)\] { + --tw-shadow: 0 26px 70px var(--tw-shadow-color, #00000047); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_28px_80px_rgba\(0\,0\,0\,0\.45\)\] { + --tw-shadow: 0 28px 80px var(--tw-shadow-color, #00000073); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0_30px_90px_rgba\(0\,0\,0\,0\.35\)\] { + --tw-shadow: 0 30px 90px var(--tw-shadow-color, #00000059); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-\[0px_4px_11px_0px_rgba\(0\,0\,0\,0\.08\)\] { + --tw-shadow: 0px 4px 11px 0px var(--tw-shadow-color, #00000014); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-lg { + --tw-shadow: + 0 10px 15px -3px var(--tw-shadow-color, #0000001a), + 0 4px 6px -4px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-md { + --tw-shadow: + 0 4px 6px -1px var(--tw-shadow-color, #0000001a), + 0 2px 4px -2px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-none { + --tw-shadow: 0 0 #0000; + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-sm { + --tw-shadow: + 0 1px 3px 0 var(--tw-shadow-color, #0000001a), + 0 1px 2px -1px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-xl { + --tw-shadow: + 0 20px 25px -5px var(--tw-shadow-color, #0000001a), + 0 8px 10px -6px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .shadow-xs { + --tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, #0000000d); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .ring-0 { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(0px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .ring-ring\/50 { + --tw-ring-color: var(--ring); + } + @supports (color: color-mix(in lab, red, red)) { + .ring-ring\/50 { + --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + .ring-sidebar-ring { + --tw-ring-color: var(--sidebar-ring); + } + .ring-offset-background { + --tw-ring-offset-color: var(--background); + } + .outline-hidden { + --tw-outline-style: none; + outline-style: none; + } + @media (forced-colors: active) { + .outline-hidden { + outline-offset: 2px; + outline: 2px solid #0000; + } + } + .outline { + outline-style: var(--tw-outline-style); + outline-width: 1px; + } + .blur-3xl { + --tw-blur: blur(var(--blur-3xl)); + 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,); + } + .backdrop-blur { + --tw-backdrop-blur: blur(8px); + -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-2xl { + --tw-backdrop-blur: blur(var(--blur-2xl)); + -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(var(--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-sm { + --tw-backdrop-blur: blur(var(--blur-sm)); + -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(var(--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,); + } + .transition { + transition-property: + color, + background-color, + border-color, + outline-color, + text-decoration-color, + fill, + stroke, + --tw-gradient-from, + --tw-gradient-via, + --tw-gradient-to, + opacity, + box-shadow, + transform, + translate, + scale, + rotate, + filter, + -webkit-backdrop-filter, + backdrop-filter, + display, + content-visibility, + overlay, + pointer-events; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-\[color\,box-shadow\] { + transition-property: color, box-shadow; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-\[left\,right\,width\] { + transition-property: left, right, width; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-\[margin\,opacity\] { + transition-property: margin, opacity; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-\[width\,height\,padding\] { + transition-property: width, height, padding; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-\[width\] { + transition-property: width; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-all { + transition-property: all; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-colors { + transition-property: + color, background-color, border-color, outline-color, + text-decoration-color, fill, stroke, --tw-gradient-from, + --tw-gradient-via, --tw-gradient-to; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-opacity { + transition-property: opacity; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-shadow { + transition-property: box-shadow; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-transform { + transition-property: transform, translate, scale, rotate; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + .transition-none { + transition-property: none; + } + .duration-100 { + --tw-duration: 0.1s; + transition-duration: 0.1s; + } + .duration-200 { + --tw-duration: 0.2s; + transition-duration: 0.2s; + } + .duration-300 { + --tw-duration: 0.3s; + transition-duration: 0.3s; + } + .duration-500 { + --tw-duration: 0.5s; + transition-duration: 0.5s; + } + .duration-1000 { + --tw-duration: 1s; + transition-duration: 1s; + } + .ease-in-out { + --tw-ease: var(--ease-in-out); + transition-timing-function: var(--ease-in-out); + } + .ease-linear { + --tw-ease: linear; + transition-timing-function: linear; + } + .ease-out { + --tw-ease: var(--ease-out); + transition-timing-function: var(--ease-out); + } + .fade-in-0 { + --tw-enter-opacity: 0; + } + .outline-none { + --tw-outline-style: none; + outline-style: none; + } + .select-none { + -webkit-user-select: none; + user-select: none; + } + .zoom-in-95 { + --tw-enter-scale: 0.95; + } + .\[--cell-size\:--spacing\(8\)\] { + --cell-size: calc(var(--spacing) * 8); + } + .group-focus-within\/menu-item\:opacity-100:is( + :where(.group\/menu-item):focus-within * + ) { + opacity: 1; + } + @media (hover: hover) { + .group-hover\:block:is(:where(.group):hover *) { + display: block; + } + .group-hover\:translate-x-1:is(:where(.group):hover *) { + --tw-translate-x: calc(var(--spacing) * 1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .group-hover\:translate-y-\[-1px\]:is(:where(.group):hover *) { + --tw-translate-y: -1px; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .group-hover\:rotate-180:is(:where(.group):hover *) { + rotate: 180deg; + } + .group-hover\:bg-white\/20:is(:where(.group):hover *) { + background-color: #fff3; + } + @supports (color: color-mix(in lab, red, red)) { + .group-hover\:bg-white\/20:is(:where(.group):hover *) { + background-color: color-mix( + in oklab, + var(--color-white) 20%, + transparent + ); + } + } + .group-hover\:text-\[var\(--accent-strong\)\]:is( + :where(.group):hover * + ) { + color: var(--accent-strong); + } + .group-hover\:opacity-100:is(:where(.group):hover *), + .group-hover\/menu-item\:opacity-100:is( + :where(.group\/menu-item):hover * + ) { + opacity: 1; + } + } + .group-has-data-\[sidebar\=menu-action\]\/menu-item\:pr-8:is( + :where(.group\/menu-item):has([data-sidebar="menu-action"]) * + ) { + padding-right: calc(var(--spacing) * 8); + } + .group-has-\[\[data-orientation\=horizontal\]\]\/field\:text-balance:is( + :where(.group\/field):has([data-orientation="horizontal"]) * + ) { + text-wrap: balance; + } + .group-has-\[\[data-slot\=item-description\]\]\/item\:translate-y-0\.5:is( + :where(.group\/item):has([data-slot="item-description"]) * + ) { + --tw-translate-y: calc(var(--spacing) * 0.5); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .group-has-\[\[data-slot\=item-description\]\]\/item\:self-start:is( + :where(.group\/item):has([data-slot="item-description"]) * + ) { + align-self: flex-start; + } + .group-has-\[\>input\]\/input-group\:pt-2\.5:is( + :where(.group\/input-group):has(> input) * + ) { + padding-top: calc(var(--spacing) * 2.5); + } + .group-has-\[\>input\]\/input-group\:pb-2\.5:is( + :where(.group\/input-group):has(> input) * + ) { + padding-bottom: calc(var(--spacing) * 2.5); + } + .group-data-\[collapsible\=icon\]\:-mt-8:is( + :where(.group)[data-collapsible="icon"] * + ) { + margin-top: calc(var(--spacing) * -8); + } + .group-data-\[collapsible\=icon\]\:hidden:is( + :where(.group)[data-collapsible="icon"] * + ) { + display: none; + } + .group-data-\[collapsible\=icon\]\:size-8\!:is( + :where(.group)[data-collapsible="icon"] * + ) { + width: calc(var(--spacing) * 8) !important; + height: calc(var(--spacing) * 8) !important; + } + .group-data-\[collapsible\=icon\]\:w-\(--sidebar-width-icon\):is( + :where(.group)[data-collapsible="icon"] * + ) { + width: var(--sidebar-width-icon); + } + .group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\)\]:is( + :where(.group)[data-collapsible="icon"] * + ) { + width: calc(var(--sidebar-width-icon) + (calc(var(--spacing) * 4))); + } + .group-data-\[collapsible\=icon\]\:w-\[calc\(var\(--sidebar-width-icon\)\+\(--spacing\(4\)\)\+2px\)\]:is( + :where(.group)[data-collapsible="icon"] * + ) { + width: calc( + var(--sidebar-width-icon) + (calc(var(--spacing) * 4)) + 2px + ); + } + .group-data-\[collapsible\=icon\]\:justify-center:is( + :where(.group)[data-collapsible="icon"] * + ) { + justify-content: center; + } + .group-data-\[collapsible\=icon\]\:overflow-hidden:is( + :where(.group)[data-collapsible="icon"] * + ) { + overflow: hidden; + } + .group-data-\[collapsible\=icon\]\:p-0\!:is( + :where(.group)[data-collapsible="icon"] * + ) { + padding: calc(var(--spacing) * 0) !important; + } + .group-data-\[collapsible\=icon\]\:p-2\!:is( + :where(.group)[data-collapsible="icon"] * + ) { + padding: calc(var(--spacing) * 2) !important; + } + .group-data-\[collapsible\=icon\]\:opacity-0:is( + :where(.group)[data-collapsible="icon"] * + ) { + opacity: 0; + } + .group-data-\[collapsible\=offcanvas\]\:right-\[calc\(var\(--sidebar-width\)\*-1\)\]:is( + :where(.group)[data-collapsible="offcanvas"] * + ) { + right: calc(var(--sidebar-width) * -1); + } + .group-data-\[collapsible\=offcanvas\]\:left-\[calc\(var\(--sidebar-width\)\*-1\)\]:is( + :where(.group)[data-collapsible="offcanvas"] * + ) { + left: calc(var(--sidebar-width) * -1); + } + .group-data-\[collapsible\=offcanvas\]\:w-0:is( + :where(.group)[data-collapsible="offcanvas"] * + ) { + width: calc(var(--spacing) * 0); + } + .group-data-\[collapsible\=offcanvas\]\:translate-x-0:is( + :where(.group)[data-collapsible="offcanvas"] * + ) { + --tw-translate-x: calc(var(--spacing) * 0); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .group-data-\[disabled\=true\]\:pointer-events-none:is( + :where(.group)[data-disabled="true"] * + ) { + pointer-events: none; + } + .group-data-\[disabled\=true\]\:opacity-50:is( + :where(.group)[data-disabled="true"] * + ), + .group-data-\[disabled\=true\]\/field\:opacity-50:is( + :where(.group\/field)[data-disabled="true"] * + ), + .group-data-\[disabled\=true\]\/input-group\:opacity-50:is( + :where(.group\/input-group)[data-disabled="true"] * + ) { + opacity: 0.5; + } + .group-data-\[focused\=true\]\/day\:relative:is( + :where(.group\/day)[data-focused="true"] * + ) { + position: relative; + } + .group-data-\[focused\=true\]\/day\:z-10:is( + :where(.group\/day)[data-focused="true"] * + ) { + z-index: 10; + } + .group-data-\[focused\=true\]\/day\:border-ring:is( + :where(.group\/day)[data-focused="true"] * + ) { + border-color: var(--ring); + } + .group-data-\[focused\=true\]\/day\:ring-\[3px\]:is( + :where(.group\/day)[data-focused="true"] * + ) { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .group-data-\[focused\=true\]\/day\:ring-ring\/50:is( + :where(.group\/day)[data-focused="true"] * + ) { + --tw-ring-color: var(--ring); + } + @supports (color: color-mix(in lab, red, red)) { + .group-data-\[focused\=true\]\/day\:ring-ring\/50:is( + :where(.group\/day)[data-focused="true"] * + ) { + --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + .group-data-\[side\=left\]\:-right-4:is( + :where(.group)[data-side="left"] * + ) { + right: calc(var(--spacing) * -4); + } + .group-data-\[side\=left\]\:border-r:is( + :where(.group)[data-side="left"] * + ) { + border-right-style: var(--tw-border-style); + border-right-width: 1px; + } + .group-data-\[side\=right\]\:left-0:is( + :where(.group)[data-side="right"] * + ) { + left: calc(var(--spacing) * 0); + } + .group-data-\[side\=right\]\:rotate-180:is( + :where(.group)[data-side="right"] * + ) { + rotate: 180deg; + } + .group-data-\[side\=right\]\:border-l:is( + :where(.group)[data-side="right"] * + ) { + border-left-style: var(--tw-border-style); + border-left-width: 1px; + } + .group-data-\[state\=open\]\:rotate-180:is( + :where(.group)[data-state="open"] * + ) { + rotate: 180deg; + } + .group-data-\[variant\=floating\]\:rounded-lg:is( + :where(.group)[data-variant="floating"] * + ) { + border-radius: var(--radius); + } + .group-data-\[variant\=floating\]\:border:is( + :where(.group)[data-variant="floating"] * + ) { + border-style: var(--tw-border-style); + border-width: 1px; + } + .group-data-\[variant\=floating\]\:border-sidebar-border:is( + :where(.group)[data-variant="floating"] * + ) { + border-color: var(--sidebar-border); + } + .group-data-\[variant\=floating\]\:shadow-sm:is( + :where(.group)[data-variant="floating"] * + ) { + --tw-shadow: + 0 1px 3px 0 var(--tw-shadow-color, #0000001a), + 0 1px 2px -1px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .group-data-\[variant\=outline\]\/field-group\:-mb-2:is( + :where(.group\/field-group)[data-variant="outline"] * + ) { + margin-bottom: calc(var(--spacing) * -2); + } + .group-data-\[vaul-drawer-direction\=bottom\]\/drawer-content\:block:is( + :where(.group\/drawer-content)[data-vaul-drawer-direction="bottom"] * + ) { + display: block; + } + .group-data-\[vaul-drawer-direction\=bottom\]\/drawer-content\:text-center:is( + :where(.group\/drawer-content)[data-vaul-drawer-direction="bottom"] * + ), + .group-data-\[vaul-drawer-direction\=top\]\/drawer-content\:text-center:is( + :where(.group\/drawer-content)[data-vaul-drawer-direction="top"] * + ) { + text-align: center; + } + .group-data-\[viewport\=false\]\/navigation-menu\:top-full:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + top: 100%; + } + .group-data-\[viewport\=false\]\/navigation-menu\:mt-1\.5:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + margin-top: calc(var(--spacing) * 1.5); + } + .group-data-\[viewport\=false\]\/navigation-menu\:overflow-hidden:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + overflow: hidden; + } + .group-data-\[viewport\=false\]\/navigation-menu\:rounded-md:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + border-radius: calc(var(--radius) - 3px); + } + .group-data-\[viewport\=false\]\/navigation-menu\:border:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + border-style: var(--tw-border-style); + border-width: 1px; + } + .group-data-\[viewport\=false\]\/navigation-menu\:bg-popover:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + background-color: var(--popover); + } + .group-data-\[viewport\=false\]\/navigation-menu\:text-popover-foreground:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + color: var(--popover-foreground); + } + .group-data-\[viewport\=false\]\/navigation-menu\:shadow:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + --tw-shadow: + 0 1px 3px 0 var(--tw-shadow-color, #0000001a), + 0 1px 2px -1px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .group-data-\[viewport\=false\]\/navigation-menu\:duration-200:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + ) { + --tw-duration: 0.2s; + transition-duration: 0.2s; + } + @media (hover: hover) { + .peer-hover\/menu-button\:text-sidebar-accent-foreground:is( + :where(.peer\/menu-button):hover ~ * + ) { + color: var(--sidebar-accent-foreground); + } + } + .peer-disabled\:cursor-not-allowed:is(:where(.peer):disabled ~ *) { + cursor: not-allowed; + } + .peer-disabled\:opacity-50:is(:where(.peer):disabled ~ *) { + opacity: 0.5; + } + .peer-data-\[active\=true\]\/menu-button\:text-sidebar-accent-foreground:is( + :where(.peer\/menu-button)[data-active="true"] ~ * + ) { + color: var(--sidebar-accent-foreground); + } + .peer-data-\[size\=default\]\/menu-button\:top-1\.5:is( + :where(.peer\/menu-button)[data-size="default"] ~ * + ) { + top: calc(var(--spacing) * 1.5); + } + .peer-data-\[size\=lg\]\/menu-button\:top-2\.5:is( + :where(.peer\/menu-button)[data-size="lg"] ~ * + ) { + top: calc(var(--spacing) * 2.5); + } + .peer-data-\[size\=sm\]\/menu-button\:top-1:is( + :where(.peer\/menu-button)[data-size="sm"] ~ * + ) { + top: calc(var(--spacing) * 1); + } + .marker\:text-\[var\(--accent-warm\)\] ::marker { + color: var(--accent-warm); + } + .marker\:text-\[var\(--accent-warm\)\]::marker { + color: var(--accent-warm); + } + .marker\:text-\[var\(--accent-warm\)\] ::-webkit-details-marker { + color: var(--accent-warm); + } + .marker\:text-\[var\(--accent-warm\)\]::-webkit-details-marker { + color: var(--accent-warm); + } + .selection\:bg-primary ::selection { + background-color: var(--primary); + } + .selection\:bg-primary::selection { + background-color: var(--primary); + } + .selection\:text-primary-foreground ::selection { + color: var(--primary-foreground); + } + .selection\:text-primary-foreground::selection { + color: var(--primary-foreground); + } + .file\:mr-4::file-selector-button { + margin-right: calc(var(--spacing) * 4); + } + .file\:inline-flex::file-selector-button { + display: inline-flex; + } + .file\:h-7::file-selector-button { + height: calc(var(--spacing) * 7); + } + .file\:rounded-full::file-selector-button { + border-radius: 3.40282e38px; + } + .file\:border-0::file-selector-button { + border-style: var(--tw-border-style); + border-width: 0; + } + .file\:bg-transparent::file-selector-button { + background-color: #0000; + } + .file\:bg-white::file-selector-button { + background-color: var(--color-white); + } + .file\:px-4::file-selector-button { + padding-inline: calc(var(--spacing) * 4); + } + .file\:py-2::file-selector-button { + padding-block: calc(var(--spacing) * 2); + } + .file\:text-sm::file-selector-button { + font-size: var(--text-sm); + line-height: var(--tw-leading, var(--text-sm--line-height)); + } + .file\:font-medium::file-selector-button { + --tw-font-weight: var(--font-weight-medium); + font-weight: var(--font-weight-medium); + } + .file\:font-semibold::file-selector-button { + --tw-font-weight: var(--font-weight-semibold); + font-weight: var(--font-weight-semibold); + } + .file\:text-\[var\(--ink-strong\)\]::file-selector-button { + color: var(--ink-strong); + } + .file\:text-foreground::file-selector-button { + color: var(--foreground); + } + .placeholder\:text-muted-foreground::placeholder { + color: var(--muted-foreground); + } + .placeholder\:text-white\/36::placeholder { + color: #ffffff5c; + } + @supports (color: color-mix(in lab, red, red)) { + .placeholder\:text-white\/36::placeholder { + color: color-mix(in oklab, var(--color-white) 36%, transparent); + } + } + .placeholder\:text-white\/40::placeholder { + color: #fff6; + } + @supports (color: color-mix(in lab, red, red)) { + .placeholder\:text-white\/40::placeholder { + color: color-mix(in oklab, var(--color-white) 40%, transparent); + } + } + .before\:absolute:before { + content: var(--tw-content); + position: absolute; + } + .before\:bottom-2:before { + content: var(--tw-content); + bottom: calc(var(--spacing) * 2); + } + .before\:left-1\/2:before { + content: var(--tw-content); + left: 50%; + } + .before\:h-1\.5:before { + content: var(--tw-content); + height: calc(var(--spacing) * 1.5); + } + .before\:w-1\.5:before { + content: var(--tw-content); + width: calc(var(--spacing) * 1.5); + } + .before\:-translate-x-1\/2:before { + content: var(--tw-content); + --tw-translate-x: -50%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .before\:rounded-full:before { + content: var(--tw-content); + border-radius: 3.40282e38px; + } + .before\:bg-\[var\(--accent-warm\)\]:before { + content: var(--tw-content); + background-color: var(--accent-warm); + } + .after\:absolute:after { + content: var(--tw-content); + position: absolute; + } + .after\:-inset-2:after { + content: var(--tw-content); + inset: calc(var(--spacing) * -2); + } + .after\:inset-y-0:after { + content: var(--tw-content); + inset-block: calc(var(--spacing) * 0); + } + .after\:left-1\/2:after { + content: var(--tw-content); + left: 50%; + } + .after\:w-1:after { + content: var(--tw-content); + width: calc(var(--spacing) * 1); + } + .after\:w-\[2px\]:after { + content: var(--tw-content); + width: 2px; + } + .after\:-translate-x-1\/2:after { + content: var(--tw-content); + --tw-translate-x: -50%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .group-data-\[collapsible\=offcanvas\]\:after\:left-full:is( + :where(.group)[data-collapsible="offcanvas"] * + ):after { + content: var(--tw-content); + left: 100%; + } + .first\:rounded-t-lg:first-child { + border-top-left-radius: var(--radius); + border-top-right-radius: var(--radius); + } + .first\:rounded-l-md:first-child { + border-top-left-radius: calc(var(--radius) - 3px); + border-bottom-left-radius: calc(var(--radius) - 3px); + } + .first\:border-l:first-child { + border-left-style: var(--tw-border-style); + border-left-width: 1px; + } + .last\:mt-0:last-child { + margin-top: calc(var(--spacing) * 0); + } + .last\:rounded-r-md:last-child { + border-top-right-radius: calc(var(--radius) - 3px); + border-bottom-right-radius: calc(var(--radius) - 3px); + } + .last\:rounded-b-lg:last-child { + border-bottom-right-radius: var(--radius); + border-bottom-left-radius: var(--radius); + } + .last\:border-b-0:last-child { + border-bottom-style: var(--tw-border-style); + border-bottom-width: 0; + } + @media (hover: hover) { + .hover\:-translate-y-0\.5:hover { + --tw-translate-y: calc(var(--spacing) * -0.5); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .hover\:-translate-y-1:hover { + --tw-translate-y: calc(var(--spacing) * -1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .hover\:-translate-y-2:hover { + --tw-translate-y: calc(var(--spacing) * -2); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .hover\:scale-105:hover { + --tw-scale-x: 105%; + --tw-scale-y: 105%; + --tw-scale-z: 105%; + scale: var(--tw-scale-x) var(--tw-scale-y); + } + .hover\:scale-\[1\.03\]:hover { + scale: 1.03; + } + .hover\:border-\[var\(--accent-strong\)\]\/30:hover { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:border-\[var\(--accent-strong\)\]\/30:hover { + border-color: color-mix( + in oklab, + var(--accent-strong) 30%, + transparent + ); + } + } + .hover\:border-\[var\(--accent-strong\)\]\/60:hover { + border-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:border-\[var\(--accent-strong\)\]\/60:hover { + border-color: color-mix( + in oklab, + var(--accent-strong) 60%, + transparent + ); + } + } + .hover\:border-\[var\(--accent-warm\)\]\/60:hover { + border-color: var(--accent-warm); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:border-\[var\(--accent-warm\)\]\/60:hover { + border-color: color-mix( + in oklab, + var(--accent-warm) 60%, + transparent + ); + } + } + .hover\:border-\[var\(--accent-warm\)\]\/75:hover { + border-color: var(--accent-warm); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:border-\[var\(--accent-warm\)\]\/75:hover { + border-color: color-mix( + in oklab, + var(--accent-warm) 75%, + transparent + ); + } + } + .hover\:border-white\/20:hover { + border-color: #fff3; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:border-white\/20:hover { + border-color: color-mix( + in oklab, + var(--color-white) 20%, + transparent + ); + } + } + .hover\:border-white\/40:hover { + border-color: #fff6; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:border-white\/40:hover { + border-color: color-mix( + in oklab, + var(--color-white) 40%, + transparent + ); + } + } + .hover\:bg-\[\#0A66C2\]\/30:hover { + background-color: #0a66c24d; + } + .hover\:bg-\[\#1a1a19\]\/90:hover { + background-color: #1a1a19e6; + } + .hover\:bg-\[\#1877F2\]\/30:hover { + background-color: #1877f24d; + } + .hover\:bg-\[rgba\(83\,213\,205\,0\.2\)\]:hover { + background-color: #53d5cd33; + } + .hover\:bg-\[var\(--accent-strong\)\]:hover, + .hover\:bg-\[var\(--accent-strong\)\]\/30:hover { + background-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-\[var\(--accent-strong\)\]\/30:hover { + background-color: color-mix( + in oklab, + var(--accent-strong) 30%, + transparent + ); + } + } + .hover\:bg-\[var\(--accent-strong\)\]\/90:hover { + background-color: var(--accent-strong); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-\[var\(--accent-strong\)\]\/90:hover { + background-color: color-mix( + in oklab, + var(--accent-strong) 90%, + transparent + ); + } + } + .hover\:bg-\[var\(--accent-warm\)\]:hover { + background-color: var(--accent-warm); + } + .hover\:bg-accent:hover, + .hover\:bg-accent\/50:hover { + background-color: var(--accent); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-accent\/50:hover { + background-color: color-mix( + in oklab, + var(--accent) 50%, + transparent + ); + } + } + .hover\:bg-blue-700:hover { + background-color: var(--color-blue-700); + } + .hover\:bg-destructive\/90:hover { + background-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-destructive\/90:hover { + background-color: color-mix( + in oklab, + var(--destructive) 90%, + transparent + ); + } + } + .hover\:bg-muted:hover, + .hover\:bg-muted\/50:hover { + background-color: var(--muted); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-muted\/50:hover { + background-color: color-mix( + in oklab, + var(--muted) 50%, + transparent + ); + } + } + .hover\:bg-primary\/20:hover { + background-color: var(--primary); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-primary\/20:hover { + background-color: color-mix( + in oklab, + var(--primary) 20%, + transparent + ); + } + } + .hover\:bg-primary\/90:hover { + background-color: var(--primary); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-primary\/90:hover { + background-color: color-mix( + in oklab, + var(--primary) 90%, + transparent + ); + } + } + .hover\:bg-red-600\/20:hover { + background-color: #e4001433; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-red-600\/20:hover { + background-color: color-mix( + in oklab, + var(--color-red-600) 20%, + transparent + ); + } + } + .hover\:bg-secondary\/80:hover { + background-color: var(--secondary); + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-secondary\/80:hover { + background-color: color-mix( + in oklab, + var(--secondary) 80%, + transparent + ); + } + } + .hover\:bg-sidebar-accent:hover { + background-color: var(--sidebar-accent); + } + .hover\:bg-slate-100:hover { + background-color: var(--color-slate-100); + } + .hover\:bg-white:hover { + background-color: var(--color-white); + } + .hover\:bg-white\/5:hover { + background-color: #ffffff0d; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-white\/5:hover { + background-color: color-mix( + in oklab, + var(--color-white) 5%, + transparent + ); + } + } + .hover\:bg-white\/6:hover { + background-color: #ffffff0f; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-white\/6:hover { + background-color: color-mix( + in oklab, + var(--color-white) 6%, + transparent + ); + } + } + .hover\:bg-white\/8:hover { + background-color: #ffffff14; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-white\/8:hover { + background-color: color-mix( + in oklab, + var(--color-white) 8%, + transparent + ); + } + } + .hover\:bg-white\/10:hover { + background-color: #ffffff1a; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-white\/10:hover { + background-color: color-mix( + in oklab, + var(--color-white) 10%, + transparent + ); + } + } + .hover\:bg-white\/20:hover { + background-color: #fff3; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:bg-white\/20:hover { + background-color: color-mix( + in oklab, + var(--color-white) 20%, + transparent + ); + } + } + .hover\:bg-\[linear-gradient\(135deg\,rgba\(83\,213\,205\,0\.42\)\,rgba\(255\,193\,43\,0\.3\)\)\]:hover { + background-image: linear-gradient(135deg, #53d5cd6b, #ffc12b4d); + } + .hover\:text-\[var\(--accent-strong\)\]:hover { + color: var(--accent-strong); + } + .hover\:text-\[var\(--accent-warm\)\]:hover { + color: var(--accent-warm); + } + .hover\:text-\[var\(--ink-strong\)\]:hover { + color: var(--ink-strong); + } + .hover\:text-accent-foreground:hover { + color: var(--accent-foreground); + } + .hover\:text-foreground:hover { + color: var(--foreground); + } + .hover\:text-muted-foreground:hover { + color: var(--muted-foreground); + } + .hover\:text-sidebar-accent-foreground:hover { + color: var(--sidebar-accent-foreground); + } + .hover\:text-slate-900:hover { + color: var(--color-slate-900); + } + .hover\:text-white:hover { + color: var(--color-white); + } + .hover\:text-white\/60:hover { + color: #fff9; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:text-white\/60:hover { + color: color-mix(in oklab, var(--color-white) 60%, transparent); + } + } + .hover\:text-white\/80:hover { + color: #fffc; + } + @supports (color: color-mix(in lab, red, red)) { + .hover\:text-white\/80:hover { + color: color-mix(in oklab, var(--color-white) 80%, transparent); + } + } + .hover\:underline:hover { + text-decoration-line: underline; + } + .hover\:opacity-80:hover { + opacity: 0.8; + } + .hover\:opacity-90:hover { + opacity: 0.9; + } + .hover\:opacity-100:hover { + opacity: 1; + } + .hover\:shadow-\[var\(--accent-strong\)\]\/30:hover { + --tw-shadow-alpha: 30%; + --tw-shadow: var(--accent-strong); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:shadow-\[0_0_0_1px_hsl\(var\(--sidebar-accent\)\)\]:hover { + --tw-shadow: 0 0 0 1px + var(--tw-shadow-color, hsl(var(--sidebar-accent))); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:shadow-\[0_20px_40px_rgba\(77\,201\,196\,0\.15\)\]:hover { + --tw-shadow: 0 20px 40px var(--tw-shadow-color, #4dc9c426); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:shadow-\[0_20px_60px_rgba\(77\,201\,196\,0\.15\)\]:hover { + --tw-shadow: 0 20px 60px var(--tw-shadow-color, #4dc9c426); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:shadow-\[0_24px_48px_rgba\(16\,122\,132\,0\.34\)\]:hover { + --tw-shadow: 0 24px 48px var(--tw-shadow-color, #107a8457); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:shadow-lg:hover { + --tw-shadow: + 0 10px 15px -3px var(--tw-shadow-color, #0000001a), + 0 4px 6px -4px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:shadow-md:hover { + --tw-shadow: + 0 4px 6px -1px var(--tw-shadow-color, #0000001a), + 0 2px 4px -2px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:shadow-xl:hover { + --tw-shadow: + 0 20px 25px -5px var(--tw-shadow-color, #0000001a), + 0 8px 10px -6px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:ring-4:hover { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(4px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .hover\:brightness-110:hover { + --tw-brightness: brightness(110%); + 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,); + } + .hover\:group-data-\[collapsible\=offcanvas\]\:bg-sidebar:hover:is( + :where(.group)[data-collapsible="offcanvas"] * + ) { + background-color: var(--sidebar); + } + .hover\:after\:bg-sidebar-border:hover:after { + content: var(--tw-content); + background-color: var(--sidebar-border); + } + } + .focus\:z-10:focus { + z-index: 10; + } + .focus\:border-\[var\(--accent-strong\)\]:focus { + border-color: var(--accent-strong); + } + .focus\:border-white\/30:focus { + border-color: #ffffff4d; + } + @supports (color: color-mix(in lab, red, red)) { + .focus\:border-white\/30:focus { + border-color: color-mix( + in oklab, + var(--color-white) 30%, + transparent + ); + } + } + .focus\:bg-accent:focus { + background-color: var(--accent); + } + .focus\:text-accent-foreground:focus { + color: var(--accent-foreground); + } + .focus\:text-destructive:focus { + color: var(--destructive); + } + .focus\:ring-1:focus { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(1px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .focus\:ring-2:focus { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(2px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .focus\:ring-ring:focus { + --tw-ring-color: var(--ring); + } + .focus\:ring-white\/20:focus { + --tw-ring-color: #fff3; + } + @supports (color: color-mix(in lab, red, red)) { + .focus\:ring-white\/20:focus { + --tw-ring-color: color-mix( + in oklab, + var(--color-white) 20%, + transparent + ); + } + } + .focus\:ring-offset-2:focus { + --tw-ring-offset-width: 2px; + --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 + var(--tw-ring-offset-width) var(--tw-ring-offset-color); + } + .focus\:outline-hidden:focus { + --tw-outline-style: none; + outline-style: none; + } + @media (forced-colors: active) { + .focus\:outline-hidden:focus { + outline-offset: 2px; + outline: 2px solid #0000; + } + } + .focus\:outline-none:focus { + --tw-outline-style: none; + outline-style: none; + } + .focus-visible\:z-10:focus-visible { + z-index: 10; + } + .focus-visible\:border-ring:focus-visible { + border-color: var(--ring); + } + .focus-visible\:ring-0:focus-visible { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(0px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .focus-visible\:ring-1:focus-visible { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(1px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .focus-visible\:ring-2:focus-visible { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(2px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .focus-visible\:ring-4:focus-visible { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(4px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .focus-visible\:ring-\[3px\]:focus-visible { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .focus-visible\:ring-destructive\/20:focus-visible { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .focus-visible\:ring-destructive\/20:focus-visible { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 20%, + transparent + ); + } + } + .focus-visible\:ring-ring:focus-visible, + .focus-visible\:ring-ring\/50:focus-visible { + --tw-ring-color: var(--ring); + } + @supports (color: color-mix(in lab, red, red)) { + .focus-visible\:ring-ring\/50:focus-visible { + --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + .focus-visible\:ring-offset-1:focus-visible { + --tw-ring-offset-width: 1px; + --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 + var(--tw-ring-offset-width) var(--tw-ring-offset-color); + } + .focus-visible\:outline-hidden:focus-visible { + --tw-outline-style: none; + outline-style: none; + } + @media (forced-colors: active) { + .focus-visible\:outline-hidden:focus-visible { + outline-offset: 2px; + outline: 2px solid #0000; + } + } + .focus-visible\:outline-1:focus-visible { + outline-style: var(--tw-outline-style); + outline-width: 1px; + } + .focus-visible\:outline-ring:focus-visible { + outline-color: var(--ring); + } + .active\:bg-sidebar-accent:active { + background-color: var(--sidebar-accent); + } + .active\:text-sidebar-accent-foreground:active { + color: var(--sidebar-accent-foreground); + } + .disabled\:pointer-events-none:disabled { + pointer-events: none; + } + .disabled\:cursor-not-allowed:disabled { + cursor: not-allowed; + } + .disabled\:opacity-50:disabled { + opacity: 0.5; + } + .disabled\:opacity-70:disabled { + opacity: 0.7; + } + :where([data-side="left"]) .in-data-\[side\=left\]\:cursor-w-resize { + cursor: w-resize; + } + :where([data-side="right"]) .in-data-\[side\=right\]\:cursor-e-resize { + cursor: e-resize; + } + .has-focus\:border-ring:has(:focus) { + border-color: var(--ring); + } + .has-focus\:ring-\[3px\]:has(:focus) { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .has-focus\:ring-ring\/50:has(:focus) { + --tw-ring-color: var(--ring); + } + @supports (color: color-mix(in lab, red, red)) { + .has-focus\:ring-ring\/50:has(:focus) { + --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + .has-disabled\:opacity-50:has(:disabled) { + opacity: 0.5; + } + .has-data-\[slot\=card-action\]\:grid-cols-\[1fr_auto\]:has( + [data-slot="card-action"] + ) { + grid-template-columns: 1fr auto; + } + .has-data-\[state\=checked\]\:border-primary:has([data-state="checked"]) { + border-color: var(--primary); + } + .has-data-\[state\=checked\]\:bg-primary\/5:has([data-state="checked"]) { + background-color: var(--primary); + } + @supports (color: color-mix(in lab, red, red)) { + .has-data-\[state\=checked\]\:bg-primary\/5:has( + [data-state="checked"] + ) { + background-color: color-mix( + in oklab, + var(--primary) 5%, + transparent + ); + } + } + .has-data-\[variant\=inset\]\:bg-sidebar:has([data-variant="inset"]) { + background-color: var(--sidebar); + } + .has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:border-ring:has( + [data-slot="input-group-control"]:focus-visible + ) { + border-color: var(--ring); + } + .has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-\[3px\]:has( + [data-slot="input-group-control"]:focus-visible + ) { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-ring\/50:has( + [data-slot="input-group-control"]:focus-visible + ) { + --tw-ring-color: var(--ring); + } + @supports (color: color-mix(in lab, red, red)) { + .has-\[\[data-slot\=input-group-control\]\:focus-visible\]\:ring-ring\/50:has( + [data-slot="input-group-control"]:focus-visible + ) { + --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + .has-\[\[data-slot\]\[aria-invalid\=true\]\]\:border-destructive:has( + [data-slot][aria-invalid="true"] + ) { + border-color: var(--destructive); + } + .has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/20:has( + [data-slot][aria-invalid="true"] + ) { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/20:has( + [data-slot][aria-invalid="true"] + ) { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 20%, + transparent + ); + } + } + .has-\[\>\[data-align\=block-end\]\]\:h-auto:has( + > [data-align="block-end"] + ) { + height: auto; + } + .has-\[\>\[data-align\=block-end\]\]\:flex-col:has( + > [data-align="block-end"] + ) { + flex-direction: column; + } + .has-\[\>\[data-align\=block-start\]\]\:h-auto:has( + > [data-align="block-start"] + ) { + height: auto; + } + .has-\[\>\[data-align\=block-start\]\]\:flex-col:has( + > [data-align="block-start"] + ) { + flex-direction: column; + } + .has-\[\>\[data-slot\=button-group\]\]\:gap-2:has( + > [data-slot="button-group"] + ) { + gap: calc(var(--spacing) * 2); + } + .has-\[\>\[data-slot\=checkbox-group\]\]\:gap-3:has( + > [data-slot="checkbox-group"] + ) { + gap: calc(var(--spacing) * 3); + } + .has-\[\>\[data-slot\=field-content\]\]\:items-start:has( + > [data-slot="field-content"] + ) { + align-items: flex-start; + } + .has-\[\>\[data-slot\=field\]\]\:w-full:has(> [data-slot="field"]) { + width: 100%; + } + .has-\[\>\[data-slot\=field\]\]\:flex-col:has(> [data-slot="field"]) { + flex-direction: column; + } + .has-\[\>\[data-slot\=field\]\]\:rounded-md:has(> [data-slot="field"]) { + border-radius: calc(var(--radius) - 3px); + } + .has-\[\>\[data-slot\=field\]\]\:border:has(> [data-slot="field"]) { + border-style: var(--tw-border-style); + border-width: 1px; + } + .has-\[\>\[data-slot\=radio-group\]\]\:gap-3:has( + > [data-slot="radio-group"] + ) { + gap: calc(var(--spacing) * 3); + } + .has-\[\>button\]\:mr-\[-0\.45rem\]:has(> button) { + margin-right: -0.45rem; + } + .has-\[\>button\]\:ml-\[-0\.45rem\]:has(> button) { + margin-left: -0.45rem; + } + .has-\[\>kbd\]\:mr-\[-0\.35rem\]:has(> kbd) { + margin-right: -0.35rem; + } + .has-\[\>kbd\]\:ml-\[-0\.35rem\]:has(> kbd) { + margin-left: -0.35rem; + } + .has-\[\>svg\]\:grid-cols-\[calc\(var\(--spacing\)\*4\)_1fr\]:has(> svg) { + grid-template-columns: calc(var(--spacing) * 4) 1fr; + } + .has-\[\>svg\]\:gap-x-3:has(> svg) { + column-gap: calc(var(--spacing) * 3); + } + .has-\[\>svg\]\:p-0:has(> svg) { + padding: calc(var(--spacing) * 0); + } + .has-\[\>svg\]\:px-2:has(> svg) { + padding-inline: calc(var(--spacing) * 2); + } + .has-\[\>svg\]\:px-2\.5:has(> svg) { + padding-inline: calc(var(--spacing) * 2.5); + } + .has-\[\>svg\]\:px-3:has(> svg) { + padding-inline: calc(var(--spacing) * 3); + } + .has-\[\>svg\]\:px-4:has(> svg) { + padding-inline: calc(var(--spacing) * 4); + } + .has-\[\>textarea\]\:h-auto:has(> textarea) { + height: auto; + } + .aria-disabled\:pointer-events-none[aria-disabled="true"] { + pointer-events: none; + } + .aria-disabled\:opacity-50[aria-disabled="true"] { + opacity: 0.5; + } + .aria-invalid\:border-destructive[aria-invalid="true"] { + border-color: var(--destructive); + } + .aria-invalid\:ring-destructive\/20[aria-invalid="true"] { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .aria-invalid\:ring-destructive\/20[aria-invalid="true"] { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 20%, + transparent + ); + } + } + .aria-selected\:text-muted-foreground[aria-selected="true"] { + color: var(--muted-foreground); + } + .data-\[active\=true\]\:z-10[data-active="true"] { + z-index: 10; + } + .data-\[active\=true\]\:border-ring[data-active="true"] { + border-color: var(--ring); + } + .data-\[active\=true\]\:bg-accent\/50[data-active="true"] { + background-color: var(--accent); + } + @supports (color: color-mix(in lab, red, red)) { + .data-\[active\=true\]\:bg-accent\/50[data-active="true"] { + background-color: color-mix( + in oklab, + var(--accent) 50%, + transparent + ); + } + } + .data-\[active\=true\]\:bg-sidebar-accent[data-active="true"] { + background-color: var(--sidebar-accent); + } + .data-\[active\=true\]\:font-medium[data-active="true"] { + --tw-font-weight: var(--font-weight-medium); + font-weight: var(--font-weight-medium); + } + .data-\[active\=true\]\:text-accent-foreground[data-active="true"] { + color: var(--accent-foreground); + } + .data-\[active\=true\]\:text-sidebar-accent-foreground[data-active="true"] { + color: var(--sidebar-accent-foreground); + } + .data-\[active\=true\]\:ring-\[3px\][data-active="true"] { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(3px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .data-\[active\=true\]\:ring-ring\/50[data-active="true"] { + --tw-ring-color: var(--ring); + } + @supports (color: color-mix(in lab, red, red)) { + .data-\[active\=true\]\:ring-ring\/50[data-active="true"] { + --tw-ring-color: color-mix(in oklab, var(--ring) 50%, transparent); + } + } + @media (hover: hover) { + .data-\[active\=true\]\:hover\:bg-accent[data-active="true"]:hover { + background-color: var(--accent); + } + } + .data-\[active\=true\]\:focus\:bg-accent[data-active="true"]:focus { + background-color: var(--accent); + } + .data-\[active\=true\]\:aria-invalid\:border-destructive[data-active="true"][aria-invalid="true"] { + border-color: var(--destructive); + } + .data-\[active\=true\]\:aria-invalid\:ring-destructive\/20[data-active="true"][aria-invalid="true"] { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .data-\[active\=true\]\:aria-invalid\:ring-destructive\/20[data-active="true"][aria-invalid="true"] { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 20%, + transparent + ); + } + } + .data-\[disabled\]\:pointer-events-none[data-disabled] { + pointer-events: none; + } + .data-\[disabled\]\:opacity-50[data-disabled] { + opacity: 0.5; + } + .data-\[disabled\=true\]\:pointer-events-none[data-disabled="true"] { + pointer-events: none; + } + .data-\[disabled\=true\]\:opacity-50[data-disabled="true"] { + opacity: 0.5; + } + .data-\[error\=true\]\:text-destructive[data-error="true"] { + color: var(--destructive); + } + .data-\[inset\]\:pl-8[data-inset] { + padding-left: calc(var(--spacing) * 8); + } + .data-\[invalid\=true\]\:text-destructive[data-invalid="true"] { + color: var(--destructive); + } + .data-\[motion\=from-end\]\:slide-in-from-right-52[data-motion="from-end"] { + --tw-enter-translate-x: calc(52 * var(--spacing)); + } + .data-\[motion\=from-start\]\:slide-in-from-left-52[data-motion="from-start"] { + --tw-enter-translate-x: calc(52 * var(--spacing) * -1); + } + .data-\[motion\=to-end\]\:slide-out-to-right-52[data-motion="to-end"] { + --tw-exit-translate-x: calc(52 * var(--spacing)); + } + .data-\[motion\=to-start\]\:slide-out-to-left-52[data-motion="to-start"] { + --tw-exit-translate-x: calc(52 * var(--spacing) * -1); + } + .data-\[motion\^\=from-\]\:animate-in[data-motion^="from-"] { + animation: enter var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[motion\^\=from-\]\:fade-in[data-motion^="from-"] { + --tw-enter-opacity: 0; + } + .data-\[motion\^\=to-\]\:animate-out[data-motion^="to-"] { + animation: exit var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[motion\^\=to-\]\:fade-out[data-motion^="to-"] { + --tw-exit-opacity: 0; + } + .data-\[orientation\=horizontal\]\:h-1\.5[data-orientation="horizontal"] { + height: calc(var(--spacing) * 1.5); + } + .data-\[orientation\=horizontal\]\:h-full[data-orientation="horizontal"] { + height: 100%; + } + .data-\[orientation\=horizontal\]\:h-px[data-orientation="horizontal"] { + height: 1px; + } + .data-\[orientation\=horizontal\]\:w-full[data-orientation="horizontal"] { + width: 100%; + } + .data-\[orientation\=vertical\]\:h-auto[data-orientation="vertical"] { + height: auto; + } + .data-\[orientation\=vertical\]\:h-full[data-orientation="vertical"] { + height: 100%; + } + .data-\[orientation\=vertical\]\:min-h-44[data-orientation="vertical"] { + min-height: calc(var(--spacing) * 44); + } + .data-\[orientation\=vertical\]\:w-1\.5[data-orientation="vertical"] { + width: calc(var(--spacing) * 1.5); + } + .data-\[orientation\=vertical\]\:w-auto[data-orientation="vertical"] { + width: auto; + } + .data-\[orientation\=vertical\]\:w-full[data-orientation="vertical"] { + width: 100%; + } + .data-\[orientation\=vertical\]\:w-px[data-orientation="vertical"] { + width: 1px; + } + .data-\[orientation\=vertical\]\:flex-col[data-orientation="vertical"] { + flex-direction: column; + } + .data-\[panel-group-direction\=vertical\]\:h-px[data-panel-group-direction="vertical"] { + height: 1px; + } + .data-\[panel-group-direction\=vertical\]\:w-full[data-panel-group-direction="vertical"] { + width: 100%; + } + .data-\[panel-group-direction\=vertical\]\:flex-col[data-panel-group-direction="vertical"] { + flex-direction: column; + } + .data-\[panel-group-direction\=vertical\]\:after\:left-0[data-panel-group-direction="vertical"]:after { + content: var(--tw-content); + left: calc(var(--spacing) * 0); + } + .data-\[panel-group-direction\=vertical\]\:after\:h-1[data-panel-group-direction="vertical"]:after { + content: var(--tw-content); + height: calc(var(--spacing) * 1); + } + .data-\[panel-group-direction\=vertical\]\:after\:w-full[data-panel-group-direction="vertical"]:after { + content: var(--tw-content); + width: 100%; + } + .data-\[panel-group-direction\=vertical\]\:after\:translate-x-0[data-panel-group-direction="vertical"]:after { + content: var(--tw-content); + --tw-translate-x: calc(var(--spacing) * 0); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[panel-group-direction\=vertical\]\:after\:-translate-y-1\/2[data-panel-group-direction="vertical"]:after { + content: var(--tw-content); + --tw-translate-y: -50%; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[placeholder\]\:text-muted-foreground[data-placeholder] { + color: var(--muted-foreground); + } + .data-\[range-end\=true\]\:rounded-md[data-range-end="true"] { + border-radius: calc(var(--radius) - 3px); + } + .data-\[range-end\=true\]\:rounded-r-md[data-range-end="true"] { + border-top-right-radius: calc(var(--radius) - 3px); + border-bottom-right-radius: calc(var(--radius) - 3px); + } + .data-\[range-end\=true\]\:bg-primary[data-range-end="true"] { + background-color: var(--primary); + } + .data-\[range-end\=true\]\:text-primary-foreground[data-range-end="true"] { + color: var(--primary-foreground); + } + .data-\[range-middle\=true\]\:rounded-none[data-range-middle="true"] { + border-radius: 0; + } + .data-\[range-middle\=true\]\:bg-accent[data-range-middle="true"] { + background-color: var(--accent); + } + .data-\[range-middle\=true\]\:text-accent-foreground[data-range-middle="true"] { + color: var(--accent-foreground); + } + .data-\[range-start\=true\]\:rounded-md[data-range-start="true"] { + border-radius: calc(var(--radius) - 3px); + } + .data-\[range-start\=true\]\:rounded-l-md[data-range-start="true"] { + border-top-left-radius: calc(var(--radius) - 3px); + border-bottom-left-radius: calc(var(--radius) - 3px); + } + .data-\[range-start\=true\]\:bg-primary[data-range-start="true"] { + background-color: var(--primary); + } + .data-\[range-start\=true\]\:text-primary-foreground[data-range-start="true"] { + color: var(--primary-foreground); + } + .data-\[selected-single\=true\]\:bg-primary[data-selected-single="true"] { + background-color: var(--primary); + } + .data-\[selected-single\=true\]\:text-primary-foreground[data-selected-single="true"] { + color: var(--primary-foreground); + } + .data-\[selected\=true\]\:rounded-none[data-selected="true"] { + border-radius: 0; + } + .data-\[selected\=true\]\:bg-accent[data-selected="true"] { + background-color: var(--accent); + } + .data-\[selected\=true\]\:text-accent-foreground[data-selected="true"] { + color: var(--accent-foreground); + } + .data-\[side\=bottom\]\:translate-y-1[data-side="bottom"] { + --tw-translate-y: calc(var(--spacing) * 1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[side\=bottom\]\:slide-in-from-top-2[data-side="bottom"] { + --tw-enter-translate-y: calc(2 * var(--spacing) * -1); + } + .data-\[side\=left\]\:-translate-x-1[data-side="left"] { + --tw-translate-x: calc(var(--spacing) * -1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[side\=left\]\:slide-in-from-right-2[data-side="left"] { + --tw-enter-translate-x: calc(2 * var(--spacing)); + } + .data-\[side\=right\]\:translate-x-1[data-side="right"] { + --tw-translate-x: calc(var(--spacing) * 1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[side\=right\]\:slide-in-from-left-2[data-side="right"] { + --tw-enter-translate-x: calc(2 * var(--spacing) * -1); + } + .data-\[side\=top\]\:-translate-y-1[data-side="top"] { + --tw-translate-y: calc(var(--spacing) * -1); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[side\=top\]\:slide-in-from-bottom-2[data-side="top"] { + --tw-enter-translate-y: calc(2 * var(--spacing)); + } + .data-\[size\=default\]\:h-9[data-size="default"] { + height: calc(var(--spacing) * 9); + } + .data-\[size\=sm\]\:h-8[data-size="sm"] { + height: calc(var(--spacing) * 8); + } + :is( + .\*\:data-\[slot\=alert-description\]\:text-destructive\/90 > * + )[data-slot="alert-description"] { + color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + :is( + .\*\:data-\[slot\=alert-description\]\:text-destructive\/90 > * + )[data-slot="alert-description"] { + color: color-mix(in oklab, var(--destructive) 90%, transparent); + } + } + .data-\[slot\=checkbox-group\]\:gap-3[data-slot="checkbox-group"] { + gap: calc(var(--spacing) * 3); + } + :is( + .\*\*\:data-\[slot\=command-input-wrapper\]\:h-12 * + )[data-slot="command-input-wrapper"] { + height: calc(var(--spacing) * 12); + } + :is( + .\*\*\:data-\[slot\=navigation-menu-link\]\:focus\:ring-0 * + )[data-slot="navigation-menu-link"]:focus { + --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 + calc(0px + var(--tw-ring-offset-width)) + var(--tw-ring-color, currentcolor); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + :is( + .\*\*\:data-\[slot\=navigation-menu-link\]\:focus\:outline-none * + )[data-slot="navigation-menu-link"]:focus { + --tw-outline-style: none; + outline-style: none; + } + :is( + .\*\:data-\[slot\=select-value\]\:line-clamp-1 > * + )[data-slot="select-value"] { + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + display: -webkit-box; + overflow: hidden; + } + :is(.\*\:data-\[slot\=select-value\]\:flex > *)[data-slot="select-value"] { + display: flex; + } + :is( + .\*\:data-\[slot\=select-value\]\:items-center > * + )[data-slot="select-value"] { + align-items: center; + } + :is(.\*\:data-\[slot\=select-value\]\:gap-2 > *)[data-slot="select-value"] { + gap: calc(var(--spacing) * 2); + } + .data-\[state\=active\]\:bg-background[data-state="active"] { + background-color: var(--background); + } + .data-\[state\=active\]\:shadow-sm[data-state="active"] { + --tw-shadow: + 0 1px 3px 0 var(--tw-shadow-color, #0000001a), + 0 1px 2px -1px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .data-\[state\=checked\]\:translate-x-\[calc\(100\%-2px\)\][data-state="checked"] { + --tw-translate-x: calc(100% - 2px); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[state\=checked\]\:border-primary[data-state="checked"] { + border-color: var(--primary); + } + .data-\[state\=checked\]\:bg-primary[data-state="checked"] { + background-color: var(--primary); + } + .data-\[state\=checked\]\:text-primary-foreground[data-state="checked"] { + color: var(--primary-foreground); + } + .data-\[state\=closed\]\:animate-accordion-up[data-state="closed"] { + animation: accordion-up + var(--tw-animation-duration, var(--tw-duration, 0.2s)) + var(--tw-ease, ease-out) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[state\=closed\]\:animate-out[data-state="closed"] { + animation: exit var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[state\=closed\]\:duration-300[data-state="closed"] { + --tw-duration: 0.3s; + transition-duration: 0.3s; + } + .data-\[state\=closed\]\:fade-out-0[data-state="closed"] { + --tw-exit-opacity: 0; + } + .data-\[state\=closed\]\:zoom-out-95[data-state="closed"] { + --tw-exit-scale: 0.95; + } + .data-\[state\=closed\]\:slide-out-to-bottom[data-state="closed"] { + --tw-exit-translate-y: 100%; + } + .data-\[state\=closed\]\:slide-out-to-left[data-state="closed"] { + --tw-exit-translate-x: -100%; + } + .data-\[state\=closed\]\:slide-out-to-right[data-state="closed"] { + --tw-exit-translate-x: 100%; + } + .data-\[state\=closed\]\:slide-out-to-top[data-state="closed"] { + --tw-exit-translate-y: -100%; + } + .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=closed\]\:animate-out:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + )[data-state="closed"] { + animation: exit var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=closed\]\:fade-out-0:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + )[data-state="closed"] { + --tw-exit-opacity: 0; + } + .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=closed\]\:zoom-out-95:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + )[data-state="closed"] { + --tw-exit-scale: 0.95; + } + .data-\[state\=hidden\]\:animate-out[data-state="hidden"] { + animation: exit var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[state\=hidden\]\:fade-out[data-state="hidden"] { + --tw-exit-opacity: 0; + } + .data-\[state\=on\]\:bg-accent[data-state="on"] { + background-color: var(--accent); + } + .data-\[state\=on\]\:text-accent-foreground[data-state="on"] { + color: var(--accent-foreground); + } + .data-\[state\=open\]\:animate-accordion-down[data-state="open"] { + animation: accordion-down + var(--tw-animation-duration, var(--tw-duration, 0.2s)) + var(--tw-ease, ease-out) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[state\=open\]\:animate-in[data-state="open"] { + animation: enter var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[state\=open\]\:bg-accent[data-state="open"], + .data-\[state\=open\]\:bg-accent\/50[data-state="open"] { + background-color: var(--accent); + } + @supports (color: color-mix(in lab, red, red)) { + .data-\[state\=open\]\:bg-accent\/50[data-state="open"] { + background-color: color-mix( + in oklab, + var(--accent) 50%, + transparent + ); + } + } + .data-\[state\=open\]\:bg-secondary[data-state="open"] { + background-color: var(--secondary); + } + .data-\[state\=open\]\:text-accent-foreground[data-state="open"] { + color: var(--accent-foreground); + } + .data-\[state\=open\]\:text-muted-foreground[data-state="open"] { + color: var(--muted-foreground); + } + .data-\[state\=open\]\:opacity-100[data-state="open"] { + opacity: 1; + } + .data-\[state\=open\]\:duration-500[data-state="open"] { + --tw-duration: 0.5s; + transition-duration: 0.5s; + } + .data-\[state\=open\]\:fade-in-0[data-state="open"] { + --tw-enter-opacity: 0; + } + .data-\[state\=open\]\:zoom-in-90[data-state="open"] { + --tw-enter-scale: 0.9; + } + .data-\[state\=open\]\:zoom-in-95[data-state="open"] { + --tw-enter-scale: 0.95; + } + .data-\[state\=open\]\:slide-in-from-bottom[data-state="open"] { + --tw-enter-translate-y: 100%; + } + .data-\[state\=open\]\:slide-in-from-left[data-state="open"] { + --tw-enter-translate-x: -100%; + } + .data-\[state\=open\]\:slide-in-from-right[data-state="open"] { + --tw-enter-translate-x: 100%; + } + .data-\[state\=open\]\:slide-in-from-top[data-state="open"] { + --tw-enter-translate-y: -100%; + } + .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=open\]\:animate-in:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + )[data-state="open"] { + animation: enter var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=open\]\:fade-in-0:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + )[data-state="open"] { + --tw-enter-opacity: 0; + } + .group-data-\[viewport\=false\]\/navigation-menu\:data-\[state\=open\]\:zoom-in-95:is( + :where(.group\/navigation-menu)[data-viewport="false"] * + )[data-state="open"] { + --tw-enter-scale: 0.95; + } + @media (hover: hover) { + .data-\[state\=open\]\:hover\:bg-accent[data-state="open"]:hover { + background-color: var(--accent); + } + .data-\[state\=open\]\:hover\:bg-sidebar-accent[data-state="open"]:hover { + background-color: var(--sidebar-accent); + } + .data-\[state\=open\]\:hover\:text-sidebar-accent-foreground[data-state="open"]:hover { + color: var(--sidebar-accent-foreground); + } + } + .data-\[state\=open\]\:focus\:bg-accent[data-state="open"]:focus { + background-color: var(--accent); + } + .data-\[state\=selected\]\:bg-muted[data-state="selected"] { + background-color: var(--muted); + } + .data-\[state\=unchecked\]\:translate-x-0[data-state="unchecked"] { + --tw-translate-x: calc(var(--spacing) * 0); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .data-\[state\=unchecked\]\:bg-input[data-state="unchecked"] { + background-color: var(--input); + } + .data-\[state\=visible\]\:animate-in[data-state="visible"] { + animation: enter var(--tw-animation-duration, var(--tw-duration, 0.15s)) + var(--tw-ease, ease) var(--tw-animation-delay, 0s) + var(--tw-animation-iteration-count, 1) + var(--tw-animation-direction, normal) + var(--tw-animation-fill-mode, none); + } + .data-\[state\=visible\]\:fade-in[data-state="visible"] { + --tw-enter-opacity: 0; + } + .data-\[variant\=destructive\]\:text-destructive[data-variant="destructive"] { + color: var(--destructive); + } + .data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant="destructive"]:focus { + background-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .data-\[variant\=destructive\]\:focus\:bg-destructive\/10[data-variant="destructive"]:focus { + background-color: color-mix( + in oklab, + var(--destructive) 10%, + transparent + ); + } + } + .data-\[variant\=destructive\]\:focus\:text-destructive[data-variant="destructive"]:focus { + color: var(--destructive); + } + .data-\[variant\=label\]\:text-sm[data-variant="label"] { + font-size: var(--text-sm); + line-height: var(--tw-leading, var(--text-sm--line-height)); + } + .data-\[variant\=legend\]\:text-base[data-variant="legend"] { + font-size: var(--text-base); + line-height: var(--tw-leading, var(--text-base--line-height)); + } + .data-\[variant\=outline\]\:border-l-0[data-variant="outline"] { + border-left-style: var(--tw-border-style); + border-left-width: 0; + } + .data-\[variant\=outline\]\:shadow-xs[data-variant="outline"] { + --tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, #0000000d); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .data-\[variant\=outline\]\:first\:border-l[data-variant="outline"]:first-child { + border-left-style: var(--tw-border-style); + border-left-width: 1px; + } + .data-\[vaul-drawer-direction\=bottom\]\:inset-x-0[data-vaul-drawer-direction="bottom"] { + inset-inline: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=bottom\]\:bottom-0[data-vaul-drawer-direction="bottom"] { + bottom: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=bottom\]\:mt-24[data-vaul-drawer-direction="bottom"] { + margin-top: calc(var(--spacing) * 24); + } + .data-\[vaul-drawer-direction\=bottom\]\:max-h-\[80vh\][data-vaul-drawer-direction="bottom"] { + max-height: 80vh; + } + .data-\[vaul-drawer-direction\=bottom\]\:rounded-t-lg[data-vaul-drawer-direction="bottom"] { + border-top-left-radius: var(--radius); + border-top-right-radius: var(--radius); + } + .data-\[vaul-drawer-direction\=bottom\]\:border-t[data-vaul-drawer-direction="bottom"] { + border-top-style: var(--tw-border-style); + border-top-width: 1px; + } + .data-\[vaul-drawer-direction\=left\]\:inset-y-0[data-vaul-drawer-direction="left"] { + inset-block: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=left\]\:left-0[data-vaul-drawer-direction="left"] { + left: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=left\]\:w-3\/4[data-vaul-drawer-direction="left"] { + width: 75%; + } + .data-\[vaul-drawer-direction\=left\]\:border-r[data-vaul-drawer-direction="left"] { + border-right-style: var(--tw-border-style); + border-right-width: 1px; + } + .data-\[vaul-drawer-direction\=right\]\:inset-y-0[data-vaul-drawer-direction="right"] { + inset-block: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=right\]\:right-0[data-vaul-drawer-direction="right"] { + right: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=right\]\:w-3\/4[data-vaul-drawer-direction="right"] { + width: 75%; + } + .data-\[vaul-drawer-direction\=right\]\:border-l[data-vaul-drawer-direction="right"] { + border-left-style: var(--tw-border-style); + border-left-width: 1px; + } + .data-\[vaul-drawer-direction\=top\]\:inset-x-0[data-vaul-drawer-direction="top"] { + inset-inline: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=top\]\:top-0[data-vaul-drawer-direction="top"] { + top: calc(var(--spacing) * 0); + } + .data-\[vaul-drawer-direction\=top\]\:mb-24[data-vaul-drawer-direction="top"] { + margin-bottom: calc(var(--spacing) * 24); + } + .data-\[vaul-drawer-direction\=top\]\:max-h-\[80vh\][data-vaul-drawer-direction="top"] { + max-height: 80vh; + } + .data-\[vaul-drawer-direction\=top\]\:rounded-b-lg[data-vaul-drawer-direction="top"] { + border-bottom-right-radius: var(--radius); + border-bottom-left-radius: var(--radius); + } + .data-\[vaul-drawer-direction\=top\]\:border-b[data-vaul-drawer-direction="top"] { + border-bottom-style: var(--tw-border-style); + border-bottom-width: 1px; + } + .nth-last-2\:-mt-1:nth-last-child(2) { + margin-top: calc(var(--spacing) * -1); + } + @supports ( + (-webkit-backdrop-filter: var(--tw)) or (backdrop-filter: var(--tw)) + ) { + .supports-\[backdrop-filter\]\:backdrop-blur { + --tw-backdrop-blur: blur(8px); + -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,); + } + } + @media (min-width: 40rem) { + .sm\:col-span-2 { + grid-column: span 2 / span 2; + } + .sm\:m-4 { + margin: calc(var(--spacing) * 4); + } + .sm\:block { + display: block; + } + .sm\:flex { + display: flex; + } + .sm\:max-w-\[425px\] { + max-width: 425px; + } + .sm\:max-w-lg { + max-width: var(--container-lg); + } + .sm\:max-w-sm { + max-width: var(--container-sm); + } + .sm\:flex-none { + flex: none; + } + .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\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + .sm\:flex-row { + flex-direction: row; + } + .sm\:flex-wrap { + flex-wrap: wrap; + } + .sm\:items-center { + align-items: center; + } + .sm\:items-start { + align-items: flex-start; + } + .sm\:justify-between { + justify-content: space-between; + } + .sm\:justify-end { + justify-content: flex-end; + } + .sm\:gap-2\.5 { + gap: calc(var(--spacing) * 2.5); + } + .sm\:p-5 { + padding: calc(var(--spacing) * 5); + } + .sm\:p-6 { + padding: calc(var(--spacing) * 6); + } + .sm\:p-8 { + padding: calc(var(--spacing) * 8); + } + .sm\:p-12 { + padding: calc(var(--spacing) * 12); + } + .sm\:px-4 { + padding-inline: calc(var(--spacing) * 4); + } + .sm\:px-6 { + padding-inline: calc(var(--spacing) * 6); + } + .sm\:px-8 { + padding-inline: calc(var(--spacing) * 8); + } + .sm\:py-10 { + padding-block: calc(var(--spacing) * 10); + } + .sm\:py-16 { + padding-block: calc(var(--spacing) * 16); + } + .sm\:pr-2\.5 { + padding-right: calc(var(--spacing) * 2.5); + } + .sm\:pl-2\.5 { + padding-left: calc(var(--spacing) * 2.5); + } + .sm\:text-left { + text-align: left; + } + .sm\:text-3xl { + font-size: var(--text-3xl); + line-height: var(--tw-leading, var(--text-3xl--line-height)); + } + .sm\:text-4xl { + font-size: var(--text-4xl); + line-height: var(--tw-leading, var(--text-4xl--line-height)); + } + .sm\:text-5xl { + font-size: var(--text-5xl); + line-height: var(--tw-leading, var(--text-5xl--line-height)); + } + .sm\:text-6xl { + font-size: var(--text-6xl); + line-height: var(--tw-leading, var(--text-6xl--line-height)); + } + .sm\:text-base { + font-size: var(--text-base); + line-height: var(--tw-leading, var(--text-base--line-height)); + } + .sm\:text-lg { + font-size: var(--text-lg); + line-height: var(--tw-leading, var(--text-lg--line-height)); + } + .sm\:text-xl { + font-size: var(--text-xl); + line-height: var(--tw-leading, var(--text-xl--line-height)); + } + .sm\:text-\[0\.98rem\] { + font-size: 0.98rem; + } + .sm\:text-\[1\.9rem\] { + font-size: 1.9rem; + } + .data-\[vaul-drawer-direction\=left\]\:sm\:max-w-sm[data-vaul-drawer-direction="left"], + .data-\[vaul-drawer-direction\=right\]\:sm\:max-w-sm[data-vaul-drawer-direction="right"] { + max-width: var(--container-sm); + } + } + @media (min-width: 48rem) { + .md\:absolute { + position: absolute; + } + .md\:col-span-2 { + grid-column: span 2 / span 2; + } + .md\:block { + display: block; + } + .md\:flex { + display: flex; + } + .md\:w-\[var\(--radix-navigation-menu-viewport-width\)\] { + width: var(--radix-navigation-menu-viewport-width); + } + .md\:w-auto { + width: auto; + } + .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-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + .md\:grid-cols-\[0\.86fr_1\.14fr\] { + grid-template-columns: 0.86fr 1.14fr; + } + .md\:grid-cols-\[1\.1fr_1\.5fr_1\.2fr_1fr_1\.6fr\] { + grid-template-columns: 1.1fr 1.5fr 1.2fr 1fr 1.6fr; + } + .md\:flex-row { + flex-direction: row; + } + .md\:gap-1\.5 { + gap: calc(var(--spacing) * 1.5); + } + :where(.md\:divide-x > :not(:last-child)) { + --tw-divide-x-reverse: 0; + border-inline-style: var(--tw-border-style); + border-inline-start-width: calc(1px * var(--tw-divide-x-reverse)); + border-inline-end-width: calc( + 1px * calc(1 - var(--tw-divide-x-reverse)) + ); + } + :where(.md\:divide-y-0 > :not(:last-child)) { + --tw-divide-y-reverse: 0; + border-bottom-style: var(--tw-border-style); + border-top-style: var(--tw-border-style); + border-top-width: calc(0px * var(--tw-divide-y-reverse)); + border-bottom-width: calc( + 0px * calc(1 - var(--tw-divide-y-reverse)) + ); + } + .md\:p-5 { + padding: calc(var(--spacing) * 5); + } + .md\:p-7 { + padding: calc(var(--spacing) * 7); + } + .md\:p-12 { + padding: calc(var(--spacing) * 12); + } + .md\:text-left { + text-align: left; + } + .md\:text-sm { + font-size: var(--text-sm); + line-height: var(--tw-leading, var(--text-sm--line-height)); + } + .md\:opacity-0 { + opacity: 0; + } + .md\:peer-data-\[variant\=inset\]\:m-2:is( + :where(.peer)[data-variant="inset"] ~ * + ) { + margin: calc(var(--spacing) * 2); + } + .md\:peer-data-\[variant\=inset\]\:ml-0:is( + :where(.peer)[data-variant="inset"] ~ * + ) { + margin-left: calc(var(--spacing) * 0); + } + .md\:peer-data-\[variant\=inset\]\:rounded-xl:is( + :where(.peer)[data-variant="inset"] ~ * + ) { + border-radius: calc(var(--radius) + 8px); + } + .md\:peer-data-\[variant\=inset\]\:shadow-sm:is( + :where(.peer)[data-variant="inset"] ~ * + ) { + --tw-shadow: + 0 1px 3px 0 var(--tw-shadow-color, #0000001a), + 0 1px 2px -1px var(--tw-shadow-color, #0000001a); + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + .md\:peer-data-\[variant\=inset\]\:peer-data-\[state\=collapsed\]\:ml-2:is( + :where(.peer)[data-variant="inset"] ~ * + ):is(:where(.peer)[data-state="collapsed"] ~ *) { + margin-left: calc(var(--spacing) * 2); + } + .md\:after\:hidden:after { + content: var(--tw-content); + display: none; + } + } + @media (min-width: 64rem) { + .lg\:col-span-1 { + grid-column: span 1 / span 1; + } + .lg\:col-span-2 { + grid-column: span 2 / span 2; + } + .lg\:block { + display: block; + } + .lg\:flex { + display: flex; + } + .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-\[0\.9fr_1\.1fr\] { + grid-template-columns: 0.9fr 1.1fr; + } + .lg\:grid-cols-\[0\.88fr_1\.12fr\] { + grid-template-columns: 0.88fr 1.12fr; + } + .lg\:grid-cols-\[0\.92fr_1\.08fr\] { + grid-template-columns: 0.92fr 1.08fr; + } + .lg\:grid-cols-\[0\.94fr_1\.06fr\] { + grid-template-columns: 0.94fr 1.06fr; + } + .lg\:grid-cols-\[0\.95fr_1\.05fr\] { + grid-template-columns: 0.95fr 1.05fr; + } + .lg\:grid-cols-\[0\.98fr_1\.02fr\] { + grid-template-columns: 0.98fr 1.02fr; + } + .lg\:grid-cols-\[1\.1fr_0\.9fr\] { + grid-template-columns: 1.1fr 0.9fr; + } + .lg\:grid-cols-\[1\.02fr_0\.98fr\] { + grid-template-columns: 1.02fr 0.98fr; + } + .lg\:grid-cols-\[1\.05fr_0\.95fr\] { + grid-template-columns: 1.05fr 0.95fr; + } + .lg\:grid-cols-\[1\.06fr_0\.94fr\] { + grid-template-columns: 1.06fr 0.94fr; + } + .lg\:grid-cols-\[1\.08fr_0\.92fr\] { + grid-template-columns: 1.08fr 0.92fr; + } + .lg\:grid-cols-\[1\.15fr_0\.85fr_0\.85fr\] { + grid-template-columns: 1.15fr 0.85fr 0.85fr; + } + .lg\:grid-cols-\[1fr_0\.95fr\] { + grid-template-columns: 1fr 0.95fr; + } + .lg\:grid-cols-\[1fr_1\.2fr\] { + grid-template-columns: 1fr 1.2fr; + } + .lg\:grid-cols-\[1fr_1fr\] { + grid-template-columns: 1fr 1fr; + } + .lg\:flex-row { + flex-direction: row; + } + .lg\:flex-row-reverse { + flex-direction: row-reverse; + } + .lg\:items-center { + align-items: center; + } + .lg\:items-end { + align-items: flex-end; + } + .lg\:items-start { + align-items: flex-start; + } + .lg\:justify-between { + justify-content: space-between; + } + .lg\:p-10 { + padding: calc(var(--spacing) * 10); + } + .lg\:px-6 { + padding-inline: calc(var(--spacing) * 6); + } + .lg\:px-8 { + padding-inline: calc(var(--spacing) * 8); + } + .lg\:py-14 { + padding-block: calc(var(--spacing) * 14); + } + .lg\:pl-8 { + padding-left: calc(var(--spacing) * 8); + } + .lg\:text-7xl { + font-size: var(--text-7xl); + line-height: var(--tw-leading, var(--text-7xl--line-height)); + } + } + @media (min-width: 80rem) { + .xl\:col-span-1 { + grid-column: span 1 / span 1; + } + .xl\:col-span-2 { + grid-column: span 2 / span 2; + } + .xl\:flex { + display: flex; + } + .xl\:hidden { + display: none; + } + .xl\:h-full { + height: 100%; + } + .xl\:grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + .xl\:grid-cols-2 { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + .xl\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + .xl\:grid-cols-4 { + grid-template-columns: repeat(4, minmax(0, 1fr)); + } + .xl\:grid-cols-5 { + grid-template-columns: repeat(5, minmax(0, 1fr)); + } + .xl\:grid-cols-\[0\.95fr_1\.05fr\] { + grid-template-columns: 0.95fr 1.05fr; + } + .xl\:flex-row { + flex-direction: row; + } + .xl\:items-center { + align-items: center; + } + .xl\:justify-between { + justify-content: space-between; + } + } + @media (min-width: 96rem) { + .\32xl\:grid-cols-3 { + grid-template-columns: repeat(3, minmax(0, 1fr)); + } + } + @container field-group (min-width:28rem) { + .\@md\/field-group\:flex-row { + flex-direction: row; + } + .\@md\/field-group\:items-center { + align-items: center; + } + .\@md\/field-group\:has-\[\>\[data-slot\=field-content\]\]\:items-start:has( + > [data-slot="field-content"] + ) { + align-items: flex-start; + } + } + .dark\:border-blue-800:is(.dark *) { + border-color: var(--color-blue-800); + } + .dark\:border-gray-800:is(.dark *) { + border-color: var(--color-gray-800); + } + .dark\:border-green-800:is(.dark *) { + border-color: var(--color-green-800); + } + .dark\:border-input:is(.dark *) { + border-color: var(--input); + } + .dark\:border-red-800:is(.dark *) { + border-color: var(--color-red-800); + } + .dark\:bg-destructive\/60:is(.dark *) { + background-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:bg-destructive\/60:is(.dark *) { + background-color: color-mix( + in oklab, + var(--destructive) 60%, + transparent + ); + } + } + .dark\:bg-input\/30:is(.dark *) { + background-color: var(--input); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:bg-input\/30:is(.dark *) { + background-color: color-mix( + in oklab, + var(--input) 30%, + transparent + ); + } + } + .dark\:bg-transparent:is(.dark *) { + background-color: #0000; + } + .dark\:text-blue-400:is(.dark *) { + color: var(--color-blue-400); + } + .dark\:text-green-400:is(.dark *) { + color: var(--color-green-400); + } + .dark\:text-muted-foreground:is(.dark *) { + color: var(--muted-foreground); + } + .dark\:text-purple-400:is(.dark *) { + color: var(--color-purple-400); + } + @media (hover: hover) { + .dark\:hover\:bg-accent\/50:is(.dark *):hover { + background-color: var(--accent); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:hover\:bg-accent\/50:is(.dark *):hover { + background-color: color-mix( + in oklab, + var(--accent) 50%, + transparent + ); + } + } + .dark\:hover\:bg-input\/50:is(.dark *):hover { + background-color: var(--input); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:hover\:bg-input\/50:is(.dark *):hover { + background-color: color-mix( + in oklab, + var(--input) 50%, + transparent + ); + } + } + .dark\:hover\:text-accent-foreground:is(.dark *):hover { + color: var(--accent-foreground); + } + } + .dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:focus-visible\:ring-destructive\/40:is(.dark *):focus-visible { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 40%, + transparent + ); + } + } + .dark\:has-data-\[state\=checked\]\:bg-primary\/10:is(.dark *):has( + [data-state="checked"] + ) { + background-color: var(--primary); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:has-data-\[state\=checked\]\:bg-primary\/10:is(.dark *):has( + [data-state="checked"] + ) { + background-color: color-mix( + in oklab, + var(--primary) 10%, + transparent + ); + } + } + .dark\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/40:is( + .dark * + ):has([data-slot][aria-invalid="true"]) { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:has-\[\[data-slot\]\[aria-invalid\=true\]\]\:ring-destructive\/40:is( + .dark * + ):has([data-slot][aria-invalid="true"]) { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 40%, + transparent + ); + } + } + .dark\:aria-invalid\:ring-destructive\/40:is(.dark *)[aria-invalid="true"] { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:aria-invalid\:ring-destructive\/40:is( + .dark * + )[aria-invalid="true"] { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 40%, + transparent + ); + } + } + .dark\:data-\[active\=true\]\:aria-invalid\:ring-destructive\/40:is( + .dark * + )[data-active="true"][aria-invalid="true"] { + --tw-ring-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:data-\[active\=true\]\:aria-invalid\:ring-destructive\/40:is( + .dark * + )[data-active="true"][aria-invalid="true"] { + --tw-ring-color: color-mix( + in oklab, + var(--destructive) 40%, + transparent + ); + } + } + .dark\:data-\[state\=active\]\:border-input:is( + .dark * + )[data-state="active"] { + border-color: var(--input); + } + .dark\:data-\[state\=active\]\:bg-input\/30:is( + .dark * + )[data-state="active"] { + background-color: var(--input); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:data-\[state\=active\]\:bg-input\/30:is( + .dark * + )[data-state="active"] { + background-color: color-mix( + in oklab, + var(--input) 30%, + transparent + ); + } + } + .dark\:data-\[state\=active\]\:text-foreground:is( + .dark * + )[data-state="active"] { + color: var(--foreground); + } + .dark\:data-\[state\=checked\]\:bg-primary:is( + .dark * + )[data-state="checked"] { + background-color: var(--primary); + } + .dark\:data-\[state\=checked\]\:bg-primary-foreground:is( + .dark * + )[data-state="checked"] { + background-color: var(--primary-foreground); + } + .dark\:data-\[state\=unchecked\]\:bg-foreground:is( + .dark * + )[data-state="unchecked"] { + background-color: var(--foreground); + } + .dark\:data-\[state\=unchecked\]\:bg-input\/80:is( + .dark * + )[data-state="unchecked"] { + background-color: var(--input); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:data-\[state\=unchecked\]\:bg-input\/80:is( + .dark * + )[data-state="unchecked"] { + background-color: color-mix( + in oklab, + var(--input) 80%, + transparent + ); + } + } + .dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is( + .dark * + )[data-variant="destructive"]:focus { + background-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + .dark\:data-\[variant\=destructive\]\:focus\:bg-destructive\/20:is( + .dark * + )[data-variant="destructive"]:focus { + background-color: color-mix( + in oklab, + var(--destructive) 20%, + transparent + ); + } + } + @media print { + .print\:mt-10 { + margin-top: calc(var(--spacing) * 10); + } + .print\:hidden { + display: none; + } + .print\:max-w-none { + max-width: none; + } + .print\:break-inside-avoid-page { + break-inside: avoid-page; + } + .print\:grid-cols-1 { + grid-template-columns: repeat(1, minmax(0, 1fr)); + } + .print\:rounded-none { + border-radius: 0; + } + .print\:border-none { + --tw-border-style: none; + border-style: none; + } + .print\:bg-white { + background-color: var(--color-white); + } + .print\:px-0 { + padding-inline: calc(var(--spacing) * 0); + } + .print\:py-0 { + padding-block: calc(var(--spacing) * 0); + } + .print\:pt-0 { + padding-top: calc(var(--spacing) * 0); + } + .print\:shadow-none { + --tw-shadow: 0 0 #0000; + box-shadow: + var(--tw-inset-shadow), var(--tw-inset-ring-shadow), + var(--tw-ring-offset-shadow), var(--tw-ring-shadow), + var(--tw-shadow); + } + } + .\[\&_\.recharts-cartesian-axis-tick_text\]\:fill-muted-foreground + .recharts-cartesian-axis-tick + text { + fill: var(--muted-foreground); + } + .\[\&_\.recharts-cartesian-grid_line\[stroke\=\'\#ccc\'\]\]\:stroke-border\/50 + .recharts-cartesian-grid + line[stroke="#ccc"] { + stroke: var(--border); + } + @supports (color: color-mix(in lab, red, red)) { + .\[\&_\.recharts-cartesian-grid_line\[stroke\=\'\#ccc\'\]\]\:stroke-border\/50 + .recharts-cartesian-grid + line[stroke="#ccc"] { + stroke: color-mix(in oklab, var(--border) 50%, transparent); + } + } + .\[\&_\.recharts-curve\.recharts-tooltip-cursor\]\:stroke-border + .recharts-curve.recharts-tooltip-cursor { + stroke: var(--border); + } + .\[\&_\.recharts-dot\[stroke\=\'\#fff\'\]\]\:stroke-transparent + .recharts-dot[stroke="#fff"] { + stroke: #0000; + } + .\[\&_\.recharts-layer\]\:outline-hidden .recharts-layer { + --tw-outline-style: none; + outline-style: none; + } + @media (forced-colors: active) { + .\[\&_\.recharts-layer\]\:outline-hidden .recharts-layer { + outline-offset: 2px; + outline: 2px solid #0000; + } + } + .\[\&_\.recharts-polar-grid_\[stroke\=\'\#ccc\'\]\]\:stroke-border + .recharts-polar-grid + [stroke="#ccc"] { + stroke: var(--border); + } + .\[\&_\.recharts-radial-bar-background-sector\]\:fill-muted + .recharts-radial-bar-background-sector, + .\[\&_\.recharts-rectangle\.recharts-tooltip-cursor\]\:fill-muted + .recharts-rectangle.recharts-tooltip-cursor { + fill: var(--muted); + } + .\[\&_\.recharts-reference-line_\[stroke\=\'\#ccc\'\]\]\:stroke-border + .recharts-reference-line + [stroke="#ccc"] { + stroke: var(--border); + } + .\[\&_\.recharts-sector\]\:outline-hidden .recharts-sector { + --tw-outline-style: none; + outline-style: none; + } + @media (forced-colors: active) { + .\[\&_\.recharts-sector\]\:outline-hidden .recharts-sector { + outline-offset: 2px; + outline: 2px solid #0000; + } + } + .\[\&_\.recharts-sector\[stroke\=\'\#fff\'\]\]\:stroke-transparent + .recharts-sector[stroke="#fff"] { + stroke: #0000; + } + .\[\&_\.recharts-surface\]\:outline-hidden .recharts-surface { + --tw-outline-style: none; + outline-style: none; + } + @media (forced-colors: active) { + .\[\&_\.recharts-surface\]\:outline-hidden .recharts-surface { + outline-offset: 2px; + outline: 2px solid #0000; + } + } + .\[\&_\.tiptap\]\:text-white .tiptap { + color: var(--color-white); + } + .\[\&_\.tiptap\]\:outline-none .tiptap { + --tw-outline-style: none; + outline-style: none; + } + .\[\&_\.tiptap_h1\]\:text-white .tiptap h1, + .\[\&_\.tiptap_h2\]\:text-white .tiptap h2 { + color: var(--color-white); + } + .\[\&_\.tiptap_img\]\:max-w-full .tiptap img { + max-width: 100%; + } + .\[\&_\.tiptap_img\]\:cursor-pointer .tiptap img { + cursor: pointer; + } + .\[\&_\.tiptap_img\]\:rounded .tiptap img { + border-radius: 0.25rem; + } + .\[\&_\.tiptap_li\]\:text-white .tiptap li, + .\[\&_\.tiptap_ol\]\:text-white .tiptap ol, + .\[\&_\.tiptap_p\]\:text-white .tiptap p, + .\[\&_\.tiptap_ul\]\:text-white .tiptap ul { + color: var(--color-white); + } + .\[\&_\[cmdk-group-heading\]\]\:px-2 [cmdk-group-heading] { + padding-inline: calc(var(--spacing) * 2); + } + .\[\&_\[cmdk-group-heading\]\]\:py-1\.5 [cmdk-group-heading] { + padding-block: calc(var(--spacing) * 1.5); + } + .\[\&_\[cmdk-group-heading\]\]\:text-xs [cmdk-group-heading] { + font-size: var(--text-xs); + line-height: var(--tw-leading, var(--text-xs--line-height)); + } + .\[\&_\[cmdk-group-heading\]\]\:font-medium [cmdk-group-heading] { + --tw-font-weight: var(--font-weight-medium); + font-weight: var(--font-weight-medium); + } + .\[\&_\[cmdk-group-heading\]\]\:text-muted-foreground [cmdk-group-heading] { + color: var(--muted-foreground); + } + .\[\&_\[cmdk-group\]\]\:px-2 [cmdk-group] { + padding-inline: calc(var(--spacing) * 2); + } + .\[\&_\[cmdk-group\]\:not\(\[hidden\]\)_\~\[cmdk-group\]\]\:pt-0 + [cmdk-group]:not([hidden]) + ~ [cmdk-group] { + padding-top: calc(var(--spacing) * 0); + } + .\[\&_\[cmdk-input-wrapper\]_svg\]\:h-5 [cmdk-input-wrapper] svg { + height: calc(var(--spacing) * 5); + } + .\[\&_\[cmdk-input-wrapper\]_svg\]\:w-5 [cmdk-input-wrapper] svg { + width: calc(var(--spacing) * 5); + } + .\[\&_\[cmdk-input\]\]\:h-12 [cmdk-input] { + height: calc(var(--spacing) * 12); + } + .\[\&_\[cmdk-item\]\]\:px-2 [cmdk-item] { + padding-inline: calc(var(--spacing) * 2); + } + .\[\&_\[cmdk-item\]\]\:py-3 [cmdk-item] { + padding-block: calc(var(--spacing) * 3); + } + .\[\&_\[cmdk-item\]_svg\]\:h-5 [cmdk-item] svg { + height: calc(var(--spacing) * 5); + } + .\[\&_\[cmdk-item\]_svg\]\:w-5 [cmdk-item] svg { + width: calc(var(--spacing) * 5); + } + .\[\&_img\]\:size-full img { + width: 100%; + height: 100%; + } + .\[\&_img\]\:object-cover img { + object-fit: cover; + } + .\[\&_p\]\:leading-relaxed p { + --tw-leading: var(--leading-relaxed); + line-height: var(--leading-relaxed); + } + .\[\&_svg\]\:pointer-events-none svg { + pointer-events: none; + } + .\[\&_svg\]\:shrink-0 svg { + flex-shrink: 0; + } + .\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3 + svg:not([class*="size-"]) { + width: calc(var(--spacing) * 3); + height: calc(var(--spacing) * 3); + } + .\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 + svg:not([class*="size-"]) { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + .\[\&_svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-6 + svg:not([class*="size-"]) { + width: calc(var(--spacing) * 6); + height: calc(var(--spacing) * 6); + } + .\[\&_svg\:not\(\[class\*\=\'text-\'\]\)\]\:text-muted-foreground + svg:not([class*="text-"]) { + color: var(--muted-foreground); + } + .\[\&_tr\]\:border-b tr { + border-bottom-style: var(--tw-border-style); + border-bottom-width: 1px; + } + .\[\&_tr\:last-child\]\:border-0 tr:last-child { + border-style: var(--tw-border-style); + border-width: 0; + } + .\[\&\+\[data-slot\=item-content\]\]\:flex-none + + [data-slot="item-content"] { + flex: none; + } + .\[\&\:first-child\[data-selected\=true\]_button\]\:rounded-l-md:first-child[data-selected="true"] + button { + border-top-left-radius: calc(var(--radius) - 3px); + border-bottom-left-radius: calc(var(--radius) - 3px); + } + .\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0:has([role="checkbox"]) { + padding-right: calc(var(--spacing) * 0); + } + .\[\.border-b\]\:pb-3.border-b { + padding-bottom: calc(var(--spacing) * 3); + } + .\[\.border-b\]\:pb-6.border-b { + padding-bottom: calc(var(--spacing) * 6); + } + .\[\.border-t\]\:pt-3.border-t { + padding-top: calc(var(--spacing) * 3); + } + .\[\.border-t\]\:pt-6.border-t { + padding-top: calc(var(--spacing) * 6); + } + :is( + .rtl\:\*\*\:\[\.rdp-button\\_next\>svg\]\:rotate-180:where( + :dir(rtl), + [dir="rtl"], + [dir="rtl"] * + ) + * + ):is(.rdp-button_next > svg), + :is( + .rtl\:\*\*\:\[\.rdp-button\\_previous\>svg\]\:rotate-180:where( + :dir(rtl), + [dir="rtl"], + [dir="rtl"] * + ) + * + ):is(.rdp-button_previous > svg) { + rotate: 180deg; + } + .\[a\]\:transition-colors:is(a) { + transition-property: + color, background-color, border-color, outline-color, + text-decoration-color, fill, stroke, --tw-gradient-from, + --tw-gradient-via, --tw-gradient-to; + transition-timing-function: var( + --tw-ease, + var(--default-transition-timing-function) + ); + transition-duration: var( + --tw-duration, + var(--default-transition-duration) + ); + } + @media (hover: hover) { + .\[a\]\:hover\:bg-accent\/50:is(a):hover { + background-color: var(--accent); + } + @supports (color: color-mix(in lab, red, red)) { + .\[a\]\:hover\:bg-accent\/50:is(a):hover { + background-color: color-mix( + in oklab, + var(--accent) 50%, + transparent + ); + } + } + } + :is(.\*\:\[span\]\:last\:flex > *):is(span):last-child { + display: flex; + } + :is(.\*\:\[span\]\:last\:items-center > *):is(span):last-child { + align-items: center; + } + :is(.\*\:\[span\]\:last\:gap-2 > *):is(span):last-child { + gap: calc(var(--spacing) * 2); + } + :is( + .data-\[variant\=destructive\]\:\*\:\[svg\]\:\!text-destructive[data-variant="destructive"] + > * + ):is(svg) { + color: var(--destructive) !important; + } + .\[\&\:last-child\[data-selected\=true\]_button\]\:rounded-r-md:last-child[data-selected="true"] + button { + border-top-right-radius: calc(var(--radius) - 3px); + border-bottom-right-radius: calc(var(--radius) - 3px); + } + .\[\&\>\*\]\:w-full > * { + width: 100%; + } + .\[\&\>\*\]\:focus-visible\:relative > :focus-visible { + position: relative; + } + .\[\&\>\*\]\:focus-visible\:z-10 > :focus-visible { + z-index: 10; + } + .\[\&\>\*\]\:data-\[slot\=field\]\:p-4 > [data-slot="field"] { + padding: calc(var(--spacing) * 4); + } + @container field-group (min-width:28rem) { + .\@md\/field-group\:\[\&\>\*\]\:w-auto > * { + width: auto; + } + } + .\[\&\>\*\:not\(\:first-child\)\]\:rounded-t-none > :not(:first-child) { + border-top-left-radius: 0; + border-top-right-radius: 0; + } + .\[\&\>\*\:not\(\:first-child\)\]\:rounded-l-none > :not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + .\[\&\>\*\:not\(\:first-child\)\]\:border-t-0 > :not(:first-child) { + border-top-style: var(--tw-border-style); + border-top-width: 0; + } + .\[\&\>\*\:not\(\:first-child\)\]\:border-l-0 > :not(:first-child) { + border-left-style: var(--tw-border-style); + border-left-width: 0; + } + .\[\&\>\*\:not\(\:last-child\)\]\:rounded-r-none > :not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } + .\[\&\>\*\:not\(\:last-child\)\]\:rounded-b-none > :not(:last-child) { + border-bottom-right-radius: 0; + border-bottom-left-radius: 0; + } + .\[\&\>\.sr-only\]\:w-auto > .sr-only { + width: auto; + } + .\[\&\>\[data-slot\=field-group\]\]\:gap-4 > [data-slot="field-group"] { + gap: calc(var(--spacing) * 4); + } + .\[\&\>\[data-slot\=field-label\]\]\:flex-auto > [data-slot="field-label"] { + flex: auto; + } + @container field-group (min-width:28rem) { + .\@md\/field-group\:\[\&\>\[data-slot\=field-label\]\]\:flex-auto + > [data-slot="field-label"] { + flex: auto; + } + } + .has-\[select\[aria-hidden\=true\]\:last-child\]\:\[\&\>\[data-slot\=select-trigger\]\:last-of-type\]\:rounded-r-md:has( + :is(select[aria-hidden="true"]:last-child) + ) + > [data-slot="select-trigger"]:last-of-type { + border-top-right-radius: calc(var(--radius) - 3px); + border-bottom-right-radius: calc(var(--radius) - 3px); + } + .\[\&\>\[data-slot\=select-trigger\]\:not\(\[class\*\=\'w-\'\]\)\]\:w-fit + > [data-slot="select-trigger"]:not([class*="w-"]) { + width: fit-content; + } + .\[\&\>\[role\=checkbox\]\]\:translate-y-\[2px\] > [role="checkbox"] { + --tw-translate-y: 2px; + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has( + > [data-slot="field-content"] + ) + > [role="checkbox"], + .has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has( + > [data-slot="field-content"] + ) + [role="radio"] { + margin-top: 1px; + } + @container field-group (min-width:28rem) { + .\@md\/field-group\:has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has( + > [data-slot="field-content"] + ) + > [role="checkbox"], + .\@md\/field-group\:has-\[\>\[data-slot\=field-content\]\]\:\[\&\>\[role\=checkbox\]\,\[role\=radio\]\]\:mt-px:has( + > [data-slot="field-content"] + ) + [role="radio"] { + margin-top: 1px; + } + } + .\[\&\>a\]\:underline > a { + text-decoration-line: underline; + } + .\[\&\>a\]\:underline-offset-4 > a { + text-underline-offset: 4px; + } + .\[\&\>a\:hover\]\:text-primary > a:hover { + color: var(--primary); + } + .\[\&\>button\]\:hidden > button { + display: none; + } + .\[\&\>input\]\:flex-1 > input { + flex: 1; + } + .has-\[\>\[data-align\=block-end\]\]\:\[\&\>input\]\:pt-3:has( + > [data-align="block-end"] + ) + > input { + padding-top: calc(var(--spacing) * 3); + } + .has-\[\>\[data-align\=block-start\]\]\:\[\&\>input\]\:pb-3:has( + > [data-align="block-start"] + ) + > input { + padding-bottom: calc(var(--spacing) * 3); + } + .has-\[\>\[data-align\=inline-end\]\]\:\[\&\>input\]\:pr-2:has( + > [data-align="inline-end"] + ) + > input { + padding-right: calc(var(--spacing) * 2); + } + .has-\[\>\[data-align\=inline-start\]\]\:\[\&\>input\]\:pl-2:has( + > [data-align="inline-start"] + ) + > input { + padding-left: calc(var(--spacing) * 2); + } + .\[\&\>kbd\]\:rounded-\[calc\(var\(--radius\)-5px\)\] > kbd { + border-radius: calc(var(--radius) - 5px); + } + .\[\&\>span\]\:text-xs > span { + font-size: var(--text-xs); + line-height: var(--tw-leading, var(--text-xs--line-height)); + } + .\[\&\>span\]\:opacity-70 > span { + opacity: 0.7; + } + .\[\&\>span\:last-child\]\:truncate > span:last-child { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + .\[\&\>svg\]\:pointer-events-none > svg { + pointer-events: none; + } + .\[\&\>svg\]\:size-3 > svg { + width: calc(var(--spacing) * 3); + height: calc(var(--spacing) * 3); + } + .\[\&\>svg\]\:size-3\.5 > svg { + width: calc(var(--spacing) * 3.5); + height: calc(var(--spacing) * 3.5); + } + .\[\&\>svg\]\:size-4 > svg { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + .\[\&\>svg\]\:h-2\.5 > svg { + height: calc(var(--spacing) * 2.5); + } + .\[\&\>svg\]\:h-3 > svg { + height: calc(var(--spacing) * 3); + } + .\[\&\>svg\]\:w-2\.5 > svg { + width: calc(var(--spacing) * 2.5); + } + .\[\&\>svg\]\:w-3 > svg { + width: calc(var(--spacing) * 3); + } + .\[\&\>svg\]\:shrink-0 > svg { + flex-shrink: 0; + } + .\[\&\>svg\]\:translate-y-0\.5 > svg { + --tw-translate-y: calc(var(--spacing) * 0.5); + translate: var(--tw-translate-x) var(--tw-translate-y); + } + .\[\&\>svg\]\:text-current > svg { + color: currentColor; + } + .\[\&\>svg\]\:text-muted-foreground > svg { + color: var(--muted-foreground); + } + .\[\&\>svg\]\:text-sidebar-accent-foreground > svg { + color: var(--sidebar-accent-foreground); + } + .\[\&\>svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-3\.5 + > svg:not([class*="size-"]) { + width: calc(var(--spacing) * 3.5); + height: calc(var(--spacing) * 3.5); + } + .\[\&\>svg\:not\(\[class\*\=\'size-\'\]\)\]\:size-4 + > svg:not([class*="size-"]) { + width: calc(var(--spacing) * 4); + height: calc(var(--spacing) * 4); + } + .\[\&\>tr\]\:last\:border-b-0 > tr:last-child { + border-bottom-style: var(--tw-border-style); + border-bottom-width: 0; + } + .\[\&\[data-panel-group-direction\=vertical\]\>div\]\:rotate-90[data-panel-group-direction="vertical"] + > div { + rotate: 90deg; + } + .\[\&\[data-state\=open\]\>svg\]\:rotate-180[data-state="open"] > svg { + rotate: 180deg; + } + [data-side="left"][data-collapsible="offcanvas"] + .\[\[data-side\=left\]\[data-collapsible\=offcanvas\]_\&\]\:-right-2 { + right: calc(var(--spacing) * -2); + } + [data-side="left"][data-state="collapsed"] + .\[\[data-side\=left\]\[data-state\=collapsed\]_\&\]\:cursor-e-resize { + cursor: e-resize; + } + [data-side="right"][data-collapsible="offcanvas"] + .\[\[data-side\=right\]\[data-collapsible\=offcanvas\]_\&\]\:-left-2 { + left: calc(var(--spacing) * -2); + } + [data-side="right"][data-state="collapsed"] + .\[\[data-side\=right\]\[data-state\=collapsed\]_\&\]\:cursor-w-resize { + cursor: w-resize; + } + [data-slot="card-content"] + .\[\[data-slot\=card-content\]_\&\]\:bg-transparent, + [data-slot="popover-content"] + .\[\[data-slot\=popover-content\]_\&\]\:bg-transparent { + background-color: #0000; + } + [data-slot="tooltip-content"] + .\[\[data-slot\=tooltip-content\]_\&\]\:bg-background\/20 { + background-color: var(--background); + } + @supports (color: color-mix(in lab, red, red)) { + [data-slot="tooltip-content"] + .\[\[data-slot\=tooltip-content\]_\&\]\:bg-background\/20 { + background-color: color-mix( + in oklab, + var(--background) 20%, + transparent + ); + } + } + [data-slot="tooltip-content"] + .\[\[data-slot\=tooltip-content\]_\&\]\:text-background { + color: var(--background); + } + [data-slot="tooltip-content"] + .dark\:\[\[data-slot\=tooltip-content\]_\&\]\:bg-background\/10:is( + .dark * + ) { + background-color: var(--background); + } + @supports (color: color-mix(in lab, red, red)) { + [data-slot="tooltip-content"] + .dark\:\[\[data-slot\=tooltip-content\]_\&\]\:bg-background\/10:is( + .dark * + ) { + background-color: color-mix( + in oklab, + var(--background) 10%, + transparent + ); + } + } + [data-variant="legend"] + .\[\[data-variant\=legend\]\+\&\]\:-mt-1\.5 { + margin-top: calc(var(--spacing) * -1.5); + } + @media (hover: hover) { + a.\[a\&\]\:hover\:bg-accent:hover { + background-color: var(--accent); + } + a.\[a\&\]\:hover\:bg-destructive\/90:hover { + background-color: var(--destructive); + } + @supports (color: color-mix(in lab, red, red)) { + a.\[a\&\]\:hover\:bg-destructive\/90:hover { + background-color: color-mix( + in oklab, + var(--destructive) 90%, + transparent + ); + } + } + a.\[a\&\]\:hover\:bg-primary\/90:hover { + background-color: var(--primary); + } + @supports (color: color-mix(in lab, red, red)) { + a.\[a\&\]\:hover\:bg-primary\/90:hover { + background-color: color-mix( + in oklab, + var(--primary) 90%, + transparent + ); + } + } + a.\[a\&\]\:hover\:bg-secondary\/90:hover { + background-color: var(--secondary); + } + @supports (color: color-mix(in lab, red, red)) { + a.\[a\&\]\:hover\:bg-secondary\/90:hover { + background-color: color-mix( + in oklab, + var(--secondary) 90%, + transparent + ); + } + } + a.\[a\&\]\:hover\:text-accent-foreground:hover { + color: var(--accent-foreground); + } + } +} +@property --tw-animation-delay { + syntax: "*"; + inherits: false; + initial-value: 0s; +} +@property --tw-animation-direction { + syntax: "*"; + inherits: false; + initial-value: normal; +} +@property --tw-animation-duration { + syntax: "*"; + inherits: false; +} +@property --tw-animation-fill-mode { + syntax: "*"; + inherits: false; + initial-value: none; +} +@property --tw-animation-iteration-count { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-enter-blur { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-enter-opacity { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-enter-rotate { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-enter-scale { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-enter-translate-x { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-enter-translate-y { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-exit-blur { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-exit-opacity { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-exit-rotate { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-exit-scale { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-exit-translate-x { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-exit-translate-y { + syntax: "*"; + inherits: false; + initial-value: 0; +} +:root, +.dark { + --radius: 1.1rem; + --ink-strong: oklch(20% 0.03 220); + --surface-elevated: oklch(18% 0.026 221); + --surface-soft: oklch(23% 0.028 219); + --accent-strong: oklch(70% 0.12 205); + --accent-muted: oklch(58% 0.09 205); + --accent-warm: oklch(84% 0.16 87); + --background: oklch(13% 0.022 223); + --foreground: oklch(96% 0.008 210); + --card: oklch(17% 0.024 221); + --card-foreground: oklch(95% 0.008 210); + --popover: oklch(17% 0.024 221); + --popover-foreground: oklch(95% 0.008 210); + --primary: oklch(70% 0.12 205); + --primary-foreground: oklch(18% 0.02 220); + --secondary: oklch(24% 0.026 220); + --secondary-foreground: oklch(90% 0.01 210); + --muted: oklch(22% 0.022 220); + --muted-foreground: oklch(72% 0.015 214); + --accent: oklch(24% 0.03 209); + --accent-foreground: oklch(95% 0.008 210); + --destructive: oklch(64% 0.21 25); + --destructive-foreground: oklch(98% 0 0); + --border: oklch(100% 0 0/0.1); + --input: oklch(100% 0 0/0.12); + --ring: oklch(70% 0.12 205/0.45); + --chart-1: oklch(70% 0.12 205); + --chart-2: oklch(58% 0.09 206); + --chart-3: oklch(46% 0.07 215); + --chart-4: oklch(84% 0.16 87); + --chart-5: oklch(63% 0.09 177); + --sidebar: oklch(15% 0.02 221); + --sidebar-foreground: oklch(95% 0.008 210); + --sidebar-primary: oklch(70% 0.12 205); + --sidebar-primary-foreground: oklch(18% 0.02 220); + --sidebar-accent: oklch(23% 0.026 219); + --sidebar-accent-foreground: oklch(95% 0.008 210); + --sidebar-border: oklch(100% 0 0/0.1); + --sidebar-ring: oklch(70% 0.12 205/0.45); +} +@property --tw-translate-x { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-translate-y { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-translate-z { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-rotate-x { + syntax: "*"; + inherits: false; +} +@property --tw-rotate-y { + syntax: "*"; + inherits: false; +} +@property --tw-rotate-z { + syntax: "*"; + inherits: false; +} +@property --tw-skew-x { + syntax: "*"; + inherits: false; +} +@property --tw-skew-y { + syntax: "*"; + inherits: false; +} +@property --tw-space-y-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-space-x-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-divide-y-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@property --tw-border-style { + syntax: "*"; + inherits: false; + initial-value: solid; +} +@property --tw-gradient-position { + syntax: "*"; + inherits: false; +} +@property --tw-gradient-from { + syntax: ""; + inherits: false; + initial-value: #0000; +} +@property --tw-gradient-via { + syntax: ""; + inherits: false; + initial-value: #0000; +} +@property --tw-gradient-to { + syntax: ""; + inherits: false; + initial-value: #0000; +} +@property --tw-gradient-stops { + syntax: "*"; + inherits: false; +} +@property --tw-gradient-via-stops { + syntax: "*"; + inherits: false; +} +@property --tw-gradient-from-position { + syntax: ""; + inherits: false; + initial-value: 0%; +} +@property --tw-gradient-via-position { + syntax: ""; + inherits: false; + initial-value: 50%; +} +@property --tw-gradient-to-position { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-leading { + syntax: "*"; + inherits: false; +} +@property --tw-font-weight { + syntax: "*"; + inherits: false; +} +@property --tw-tracking { + syntax: "*"; + inherits: false; +} +@property --tw-ordinal { + syntax: "*"; + inherits: false; +} +@property --tw-slashed-zero { + syntax: "*"; + inherits: false; +} +@property --tw-numeric-figure { + syntax: "*"; + inherits: false; +} +@property --tw-numeric-spacing { + syntax: "*"; + inherits: false; +} +@property --tw-numeric-fraction { + syntax: "*"; + inherits: false; +} +@property --tw-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-shadow-color { + syntax: "*"; + inherits: false; +} +@property --tw-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-inset-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-inset-shadow-color { + syntax: "*"; + inherits: false; +} +@property --tw-inset-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-ring-color { + syntax: "*"; + inherits: false; +} +@property --tw-ring-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-inset-ring-color { + syntax: "*"; + inherits: false; +} +@property --tw-inset-ring-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-ring-inset { + syntax: "*"; + inherits: false; +} +@property --tw-ring-offset-width { + syntax: ""; + inherits: false; + initial-value: 0; +} +@property --tw-ring-offset-color { + syntax: "*"; + inherits: false; + initial-value: #fff; +} +@property --tw-ring-offset-shadow { + syntax: "*"; + inherits: false; + initial-value: 0 0 #0000; +} +@property --tw-outline-style { + syntax: "*"; + inherits: false; + initial-value: solid; +} +@property --tw-blur { + syntax: "*"; + inherits: false; +} +@property --tw-brightness { + syntax: "*"; + inherits: false; +} +@property --tw-contrast { + syntax: "*"; + inherits: false; +} +@property --tw-grayscale { + syntax: "*"; + inherits: false; +} +@property --tw-hue-rotate { + syntax: "*"; + inherits: false; +} +@property --tw-invert { + syntax: "*"; + inherits: false; +} +@property --tw-opacity { + syntax: "*"; + inherits: false; +} +@property --tw-saturate { + syntax: "*"; + inherits: false; +} +@property --tw-sepia { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow-color { + syntax: "*"; + inherits: false; +} +@property --tw-drop-shadow-alpha { + syntax: ""; + inherits: false; + initial-value: 100%; +} +@property --tw-drop-shadow-size { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-blur { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-brightness { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-contrast { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-grayscale { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-hue-rotate { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-invert { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-opacity { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-saturate { + syntax: "*"; + inherits: false; +} +@property --tw-backdrop-sepia { + syntax: "*"; + inherits: false; +} +@property --tw-duration { + syntax: "*"; + inherits: false; +} +@property --tw-ease { + syntax: "*"; + inherits: false; +} +@property --tw-content { + syntax: "*"; + inherits: false; + initial-value: ""; +} +@property --tw-scale-x { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-scale-y { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-scale-z { + syntax: "*"; + inherits: false; + initial-value: 1; +} +@property --tw-divide-x-reverse { + syntax: "*"; + inherits: false; + initial-value: 0; +} +@keyframes spin { + to { + transform: rotate(360deg); + } +} +@keyframes pulse { + 50% { + opacity: 0.5; + } +} +@keyframes enter { + 0% { + opacity: var(--tw-enter-opacity, 1); + transform: translate3d( + var(--tw-enter-translate-x, 0), + var(--tw-enter-translate-y, 0), + 0 + ) + scale3d( + var(--tw-enter-scale, 1), + var(--tw-enter-scale, 1), + var(--tw-enter-scale, 1) + ) + rotate(var(--tw-enter-rotate, 0)); + filter: blur(var(--tw-enter-blur, 0)); + } +} +@keyframes exit { + to { + opacity: var(--tw-exit-opacity, 1); + transform: translate3d( + var(--tw-exit-translate-x, 0), + var(--tw-exit-translate-y, 0), + 0 + ) + scale3d( + var(--tw-exit-scale, 1), + var(--tw-exit-scale, 1), + var(--tw-exit-scale, 1) + ) + rotate(var(--tw-exit-rotate, 0)); + filter: blur(var(--tw-exit-blur, 0)); + } +} +@keyframes accordion-down { + 0% { + height: 0; + } + to { + height: var( + --radix-accordion-content-height, + var( + --bits-accordion-content-height, + var( + --reka-accordion-content-height, + var( + --kb-accordion-content-height, + var(--ngp-accordion-content-height, auto) + ) + ) + ) + ); + } +} +@keyframes accordion-up { + 0% { + height: var( + --radix-accordion-content-height, + var( + --bits-accordion-content-height, + var( + --reka-accordion-content-height, + var( + --kb-accordion-content-height, + var(--ngp-accordion-content-height, auto) + ) + ) + ) + ); + } + to { + height: 0; + } +} +@keyframes caret-blink { + 0%, + 70%, + to { + opacity: 1; + } + 20%, + 50% { + opacity: 0; + } } From 5851668bd8a456d7fa5d3f31fc3cda37ae3ba065 Mon Sep 17 00:00:00 2001 From: Majd Ibrahim <“3021285@stud.hs-mannheim.de”> Date: Thu, 24 Sep 2026 16:33:09 +0000 Subject: [PATCH 04/27] US2.1: Datenstruktur, Komponenten-Aufbau, Styling, Testing --- src/styles/packages.css | 123 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 src/styles/packages.css diff --git a/src/styles/packages.css b/src/styles/packages.css new file mode 100644 index 0000000..d02cbe3 --- /dev/null +++ b/src/styles/packages.css @@ -0,0 +1,123 @@ +:root { + --bg-dark: #0d0d1a; + --bg-card: #1a1a2e; + --accent-teal: #10b981; + --text-white: #ffffff; + --text-lightgray: #f8fafc; + --text-muted: #94a3b8; + + --font-display: "Playfair Display", serif; + --font-body: "Inter", system-ui, sans-serif; +} + +.packages-section { + background-color: var(--bg-dark); + padding: 4rem 2rem; + font-family: var(--font-body); + color: var(--text-lightgray); +} + +.packages-container { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 2rem; + max-width: 1200px; + margin: 0 auto; +} + +.package-card { + background-color: var(--bg-card); + border-radius: 12px; + padding: 2rem; + display: flex; + flex-direction: column; + position: relative; + border: 1px solid rgba(255, 255, 255, 0.05); +} + +.package-type-container { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; +} + +.package-type { + color: var(--text-muted); + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.1em; +} + +.package-highlight { + background-color: rgba(16, 185, 129, 0.1); + color: var(--accent-teal); + font-size: 0.7rem; + padding: 0.25rem 0.75rem; + border-radius: 9999px; + font-weight: 600; + text-transform: uppercase; +} + +.package-title { + font-family: var(--font-display); + font-size: 1.75rem; + color: var(--text-white); + margin-bottom: 1rem; + line-height: 1.2; +} + +.package-desc { + color: var(--text-lightgray); + font-size: 0.95rem; + line-height: 1.5; + margin-bottom: 2rem; +} + +.package-details-box { + background-color: rgba(255, 255, 255, 0.03); + border-radius: 8px; + padding: 1.5rem; + margin-bottom: 2rem; + flex-grow: 1; /* Schiebt den Button immer ganz nach unten */ +} + +.detail-item { + margin-bottom: 1.25rem; +} + +.detail-item:last-child { + margin-bottom: 0; +} + +.detail-title { + color: var(--text-white); + font-weight: 600; + font-size: 0.9rem; + margin-bottom: 0.5rem; +} + +.detail-text { + color: var(--text-muted); + font-size: 0.85rem; + line-height: 1.5; +} + +.package-btn { + display: block; + text-align: center; + background-color: transparent; + color: var(--text-white); + border: 1px solid rgba(255, 255, 255, 0.2); + padding: 1rem; + border-radius: 8px; + text-decoration: none; + font-weight: 500; + transition: all 0.2s ease; +} + +.package-btn:hover { + background-color: rgba(255, 255, 255, 0.05); + border-color: var(--accent-teal); + color: var(--accent-teal); +} From ef0745e59df8a4bdf8535191b88dc0b4032bfec7 Mon Sep 17 00:00:00 2001 From: Majd Ibrahim <“3021285@stud.hs-mannheim.de”> Date: Thu, 24 Sep 2026 16:36:53 +0000 Subject: [PATCH 05/27] US2.1: Datenstruktur, Komponenten-Aufbau, Styling, Testing --- .../angebote/angebotsuebersicht.html | 11 ++++ src/components/angebote/angebotsuebersicht.js | 58 +++++++++++++++++++ src/shared/packages.js | 44 ++++++++++++++ src/tests/angebotsuebersicht.test.js | 36 ++++++++++++ src/tests/packages.test.js | 19 ++++++ 5 files changed, 168 insertions(+) create mode 100644 src/components/angebote/angebotsuebersicht.html create mode 100644 src/components/angebote/angebotsuebersicht.js create mode 100644 src/shared/packages.js create mode 100644 src/tests/angebotsuebersicht.test.js create mode 100644 src/tests/packages.test.js diff --git a/src/components/angebote/angebotsuebersicht.html b/src/components/angebote/angebotsuebersicht.html new file mode 100644 index 0000000..58bbf85 --- /dev/null +++ b/src/components/angebote/angebotsuebersicht.html @@ -0,0 +1,11 @@ +
+
+ +

Unsere Ernährungs-Pakete

+
+ + +
+ +
+
diff --git a/src/components/angebote/angebotsuebersicht.js b/src/components/angebote/angebotsuebersicht.js new file mode 100644 index 0000000..01a7e7e --- /dev/null +++ b/src/components/angebote/angebotsuebersicht.js @@ -0,0 +1,58 @@ +import { packages } from "../../shared/packages.js"; + +export function renderAngebotsuebersicht() { + // Wir suchen den Container, der in der angebotsuebersicht.html definiert wurde + const container = document.getElementById("packages-container"); + + // Sicherheitscheck: Wenn der Container nicht da ist, brechen wir ab + if (!container) { + console.warn("Container für Angebotsübersicht nicht gefunden."); + return; + } + + // Container leeren (falls die Funktion mehrfach aufgerufen wird) + container.innerHTML = ""; + + // Durch jedes Paket iterieren und das HTML generieren + packages.forEach((pkg) => { + // Highlight-Badge nur anzeigen, wenn es eins gibt + const highlightHtml = pkg.highlight + ? `${pkg.highlight}` + : ""; + + const cardHtml = ` +
+ +
+ ${pkg.type} + ${highlightHtml} +
+ +

${pkg.title}

+

${pkg.description}

+ +
+
+
Fokus
+
${pkg.fokus}
+
+
+
Anamnese
+
${pkg.anamnese}
+
+
+
Begleitung
+
${pkg.begleitung}
+
+
+ + + ${pkg.buttonText} + +
+ `; + + // Das generierte HTML dem Container hinzufügen + container.innerHTML += cardHtml; + }); +} diff --git a/src/shared/packages.js b/src/shared/packages.js new file mode 100644 index 0000000..76dac1a --- /dev/null +++ b/src/shared/packages.js @@ -0,0 +1,44 @@ +export const packages = [ + { + id: "ernaehrung-starter", + type: "STARTER", + title: "Für den sicheren Einstieg in die Sporternährung", + description: + "Für Menschen 50+, die mit Triathlon beginnen und ihre Ernährung an die neue Belastung anpassen möchten.", + fokus: "Grundlagen der Makronährstoffe, Hydration im Training und Gewichtsmanagement.", + anamnese: + "Zu Beginn gehört eine ausführliche gemeinsame Anamnese dazu, damit deine Essgewohnheiten und Ziele sauber eingeordnet werden.", + begleitung: + "Ein individueller Basis-Ernährungsplan und regelmäßige Check-ins im ersten Monat.", + buttonText: "Starter-Paket ansehen", + highlight: null, + }, + { + id: "ernaehrung-standard", + type: "STANDARD", + title: "Für Best Ager mit ersten Erfahrungen und klaren Zielen", + description: + "Für Athletinnen und Athleten, die ihre Ernährung für die Kurzdistanz und im Alltag gezielt weiterentwickeln wollen.", + fokus: "Carb-Loading, Pacing-Strategien für den Magen und Timing der Nahrungsaufnahme.", + anamnese: + "Auch hier startet das Coaching mit einer ausführlichen gemeinsamen Anamnese, damit Trainingsrealität, Erfahrung und Verträglichkeit präzise erfasst werden.", + begleitung: + "Monatliche Anpassung des Plans und enger Austausch bei Magen-Darm-Herausforderungen.", + buttonText: "Standard-Paket ansehen", + highlight: "BESONDERS PASSEND", + }, + { + id: "ernaehrung-premium", + type: "PREMIUM", + title: "Für maximale Individualität mit klarem Fokus", + description: + "Für Menschen, die sich eine besonders enge Coaching-Beziehung und laufende Anpassung ihrer Ernährung wünschen.", + fokus: "Individuelle Stoffwechselanalyse, Mikronährstoff-Optimierung und Raceday-Nutrition.", + anamnese: + "Wie in allen Paketen ist eine ausführliche gemeinsame Anamnese enthalten, damit Betreuung, Regeneration und Zielbild von Beginn an abgestimmt werden.", + begleitung: + "Wöchentliche Anpassungen, direkte Feedback-Schleifen und passgenaue Einkaufslisten.", + buttonText: "Premium-Paket ansehen", + highlight: null, + }, +]; diff --git a/src/tests/angebotsuebersicht.test.js b/src/tests/angebotsuebersicht.test.js new file mode 100644 index 0000000..08dfee6 --- /dev/null +++ b/src/tests/angebotsuebersicht.test.js @@ -0,0 +1,36 @@ +export async function renderAngebotsuebersicht() { + const container = document.getElementById("packages-container"); + if (!container) return; + + try { + // 1. Daten vom Backend des Kollegen abfragen + const response = await fetch("/api/angebote"); // Die genaue URL musst du den Kollegen fragen + const backendPackages = await response.json(); + + container.innerHTML = ""; + + // 2. Mit den echten Daten die Karten generieren + backendPackages.forEach((pkg) => { + const highlightHtml = pkg.highlight + ? `${pkg.highlight}` + : ""; + + const cardHtml = ` +
+
+ ${pkg.type} + ${highlightHtml} +
+

${pkg.title}

+

${pkg.description}

+ + ${pkg.buttonText} +
+ `; + container.innerHTML += cardHtml; + }); + } catch (error) { + console.error("Fehler beim Laden der Angebote vom Backend:", error); + container.innerHTML = "

Angebote konnten nicht geladen werden.

"; + } +} diff --git a/src/tests/packages.test.js b/src/tests/packages.test.js new file mode 100644 index 0000000..d957691 --- /dev/null +++ b/src/tests/packages.test.js @@ -0,0 +1,19 @@ +import { packages } from "../shared/packages.js"; + +describe("Angebots-Daten (packages.js)", () => { + test("sollte ein Array mit Angeboten exportieren", () => { + expect(Array.isArray(nutritionPackages)).toBe(true); + expect(nutritionPackages.length).toBeGreaterThan(0); + }); + + test("jedes Paket muss eine ID und einen Titel für das Routing besitzen", () => { + nutritionPackages.forEach((pkg) => { + expect(pkg).toHaveProperty("id"); + expect(pkg).toHaveProperty("title"); + expect(pkg).toHaveProperty("buttonText"); + // Prüft, ob die ID das korrekte Format für die URLs hat + expect(typeof pkg.id).toBe("string"); + expect(pkg.id.length).toBeGreaterThan(0); + }); + }); +}); From f507fb516659597ff050c8a08c640aedc605221c Mon Sep 17 00:00:00 2001 From: Niels Yannick Tietze <3020513@stud.hs-mannheim.de> Date: Fri, 25 Sep 2026 11:08:39 +0200 Subject: [PATCH 06/27] Eigentliche Version --- src/design/tri-hub-ernaehrungsberatung.html | 1195 ++++++++++++++----- 1 file changed, 890 insertions(+), 305 deletions(-) diff --git a/src/design/tri-hub-ernaehrungsberatung.html b/src/design/tri-hub-ernaehrungsberatung.html index aeecc7d..8a6bbcb 100644 --- a/src/design/tri-hub-ernaehrungsberatung.html +++ b/src/design/tri-hub-ernaehrungsberatung.html @@ -1,321 +1,906 @@ - - - - TRI-HUB – Ernährungsberatung - - - - - -
-
- - TRI-HUB - | Ernährungsberatung - - + + + + + Ernährung · Tri-hub + + + + + + + + + + + + + + + +
+ +
+
+
+
TRI-HUB · ERNÄHRUNG
+ +

+ Essen, Trinken
+ und Energie. +

+ +

+ Ernährung gehört zu deinem Training genauso wie Schwimmen, Radfahren + und Laufen. Hier findest du Wissen und Orientierung rund um + Sporternährung, Hydration, Energieaufnahme und Magenmanagement – + passend zu deinem Triathlon ab 50. +

+ + +
+ + +
+
+ + +
+
+
+
+
TRI-HUB SPEKTRUM
+

Ernährung,
die mitdenkt.

+
+ +

+ Die Ernährungsberatung ergänzt das Tri-hub-Angebot um die Themen, + die für Training und Wettkampf besonders wichtig sind. Von den + Grundlagen der Sporternährung bis zur Frage, was dein Magen + während einer langen Einheit verträgt. +

+
+ +
+
+
01 · SPORTERNÄHRUNG
+

Die Grundlagen

+

+ Verstehe, welche Rolle deine Ernährung vor, während und nach + dem Training spielen kann. +

+
+ +
+
02 · HYDRATION
+

Trinken & Energie

+

+ Orientierung rund um Flüssigkeit und Energieaufnahme – + besonders dann, wenn die Belastung länger wird. +

+
+ +
+
03 · MAGENMANAGEMENT
+

Was verträgst du?

+

+ Ernährung soll nicht nur auf dem Papier funktionieren. + Sie muss auch unter Belastung zu dir passen. +

+
+
+
+
+ + +
+
+
+
FACHBEITRÄGE
+

Wissen,
das dich
weiterbringt.

+
+ +
+

+ Rund um Ernährung gibt es viele Empfehlungen. Bei Tri-hub geht es + darum, daraus verständliche Orientierung für deinen Sport und + deinen Alltag zu machen. Die Inhalte dieser Seite bauen auf dem + Ernährungsspektrum von Tri-hub auf. +

+ +
    +
  • + +
    + Sporternährung
    + Grundlagen für Training und Wettkampf. +
    +
  • +
  • + +
    + Hydration
    + Trinken und Flüssigkeitsversorgung im Ausdauertraining. +
    +
  • +
  • + +
    + Energieaufnahme
    + Wie du Energie rund um Belastung bewusst planen kannst. +
    +
  • +
  • + +
    + Magenmanagement
    + Verträglichkeit als Teil deiner persönlichen Strategie. +
    +
  • +
+
+
+
+ + +
+
+
+
DEINE ERFAHRUNG
+

Was hat dir
geholfen?

+

+ Deine Erfahrung kann anderen helfen, das Angebot besser + kennenzulernen. Teile, was du aus der Ernährungsberatung + mitgenommen hast und was sich für dich im Training bewährt hat. +

+ + + Bewertung schreiben + +
+ +
+ +
+
+
+
T
+
+ Deine Erfahrung + Hier kann eine Kundenstimme erscheinen +
+
+
★★★★★
-
- -
-
-
- Performance & Gesundheit -

- Maßgeschneiderte Ernährungsberatung für Ausdauerathleten -

-

- Optimiere deine Energieversorgung im Training und - Wettkampf. Wir entwickeln deine individuelle - Ernährungsstrategie auf wissenschaftlicher Basis. -

- -
+
+ „Teile hier deine Erfahrung mit der Ernährungsberatung – + ehrlich, persönlich und so, wie du sie erlebt hast.“ +
+ + +

+ Hinweis: Die Bewertungen in dieser HTML-Demo werden nicht dauerhaft + gespeichert. Für den Live-Betrieb sollte das Formular an das + bestehende Backend oder CMS von Tri-hub angebunden werden. +

+
+ +
+ + +
+
+
+
+
REZENSION VERFASSEN
+

Deine Stimme
zählt.

+
+ +

+ Erzähle anderen von deiner persönlichen Erfahrung mit dem + Ernährungsangebot. Kurz und ehrlich reicht völlig. +

+
+ +
+
+ + +
+ +
+ +
+ + + + + + + + + + + + + +
-
+ - -
- -
-
- Wissenschaftlich & Individuell -

- Warum Ernährungsberatung bei TRI-HUB? -

-
+
+ + +
-
-
-
- 1 -
-

- Analyse & BIA-Messung -

-

- Fundierte Bestandsaufnahme von - Körperzusammensetzung, Stoffwechsel und aktuellen - Ernährungsgewohnheiten. -

-
+ -
-
- 2 -
-

- Wettkampfstrategie -

-

- Präzise Verpflegungspläne für Renntage - (Carboloading, Magenverträglichkeit, Kohlenhydrat- & - Flüssigkeitszufuhr). -

-
+ -
-
- 3 -
-

- Nachhaltige Integration -

-

- Praxisnahe Empfehlungen, die sich nahtlos in deinen - Berufs- und Trainingsalltag integrieren lassen. -

-
-
-
+
+ + + +
- -
-
- Angebote -

- Unsere Beratungspakete -

-
+
+
+
tri-hub · Triathlon als sportliches Zuhause ab 50.
+
Ernährung · Wissen · Erfahrungen
+
+
-
- -
-
-

- Starter -

-

- Ideal für den Einstieg und die Optimierung der - Grundernährung. -

-
- 149 € -
-
    -
  • ✓ Anamnese & Ist-Analyse
  • -
  • ✓ 60 Min. Einzelsession
  • -
  • ✓ Konkrete Handlungsempfehlungen
  • -
-
- Buchen -
+ + From 762f21dfa132d9aaf1d8b5aa2f838da8b2ddfb92 Mon Sep 17 00:00:00 2001 From: Majd Ibrahim <“3021285@stud.hs-mannheim.de”> Date: Fri, 25 Sep 2026 11:32:18 +0000 Subject: [PATCH 07/27] packagestruktur angepasst --- .../angebote/angebotsuebersicht.html => angebotsuebersicht.html | 0 src/{components => features}/angebote/angebotsuebersicht.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/components/angebote/angebotsuebersicht.html => angebotsuebersicht.html (100%) rename src/{components => features}/angebote/angebotsuebersicht.js (96%) diff --git a/src/components/angebote/angebotsuebersicht.html b/angebotsuebersicht.html similarity index 100% rename from src/components/angebote/angebotsuebersicht.html rename to angebotsuebersicht.html diff --git a/src/components/angebote/angebotsuebersicht.js b/src/features/angebote/angebotsuebersicht.js similarity index 96% rename from src/components/angebote/angebotsuebersicht.js rename to src/features/angebote/angebotsuebersicht.js index 01a7e7e..765dd62 100644 --- a/src/components/angebote/angebotsuebersicht.js +++ b/src/features/angebote/angebotsuebersicht.js @@ -47,7 +47,7 @@ export function renderAngebotsuebersicht() {
- ${pkg.buttonText} + ${pkg.buttonText} `; From 4ed3ab54e816b6e6089f2991baa73099d54591a8 Mon Sep 17 00:00:00 2001 From: Marvin Gemlin Date: Fri, 25 Sep 2026 11:53:07 +0000 Subject: [PATCH 08/27] =?UTF-8?q?Angebots=C3=BCbersicht=20und=20Buchung=20?= =?UTF-8?q?verbinden,=20Mailpit=20einrichten?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .devcontainer/Dockerfile | 5 ++ .devcontainer/devcontainer.json | 12 +++- .devcontainer/setup.sh | 10 ++++ README.md | 59 +++++++++++++++++++ angebotsuebersicht.html | 30 ++++++---- docs/booking.md | 22 ++++--- index.html | 5 +- package.json | 5 +- src/features/angebote/angebote-entry.js | 5 ++ src/features/angebote/angebotsuebersicht.js | 6 +- src/features/booking/booking-page.js | 2 +- src/shared/packages.js | 22 +++++-- src/styles/packages.css | 2 +- src/tests/angebotsuebersicht.test.js | 64 ++++++++++----------- src/tests/browser/booking.spec.js | 52 +++++++++++++++++ src/tests/helpers/browser-server.js | 4 +- src/tests/packages.test.js | 43 ++++++++------ vite.config.js | 3 + 18 files changed, 259 insertions(+), 92 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/setup.sh create mode 100644 README.md create mode 100644 src/features/angebote/angebote-entry.js diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..460e349 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,5 @@ +# Version bewusst festhalten, damit alle dieselbe Mailpit-Version verwenden. +FROM axllent/mailpit:v1.31.2 AS mailpit +FROM mcr.microsoft.com/devcontainers/javascript-node:24-bookworm + +COPY --from=mailpit /mailpit /usr/local/bin/mailpit diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c2df1da..1f24ae9 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,15 +1,18 @@ { "name": "Ernaehrungsberatung", - "image": "mcr.microsoft.com/devcontainers/javascript-node:24-bookworm", "remoteUser": "node", - "forwardPorts": [5173], + "forwardPorts": [5173, 8025], "portsAttributes": { "5173": { "label": "Webseite", "onAutoForward": "notify" + }, + "8025": { + "label": "Mailpit – Testmails", + "onAutoForward": "notify" } }, - "postCreateCommand": "if [ -f package-lock.json ]; then npm ci; else npm install; fi", + "postCreateCommand": "bash .devcontainer/setup.sh", "customizations": { "vscode": { "extensions": [ @@ -17,5 +20,8 @@ "EditorConfig.EditorConfig" ] } + }, + "build": { + "dockerfile": "Dockerfile" } } diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh new file mode 100644 index 0000000..0afa5c9 --- /dev/null +++ b/.devcontainer/setup.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail + +npm ci +if [ ! -e .env ]; then + cp .env.example .env +fi + +# Buchungsdatei nicht vorab anlegen: Das Backend erstellt sie bei der ersten Buchung. +echo "Bereit. In drei Terminals starten: npm run dev:mail, npm run dev:server, npm run dev" diff --git a/README.md b/README.md new file mode 100644 index 0000000..0ca7144 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# Tri-Hub Ernährungsberatung + +## Im Dev Container starten + +1. Repository klonen und in VS Code „Dev Containers: Reopen in Container“ wählen. + Bei einem bereits vorhandenen Container einmal „Dev Containers: Rebuild Container“ + ausführen, damit Mailpit installiert wird. +2. Die Einrichtung installiert automatisch die npm-Abhängigkeiten und erstellt + `.env` aus `.env.example`, sofern noch keine `.env` vorhanden ist. +3. In drei getrennten Terminals in dieser Reihenfolge starten: + +```bash +npm run dev:mail +``` + +```bash +npm run dev:server +``` + +```bash +npm run dev +``` + +Startseite: http://localhost:5173 — über „Beratung kennenlernen“ zur +Angebotsübersicht und von dort zum Buchungsformular des gewählten Pakets. +Direkt: http://localhost:5173/angebotsuebersicht.html +Mailansicht: http://localhost:8025. Beide Web-Ports werden vom Dev Container +weitergeleitet. Mailpit läuft im selben Container auf SMTP-Port 1025 und fängt +Testmails lokal ab, ohne sie extern zu versenden. + +Die Container-Konfiguration installiert Mailpit v1.31.2 aus dem +[offiziellen Docker-Image](https://mailpit.axllent.org/docs/install/docker/). +Die Binärdatei, lokale Mails, `.env` und Buchungsdaten werden nicht committed. +Mailpit verwendet ohne Datenbankpfad einen temporären Speicher; Testmails müssen +nach einem Neustart nicht erhalten bleiben. + +Ohne Dev Container: Node.js 24 und Mailpit installieren, `npm ci` ausführen und +`.env.example` nach `.env` kopieren, falls `.env` fehlt. Danach dieselben drei +Startbefehle verwenden. + +Ohne Mailpit wird die Buchung gespeichert, aber der fehlgeschlagene Mailversand +angezeigt. Der Ablauf bleibt eine Simulation ohne Zahlung oder verbindliche +Beratung. Buchungen liegen lokal in `src/server/data/bookings.csv`. Diese Datei +nicht vorab leer anlegen: Das Backend erzeugt sie bei der ersten Buchung selbst. + +## Prüfen + +```bash +npm test +npm run build +npm run test:browser +``` + +Die Browsertests starten eigene Server auf 3000 und 5173; diese Ports vorher +freihalten. Sie verwenden eine temporäre CSV und simulierten Mailversand. +Falls Chromium fehlt: `npx playwright install chromium`. + +Der Build enthält Startseite, Angebotsübersicht und Buchungsseite. Für ein +Deployment zusätzlich das Node-Backend betreiben und `/api` dorthin weiterleiten. diff --git a/angebotsuebersicht.html b/angebotsuebersicht.html index 58bbf85..517d32f 100644 --- a/angebotsuebersicht.html +++ b/angebotsuebersicht.html @@ -1,11 +1,19 @@ -
-
- -

Unsere Ernährungs-Pakete

-
- - -
- -
-
+ + + + + + Ernährungs-Pakete – Tri-Hub + + +
Zur Startseite
+
+

Unsere Ernährungs-Pakete

+
+
+ + + diff --git a/docs/booking.md b/docs/booking.md index 5f0541d..9dd1d1a 100644 --- a/docs/booking.md +++ b/docs/booking.md @@ -33,14 +33,16 @@ cp .env.example .env Eine vorhandene `.env` vorher prüfen und nicht überschreiben. Die Beispielwerte aktivieren das klar gekennzeichnete Testpaket und verwenden nur einen Mailfänger. -Ohne `.env` bleibt der Demo-Modus ausgeschaltet und der echte Paketkatalog leer. +Ohne `.env` bleibt der Demo-Modus ausgeschaltet; die drei regulären Pakete bleiben verfügbar. -Einmalig [Mailpit installieren](https://mailpit.axllent.org/docs/install/). +Mailpit ist über `.devcontainer/Dockerfile` bereits Bestandteil neuer Dev Container. +Bestehende Container einmal neu bauen. Außerhalb davon einmalig +[Mailpit installieren](https://mailpit.axllent.org/docs/install/). Es läuft als einzelne Binärdatei auch direkt im Dev Container. Kein Docker im Container erforderlich. Mailpit ohne Weiterleitung an echte SMTP-Server starten: ```bash -mailpit --listen 127.0.0.1:8025 --smtp 127.0.0.1:1025 --disable-version-check +npm run dev:mail ``` In Terminal 2 das Backend starten: @@ -56,8 +58,8 @@ npm run dev ``` Öffnen: . -Mailfänger: . Im Dev Container bei Bedarf Port 8025 über -VS Codes Ansicht „Ports“ weiterleiten. Port 5173 ist bereits vorkonfiguriert. +Mailfänger: . Im Dev Container sind die Ports 8025 und 5173 +für die Weiterleitung vorkonfiguriert. Port 3000 muss für den Browser nicht weitergeleitet werden: Vite vermittelt `/api`. Diese Beschreibung setzt voraus, dass Mailpit im selben Container läuft. @@ -92,7 +94,9 @@ Die `.env.example` enthält nur Testwerte und keine Geheimnisse. ## Paketdaten und spätere Router-Anbindung Person 3 pflegt `packages` in **`src/shared/packages.js`**. -Das derzeit leere Array enthält später ausschließlich abgestimmte Angebote: +Das Array enthält die drei Angebote Starter, Standard und Premium. Übersicht und +Buchung verwenden gemeinsam `id`, `name` und `summary`; zusätzliche Felder +beschreiben die Darstellung der Angebotskarten. ```js // Nur Schema-Beispiel, kein verbindliches Angebot: @@ -127,7 +131,7 @@ cleanup(); Das Modul bringt seine begrenzten `.booking`-Styles mit. Farben und Schrift orientieren sich am vorhandenen Basis-CSS. Gemeinsame Designvariablen und eine Button-Komponente gibt es bisher nicht. Layout und Navigation bleiben Aufgabe -der anderen Teammitglieder. „Zur Angebotsseite“ zeigt vorerst auf `/#beratung`. +der anderen Teammitglieder. „Zur Angebotsseite“ führt zu `/angebotsuebersicht.html`. ## API-Beispiele @@ -336,7 +340,7 @@ Binärdatei außerhalb des Repositorys. ## Offene Punkte und gemeinsame Dateien -Offen: echte Paketdaten, Anschluss an den gemeinsamen Router, abschließendes +Die Angebotsübersicht ist als eigene HTML-Seite angebunden. Offen: Anschluss an einen gemeinsamen Router, abschließendes Teamdesign, reale Zustellprüfung und Screenreader-Abnahme. Vor öffentlichem Betrieb sind außerdem Betriebsfragen wie Zugriff auf CSV/Backups, Aufbewahrung und Schutz des öffentlichen Buchungsendpunkts vor automatisiertem Massenversand @@ -347,7 +351,7 @@ Gemeinsame Änderungen: `package.json` und Lockdatei (Abhängigkeiten/Startbefeh `.prettierignore` (private Daten/Testergebnisse ausschließen). Neu hinzugefügt: `vite.config.js` (Proxy, Dateizugriffsschutz, zwei HTML-Einstiegspunkte), `src/shared/packages.js` (gemeinsamer Paketvertrag), `.env.example`, `booking.html` -und `playwright.config.js`. Die Dev-Container-Konfiguration bleibt unverändert. +und `playwright.config.js`. Die Dev-Container-Konfiguration installiert Mailpit und leitet dessen Web-Port weiter. Technische Referenzen: [CSV-Parser](https://csv.js.org/parse/api/sync/), [Playwright-Testserver](https://playwright.dev/docs/test-webserver). diff --git a/index.html b/index.html index dc976fa..a750b86 100644 --- a/index.html +++ b/index.html @@ -20,12 +20,13 @@ Individuelle 1:1-Beratung für deinen Alltag und deine sportlichen Ziele.

- Beratung kennenlernen + Beratung kennenlernen

Deine persönliche Beratung

- Hier stellen wir bald unseren Ablauf und unser Angebot vor. + Entdecke unsere Pakete für deine Ernährung im Alltag und + Sport.

diff --git a/package.json b/package.json index 2af1f0a..d903bdd 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,9 @@ "format:check": "prettier --check .", "dev:server": "node --env-file-if-exists=.env --watch src/server/index.js", "start:server": "node --env-file-if-exists=.env src/server/index.js", - "test": "node --test src/tests/bookings.test.js", - "test:browser": "playwright test" + "test": "node --test src/tests/*.test.js", + "test:browser": "playwright test", + "dev:mail": "mailpit --listen 127.0.0.1:8025 --smtp 127.0.0.1:1025 --disable-version-check" }, "devDependencies": { "@playwright/test": "^1.63.0", diff --git a/src/features/angebote/angebote-entry.js b/src/features/angebote/angebote-entry.js new file mode 100644 index 0000000..43eb523 --- /dev/null +++ b/src/features/angebote/angebote-entry.js @@ -0,0 +1,5 @@ +import "../../styles/styles.css"; +import "../../styles/packages.css"; +import { renderAngebotsuebersicht } from "./angebotsuebersicht.js"; + +renderAngebotsuebersicht(); diff --git a/src/features/angebote/angebotsuebersicht.js b/src/features/angebote/angebotsuebersicht.js index 765dd62..02f7852 100644 --- a/src/features/angebote/angebotsuebersicht.js +++ b/src/features/angebote/angebotsuebersicht.js @@ -28,8 +28,8 @@ export function renderAngebotsuebersicht() { ${highlightHtml} -

${pkg.title}

-

${pkg.description}

+

${pkg.name}

+

${pkg.summary}

@@ -47,7 +47,7 @@ export function renderAngebotsuebersicht() {
- ${pkg.buttonText} + ${pkg.buttonText}
`; diff --git a/src/features/booking/booking-page.js b/src/features/booking/booking-page.js index e15a656..420d238 100644 --- a/src/features/booking/booking-page.js +++ b/src/features/booking/booking-page.js @@ -9,7 +9,7 @@ export function mountBookingPage(root, { packageId } = {}) { const { signal } = controller; root.innerHTML = `
- Zur Angebotsseite + Zur Angebotsseite

Paket buchen

Paket wird geladen …

diff --git a/src/shared/packages.js b/src/shared/packages.js index 76dac1a..7615755 100644 --- a/src/shared/packages.js +++ b/src/shared/packages.js @@ -2,8 +2,8 @@ export const packages = [ { id: "ernaehrung-starter", type: "STARTER", - title: "Für den sicheren Einstieg in die Sporternährung", - description: + name: "Für den sicheren Einstieg in die Sporternährung", + summary: "Für Menschen 50+, die mit Triathlon beginnen und ihre Ernährung an die neue Belastung anpassen möchten.", fokus: "Grundlagen der Makronährstoffe, Hydration im Training und Gewichtsmanagement.", anamnese: @@ -16,8 +16,8 @@ export const packages = [ { id: "ernaehrung-standard", type: "STANDARD", - title: "Für Best Ager mit ersten Erfahrungen und klaren Zielen", - description: + name: "Für Best Ager mit ersten Erfahrungen und klaren Zielen", + summary: "Für Athletinnen und Athleten, die ihre Ernährung für die Kurzdistanz und im Alltag gezielt weiterentwickeln wollen.", fokus: "Carb-Loading, Pacing-Strategien für den Magen und Timing der Nahrungsaufnahme.", anamnese: @@ -30,8 +30,8 @@ export const packages = [ { id: "ernaehrung-premium", type: "PREMIUM", - title: "Für maximale Individualität mit klarem Fokus", - description: + name: "Für maximale Individualität mit klarem Fokus", + summary: "Für Menschen, die sich eine besonders enge Coaching-Beziehung und laufende Anpassung ihrer Ernährung wünschen.", fokus: "Individuelle Stoffwechselanalyse, Mikronährstoff-Optimierung und Raceday-Nutrition.", anamnese: @@ -42,3 +42,13 @@ export const packages = [ highlight: null, }, ]; + +// Nur für lokale Demonstrationen und isolierte Tests. +export const demoPackages = [ + { + id: "demo-booking", + name: "1:1-Ernährungsberatung", + summary: "Testpaket für den simulierten Buchungsablauf.", + testOnly: true, + }, +]; diff --git a/src/styles/packages.css b/src/styles/packages.css index d02cbe3..da50358 100644 --- a/src/styles/packages.css +++ b/src/styles/packages.css @@ -19,7 +19,7 @@ .packages-container { display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); gap: 2rem; max-width: 1200px; margin: 0 auto; diff --git a/src/tests/angebotsuebersicht.test.js b/src/tests/angebotsuebersicht.test.js index 08dfee6..3a8a51c 100644 --- a/src/tests/angebotsuebersicht.test.js +++ b/src/tests/angebotsuebersicht.test.js @@ -1,36 +1,30 @@ -export async function renderAngebotsuebersicht() { - const container = document.getElementById("packages-container"); - if (!container) return; +import test from "node:test"; +import assert from "node:assert/strict"; +import { createApp } from "../server/index.js"; +import { packages } from "../shared/packages.js"; - try { - // 1. Daten vom Backend des Kollegen abfragen - const response = await fetch("/api/angebote"); // Die genaue URL musst du den Kollegen fragen - const backendPackages = await response.json(); - - container.innerHTML = ""; - - // 2. Mit den echten Daten die Karten generieren - backendPackages.forEach((pkg) => { - const highlightHtml = pkg.highlight - ? `${pkg.highlight}` - : ""; - - const cardHtml = ` -
-
- ${pkg.type} - ${highlightHtml} -
-

${pkg.title}

-

${pkg.description}

- - ${pkg.buttonText} -
- `; - container.innerHTML += cardHtml; - }); - } catch (error) { - console.error("Fehler beim Laden der Angebote vom Backend:", error); - container.innerHTML = "

Angebote konnten nicht geladen werden.

"; - } -} +test("API liefert für jedes angezeigte Angebot dieselbe ID, Namen und Beschreibung", async (t) => { + const app = createApp({ sendConfirmation: async () => {} }); + await new Promise((resolve) => app.listen(0, "127.0.0.1", resolve)); + t.after( + () => + new Promise((resolve) => { + app.closeAllConnections(); + app.close(resolve); + }), + ); + const response = await fetch( + `http://127.0.0.1:${app.address().port}/api/angebote`, + ); + assert.equal(response.status, 200); + const body = await response.json(); + assert.deepEqual( + body.packages, + packages.map(({ id, name, summary }) => ({ + id, + name, + summary, + testOnly: false, + })), + ); +}); diff --git a/src/tests/browser/booking.spec.js b/src/tests/browser/booking.spec.js index c9d584d..551c803 100644 --- a/src/tests/browser/booking.spec.js +++ b/src/tests/browser/booking.spec.js @@ -1,5 +1,7 @@ import { test, expect } from "@playwright/test"; +import { packages } from "../../shared/packages.js"; + const pageUrl = "/booking.html#/buchen/demo-booking"; async function fill(page) { await page @@ -179,3 +181,53 @@ test("Vite liefert Serverdateien und Umgebungsdateien nicht aus", async ({ expect([403, 404]).toContain(response.status()); } }); + +for (const pkg of packages) { + test(`Von der Startseite zur Buchung: ${pkg.type}`, async ({ page }) => { + await page.goto("/"); + await page.getByRole("link", { name: "Beratung kennenlernen" }).click(); + await expect(page.locator(".package-card")).toHaveCount( + packages.length, + ); + await page.getByRole("link", { name: pkg.buttonText }).click(); + await expect(page).toHaveURL( + new RegExp(`booking\\.html#/buchen/${pkg.id}$`), + ); + await expect( + page.getByRole("heading", { name: pkg.name, exact: true }), + ).toBeVisible(); + await expect(page.locator(".booking__summary")).toHaveText(pkg.summary); + await fill(page); + const responsePromise = page.waitForResponse( + (response) => + response.url().endsWith("/api/bookings") && + response.request().method() === "POST", + ); + await page + .getByRole("button", { name: "Paket buchen", exact: true }) + .click(); + const response = await responsePromise; + expect(response.status()).toBe(201); + const booking = await response.json(); + expect(booking.packageId).toBe(pkg.id); + expect(booking.packageName).toBe(pkg.name); + await expect(page.locator(".booking__result")).toContainText( + booking.bookingId, + ); + await page.getByRole("link", { name: "Zur Angebotsseite" }).click(); + await expect(page.locator(".package-card")).toHaveCount( + packages.length, + ); + }); +} + +test("Angebotsübersicht passt auf ein schmales Display", async ({ page }) => { + await page.setViewportSize({ width: 320, height: 640 }); + await page.goto("/angebotsuebersicht.html"); + await expect(page.locator(".package-card")).toHaveCount(packages.length); + expect( + await page.evaluate( + () => document.documentElement.scrollWidth <= window.innerWidth, + ), + ).toBe(true); +}); diff --git a/src/tests/helpers/browser-server.js b/src/tests/helpers/browser-server.js index f9f02d8..395967c 100644 --- a/src/tests/helpers/browser-server.js +++ b/src/tests/helpers/browser-server.js @@ -4,11 +4,11 @@ import { tmpdir } from "node:os"; import { join } from "node:path"; import { createApp } from "../../server/index.js"; import { createCsvStorage } from "../../server/services/csv-storage.js"; -import { demoPackages } from "../../shared/packages.js"; +import { packages, demoPackages } from "../../shared/packages.js"; const directory = await mkdtemp(join(tmpdir(), "trihub-browser-")); const server = createApp({ - catalog: demoPackages, + catalog: [...packages, ...demoPackages], storage: createCsvStorage(join(directory, "bookings.csv")), sendConfirmation: async () => {}, }); diff --git a/src/tests/packages.test.js b/src/tests/packages.test.js index d957691..01fcae2 100644 --- a/src/tests/packages.test.js +++ b/src/tests/packages.test.js @@ -1,19 +1,28 @@ -import { packages } from "../shared/packages.js"; +import test from "node:test"; +import assert from "node:assert/strict"; +import { packages, demoPackages } from "../shared/packages.js"; +import { createApp } from "../server/index.js"; -describe("Angebots-Daten (packages.js)", () => { - test("sollte ein Array mit Angeboten exportieren", () => { - expect(Array.isArray(nutritionPackages)).toBe(true); - expect(nutritionPackages.length).toBeGreaterThan(0); - }); - - test("jedes Paket muss eine ID und einen Titel für das Routing besitzen", () => { - nutritionPackages.forEach((pkg) => { - expect(pkg).toHaveProperty("id"); - expect(pkg).toHaveProperty("title"); - expect(pkg).toHaveProperty("buttonText"); - // Prüft, ob die ID das korrekte Format für die URLs hat - expect(typeof pkg.id).toBe("string"); - expect(pkg.id.length).toBeGreaterThan(0); - }); - }); +test("Angebote erfüllen den gemeinsamen Vertrag von Übersicht und Buchung", () => { + assert.ok(packages.length > 0); + assert.doesNotThrow(() => + createApp({ + catalog: [...packages, ...demoPackages], + sendConfirmation: async () => {}, + }), + ); + for (const pkg of packages) { + for (const field of [ + "name", + "summary", + "type", + "fokus", + "anamnese", + "begleitung", + "buttonText", + ]) { + assert.equal(typeof pkg[field], "string"); + assert.ok(pkg[field].trim()); + } + } }); diff --git a/vite.config.js b/vite.config.js index 665b58f..58bccce 100644 --- a/vite.config.js +++ b/vite.config.js @@ -19,6 +19,9 @@ export default defineConfig({ build: { rollupOptions: { input: { + angebote: fileURLToPath( + new URL("./angebotsuebersicht.html", import.meta.url), + ), main: fileURLToPath(new URL("./index.html", import.meta.url)), booking: fileURLToPath( new URL("./booking.html", import.meta.url), From e84d8ae6b3bf4c2a329e9f61cb216923e1f3cab2 Mon Sep 17 00:00:00 2001 From: Majd Ibrahim <“3021285@stud.hs-mannheim.de”> Date: Fri, 25 Sep 2026 13:03:04 +0000 Subject: [PATCH 09/27] packages erweitert und swagger erstellt --- paket-details.html | 30 ++ src/features/angebote/angebotsuebersicht.js | 2 +- src/features/angebote/paket-details.js | 132 ++++++ src/server/swagger.json | 481 ++++++++++++++++++++ src/shared/packagesdetails.js | 149 ++++++ 5 files changed, 793 insertions(+), 1 deletion(-) create mode 100644 paket-details.html create mode 100644 src/features/angebote/paket-details.js create mode 100644 src/server/swagger.json create mode 100644 src/shared/packagesdetails.js diff --git a/paket-details.html b/paket-details.html new file mode 100644 index 0000000..c79f82d --- /dev/null +++ b/paket-details.html @@ -0,0 +1,30 @@ + + + + + + + Paket-Details – Tri-Hub Ernährungsberatung + + + + + + + +
+ ← Zurück zur Angebotsübersicht +
+ +
+
+ +
+
+ + + + + diff --git a/src/features/angebote/angebotsuebersicht.js b/src/features/angebote/angebotsuebersicht.js index 02f7852..77afcf9 100644 --- a/src/features/angebote/angebotsuebersicht.js +++ b/src/features/angebote/angebotsuebersicht.js @@ -47,7 +47,7 @@ export function renderAngebotsuebersicht() {
- ${pkg.buttonText} + ${pkg.buttonText} `; diff --git a/src/features/angebote/paket-details.js b/src/features/angebote/paket-details.js new file mode 100644 index 0000000..790f481 --- /dev/null +++ b/src/features/angebote/paket-details.js @@ -0,0 +1,132 @@ +// src/features/angebote/paket-details.js +import { packagesDetails } from "../../shared/packagesdetails.js"; + +document.addEventListener("DOMContentLoaded", async () => { + const container = document.getElementById("package-detail-container"); + if (!container) return; + + // 1. ID aus der URL lesen (z.B. ?id=ernaehrung-standard) + const params = new URLSearchParams(window.location.search); + const packageId = params.get("id"); + + // 2. Passendes Paket in packagesdetails.js suchen + // (Später kann hier alternativ: await fetch(`/api/packages/${packageId}`) genutzt werden) + const pkg = packagesDetails.find((item) => item.id === packageId); + + if (!pkg) { + container.innerHTML = ` +
+

Paket nicht gefunden

+

Bitte wähle ein gültiges Angebot aus der Übersicht.

+ Zur Übersicht +
+ `; + return; + } + + // 3. Optional: Varianten-Auswahl rendern (für das Premium-Paket) + const variantsHtml = pkg.variants + ? `
+

Wähle deine Variante

+ +
` + : ""; + + // 4. Detailseite im Tri-Hub-Stil rendern + container.innerHTML = ` +
+
+ ${pkg.type} · ${pkg.durationLabel} + ${pkg.badge} +
+ +

${pkg.title}

+

${pkg.subtitle}

+ +
+ ${pkg.price.toFixed(2)} € + inkl. ${pkg.taxRate}% MwSt. (${pkg.billingInterval}) +
+ + ${variantsHtml} + +
+
+

Für wen ist dieses Paket ideal?

+
    + ${pkg.targetGroup.map((item) => `
  • ${item}
  • `).join("")} +
+
+ +
+

Das ist im Paket enthalten

+
    + ${pkg.includedFeatures.map((feat) => `
  • ${feat}
  • `).join("")} +
+
+ +
+

So läuft deine Begleitung ab

+ ${pkg.processSteps + .map( + (s) => ` +
+ ${s.step}: ${s.text} +
+ `, + ) + .join("")} +
+
+ + +
+ `; + + // 5. Preis aktualisieren, falls eine andere Variante (24 vs. 52 Wochen) gewählt wird + const variantSelect = document.getElementById("variant-select"); + const displayedPrice = document.getElementById("displayed-price"); + if (variantSelect && displayedPrice) { + variantSelect.addEventListener("change", (e) => { + const selectedOption = e.target.selectedOptions[0]; + const newPrice = parseFloat( + selectedOption.getAttribute("data-price"), + ); + displayedPrice.textContent = `${newPrice.toFixed(2)} €`; + }); + } + + // 6. Klick auf "Jetzt buchen" -> Daten für bookingService bereitstellen & weiterleiten + const bookingBtn = document.getElementById("proceed-to-booking-btn"); + bookingBtn.addEventListener("click", () => { + const selectedVariantId = variantSelect ? variantSelect.value : pkg.id; + const selectedVariant = pkg.variants + ? pkg.variants.find((v) => v.variantId === selectedVariantId) + : null; + + const bookingPayload = { + packageId: pkg.id, + variantId: selectedVariantId, + title: pkg.title, + type: pkg.type, + durationWeeks: selectedVariant + ? selectedVariant.durationWeeks + : pkg.durationWeeks, + price: selectedVariant ? selectedVariant.price : pkg.price, + currency: pkg.currency, + taxRate: pkg.taxRate, + summaryForBooking: pkg.summaryForBooking, + }; + + // Im sessionStorage zwischenspeichern + per URL an die Buchungsseite übergeben + sessionStorage.setItem( + "selectedPackageForBooking", + JSON.stringify(bookingPayload), + ); + window.location.href = `booking.html?id=${pkg.id}&variant=${selectedVariantId}`; + }); +}); diff --git a/src/server/swagger.json b/src/server/swagger.json new file mode 100644 index 0000000..f356c8b --- /dev/null +++ b/src/server/swagger.json @@ -0,0 +1,481 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "Tri-Hub Ernährungsberatung & Booking API", + "description": "API-Spezifikation für die Angebotsübersicht (US2.1), die Paket-Detailansicht und den Buchungsservice (US2.4 Paket buchen).", + "version": "1.0.0" + }, + "servers": [ + { + "url": "http://localhost:3000/api", + "description": "Lokaler Node.js Entwicklungsserver" + } + ], + "paths": { + "/packages": { + "get": { + "summary": "Alle Ernährungs-Pakete abrufen (Kurzübersicht)", + "description": "Liefert die Basisdaten aus packages.js für die Angebotsübersicht (US2.1).", + "tags": ["Packages"], + "responses": { + "200": { + "description": "Erfolgreiche Rückgabe der Paketliste", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PackageOverview" + } + } + } + } + } + } + } + }, + "/packages/{id}": { + "get": { + "summary": "Detaillierte Paketinformationen für Detailseite & Bestellprozess abrufen", + "description": "Liefert die ausführlichen Paketdaten aus packagesdetails.js (inkl. Preis, Laufzeit, Leistungen und Varianten), um sie auf der Detailseite und in der Bestellzusammenfassung des Booking-Service anzuzeigen.", + "tags": ["Packages", "Booking"], + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "Eindeutige ID des Pakets (z. B. ernaehrung-starter)", + "schema": { + "type": "string", + "example": "ernaehrung-standard" + } + } + ], + "responses": { + "200": { + "description": "Detaillierte Paketdaten erfolgreich geladen", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PackageDetail" + } + } + } + }, + "404": { + "description": "Paket mit dieser ID wurde nicht gefunden", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/bookings/preview": { + "post": { + "summary": "Preis- und Leistungsübersicht für den Bestellprozess berechnen", + "description": "Wird vom bookingService aufgerufen, um vor Abschluss der Buchung die relevanten Bestelldaten (Nettopreis, MwSt., Bruttopreis, gewählte Variante) validiert anzuzeigen.", + "tags": ["Booking"], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingPreviewRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Berechnete Bestellübersicht für das Checkout-Formular", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingPreviewResponse" + } + } + } + }, + "400": { + "description": "Ungültige Paket- oder Varianten-ID", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + }, + "/bookings": { + "post": { + "summary": "Paket verbindlich buchen (US2.4)", + "description": "Nimmt die ausgewählten Paketdetails und die Kundendaten entgegen, speichert die Buchung im bookingService und gibt eine Bestellbestätigung zurück.", + "tags": ["Booking"], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingCreateRequest" + } + } + } + }, + "responses": { + "201": { + "description": "Buchung erfolgreich angelegt (Bestellbestätigung)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BookingConfirmation" + } + } + } + }, + "400": { + "description": "Fehlende oder ungültige Eingabedaten", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "PackageOverview": { + "type": "object", + "required": [ + "id", + "type", + "title", + "description", + "fokus", + "anamnese", + "begleitung", + "buttonText" + ], + "properties": { + "id": { + "type": "string", + "example": "ernaehrung-standard" + }, + "type": { + "type": "string", + "example": "STANDARD" + }, + "title": { + "type": "string", + "example": "Für Best Ager mit ersten Erfahrungen und klaren Zielen" + }, + "description": { + "type": "string" + }, + "fokus": { + "type": "string" + }, + "anamnese": { + "type": "string" + }, + "begleitung": { + "type": "string" + }, + "buttonText": { + "type": "string", + "example": "Standard-Paket ansehen" + }, + "highlight": { + "type": "string", + "nullable": true, + "example": "BESONDERS PASSEND" + } + } + }, + "PackageVariant": { + "type": "object", + "properties": { + "variantId": { + "type": "string", + "example": "ernaehrung-premium-24" + }, + "label": { + "type": "string", + "example": "24-Wochen-Variante" + }, + "durationWeeks": { + "type": "integer", + "example": 24 + }, + "price": { + "type": "number", + "format": "float", + "example": 799.0 + } + } + }, + "PackageDetail": { + "type": "object", + "required": [ + "id", + "type", + "title", + "subtitle", + "durationWeeks", + "price", + "currency", + "taxRate", + "includedFeatures" + ], + "properties": { + "id": { + "type": "string", + "example": "ernaehrung-standard" + }, + "type": { + "type": "string", + "example": "STANDARD" + }, + "badge": { + "type": "string", + "example": "BESONDERS PASSEND" + }, + "title": { + "type": "string", + "example": "Für Best Ager mit ersten Erfahrungen und klaren Zielen" + }, + "subtitle": { + "type": "string" + }, + "durationWeeks": { + "type": "integer", + "example": 24 + }, + "durationLabel": { + "type": "string", + "example": "24 Wochen Begleitung" + }, + "price": { + "type": "number", + "format": "float", + "example": 449.0 + }, + "currency": { + "type": "string", + "example": "EUR" + }, + "billingInterval": { + "type": "string", + "example": "einmalig" + }, + "taxRate": { + "type": "integer", + "example": 19 + }, + "summaryForBooking": { + "type": "string", + "example": "Standard-Paket Sporternährung (24 Wochen Coaching)" + }, + "targetGroup": { + "type": "array", + "items": { + "type": "string" + } + }, + "includedFeatures": { + "type": "array", + "items": { + "type": "string" + } + }, + "processSteps": { + "type": "array", + "items": { + "type": "object", + "properties": { + "step": { + "type": "string" + }, + "text": { + "type": "string" + } + } + } + }, + "variants": { + "type": "array", + "nullable": true, + "items": { + "$ref": "#/components/schemas/PackageVariant" + } + }, + "ctaButtonText": { + "type": "string", + "example": "Standard-Paket jetzt buchen" + } + } + }, + "BookingPreviewRequest": { + "type": "object", + "required": ["packageId"], + "properties": { + "packageId": { + "type": "string", + "example": "ernaehrung-premium" + }, + "variantId": { + "type": "string", + "nullable": true, + "example": "ernaehrung-premium-52" + } + } + }, + "BookingPreviewResponse": { + "type": "object", + "properties": { + "packageId": { + "type": "string", + "example": "ernaehrung-premium" + }, + "variantId": { + "type": "string", + "example": "ernaehrung-premium-52" + }, + "title": { + "type": "string", + "example": "Für maximale Individualität und intensive 1:1-Begleitung" + }, + "summaryForBooking": { + "type": "string" + }, + "durationWeeks": { + "type": "integer", + "example": 52 + }, + "netPrice": { + "type": "number", + "format": "float", + "example": 1175.63 + }, + "taxAmount": { + "type": "number", + "format": "float", + "example": 223.37 + }, + "grossPrice": { + "type": "number", + "format": "float", + "example": 1399.0 + }, + "currency": { + "type": "string", + "example": "EUR" + } + } + }, + "BookingCreateRequest": { + "type": "object", + "required": ["packageId", "customer", "acceptedTerms"], + "properties": { + "packageId": { + "type": "string", + "example": "ernaehrung-standard" + }, + "variantId": { + "type": "string", + "nullable": true, + "example": "ernaehrung-standard" + }, + "customer": { + "type": "object", + "required": ["firstName", "lastName", "email"], + "properties": { + "firstName": { + "type": "string", + "example": "Thomas" + }, + "lastName": { + "type": "string", + "example": "Müller" + }, + "email": { + "type": "string", + "format": "email", + "example": "thomas.mueller@example.de" + }, + "phone": { + "type": "string", + "example": "+49 170 1234567" + }, + "ageGroup": { + "type": "string", + "example": "50-59" + }, + "notes": { + "type": "string", + "example": "Vorbereitung auf meine erste Mitteldistanz im August." + } + } + }, + "acceptedTerms": { + "type": "boolean", + "example": true + } + } + }, + "BookingConfirmation": { + "type": "object", + "properties": { + "bookingId": { + "type": "string", + "example": "BK-2026-84920" + }, + "status": { + "type": "string", + "example": "CONFIRMED" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "bookedPackage": { + "$ref": "#/components/schemas/BookingPreviewResponse" + }, + "customerEmail": { + "type": "string", + "example": "thomas.mueller@example.de" + }, + "confirmationMessage": { + "type": "string", + "example": "Vielen Dank für deine Buchung! Deine Bestellbestätigung wurde per E-Mail versendet." + } + } + }, + "ErrorResponse": { + "type": "object", + "properties": { + "error": { + "type": "string", + "example": "Paket nicht gefunden" + }, + "code": { + "type": "integer", + "example": 404 + } + } + } + } + } +} diff --git a/src/shared/packagesdetails.js b/src/shared/packagesdetails.js new file mode 100644 index 0000000..9518acb --- /dev/null +++ b/src/shared/packagesdetails.js @@ -0,0 +1,149 @@ +// src/shared/packagesdetails.js + +export const packagesDetails = [ + { + id: "ernaehrung-starter", + type: "STARTER", + badge: "EINSTIEG", + title: "Für den sicheren Einstieg in die Sporternährung", + subtitle: + "Strukturierte Grundlagen für Menschen 50+, die ihre Ernährung optimal auf das Triathlon-Training abstimmen wollen.", + durationWeeks: 12, + durationLabel: "12 Wochen Begleitung", + price: 249.0, + currency: "EUR", + billingInterval: "einmalig", + taxRate: 19, + summaryForBooking: + "Starter-Paket Sporternährung (12 Wochen Basis-Begleitung inkl. Anamnese & Basisplan)", + targetGroup: [ + "Du steigst neu in den Triathlon ein oder baust nach einer Pause wieder Struktur auf.", + "Du möchtest verstehen, wie du Energielevel und Regeneration über die Alltagsernährung steuerst.", + "Du suchst einen klaren, alltagstauglichen Plan ohne komplizierte Wissenschaft.", + ], + includedFeatures: [ + "Ausführliche Eingangs-Anamnese (60 Min. Video-Call) zu Gesundheit, Alltag und Zielen", + "Individueller Basis-Ernährungsplan abgestimmt auf deine Trainingswoche", + "Trink- und Hydrationsstrategie für Grundlageneinheiten", + "2x Check-in-Calls (je 30 Min.) zur Plananpassung im Verlauf der 12 Wochen", + "Rezept- und Einkaufsguide für schnelle, nährstoffreiche Mahlzeiten", + ], + processSteps: [ + { + step: "1. Anamnese & Status Quo", + text: "Auswertung deines 7-Tage-Ernährungsprotokolls und gemeinsames Erstgespräch.", + }, + { + step: "2. Dein individueller Plan", + text: "Erstellung deines alltagstauglichen Ernährungskonzepts für Trainingstage und Ruhetage.", + }, + { + step: "3. Umsetzung & Rückmeldung", + text: "Regelmäßige Check-ins, um das Tempo anzupassen und Überforderung zu vermeiden.", + }, + ], + ctaButtonText: "Starter-Paket jetzt buchen", + }, + { + id: "ernaehrung-standard", + type: "STANDARD", + badge: "BESONDERS PASSEND", + title: "Für Best Ager mit ersten Erfahrungen und klaren Zielen", + subtitle: + "Gezielte Ernährungssteuerung für Alltag, Belastungsspitzen und deinen nächsten Wettkampf.", + durationWeeks: 24, + durationLabel: "24 Wochen Begleitung", + price: 449.0, + currency: "EUR", + billingInterval: "einmalig", + taxRate: 19, + summaryForBooking: + "Standard-Paket Sporternährung (24 Wochen Coaching inkl. Wettkampf-Verpflegungsstrategie)", + targetGroup: [ + "Du trainierst bereits regelmäßig und möchtest deine Leistung über die Ernährung spürbar verbessern.", + "Du bereitest dich gezielt auf eine Kurz- oder Mitteldistanz vor.", + "Du möchtest Magen-Darm-Probleme im Training und Wettkampf sicher vermeiden.", + ], + includedFeatures: [ + "Umfassende Anamnese inkl. Analyse der bisherigen Trainings- und Wettkampfernährung", + "Dynamischer Ernährungsplan (gekoppelt an deine Trainingsphasen)", + "Spezifisches Carb-Loading- und Pacing-Konzept für den Wettkampftag", + "Monatliche 1:1 Video-Calls (6x 45 Min.) zur Feinjustierung", + "Gezieltes Magen-Training (Gut-Training) für Gels, Riegel und Sportgetränke", + "Support per Messenger bei Fragen im Trainingsalltag (Antwort innerhalb von 48h)", + ], + processSteps: [ + { + step: "1. Tiefen-Anamnese", + text: "Präzise Erfassung von Trainingsrealität, Erfahrung, Belastbarkeit und Verträglichkeiten.", + }, + { + step: "2. Periodisierte Planung", + text: "Abstimmung der Makro- und Mikronährstoffe auf deine konkreten Trainingsblöcke.", + }, + { + step: "3. Wettkampf-Simulation", + text: "Erprobung der Raceday-Strategie im Training für maximale Sicherheit am Starttag.", + }, + ], + ctaButtonText: "Standard-Paket jetzt buchen", + }, + { + id: "ernaehrung-premium", + type: "PREMIUM", + badge: "MAXIMALE INDIVIDUALITÄT", + title: "Für maximale Individualität und intensive 1:1-Begleitung", + subtitle: + "Enge Coaching-Beziehung, laufende Feinabstimmung und zwei wählbare Laufzeit-Varianten.", + durationWeeks: 24, + durationLabel: "24 oder 52 Wochen (wählbar)", + price: 799.0, + currency: "EUR", + billingInterval: "einmalig", + taxRate: 19, + variants: [ + { + variantId: "ernaehrung-premium-24", + label: "24-Wochen-Variante", + durationWeeks: 24, + price: 799.0, + }, + { + variantId: "ernaehrung-premium-52", + label: "52-Wochen-Variante (Jahresbegleitung)", + durationWeeks: 52, + price: 1399.0, + }, + ], + summaryForBooking: + "Premium-Paket Sporternährung (Intensive 1:1-Begleitung inkl. Raceday- & Regenerations-Steuerung)", + targetGroup: [ + "Du wünschst dir eine besonders enge Coaching-Beziehung mit kurzen Reaktionswegen.", + "Du möchtest Training, Ernährung, Blutwerte und Regeneration ganzheitlich abstimmen.", + "Du suchst maximale Verbindlichkeit für ein großes Saison-Ziel.", + ], + includedFeatures: [ + "Exklusive 90-Minuten-Start-Anamnese inkl. Auswertung vorhandener Blut-/Laborwerte", + "Wöchentliche Feinabstimmung des Ernährungsplans parallel zum Trainingsplan", + "Individueller Supplement- & Mikronährstoff-Plan für optimale Regeneration ab 50", + "14-tägige 1:1 Video-Calls sowie direkter Priority-Messenger-Support", + "Detailliertes Raceday-Playbook (Stunde-für-Stunde-Plan für vor, während und nach dem Rennen)", + "Direkte Abstimmung mit deinem Triathlon-Trainer bei Tri-Hub", + ], + processSteps: [ + { + step: "1. Ganzheitliches Onboarding", + text: "Gemeinsame Analyse von Gesundheitsdaten, Leistungszielen und Alltagsstruktur.", + }, + { + step: "2. Laufende Steuerung", + text: "Wöchentliche Anpassung an Trainingsumfang, Schlafqualität und Regeneration.", + }, + { + step: "3. Punktlandung am Wettkampftag", + text: "Individuelle Tapering- und Wettkampfernährung für deine persönliche Bestleistung.", + }, + ], + ctaButtonText: "Premium-Paket jetzt buchen", + }, +]; From 5fb3f51f8fed28eee8ffebc6f20f93faac9fb416 Mon Sep 17 00:00:00 2001 From: Marvin Gemlin Date: Fri, 25 Sep 2026 20:04:22 +0000 Subject: [PATCH 10/27] =?UTF-8?q?Paket=C3=BCbergabe=20korrigiert,=20Bestel?= =?UTF-8?q?l=C3=BCbersicht=20und=20Best=C3=A4tigungsmail=20erweitert?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 1 - docs/booking.md | 430 +++++--------------- paket-details.html | 50 +-- src/features/angebote/angebotsuebersicht.js | 7 - src/features/angebote/paket-details.js | 38 +- src/features/booking/booking-api.js | 13 +- src/features/booking/booking-entry.js | 7 +- src/features/booking/booking-page.js | 92 ++++- src/features/booking/booking.css | 23 ++ src/server/index.js | 58 ++- src/server/routes/bookings.js | 8 +- src/server/services/booking-service.js | 155 ++++++- src/server/services/csv-storage.js | 25 +- src/server/services/email-service.js | 75 +++- src/server/swagger.json | 237 ++++++++++- src/shared/booking-copy.js | 4 +- src/shared/order-summary.js | 28 ++ src/shared/packages.js | 10 - src/tests/angebotsuebersicht.test.js | 1 - src/tests/bookings.test.js | 224 +++++++++- src/tests/browser/booking.spec.js | 71 +++- src/tests/helpers/browser-server.js | 4 +- src/tests/packages.test.js | 4 +- vite.config.js | 3 + 24 files changed, 1059 insertions(+), 509 deletions(-) create mode 100644 src/shared/order-summary.js diff --git a/.env.example b/.env.example index 2968da5..318f119 100644 --- a/.env.example +++ b/.env.example @@ -1,7 +1,6 @@ # Lokal nur einen Mailfänger verwenden. Keine echten Zugangsdaten eintragen. HOST=127.0.0.1 PORT=3000 -BOOKING_DEMO=true SMTP_HOST=127.0.0.1 SMTP_PORT=1025 SMTP_SECURE=false diff --git a/docs/booking.md b/docs/booking.md index 9dd1d1a..884e0d4 100644 --- a/docs/booking.md +++ b/docs/booking.md @@ -1,357 +1,143 @@ -# US2.4 – Paket buchen +# Paketbuchung -## Stand und Zusammenarbeit +## Ablauf -Entwicklung auf `feature/paket-buchen-backend`, späteres PR-Ziel: `dev`. -Zu Beginn waren HEAD und frisch abgerufenes `origin/dev` identisch. Kein Merge, -Commit oder Push wurde durchgeführt. Es gab keinen Router, keine Paketdaten und -kein Backend. Die Implementierung verwendet die vereinbarte Startstruktur: -Backend unter `src/server/`, gemeinsame Paketdaten unter `src/shared/` und -Tests unter `src/tests/`. Die früheren Doppelordner im Projektwurzelverzeichnis -wurden in diese vorhandenen Ordner überführt. +Die Angebotsübersicht führt zur Detailseite eines Pakets. Der Buchungsbutton +übergibt die Paket-ID und gegebenenfalls die gewählte Variante als URL-Parameter +an `booking.html`. Bestehende Links mit `#/buchen/:packageId` funktionieren weiterhin. -## So funktioniert der Ablauf +Booking lädt die Bestellübersicht vom Server. Sie enthält Paket und Variante, +Laufzeit, Leistungen, Abrechnung, Nettopreis, MwSt. und Gesamtbetrag. Die Preise +stammen aus `src/shared/packagesdetails.js`; Preisangaben des Browsers werden +nicht übernommen. Ohne Varianten-ID wird die erste Variante des Pakets gewählt. -1. Der Browser lädt die verfügbaren Pakete über `GET /api/angebote`. -2. Das Formular sendet Paket-ID, Name und E-Mail an `POST /api/bookings`. - Es merkt sich dafür einen zufälligen Anfrageschlüssel (Idempotency-Key). -3. Der Server prüft die Angaben und liest den Paketnamen aus `src/shared/packages.js`. -4. Er speichert Buchungsnummer, Zeitpunkt, Angaben und Schlüssel in der CSV. -5. Erst danach übergibt er die Bestätigung an SMTP und aktualisiert den Versandstatus. -6. Bei einem erneuten Versuch mit denselben Daten und demselben Schlüssel gibt - er die vorhandene Buchung zurück. Es entsteht keine zweite Bestellung. +Beim Absenden speichert der Server Kundendaten und den vollständigen Bestellstand +in der CSV. Anschließend sendet er eine Bestätigung als Text und HTML mit +Buchungsnummer, Datum, gebuchten Leistungen und Preisen. Änderungen am Katalog +verändern bereits gespeicherte Bestellungen nicht. + +Der Zahlungsvorgang ist weiterhin simuliert. Es erfolgt keine Abbuchung und +keine verbindliche Beratungsbuchung. Darauf weisen Formular und E-Mail hin. ## Lokal starten -Node.js 24 ist im vorhandenen Dev Container vorgesehen. Alle Befehle laufen im -Projektverzeichnis. Vite und Backend benötigen jeweils ein eigenes Terminal. +Voraussetzung ist Node.js 24. Abhängigkeiten mit `npm ci` installieren und die +Werte aus `.env.example` in eine lokale `.env` übernehmen. Eine vorhandene +Konfiguration dabei erhalten. -```bash -npm ci -cp .env.example .env -``` +In separaten Terminals starten: -Eine vorhandene `.env` vorher prüfen und nicht überschreiben. Die Beispielwerte -aktivieren das klar gekennzeichnete Testpaket und verwenden nur einen Mailfänger. -Ohne `.env` bleibt der Demo-Modus ausgeschaltet; die drei regulären Pakete bleiben verfügbar. +- `npm run dev:mail`: Mailpit mit SMTP auf Port 1025 und Weboberfläche auf 8025. +- `npm run dev:server`: Buchungs-API auf Port 3000. +- `npm run dev`: Frontend auf Port 5173; `/api` wird an Node weitergeleitet. -Mailpit ist über `.devcontainer/Dockerfile` bereits Bestandteil neuer Dev Container. -Bestehende Container einmal neu bauen. Außerhalb davon einmalig -[Mailpit installieren](https://mailpit.axllent.org/docs/install/). -Es läuft als einzelne Binärdatei auch direkt im Dev Container. Kein Docker im -Container erforderlich. Mailpit ohne Weiterleitung an echte SMTP-Server starten: +Die Angebotsübersicht liegt unter , +der Mailfänger unter . Mailpit ist im Dev Container enthalten. -```bash -npm run dev:mail -``` - -In Terminal 2 das Backend starten: - -```bash -npm run dev:server -``` - -In Terminal 3 das Frontend starten: - -```bash -npm run dev -``` - -Öffnen: . -Mailfänger: . Im Dev Container sind die Ports 8025 und 5173 -für die Weiterleitung vorkonfiguriert. -Port 3000 muss für den Browser nicht weitergeleitet werden: Vite vermittelt `/api`. -Diese Beschreibung setzt voraus, dass Mailpit im selben Container läuft. - -`npm run start:server` startet das Backend ohne automatischen Neustart. -Bestehende Befehle `dev`, `build`, `preview`, `format` und `format:check` bleiben erhalten. -`npm run preview` zeigt ausschließlich den gebauten Frontend-Stand. Für eine -vollständige Buchung im Deployment muss der Webserver `/api` separat an Node -weiterleiten; der Vite-Entwicklungsproxy ist kein Produktionsserver. +`npm run build` erzeugt die Seiten in `dist/`, einschließlich der Paketdetailseite. +Für ein Deployment muss der Webserver `/api` an den Node-Server weiterleiten. +`npm run start:server` startet die API ohne automatischen Neustart. ## Konfiguration -Alle SMTP-Einstellungen werden ausschließlich in Node geladen. Niemals mit einem -`VITE_`-Präfix versehen: Solche Variablen könnten im Browser landen. +| Variable | Standardwert | Bedeutung | +| ------------------------ | -------------------------------- | --------------------------------------------------- | +| `HOST` | `127.0.0.1` | Bind-Adresse der API | +| `PORT` | `3000` | API-Port; bei Änderung auch den Vite-Proxy anpassen | +| `SMTP_HOST` | `127.0.0.1` | SMTP-Server | +| `SMTP_PORT` | `1025` lokal, sonst `587` | SMTP-Port | +| `SMTP_SECURE` | `false` | Direktes TLS aktivieren | +| `SMTP_USER`, `SMTP_PASS` | leer | SMTP-Zugangsdaten; beide gemeinsam setzen | +| `SMTP_FROM` | `Tri-Hub ` | Absender | +| `SMTP_ALLOW_EXTERNAL` | `false` | Versand über externe SMTP-Server aktivieren | -| Variable | Lokaler Beispielwert | Bedeutung | -| ------------------------- | -------------------------------- | --------------------------------------------------------------------- | -| `HOST` | `127.0.0.1` | Bind-Adresse des Backends | -| `PORT` | `3000` | Backend-Port; bei Änderung auch Vite-Proxy anpassen | -| `BOOKING_DEMO` | `true` | Fügt ausschließlich lokal das Testpaket hinzu; in Produktion gesperrt | -| `SMTP_HOST` | `127.0.0.1` | Mailfänger im selben Container | -| `SMTP_PORT` | `1025` | SMTP-Port des Mailfängers | -| `SMTP_SECURE` | `false` | Bei echtem SMTP mit direktem TLS üblicherweise `true` auf 465 | -| `SMTP_USER` / `SMTP_PASS` | leer | Beide gemeinsam setzen, falls Authentifizierung benötigt wird | -| `SMTP_FROM` | `Tri-Hub ` | Absender; später abgestimmten echten Absender verwenden | -| `SMTP_ALLOW_EXTERNAL` | `false` | Externes SMTP gesperrt; erst nach ausdrücklicher Freigabe aktivieren | +Externe SMTP-Verbindungen erfordern TLS. Zugangsdaten gehören ausschließlich +in die Serverkonfiguration und dürfen kein `VITE_`-Präfix erhalten. -Für externe SMTP-Hosts wird TLS verlangt; Zertifikatsprüfung bleibt aktiv. -Für STARTTLS (typischerweise Port 587) bleibt `SMTP_SECURE=false`. -Die `.env.example` enthält nur Testwerte und keine Geheimnisse. -[SMTP-Optionen bei Nodemailer](https://nodemailer.com/smtp). +## API -## Paketdaten und spätere Router-Anbindung +Der vollständige Vertrag steht in `src/server/swagger.json`. -Person 3 pflegt `packages` in **`src/shared/packages.js`**. -Das Array enthält die drei Angebote Starter, Standard und Premium. Übersicht und -Buchung verwenden gemeinsam `id`, `name` und `summary`; zusätzliche Felder -beschreiben die Darstellung der Angebotskarten. +| Endpunkt | Funktion | +| ---------------------------- | ----------------------------------------------------------------------------------- | +| `GET /api/angebote` | Bestehende Kurzübersicht mit `id`, `name` und `summary` | +| `GET /api/packages` | Kurzübersicht im Swagger-Format | +| `GET /api/packages/{id}` | Paketdetails einschließlich Varianten und Leistungen | +| `POST /api/bookings/preview` | Bestellübersicht für `packageId` und optionale `variantId`; speichert keine Buchung | +| `POST /api/bookings` | Buchung speichern und Bestätigung versenden | -```js -// Nur Schema-Beispiel, kein verbindliches Angebot: -{ id: "stabile-paket-id", name: "Abgestimmter Paketname", summary: "Kurze Beschreibung" } -``` +POST-Anfragen benötigen `Content-Type: application/json`. Beim Buchen ist +zusätzlich ein `Idempotency-Key` als UUID v4 erforderlich. Das Formular sendet +`packageId`, gegebenenfalls `variantId`, `name` und `email`. -IDs: eindeutig, maximal 80 Zeichen, Kleinbuchstaben/Ziffern mit einzelnen -Bindestrichen. `name` und `summary` sind nicht leere Strings. IDs nach Freigabe -nicht für andere Angebote wiederverwenden. Das Backend prüft den Vertrag beim Start. -Das separate `demoPackages`-Array in derselben Datei enthält nur `demo-booking`, -explizit als Test markiert. Es wird mit `BOOKING_DEMO=true` zugeschaltet. -Keine Preise wurden erfunden. Sobald verbindliche Preisfelder vereinbart sind, -müssen Anzeige und serverseitiger Buchungssnapshot gemeinsam ergänzt werden. -Angaben wie `packageName` oder `price` aus API-Anfragen werden nicht übernommen. +Alternativ akzeptiert die API das Swagger-Objekt `customer` mit `firstName`, +`lastName`, `email` sowie optional `phone`, `ageGroup` und `notes`. Dabei muss +`acceptedTerms` den Wert `true` haben. Diese Angaben werden ebenfalls gespeichert. -Die Angebotsseite kann vorerst auf die eigenständige Ansicht verlinken: +Die Antwort enthält die Buchungsnummer, den Erstellungszeitpunkt, das gebuchte +Paket unter `bookedPackage` und den Versandstatus. Eine neue Buchung mit +SMTP-Annahme erhält HTTP 201, eine Wiederholung HTTP 200. Bei ausstehendem, +fehlgeschlagenem oder unklarem Versand wird HTTP 202 zurückgegeben. -```js -link.href = `/booking.html#/buchen/${encodeURIComponent(paket.id)}`; -``` +Ungültige Angaben, Pakete und Varianten ergeben HTTP 400; ein unbekanntes Paket +am Detail-Endpunkt HTTP 404. Derselbe Anfrageschlüssel mit anderen Daten ergibt +HTTP 409. JSON-Anfragen sind auf 8 KiB begrenzt. Fehlerantworten enthalten +`error.code`, `error.message` und gegebenenfalls `error.fields`. -Es wurde kein Router in die gemeinsame Startseite eingebaut. Wenn Person 2 den -Router ergänzt, kann sie bei `/#/buchen/:packageId` diese Funktion aufrufen: +## Speicherung und Wiederholungen -```js -import { mountBookingPage } from "./features/booking/booking-page.js"; -const cleanup = mountBookingPage(container, { packageId }); -// Beim Verlassen der Route: -cleanup(); -``` +Die Buchungen liegen in `src/server/data/bookings.csv`. Neue Buchungsnummern +werden fortlaufend als `TH-000001`, `TH-000002` usw. vergeben. Der höchste +vorhandene Wert bestimmt die nächste Nummer. Die CSV muss deshalb vollständig +erhalten bleiben; ältere UUID-Buchungsnummern werden weiterhin unterstützt. -Das Modul bringt seine begrenzten `.booking`-Styles mit. Farben und Schrift -orientieren sich am vorhandenen Basis-CSS. Gemeinsame Designvariablen und eine -Button-Komponente gibt es bisher nicht. Layout und Navigation bleiben Aufgabe -der anderen Teammitglieder. „Zur Angebotsseite“ führt zu `/angebotsuebersicht.html`. +`bookedPackage` speichert den Bestellstand als JSON, `customer` die optionalen +strukturierten Kundendaten und `acceptedTerms` die zugehörige Zustimmung. +CSV-Dateien mit dem früheren Spaltensatz bleiben lesbar und werden beim nächsten +Speichern erweitert. Für alte Buchungen ohne Bestellstand liefert die API +`bookedPackage: null`. -## API-Beispiele +Lesen, Schreiben und Versand werden innerhalb einer Warteschlange abgearbeitet. +Die Datei wird über eine temporäre Datei atomar ersetzt. Unterstützt wird ein +Backend-Prozess auf einem lokalen Dateisystem. Mehrere Serverinstanzen benötigen +einen gemeinsam abgesicherten Speicher. CSV-Zellen werden gegen die Auswertung +als Tabellenformeln geschützt; Dateien erhalten den Zugriffsmodus 0600. -`GET /api/angebote` liefert `{ "packages": [...] }` mit `id`, `name`, `summary` -und `testOnly`. Keine personenbezogenen Daten oder SMTP-Einstellungen. +Wiederholte Anfragen mit demselben Schlüssel und denselben Daten liefern die +vorhandene Buchung. Es wird keine zweite Bestellung angelegt. Die Auswahl einer +anderen Variante mit demselben Schlüssel wird abgelehnt. -Für `POST /api/bookings` sind `Content-Type: application/json` und ein -`Idempotency-Key` als UUID v4 erforderlich. Ein Schlüssel gehört genau zu einer -Buchung. Bei Wiederholung denselben Schlüssel und dieselben Angaben verwenden. +Das Formular merkt sich Anfrage und Bestellübersicht je Paket und Variante im +`sessionStorage`. Nach einem Verbindungsfehler bleiben die Angaben gesperrt, +damit dieselbe Anfrage erneut geprüft werden kann. Ein neuer Tab oder gelöschter +Sitzungsspeicher liegt außerhalb dieser Absicherung. -```http -POST /api/bookings -Content-Type: application/json -Idempotency-Key: 36c45d7f-585f-47e9-bf5c-7ea1b7a162ed +## Versandstatus -{"packageId":"demo-booking","name":"Test Person","email":"person@example.test"} -``` +| Status | Bedeutung | +| ---------- | ------------------------------------------------------------------------------------------------ | +| `pending` | Buchung gespeichert; Versand noch nicht gestartet. Dieselbe Anfrage kann den Versand fortsetzen. | +| `sending` | Versandabsicht gespeichert. Nach einem Prozessabbruch liefert die API `unknown`. | +| `accepted` | SMTP hat die Nachricht angenommen; die Zustellung ist noch nicht bestätigt. | +| `failed` | Versand fehlgeschlagen oder ausdrücklich abgelehnt. | +| `unknown` | SMTP-Annahme oder anschließende Statusspeicherung blieb unklar. | -Neue Buchung mit SMTP-Annahme: HTTP `201`; gespeicherte Wiederholung: `200`. +Bei `failed` und `unknown` wird nicht automatisch erneut versendet. Vor einem +manuellen Neuversand muss der Mailserver anhand der Buchungsnummer geprüft werden. +CSV und SMTP bilden keine gemeinsame Transaktion. -```json -{ - "bookingId": "TH-000001", - "createdAt": "2026-09-24T12:00:00.000Z", - "packageId": "demo-booking", - "packageName": "1:1-Ernährungsberatung", - "saved": true, - "emailStatus": "accepted", - "replayed": false -} -``` +## Tests -Gespeichert, aber E-Mail nicht bestätigt: HTTP `202` mit derselben Struktur und -`emailStatus: "failed"`, `"pending"` oder `"unknown"`. Das bedeutet ausdrücklich -nicht, dass die E-Mail zugestellt wurde. Die Buchungsnummer bleibt gültig. +`npm test` prüft Validierung, Preisberechnung, Varianten, CSV-Kompatibilität, +Wiederholungen und Versandfehler. `npm run test:browser` prüft den Weg von der +Angebotsübersicht über die Paketdetails bis zur Bestätigung, einschließlich +Premium-Jahresvariante, Tastaturbedienung und schmalem Bildschirm. -| Status/Code | Bedeutung und Verhalten | -| ------------------------------ | ----------------------------------------------------------------------- | -| `400 INVALID_INPUT` | Angaben korrigieren; Feldhinweise stehen in `error.fields` | -| `400 UNKNOWN_PACKAGE` | Paket nicht verfügbar; verfügbares Paket auswählen | -| `400 INVALID_KEY` | UUID-v4-Anfrageschlüssel fehlt oder ist ungültig | -| `400 INVALID_JSON` | Anfrage ist kein gültiges JSON | -| `409 IDEMPOTENCY_CONFLICT` | Derselbe Schlüssel wurde mit anderen Angaben verwendet | -| `413 BODY_TOO_LARGE` | Mehr als 8 KiB Anfrageinhalt | -| `415 UNSUPPORTED_CONTENT_TYPE` | JSON-Content-Type erforderlich | -| `503 BOOKING_NOT_SAVED` | Speicherung fehlgeschlagen; mit gleichem Schlüssel wiederholen | -| `503 STORAGE_UNAVAILABLE` | Vorhandener Status nicht lesbar; mit gleichem Schlüssel wiederholen | -| `500 INTERNAL_ERROR` | Status unklar; keine neue Buchung erzeugen, gleiche Anfrage wiederholen | +Die Tests verwenden die regulären Paketdaten, temporäre CSV-Dateien und einen +simulierten Mailversand. Playwright benötigt Chromium; die Installation erfolgt +mit `npx playwright install --with-deps chromium`. Die Ports 3000 und 5173 müssen +für die automatisch gestarteten Testserver frei sein. -Die E-Mail-Prüfung unterstützt übliche unquotierte ASCII-Adressen. Internationale -Domains können in Punycode angegeben werden; SMTPUTF8-Adressen sind nicht Teil -dieses Sprints. Eine Formatprüfung beweist nicht, dass das Postfach existiert. - -Beispiel für einen Validierungsfehler: - -```json -{ - "error": { - "code": "INVALID_INPUT", - "message": "Bitte die markierten Angaben prüfen.", - "fields": { "email": "Bitte eine gültige E-Mail-Adresse eingeben." } - } -} -``` - -## Simulation und spätere Zahlung - -Das Demonstrationspaket heißt in der Oberfläche „1:1-Ernährungsberatung“. -Die ID `demo-booking` und die Freigabe über `BOOKING_DEMO` bleiben unverändert; -es wurden keine verbindlichen Angebote, Leistungsumfänge oder Preise ergänzt. -Ein gemeinsamer Hinweis aus `src/shared/booking-copy.js` erklärt vor dem Absenden -und in der E-Mail, dass der Ablauf simuliert ist: Der Zahlungsvorgang wird -übersprungen, es wird nichts abgebucht und keine verbindliche Beratung gebucht. -Bei vollständiger Integration kann der Zahlungsschritt ergänzt werden. Dafür -müssen tatsächliche Zahlungsbestätigung und Buchungsstatus serverseitig verbunden -werden; das Entfernen des Hinweises allein aktiviert keine Zahlung. - -## Fortlaufende Buchungsnummern - -Neue Buchungen erhalten `TH-000001`, `TH-000002` usw. Dieselbe Nummer steht in der -Antwort, CSV und E-Mail. Sie wird innerhalb der Warteschlange aus dem höchsten -vorhandenen TH-Wert berechnet und mit der Buchung gespeichert. Ein Neustart setzt -sie nicht zurück; Wiederholungen behalten dieselbe Nummer. Sechs Stellen sind -die Mindestbreite, nach `TH-999999` folgt `TH-1000000`. Es gibt keinen Jahresreset. - -Bestehende UUID-Buchungen und gespeicherte Browserbestätigungen behalten ihre -bisherige Nummer, damit bereits ausgegebene Referenzen gültig bleiben. Nur neue -Buchungen verwenden das neue Format. Der technische Idempotency-Key bleibt eine -zufällige UUID; die lesbare Buchungsnummer ist kein Geheimnis oder Zugriffstoken. - -Der Zähler setzt voraus, dass die CSV vollständig erhalten bleibt. Löschen von -Zeilen, Entfernen der Datei oder Zurückspielen eines älteren Backups kann Nummern -wiederverwendbar machen. Vor einer Archivierungs-/Löschfunktion ist ein separat -persistierter, transaktionaler Zähler nötig. Weiterhin nur ein Backend-Prozess. - -## CSV, Wiederholungen und Fehlergrenzen - -Pfad: `src/server/data/bookings.csv`. Spalten: `bookingId`, `createdAt`, `packageId`, -`packageName`, `name`, `email`, `emailStatus`, `idempotencyKey`, `requestHash`. -`requestHash` verknüpft den Schlüssel mit den normalisierten Anfragedaten, ohne -sie in einer zweiten Datenquelle zu duplizieren. Bereits gespeicherte Buchungen -können auch nach dem Entfernen eines Pakets weiterhin wiederholt abgefragt werden. - -CSV-Bibliotheken behandeln Kommas, Anführungszeichen und Zeilenumbrüche. Zellen, -die wie Tabellenformeln beginnen, erhalten ein Apostroph. Bereits führende -Apostrophe werden ebenfalls maskiert, damit internes Lesen die Originalwerte -wiederherstellen kann. Persönliche Namen dürfen in der API keine Steuerzeichen -enthalten; die Speicherschicht unterstützt Zeilenumbrüche für andere CSV-Felder. -Dateien werden mit Zugriffsmodus `0600` geschrieben. - -Eine Warteschlange führt komplette Buchungen nacheinander aus. Schreiben erfolgt -über eine temporäre Datei und atomisches Umbenennen. **Nur ein laufender -Backend-Prozess auf einem lokalen Dateisystem wird unterstützt.** Keine Cluster, -mehreren Containerinstanzen, manuelle Dateiedits während des Betriebs oder -Netzlaufwerke. Die gesamte CSV wird bei Änderungen gelesen/neu geschrieben; -geeignet für den ersten Sprint mit kleinem Volumen. Es gibt keine verteilte -Transaktion oder vollständige Garantie gegen Hardware-/Stromausfall. - -Versandzustände: - -- `pending`: Buchung gespeichert, Versand noch nicht gestartet. Derselbe Schlüssel - darf den Versand nach einem Schreibfehler fortsetzen. -- `sending`: Versandabsicht dauerhaft gespeichert. Bei Prozessabbruch zeigt die - API diesen Zustand als `unknown`; kein automatischer Neuversand. -- `accepted`: SMTP hat den Empfänger und die Nachricht angenommen. -- `failed`: Der Versand ist fehlgeschlagen bzw. wurde ausdrücklich abgelehnt. -- `unknown`: Verbindung oder Statusspeicherung brach zu einem unklaren Zeitpunkt ab. - -Für `failed` und `unknown` gibt es bewusst keinen automatischen erneuten Versand. -Erst CSV und Mailserver/Mailfänger anhand der Buchungsnummer prüfen. Eine -administrative Wiederholungsfunktion ist offen; keinesfalls eine neue Buchung -anlegen oder `sending` blind zurücksetzen. SMTP und CSV können nicht gemeinsam -atomar abgeschlossen werden, weshalb ein exakt einmaliger Versand nicht garantiert -werden kann. Ein Verbindungsabbruch nach SMTP-Annahme kann doppelte Mails verursachen, -wenn später ohne Prüfung manuell erneut gesendet wird. - -Das Formular speichert Schlüssel und Angaben je Paket in `sessionStorage`, also -für den aktuellen Browser-Tab, auch über Neuladen hinweg. Bei unklarem Ergebnis -bleiben die Angaben schreibgeschützt und können mit demselben Schlüssel erneut -geprüft werden. Nach gespeicherter Buchung bleibt das Formular gesperrt. Ein -bewusst neuer Auftrag benötigt einen neuen Tab bzw. eine neue Sitzung. Das -Schließen des Tabs, gelöschter Sitzungsspeicher, ein anderes Gerät oder ein neuer -Schlüssel liegen außerhalb der Doppelbuchungsabsicherung. Bei unklarem Ausgang -zuerst den bestehenden Status prüfen. Es gibt keine globale Erkennung anhand -von Name/E-Mail, weil zwei bewusst getrennte Buchungen möglich sein müssen. - -`.gitignore` schließt Buchungsdaten einschließlich temporärer Dateien und `.env` -aus. Das Backend liefert ausschließlich API-Antworten aus. Vite blockiert -Serververzeichnis und Umgebungsdateien auch beim direkten Dateizugriff. Im -Deployment nur `dist/` öffentlich bereitstellen, nie den gesamten Projektordner. - -## Prüfen - -Automatisierte Tests erzeugen temporäre CSV-Dateien und simulieren E-Mail-Versand. -Sie schreiben nicht in `src/server/data/bookings.csv` und kontaktieren kein echtes SMTP. -Vor Browsertests lokale Server auf 3000/5173 stoppen; Playwright startet eigene -Testserver und übernimmt keine bereits laufenden Instanzen. - -```bash -npm test -npx playwright install --with-deps chromium -npm run test:browser -npm run format:check -npm run build -``` - -Die einmalige Chromium-Installation benötigt Netzwerk und unter Linux eventuell -Systempakete. Der Dev Container wurde dafür nicht global umkonfiguriert. - -Manuelle Abnahme im lokalen Mailfänger: - -1. Demo-Modus, Mailpit, Backend und Vite wie oben starten. -2. Testpaket mit `person@example.test` buchen. Buchungsnummer notieren. -3. CSV im Editor öffnen: genau eine passende Zeile, Paket, Zeitpunkt und Versandstatus prüfen. -4. In Mailpit die Nachricht öffnen: Empfänger, Paket und Buchungsnummer vergleichen. -5. Seite neu laden: gleiche Buchungsnummer, keine neue CSV-Zeile und keine zweite Mail. -6. Mit Tab/Shift+Tab und Enter bedienen, Feldfehler und sichtbaren Fokus prüfen. - Auf 320 Pixel Breite und mit Browser-Zoom prüfen; ergänzend Screenreader verwenden. -7. Für den Versandfehler einen neuen Test in neuer Sitzung bei gestopptem Mailpit - ausführen: Buchung bleibt gespeichert, Anzeige meldet keine erfolgreiche Zustellung. - -**Das Akzeptanzkriterium „E-Mail zugestellt“ ist mit SMTP-Annahme oder Mailpit allein -noch nicht für einen echten Empfänger nachgewiesen.** Erst nach ausdrücklicher -Freigabe echtes SMTP konfigurieren und an eine vereinbarte Testadresse senden. -Dann im tatsächlichen Empfängerpostfach (auch Spam) Eingang, Buchungsnummer und -Paket kontrollieren und Zeitpunkt sowie Ergebnis im Sprint-Nachweis festhalten. -Es gibt bisher keine automatische Bounce-/Zustellverfolgung. - -## Durchgeführte Prüfungen - -- 17 Backend-Tests: Validierung, CSV-Sonderzeichen/Formelschutz, parallele - Anfragen, Wiederholung mit neuer Serviceinstanz, Speicher- und SMTP-Fehler. -- 6 Chromium-Prüfungen: Tastaturbedienung/Fokus, 320-Pixel-Ansicht, verlorene - Antwort mit Neuladen, Ladezustand, Versandfehler und private Dateipfade. -- Lokaler SMTP-Test mit Mailpit und temporärer CSV: Nachricht tatsächlich im - Mailfänger empfangen, Paket/Buchungsnummer geprüft, wiederholte Anfrage ohne - zusätzliche CSV-Zeile oder E-Mail. Ausschließlich `example.test`-Empfänger. -- Projektweite Formatprüfung, Produktionsbuild und Git-Whitespace-Prüfung. - -Die automatisierten Prüfungen ersetzen keine Screenreader-Abnahme oder Prüfung -in weiteren Browsern. Keine echte E-Mail-Zustellung wurde getestet. Zum Ausführen -der Browserprüfungen wurden Chromium und die benötigten Systembibliotheken in -dieser Arbeitsumgebung installiert; die Dev-Container-Konfigurationsdatei wurde -nicht verändert. Der Mailpit-Test verwendete eine temporär heruntergeladene -Binärdatei außerhalb des Repositorys. - -## Offene Punkte und gemeinsame Dateien - -Die Angebotsübersicht ist als eigene HTML-Seite angebunden. Offen: Anschluss an einen gemeinsamen Router, abschließendes -Teamdesign, reale Zustellprüfung und Screenreader-Abnahme. Vor öffentlichem -Betrieb sind außerdem Betriebsfragen wie Zugriff auf CSV/Backups, Aufbewahrung -und Schutz des öffentlichen Buchungsendpunkts vor automatisiertem Massenversand -zu klären. Keine Zahlungen, Benutzerkonten oder Datenbank implementiert. - -Gemeinsame Änderungen: `package.json` und Lockdatei (Abhängigkeiten/Startbefehle), -`src/main.js` (bestehenden falschen CSS-Import korrigiert), `.gitignore` und -`.prettierignore` (private Daten/Testergebnisse ausschließen). Neu hinzugefügt: -`vite.config.js` (Proxy, Dateizugriffsschutz, zwei HTML-Einstiegspunkte), -`src/shared/packages.js` (gemeinsamer Paketvertrag), `.env.example`, `booking.html` -und `playwright.config.js`. Die Dev-Container-Konfiguration installiert Mailpit und leitet dessen Web-Port weiter. - -Technische Referenzen: [CSV-Parser](https://csv.js.org/parse/api/sync/), -[Playwright-Testserver](https://playwright.dev/docs/test-webserver). +Vor einem Commit außerdem `npm run format:check`, `npm run build` und +`git diff --check` ausführen. Für eine manuelle Mailprüfung eine Buchung mit einer +Adresse unter `example.test` anlegen und die Bestätigung in Mailpit kontrollieren. diff --git a/paket-details.html b/paket-details.html index c79f82d..36844f5 100644 --- a/paket-details.html +++ b/paket-details.html @@ -1,30 +1,32 @@ + + + + Paket-Details – Tri-Hub Ernährungsberatung + + + + + - - - - Paket-Details – Tri-Hub Ernährungsberatung - - - - - + +
+ ← Zurück zur Angebotsübersicht +
- -
- ← Zurück zur Angebotsübersicht -
- -
-
- -
-
- - - +
+
+ +
+
+ + diff --git a/src/features/angebote/angebotsuebersicht.js b/src/features/angebote/angebotsuebersicht.js index 77afcf9..c0ca328 100644 --- a/src/features/angebote/angebotsuebersicht.js +++ b/src/features/angebote/angebotsuebersicht.js @@ -1,21 +1,16 @@ import { packages } from "../../shared/packages.js"; export function renderAngebotsuebersicht() { - // Wir suchen den Container, der in der angebotsuebersicht.html definiert wurde const container = document.getElementById("packages-container"); - // Sicherheitscheck: Wenn der Container nicht da ist, brechen wir ab if (!container) { console.warn("Container für Angebotsübersicht nicht gefunden."); return; } - // Container leeren (falls die Funktion mehrfach aufgerufen wird) container.innerHTML = ""; - // Durch jedes Paket iterieren und das HTML generieren packages.forEach((pkg) => { - // Highlight-Badge nur anzeigen, wenn es eins gibt const highlightHtml = pkg.highlight ? `${pkg.highlight}` : ""; @@ -46,13 +41,11 @@ export function renderAngebotsuebersicht() { - ${pkg.buttonText} `; - // Das generierte HTML dem Container hinzufügen container.innerHTML += cardHtml; }); } diff --git a/src/features/angebote/paket-details.js b/src/features/angebote/paket-details.js index 790f481..d3ad192 100644 --- a/src/features/angebote/paket-details.js +++ b/src/features/angebote/paket-details.js @@ -1,16 +1,12 @@ -// src/features/angebote/paket-details.js import { packagesDetails } from "../../shared/packagesdetails.js"; -document.addEventListener("DOMContentLoaded", async () => { +document.addEventListener("DOMContentLoaded", () => { const container = document.getElementById("package-detail-container"); if (!container) return; - // 1. ID aus der URL lesen (z.B. ?id=ernaehrung-standard) const params = new URLSearchParams(window.location.search); const packageId = params.get("id"); - // 2. Passendes Paket in packagesdetails.js suchen - // (Später kann hier alternativ: await fetch(`/api/packages/${packageId}`) genutzt werden) const pkg = packagesDetails.find((item) => item.id === packageId); if (!pkg) { @@ -24,7 +20,6 @@ document.addEventListener("DOMContentLoaded", async () => { return; } - // 3. Optional: Varianten-Auswahl rendern (für das Premium-Paket) const variantsHtml = pkg.variants ? `

Wähle deine Variante

@@ -34,7 +29,6 @@ document.addEventListener("DOMContentLoaded", async () => {
` : ""; - // 4. Detailseite im Tri-Hub-Stil rendern container.innerHTML = `
@@ -87,7 +81,6 @@ document.addEventListener("DOMContentLoaded", async () => {
`; - // 5. Preis aktualisieren, falls eine andere Variante (24 vs. 52 Wochen) gewählt wird const variantSelect = document.getElementById("variant-select"); const displayedPrice = document.getElementById("displayed-price"); if (variantSelect && displayedPrice) { @@ -100,33 +93,10 @@ document.addEventListener("DOMContentLoaded", async () => { }); } - // 6. Klick auf "Jetzt buchen" -> Daten für bookingService bereitstellen & weiterleiten const bookingBtn = document.getElementById("proceed-to-booking-btn"); bookingBtn.addEventListener("click", () => { - const selectedVariantId = variantSelect ? variantSelect.value : pkg.id; - const selectedVariant = pkg.variants - ? pkg.variants.find((v) => v.variantId === selectedVariantId) - : null; - - const bookingPayload = { - packageId: pkg.id, - variantId: selectedVariantId, - title: pkg.title, - type: pkg.type, - durationWeeks: selectedVariant - ? selectedVariant.durationWeeks - : pkg.durationWeeks, - price: selectedVariant ? selectedVariant.price : pkg.price, - currency: pkg.currency, - taxRate: pkg.taxRate, - summaryForBooking: pkg.summaryForBooking, - }; - - // Im sessionStorage zwischenspeichern + per URL an die Buchungsseite übergeben - sessionStorage.setItem( - "selectedPackageForBooking", - JSON.stringify(bookingPayload), - ); - window.location.href = `booking.html?id=${pkg.id}&variant=${selectedVariantId}`; + const params = new URLSearchParams({ id: pkg.id }); + if (variantSelect) params.set("variant", variantSelect.value); + window.location.href = `booking.html?${params}`; }); }); diff --git a/src/features/booking/booking-api.js b/src/features/booking/booking-api.js index 65d8613..ba978d6 100644 --- a/src/features/booking/booking-api.js +++ b/src/features/booking/booking-api.js @@ -33,10 +33,6 @@ async function request(path, options = {}) { return body; } -export function loadPackages(signal) { - return request("/api/angebote", { signal }); -} - export function submitBooking(payload, idempotencyKey) { return request("/api/bookings", { method: "POST", @@ -47,3 +43,12 @@ export function submitBooking(payload, idempotencyKey) { body: JSON.stringify(payload), }); } + +export function previewBooking(packageId, variantId, signal) { + return request("/api/bookings/preview", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ packageId, variantId }), + signal, + }); +} diff --git a/src/features/booking/booking-entry.js b/src/features/booking/booking-entry.js index 81d6433..54c7513 100644 --- a/src/features/booking/booking-entry.js +++ b/src/features/booking/booking-entry.js @@ -5,9 +5,12 @@ const root = document.querySelector("#booking-root"); let cleanup; function render() { cleanup?.(); - // Eigenständige Testseite, bis Person 2 den gemeinsamen Router bereitstellt. const match = location.hash.match(/^#\/buchen\/([a-z0-9-]+)$/); - cleanup = mountBookingPage(root, { packageId: match?.[1] }); + const params = new URLSearchParams(location.search); + cleanup = mountBookingPage(root, { + packageId: params.get("id") || match?.[1], + variantId: params.get("variant") || undefined, + }); } window.addEventListener("hashchange", render); render(); diff --git a/src/features/booking/booking-page.js b/src/features/booking/booking-page.js index 420d238..30bca44 100644 --- a/src/features/booking/booking-page.js +++ b/src/features/booking/booking-page.js @@ -1,10 +1,9 @@ -import { loadPackages, submitBooking } from "./booking-api.js"; +import { previewBooking, submitBooking } from "./booking-api.js"; import "./booking.css"; +import { orderSummaryRows } from "../../shared/order-summary.js"; import { paymentNotice } from "../../shared/booking-copy.js"; -// Kann später vom gemeinsamen Router aufgerufen werden. cleanup entfernt -// Listener und bricht das Laden beim Verlassen der Ansicht ab. -export function mountBookingPage(root, { packageId } = {}) { +export function mountBookingPage(root, { packageId, variantId } = {}) { const controller = new AbortController(); const { signal } = controller; root.innerHTML = ` @@ -24,20 +23,22 @@ export function mountBookingPage(root, { packageId } = {}) { return; } try { - const data = await loadPackages(signal); + const selected = await previewBooking(packageId, variantId, signal); if (signal.aborted) return; - const selected = data.packages.find( - (entry) => entry.id === packageId, - ); - if (!selected) { - intro.textContent = - "Dieses Paket ist nicht verfügbar. Bitte wähle ein Paket auf der Angebotsseite aus."; - return; - } intro.remove(); renderForm(selected); - } catch { + } catch (error) { if (signal.aborted) return; + if ( + [ + "UNKNOWN_PACKAGE", + "INVALID_VARIANT", + "INVALID_INPUT", + ].includes(error.code) + ) { + intro.textContent = error.message; + return; + } intro.textContent = "Das Paket konnte nicht geladen werden. Bitte prüfe deine Verbindung und versuche es erneut."; const retry = document.createElement("button"); @@ -61,6 +62,11 @@ export function mountBookingPage(root, { packageId } = {}) {

+

+
+

Enthaltene Leistungen

+
    +
    + diff --git a/src/components/navigationsbar/navigation.js b/src/components/navigationsbar/navigation.js index eaf7d71..b70d427 100644 --- a/src/components/navigationsbar/navigation.js +++ b/src/components/navigationsbar/navigation.js @@ -1,51 +1,111 @@ -(function () { - var toggle = document.querySelector(".navbar__toggle"); - var nav = document.querySelector(".navbar__nav"); - var dropdownToggle = document.querySelector(".navbar__dropdown-toggle"); - var dropdown = document.querySelector("#dropdown-spektrum"); +/** + * Lädt das Navbar-Fragment (navigation.html) per fetch() und ersetzt + * damit den Platzhalter im Header der jeweiligen Seite. + */ +export function initNavbar(placeholderSelector = "#navbar-placeholder") { + const placeholder = document.querySelector(placeholderSelector); + if (!placeholder) { + console.warn("Navbar-Platzhalter nicht gefunden:", placeholderSelector); + return; + } - if (!toggle || !nav || !dropdownToggle || !dropdown) return; + fetch(new URL("./navigation.html", import.meta.url)) + .then((res) => res.text()) + .then((html) => { + placeholder.outerHTML = html; // Platzhalter durch echtes Navbar-Markup ersetzen + setupBehavior(); + markCurrentPage(); + }) + .catch((err) => + console.error("Navbar konnte nicht geladen werden:", err), + ); +} - // Mobiles Menü öffnen/schließen - toggle.addEventListener("click", function () { - var isOpen = nav.classList.toggle("is-open"); +/** + * Richtet alle interaktiven Funktionen der Navbar ein: + * - Mobiles Menü öffnen/schließen per Klick + * - Automatischer Fokus auf ersten Link, wenn per Tastatur geöffnet + * - Escape schließt das Menü und gibt den Fokus zurück + * - Menü schließt sich, wenn der Fokus die Navbar per Tab verlässt + * - Klick außerhalb der Navbar schließt das Menü (Maus/Touch) + */ +export function setupBehavior() { + const navbar = document.querySelector(".navbar"); + const toggle = document.querySelector(".navbar__toggle"); + const nav = document.querySelector(".navbar__nav"); + const menu = document.querySelector(".navbar__menu"); + if (!navbar || !toggle || !nav || !menu) return; + + let openedViaKeyboard = false; + + // Merken, ob der Toggle-Button per Tastatur (statt Maus) ausgelöst wurde + toggle.addEventListener("keydown", (e) => { + if (e.key === "Enter" || e.key === " ") { + openedViaKeyboard = true; + } + }); + + // Menü auf-/zuklappen; funktioniert nativ per Klick UND per Enter/Space, + // da .navbar__toggle ein echtes + +
    + + +
    + + +
    +
    +
    + tri-hub · Triathlon als sportliches Zuhause + ab 50. +
    +
    Ernährung · Wissen · Erfahrungen
    +
    -
    - „Teile hier deine Erfahrung mit der Ernährungsberatung – - ehrlich, persönlich und so, wie du sie erlebt hast.“ -
    - + - + form.reset(); + }); + + From 48fdb2ecc182435ab989dd2a6691848f1f963fa7 Mon Sep 17 00:00:00 2001 From: Emirhan3 Date: Mon, 28 Sep 2026 11:07:51 +0000 Subject: [PATCH 16/27] fix: Navbar-Links und Produktionsbuild anpassen --- index.html | 5 ++- public/images/logo.png | Bin 0 -> 64906 bytes src/components/navigationsbar/README.md | 14 ++++++++ src/components/navigationsbar/navigation.html | 32 ++++++++++++------ src/components/navigationsbar/navigation.js | 19 ++++------- src/main.js | 3 +- 6 files changed, 46 insertions(+), 27 deletions(-) create mode 100644 public/images/logo.png create mode 100644 src/components/navigationsbar/README.md diff --git a/index.html b/index.html index dc976fa..722aa9e 100644 --- a/index.html +++ b/index.html @@ -2,6 +2,7 @@ + 1:1-Ernährungsberatung -
    - Ernährungsberatung -
    +

    Ernährung, die zu dir passt.

    diff --git a/public/images/logo.png b/public/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3063d261743040aa22d08f743f79b611ce78acbb GIT binary patch literal 64906 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 CIA1LQ literal 0 HcmV?d00001 diff --git a/src/components/navigationsbar/README.md b/src/components/navigationsbar/README.md new file mode 100644 index 0000000..e349d1c --- /dev/null +++ b/src/components/navigationsbar/README.md @@ -0,0 +1,14 @@ +# Navigation einbinden + +Füge an der gewünschten Stelle im HTML einen Platzhalter ein und binde CSS +und JavaScript separat ein: + +```html + + + +``` + +Das Script lädt `navigation.html` und initialisiert die Navigation automatisch, +`initNavbar()` muss nicht zusätzlich aufgerufen werden. Die Stylesheet-Datei wird +nicht vom Script geladen und muss deshalb separat eingebunden werden. diff --git a/src/components/navigationsbar/navigation.html b/src/components/navigationsbar/navigation.html index 16665fb..255751a 100644 --- a/src/components/navigationsbar/navigation.html +++ b/src/components/navigationsbar/navigation.html @@ -3,11 +3,7 @@
    diff --git a/paket-details.html b/paket-details.html index 36844f5..9ec9e46 100644 --- a/paket-details.html +++ b/paket-details.html @@ -1,32 +1,372 @@ - - - - - Paket-Details – Tri-Hub Ernährungsberatung - - - - - + - -
    - ← Zurück zur Angebotsübersicht -
    + + + + + + Paket-Details – Tri-Hub Ernährungsberatung -
    -
    - -
    -
    + + + + + + + + + + +
    +
    + +
    +
    + + + + - - diff --git a/src/features/angebote/paket-details.js b/src/features/angebote/paket-details.js index d3ad192..7f7cccd 100644 --- a/src/features/angebote/paket-details.js +++ b/src/features/angebote/paket-details.js @@ -1,4 +1,5 @@ import { packagesDetails } from "../../shared/packagesdetails.js"; +import { mountBookingPage } from "../booking/booking-page.js"; document.addEventListener("DOMContentLoaded", () => { const container = document.getElementById("package-detail-container"); @@ -11,92 +12,188 @@ document.addEventListener("DOMContentLoaded", () => { if (!pkg) { container.innerHTML = ` -
    -

    Paket nicht gefunden

    -

    Bitte wähle ein gültiges Angebot aus der Übersicht.

    - Zur Übersicht -
    - `; +
    +

    Paket nicht gefunden

    +

    Bitte wähle ein gültiges Angebot aus der Übersicht.

    + + Zur Übersicht + +
    + `; return; } - const variantsHtml = pkg.variants - ? `
    -

    Wähle deine Variante

    - -
    ` + const variantsHtml = pkg.variants?.length + ? ` +
    +

    Wähle deine Variante

    + +
    + ` : ""; container.innerHTML = ` -
    -
    - ${pkg.type} · ${pkg.durationLabel} - ${pkg.badge} -
    - -

    ${pkg.title}

    -

    ${pkg.subtitle}

    - -
    - ${pkg.price.toFixed(2)} € - inkl. ${pkg.taxRate}% MwSt. (${pkg.billingInterval}) -
    - - ${variantsHtml} - -
    -
    -

    Für wen ist dieses Paket ideal?

    -
      - ${pkg.targetGroup.map((item) => `
    • ${item}
    • `).join("")} -
    -
    - -
    -

    Das ist im Paket enthalten

    -
      - ${pkg.includedFeatures.map((feat) => `
    • ${feat}
    • `).join("")} -
    -
    - -
    -

    So läuft deine Begleitung ab

    - ${pkg.processSteps - .map( - (s) => ` -
    - ${s.step}: ${s.text} +
    +
    + + ${pkg.type} · ${pkg.durationLabel} + + ${ + pkg.badge + ? `${pkg.badge}` + : "" + }
    - `, - ) - .join("")} -
    -
    - -
    - `; +

    ${pkg.title}

    +

    ${pkg.subtitle}

    + +
    + + ${pkg.price.toFixed(2)} € + + + inkl. ${pkg.taxRate}% MwSt. (${pkg.billingInterval}) + +
    + + ${variantsHtml} + +
    +
    +

    + Für wen ist dieses Paket ideal? +

    +
      + ${pkg.targetGroup + .map((item) => `
    • ${item}
    • `) + .join("")} +
    +
    + +
    +

    + Das ist im Paket enthalten +

    +
      + ${pkg.includedFeatures + .map((feature) => `
    • ${feature}
    • `) + .join("")} +
    +
    + +
    +

    + So läuft deine Begleitung ab +

    + ${pkg.processSteps + .map( + (step) => ` +
    + ${step.step}: + + ${step.text} + +
    + `, + ) + .join("")} +
    +
    + + + +

    + Dieser Buchungsablauf ist eine Simulation. Es erfolgt keine + Abbuchung und keine verbindliche Beratungsbuchung. +

    + +

    +
    +
    + `; const variantSelect = document.getElementById("variant-select"); const displayedPrice = document.getElementById("displayed-price"); - if (variantSelect && displayedPrice) { - variantSelect.addEventListener("change", (e) => { - const selectedOption = e.target.selectedOptions[0]; - const newPrice = parseFloat( - selectedOption.getAttribute("data-price"), - ); - displayedPrice.textContent = `${newPrice.toFixed(2)} €`; + const bookingBtn = document.getElementById("proceed-to-booking-btn"); + const bookingRoot = document.getElementById("booking-root"); + const bookingStatus = document.getElementById("booking-status"); + + let cleanupBooking; + + function prepareBooking() { + cleanupBooking?.(); + bookingStatus.textContent = ""; + + cleanupBooking = mountBookingPage(bookingRoot, { + packageId: pkg.id, + variantId: variantSelect?.value, }); } - const bookingBtn = document.getElementById("proceed-to-booking-btn"); + function updatePrice() { + if (!variantSelect) return; + + const selectedOption = variantSelect.selectedOptions[0]; + const price = Number(selectedOption.dataset.price); + + displayedPrice.textContent = `${price.toFixed(2)} €`; + } + + updatePrice(); + prepareBooking(); + + variantSelect?.addEventListener("change", () => { + updatePrice(); + prepareBooking(); + }); + bookingBtn.addEventListener("click", () => { - const params = new URLSearchParams({ id: pkg.id }); - if (variantSelect) params.set("variant", variantSelect.value); - window.location.href = `booking.html?${params}`; + const dialog = bookingRoot.querySelector(".booking__dialog"); + + if (!dialog) { + bookingStatus.textContent = + "Das Buchungsformular ist noch nicht verfügbar. Bitte beachte den Ladehinweis im Buchungsbereich."; + return; + } + + if (dialog.open) return; + + bookingStatus.textContent = ""; + + dialog.addEventListener( + "close", + () => { + bookingBtn.focus(); + }, + { once: true }, + ); + + dialog.showModal(); + dialog.querySelector('[name="name"]').focus(); }); }); diff --git a/src/main.js b/src/main.js index a8ccb3d..83e72f9 100644 --- a/src/main.js +++ b/src/main.js @@ -1,4 +1,7 @@ import "./styles/styles.css"; +<<<<<<< HEAD +======= import "./components/navigationsbar/navigation.js"; +>>>>>>> dev document.querySelector("#year").textContent = new Date().getFullYear(); diff --git a/src/styles/packages.css b/src/styles/packages.css index da50358..c40e4f5 100644 --- a/src/styles/packages.css +++ b/src/styles/packages.css @@ -1,123 +1,10 @@ -:root { - --bg-dark: #0d0d1a; - --bg-card: #1a1a2e; - --accent-teal: #10b981; - --text-white: #ffffff; - --text-lightgray: #f8fafc; - --text-muted: #94a3b8; - - --font-display: "Playfair Display", serif; - --font-body: "Inter", system-ui, sans-serif; +#booking-root .booking__open { + display: none; } - -.packages-section { - background-color: var(--bg-dark); - padding: 4rem 2rem; - font-family: var(--font-body); - color: var(--text-lightgray); -} - -.packages-container { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr)); - gap: 2rem; - max-width: 1200px; - margin: 0 auto; -} - -.package-card { - background-color: var(--bg-card); - border-radius: 12px; - padding: 2rem; - display: flex; - flex-direction: column; - position: relative; - border: 1px solid rgba(255, 255, 255, 0.05); -} - -.package-type-container { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 1rem; -} - -.package-type { - color: var(--text-muted); - font-size: 0.75rem; - text-transform: uppercase; - letter-spacing: 0.1em; -} - -.package-highlight { - background-color: rgba(16, 185, 129, 0.1); - color: var(--accent-teal); - font-size: 0.7rem; - padding: 0.25rem 0.75rem; - border-radius: 9999px; - font-weight: 600; - text-transform: uppercase; -} - -.package-title { - font-family: var(--font-display); - font-size: 1.75rem; - color: var(--text-white); - margin-bottom: 1rem; - line-height: 1.2; -} - -.package-desc { - color: var(--text-lightgray); - font-size: 0.95rem; - line-height: 1.5; - margin-bottom: 2rem; -} - -.package-details-box { - background-color: rgba(255, 255, 255, 0.03); - border-radius: 8px; - padding: 1.5rem; - margin-bottom: 2rem; - flex-grow: 1; /* Schiebt den Button immer ganz nach unten */ -} - -.detail-item { - margin-bottom: 1.25rem; -} - -.detail-item:last-child { - margin-bottom: 0; -} - -.detail-title { - color: var(--text-white); - font-weight: 600; - font-size: 0.9rem; - margin-bottom: 0.5rem; -} - -.detail-text { - color: var(--text-muted); - font-size: 0.85rem; - line-height: 1.5; -} - -.package-btn { - display: block; - text-align: center; - background-color: transparent; - color: var(--text-white); - border: 1px solid rgba(255, 255, 255, 0.2); - padding: 1rem; - border-radius: 8px; - text-decoration: none; - font-weight: 500; - transition: all 0.2s ease; -} - -.package-btn:hover { - background-color: rgba(255, 255, 255, 0.05); - border-color: var(--accent-teal); - color: var(--accent-teal); +/* Zusätzliche Booking-Übersicht ausblenden, Dialog erhalten. */ +#booking-root > .booking > h2, +#booking-root .booking__package, +#booking-root .booking__payment, +#booking-root .booking__open { + display: none; } From 8fb2044a8168ec837b6557fd0286600555ff3c25 Mon Sep 17 00:00:00 2001 From: Marvin Gemlin Date: Mon, 28 Sep 2026 22:07:11 +0000 Subject: [PATCH 26/27] paket seiten angepasst und booking mit telefonvalidierung integriert --- angebotsuebersicht.html | 313 ++------------- package-lock.json | 7 + package.json | 1 + paket-details.html | 393 ++----------------- src/features/angebote/angebote-entry.js | 2 - src/features/angebote/angebotsuebersicht.js | 4 +- src/features/angebote/paket-details.js | 249 +++++------- src/features/booking/booking-confirmation.js | 2 +- src/features/booking/booking-page.js | 12 +- src/server/services/booking-service.js | 20 +- src/server/swagger.json | 4 +- src/shared/phone-validation.js | 14 + src/styles/packages.css | 283 ++++++++++++- src/tests/bookings.test.js | 41 ++ 14 files changed, 520 insertions(+), 825 deletions(-) create mode 100644 src/shared/phone-validation.js diff --git a/angebotsuebersicht.html b/angebotsuebersicht.html index 4cf1c87..06d0290 100644 --- a/angebotsuebersicht.html +++ b/angebotsuebersicht.html @@ -1,285 +1,34 @@ - - - - - - Ernährungs-Pakete – Tri-Hub - - - - - - - - - - - -
    -
    -
    -
    -
    -
    TRI-HUB · ERNÄHRUNG
    -

    Unsere Ernährungs-Pakete

    -
    -

    - Wähle das Paket, das zu deinem aktuellen Stand passt. Von den Grundlagen bis zur Wettkampf-Vorbereitung. -

    -
    - -
    -
    -
    -
    - - - - - + + + + Ernährungs-Pakete – Tri-Hub + + + + + +
    +
    +
    +

    TRI-HUB · ERNÄHRUNG

    +

    Unsere Ernährungs-Pakete

    +
    +

    + Wähle das Paket, das zu deinem aktuellen Stand passt. Von + den Grundlagen bis zur Wettkampf-Vorbereitung. +

    +
    +
    +
    + + + diff --git a/package-lock.json b/package-lock.json index 7e92738..eb86a80 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "csv-parse": "^7.0.2", "csv-stringify": "^6.8.3", + "libphonenumber-js": "^1.13.14", "nodemailer": "^10.0.10" }, "devDependencies": { @@ -982,6 +983,12 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.13.14", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.13.14.tgz", + "integrity": "sha512-llihgCcx0BFLksecLP+x1J+6JDE1GsXS1RN/LoPF6qcwpeQcnjj0lcvZxY8AzbEpYwyZWPZW/nDuqkqzm3amiw==", + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.19", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.19.tgz", diff --git a/package.json b/package.json index d903bdd..cb17e0c 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "dependencies": { "csv-parse": "^7.0.2", "csv-stringify": "^6.8.3", + "libphonenumber-js": "^1.13.14", "nodemailer": "^10.0.10" } } diff --git a/paket-details.html b/paket-details.html index 9ec9e46..e3fabf7 100644 --- a/paket-details.html +++ b/paket-details.html @@ -1,372 +1,27 @@ - - - - - - - Paket-Details – Tri-Hub Ernährungsberatung - - - - - - - - - - - -
    -
    - -
    -
    - - - - - + + + + Paket-Details – Tri-Hub Ernährungsberatung + + + + + +
    + ← Zurück zur Angebotsübersicht +
    +
    + + + diff --git a/src/features/angebote/angebote-entry.js b/src/features/angebote/angebote-entry.js index 43eb523..2cf63e5 100644 --- a/src/features/angebote/angebote-entry.js +++ b/src/features/angebote/angebote-entry.js @@ -1,5 +1,3 @@ -import "../../styles/styles.css"; -import "../../styles/packages.css"; import { renderAngebotsuebersicht } from "./angebotsuebersicht.js"; renderAngebotsuebersicht(); diff --git a/src/features/angebote/angebotsuebersicht.js b/src/features/angebote/angebotsuebersicht.js index c0ca328..57133ac 100644 --- a/src/features/angebote/angebotsuebersicht.js +++ b/src/features/angebote/angebotsuebersicht.js @@ -23,7 +23,7 @@ export function renderAngebotsuebersicht() { ${highlightHtml}
    -

    ${pkg.name}

    +

    ${pkg.name}

    ${pkg.summary}

    @@ -41,7 +41,7 @@ export function renderAngebotsuebersicht() {
    - ${pkg.buttonText} + ${pkg.buttonText} `; diff --git a/src/features/angebote/paket-details.js b/src/features/angebote/paket-details.js index 7f7cccd..5c70cc2 100644 --- a/src/features/angebote/paket-details.js +++ b/src/features/angebote/paket-details.js @@ -1,199 +1,158 @@ import { packagesDetails } from "../../shared/packagesdetails.js"; import { mountBookingPage } from "../booking/booking-page.js"; +import { formatMoney } from "../../shared/order-summary.js"; +import { paymentNotice } from "../../shared/booking-copy.js"; document.addEventListener("DOMContentLoaded", () => { const container = document.getElementById("package-detail-container"); if (!container) return; const params = new URLSearchParams(window.location.search); - const packageId = params.get("id"); + const pkg = packagesDetails.find((item) => item.id === params.get("id")); + const requestedVariant = params.get("variant"); + const invalidVariant = + requestedVariant && + !pkg?.variants?.some( + (variant) => variant.variantId === requestedVariant, + ); - const pkg = packagesDetails.find((item) => item.id === packageId); - - if (!pkg) { + if (!pkg || invalidVariant) { container.innerHTML = `
    -

    Paket nicht gefunden

    -

    Bitte wähle ein gültiges Angebot aus der Übersicht.

    - - Zur Übersicht - -
    - `; +

    Paket nicht gefunden

    +

    Bitte wähle ein gültiges Paket und eine passende Variante aus.

    + Zur Übersicht + `; return; } + document.title = `${pkg.type} – Tri-Hub Ernährungsberatung`; const variantsHtml = pkg.variants?.length - ? ` -
    -

    Wähle deine Variante

    - ${pkg.variants .map( (variant) => ` - - `, + `, ) .join("")} -
    - ` + ` : ""; container.innerHTML = ` -
    +
    - - ${pkg.type} · ${pkg.durationLabel} - - ${ - pkg.badge - ? `${pkg.badge}` - : "" - } + ${pkg.type} · + ${pkg.badge ? `${pkg.badge}` : ""}
    - -

    ${pkg.title}

    +

    ${pkg.title}

    ${pkg.subtitle}

    - -
    - - ${pkg.price.toFixed(2)} € - - - inkl. ${pkg.taxRate}% MwSt. (${pkg.billingInterval}) - -
    - - ${variantsHtml} - -
    -
    -

    - Für wen ist dieses Paket ideal? -

    +
    +
    +
    +
    +

    Für wen ist dieses Paket ideal?

      - ${pkg.targetGroup - .map((item) => `
    • ${item}
    • `) - .join("")} + ${pkg.targetGroup.map((item) => `
    • ${item}
    • `).join("")}
    -
    - -
    -

    - Das ist im Paket enthalten -

    + +
    +

    Das ist im Paket enthalten

      - ${pkg.includedFeatures - .map((feature) => `
    • ${feature}
    • `) - .join("")} + ${pkg.includedFeatures.map((feature) => `
    • ${feature}
    • `).join("")}
    -
    - -
    -

    - So läuft deine Begleitung ab -

    + +
    +

    So läuft deine Begleitung ab

    ${pkg.processSteps .map( (step) => ` -
    - ${step.step}: - - ${step.text} - -
    - `, +
    + ${step.step} +

    ${step.text}

    +
    `, ) .join("")} -
    +
    +
    +

    Dein ${pkg.type.toLowerCase().replace(/^./, (letter) => letter.toUpperCase())}-Paket

    +
    + + inkl. ${pkg.taxRate} % MwSt. · ${pkg.billingInterval} +

    +
    + ${variantsHtml} + +

    +
    +
    +
    `; - - -

    - Dieser Buchungsablauf ist eine Simulation. Es erfolgt keine - Abbuchung und keine verbindliche Beratungsbuchung. -

    - -

    -
    - - `; - - const variantSelect = document.getElementById("variant-select"); - const displayedPrice = document.getElementById("displayed-price"); - const bookingBtn = document.getElementById("proceed-to-booking-btn"); - const bookingRoot = document.getElementById("booking-root"); - const bookingStatus = document.getElementById("booking-status"); - + container.querySelector("#booking-notice").textContent = paymentNotice; + const variantSelect = container.querySelector("#variant-select"); + if (variantSelect && requestedVariant) + variantSelect.value = requestedVariant; + const bookingBtn = container.querySelector("#proceed-to-booking-btn"); + const bookingRoot = container.querySelector("#booking-root"); let cleanupBooking; + let activeDialog; function prepareBooking() { cleanupBooking?.(); - bookingStatus.textContent = ""; - + activeDialog = null; + bookingBtn.disabled = true; + bookingBtn.textContent = "Buchungsformular wird geladen …"; + bookingBtn.setAttribute("aria-expanded", "false"); + bookingBtn.removeAttribute("aria-controls"); + const variant = pkg.variants?.find( + (item) => item.variantId === variantSelect?.value, + ); + const duration = variant + ? `${variant.durationWeeks} Wochen Begleitung` + : pkg.durationLabel; + container.querySelector("#displayed-price").textContent = formatMoney( + variant?.price ?? pkg.price, + pkg.currency, + ); + container.querySelector("#package-duration").textContent = duration; + container.querySelector("#selected-duration").textContent = duration; cleanupBooking = mountBookingPage(bookingRoot, { packageId: pkg.id, variantId: variantSelect?.value, }); } - function updatePrice() { - if (!variantSelect) return; - - const selectedOption = variantSelect.selectedOptions[0]; - const price = Number(selectedOption.dataset.price); - - displayedPrice.textContent = `${price.toFixed(2)} €`; - } - - updatePrice(); - prepareBooking(); - - variantSelect?.addEventListener("change", () => { - updatePrice(); - prepareBooking(); + // Booking lädt die Paketvorschau asynchron, auch bei einem erneuten Versuch. + const observer = new MutationObserver(() => { + const dialog = bookingRoot.querySelector(".booking__dialog"); + if (!dialog || dialog === activeDialog) return; + activeDialog = dialog; + bookingBtn.disabled = false; + bookingBtn.textContent = "Jetzt buchen"; + bookingBtn.setAttribute("aria-controls", dialog.id); + dialog.addEventListener("close", () => { + bookingBtn.setAttribute("aria-expanded", "false"); + bookingBtn.focus(); + }); }); + observer.observe(bookingRoot, { childList: true, subtree: true }); + prepareBooking(); + variantSelect?.addEventListener("change", prepareBooking); bookingBtn.addEventListener("click", () => { - const dialog = bookingRoot.querySelector(".booking__dialog"); - - if (!dialog) { - bookingStatus.textContent = - "Das Buchungsformular ist noch nicht verfügbar. Bitte beachte den Ladehinweis im Buchungsbereich."; - return; - } - - if (dialog.open) return; - - bookingStatus.textContent = ""; - - dialog.addEventListener( - "close", - () => { - bookingBtn.focus(); - }, - { once: true }, + if (!activeDialog || activeDialog.open) return; + activeDialog.showModal(); + bookingBtn.setAttribute("aria-expanded", "true"); + const result = activeDialog.querySelector( + ".booking__result:not([hidden])", ); - - dialog.showModal(); - dialog.querySelector('[name="name"]').focus(); + (result || activeDialog.querySelector('[name="name"]')).focus(); }); }); diff --git a/src/features/booking/booking-confirmation.js b/src/features/booking/booking-confirmation.js index 5e67a8b..7311709 100644 --- a/src/features/booking/booking-confirmation.js +++ b/src/features/booking/booking-confirmation.js @@ -90,7 +90,7 @@ if ( if (body.emailStatus === "pending" && body.packageId) { const params = new URLSearchParams({ id: body.packageId }); if (order?.variantId) params.set("variant", order.variantId); - select("confirmation-retry").href = `/booking.html?${params}`; + select("confirmation-retry").href = `/paket-details.html?${params}`; select("confirmation-retry").hidden = false; } select("confirmation-empty").hidden = true; diff --git a/src/features/booking/booking-page.js b/src/features/booking/booking-page.js index 4d97d52..2366c50 100644 --- a/src/features/booking/booking-page.js +++ b/src/features/booking/booking-page.js @@ -1,3 +1,7 @@ +import { + isValidBookingPhone, + phoneError, +} from "../../shared/phone-validation.js"; import { previewBooking, submitBooking } from "./booking-api.js"; import "./booking.css"; import { orderSummaryRows } from "../../shared/order-summary.js"; @@ -284,12 +288,8 @@ export function mountBookingPage( fields.email = "Bitte eine gültige E-Mail-Adresse eingeben."; phone.value = phone.value.trim(); - if ( - phone.value.length > 50 || - /[\x00-\x1f\x7f]/.test(phone.value) - ) - fields.phone = - "Bitte höchstens 50 Zeichen ohne Zeilenumbrüche eingeben."; + if (!isValidBookingPhone(phone.value)) + fields.phone = phoneError; if (Object.keys(fields).length) { showError( "Bitte die markierten Angaben prüfen.", diff --git a/src/server/services/booking-service.js b/src/server/services/booking-service.js index f62c6d3..5ecf2e2 100644 --- a/src/server/services/booking-service.js +++ b/src/server/services/booking-service.js @@ -1,3 +1,7 @@ +import { + isValidBookingPhone, + phoneError, +} from "../../shared/phone-validation.js"; import { createHash } from "node:crypto"; import { packagesDetails } from "../../shared/packagesdetails.js"; @@ -112,16 +116,12 @@ function validateInput(input, key) { ) { fields.email = "Bitte eine gültige E-Mail-Adresse eingeben."; } - if (!customer && input.phone !== undefined) { - if ( - typeof input.phone !== "string" || - input.phone.length > 50 || - /[\x00-\x1f\x7f]/.test(input.phone) - ) { - fields.phone = - "Bitte höchstens 50 Zeichen ohne Zeilenumbrüche eingeben."; - } else if (input.phone.trim()) { - result.phone = input.phone.trim(); + const phone = customer ? customer.phone : input.phone; + if (phone !== undefined) { + if (!isValidBookingPhone(phone)) { + fields.phone = phoneError; + } else if (!customer && phone.trim()) { + result.phone = phone.trim(); } } if (Object.keys(fields).length) { diff --git a/src/server/swagger.json b/src/server/swagger.json index 409c41d..7ba05eb 100644 --- a/src/server/swagger.json +++ b/src/server/swagger.json @@ -553,7 +553,7 @@ "phone": { "type": "string", "maxLength": 50, - "description": "Optionale Telefonnummer bei Übergabe von name und email, ohne Steuerzeichen. Bei customer gilt customer.phone.", + "description": "Optionale Telefonnummer: deutsche Nummer mit Vorwahl oder internationale Nummer mit Ländervorwahl. Prüfung des Nummernformats, keine Bestätigung der Erreichbarkeit. Bei customer gilt customer.phone.", "example": "+49 170 1234567" }, "customer": { @@ -575,6 +575,8 @@ }, "phone": { "type": "string", + "maxLength": 50, + "description": "Optional. Deutsche Nummer mit Vorwahl oder internationale Nummer mit Ländervorwahl; keine Bestätigung der Erreichbarkeit.", "example": "+49 170 1234567" }, "ageGroup": { diff --git a/src/shared/phone-validation.js b/src/shared/phone-validation.js new file mode 100644 index 0000000..8958dcf --- /dev/null +++ b/src/shared/phone-validation.js @@ -0,0 +1,14 @@ +import { isValidPhoneNumber } from "libphonenumber-js/max"; + +export const phoneError = + "Bitte eine gültige Telefonnummer mit Vorwahl eingeben, z. B. 0170 1234567 oder +49 170 1234567."; + +export function isValidBookingPhone(value) { + if (typeof value !== "string" || value.length > 50) return false; + if (/[\x00-\x1f\x7f]/.test(value)) return false; + const phone = value.trim(); + if (!phone) return true; + if (!/^\+?[0-9 ()/.-]+$/.test(phone)) return false; + if (!/^(?:\+|0)/.test(phone)) return false; + return isValidPhoneNumber(phone, "DE"); +} diff --git a/src/styles/packages.css b/src/styles/packages.css index c40e4f5..dd8c95b 100644 --- a/src/styles/packages.css +++ b/src/styles/packages.css @@ -1,10 +1,279 @@ -#booking-root .booking__open { +.packages-page { + margin: 0; + min-height: 100vh; + color: #f8fafc; + background: + radial-gradient(circle at 12% 0%, #10b98117, transparent 30%), + radial-gradient(circle at 88% 26%, #10b9810e, transparent 27%), + linear-gradient(145deg, #0d0d1a, #1a1a2e); + font-family: Inter, system-ui, sans-serif; + line-height: 1.6; + color-scheme: dark; +} +.packages-section, +.packages-section * { + box-sizing: border-box; +} +.packages-section { + width: min(1180px, calc(100% - 3rem)); + margin: auto; + padding-block: 5rem; + overflow-wrap: anywhere; +} +.packages-header { + display: grid; + grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr); + gap: 3rem; + align-items: end; + margin-bottom: 3rem; +} +.packages-eyebrow, +.package-type { + color: #10b981; + font-size: 0.75rem; + font-weight: 600; + letter-spacing: 0.14em; + text-transform: uppercase; +} +.packages-header h1, +.detail-heading h1 { + margin: 0.75rem 0 1rem; + font-size: clamp(2rem, 4.5vw, 3.75rem); + font-weight: 600; + line-height: 1.15; + letter-spacing: -0.035em; +} +.packages-intro, +.package-desc, +.detail-text, +.detail-list, +.price-note, +.booking-notice { + color: #94a3b8; +} +.packages-intro { + margin: 0; + max-width: 40rem; +} +.packages-container { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1.5rem; +} +.package-card, +.package-checkout { + min-width: 0; + padding: 1.75rem; + border: 1px solid #f8fafc1f; + border-radius: 1.4rem; + background: #ffffff0b; +} +.package-card { + display: flex; + flex-direction: column; +} +.package-type-container { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.75rem; + margin-bottom: 1rem; +} +.package-highlight { + border-radius: 999px; + padding: 0.25rem 0.65rem; + background: #10b98114; + color: #34d399; + border: 1px solid #10b98133; + font-size: 0.65rem; + font-weight: 600; + letter-spacing: 0.05em; +} +.package-title { + margin: 0 0 1rem; + color: #fff; + font-size: 1.55rem; + font-weight: 600; + line-height: 1.25; + letter-spacing: -0.025em; +} +.package-desc { + margin: 0 0 1.5rem; +} +.package-details-box { + padding-block: 1.5rem; + border-top: 1px solid #f8fafc1f; + flex: 1; +} +.detail-item + .detail-item { + margin-top: 1.25rem; +} +.detail-title { + margin: 0 0 0.5rem; + color: #f8fafc; + font-size: 1rem; + font-weight: 600; +} +.detail-text { + font-size: 0.9rem; +} +.package-btn { + display: flex; + align-items: center; + justify-content: center; + gap: 0.75rem; + width: 100%; + min-height: 3rem; + padding: 0.8rem 1.1rem; + border: 1px solid #94a3b840; + border-radius: 999px; + background: #ffffff06; + color: #f8fafc; + font: inherit; + font-size: 0.9rem; + font-weight: 600; + text-align: center; + text-decoration: none; + cursor: pointer; +} +.package-btn:hover { + border-color: #10b981; + background: #10b98114; +} +.package-btn.primary-cta { + background: #10b981; + border-color: #10b981; + color: #06130f; +} +.package-btn.primary-cta:hover { + background: #34d399; +} +.package-btn:disabled { + opacity: 0.65; + cursor: wait; +} +.packages-section :focus-visible { + outline: 3px solid #10b981; + outline-offset: 4px; +} +.packages-back { + display: inline-block; + margin-bottom: 2rem; + padding-block: 0.5rem; + color: #34d399; + text-decoration: none; +} +.detail-heading { + max-width: 52rem; + margin-bottom: 3rem; +} +.detail-grid { + display: grid; + grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr); + gap: 2.5rem; + align-items: start; +} +.detail-content > section + section { + margin-top: 2.5rem; +} +.detail-content h2, +.package-checkout h2 { + margin: 0 0 1rem; + font-size: 1.25rem; +} +.detail-list { + padding-left: 1.25rem; +} +.detail-list li { + margin-block: 0.65rem; +} +.detail-list li::marker { + color: #10b981; +} +.process-step { + margin-top: 1rem; + padding: 1.25rem; + border: 1px solid #f8fafc1f; + border-radius: 1rem; + background: #ffffff06; +} +.process-step strong { + display: block; + margin-bottom: 0.4rem; +} +.process-step p { + margin: 0; +} +.price-box { + margin-bottom: 1.5rem; +} +.price-amount { + display: block; + font-size: clamp(2rem, 4vw, 3rem); + font-weight: 600; + letter-spacing: -0.04em; + color: #34d399; +} +.price-note, +.booking-notice { + font-size: 0.85rem; +} +.detail-box { + margin-bottom: 1.5rem; +} +.detail-box label { + display: block; + margin-bottom: 0.5rem; +} +.variant-select { + width: 100%; + max-width: 100%; + min-width: 0; + min-height: 3rem; + padding: 0.65rem; + border: 1px solid #94a3b866; + border-radius: 0.75rem; + color: #f8fafc; + background: #0d0d1a; + font: inherit; +} +/* Die Detailseite zeigt ihre eigene Übersicht; Booking liefert den Dialog. */ +.packages-section #booking-root > .booking > h2, +.packages-section #booking-root .booking__package, +.packages-section #booking-root .booking__payment, +.packages-section #booking-root .booking__open { display: none; } -/* Zusätzliche Booking-Übersicht ausblenden, Dialog erhalten. */ -#booking-root > .booking > h2, -#booking-root .booking__package, -#booking-root .booking__payment, -#booking-root .booking__open { - display: none; +.packages-section .booking__dialog { + background: #0d0d1a; + color: #f8fafc; + border-color: #94a3b840; +} +.packages-section .booking__dialog .booking__button:not(.booking__cancel) { + background: #10b981; + border-color: #10b981; + color: #06130f; +} +.packages-section .booking__dialog :focus-visible { + outline-color: #10b981; +} +@media (max-width: 60rem) { + .packages-container { + grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr)); + } + .packages-header, + .detail-grid { + grid-template-columns: minmax(0, 1fr); + gap: 1.5rem; + } +} +@media (max-width: 30rem) { + .packages-section { + width: calc(100% - 2rem); + padding-block: 2.5rem; + } + .package-card, + .package-checkout { + padding: 1.25rem; + } } diff --git a/src/tests/bookings.test.js b/src/tests/bookings.test.js index e3b510d..2be862c 100644 --- a/src/tests/bookings.test.js +++ b/src/tests/bookings.test.js @@ -619,3 +619,44 @@ test("Swagger-Kundendaten und Zustimmung werden validiert und dauerhaft gespeich 409, ); }); + +test("Telefonnummern werden in beiden Anfrageformaten geprüft", async (t) => { + const { post, sent } = await setup(t); + for (const phone of [ + "123", + "00000000000", + "abc", + "+999 123456789", + "0170 test 1234567", + ]) { + for (const data of [ + { ...input, phone }, + { + packageId: input.packageId, + acceptedTerms: true, + customer: { + firstName: "Test", + lastName: "Person", + email: input.email, + phone, + }, + }, + ]) { + const result = await post(data); + assert.equal(result.status, 400, phone); + assert.ok(result.body.error.fields.phone); + } + } + assert.equal(sent.length, 0); + for (const phone of [ + "", + "0170 1234567", + "+49 170 1234567", + "0049 170 1234567", + "030 123456", + "+44 20 7946 0958", + ]) { + const result = await post({ ...input, phone }); + assert.equal(result.status, 201, phone); + } +}); From ae994c29f4324a01c57e8afc6057025fbd35512c Mon Sep 17 00:00:00 2001 From: Marvin Gemlin Date: Mon, 28 Sep 2026 22:34:10 +0000 Subject: [PATCH 27/27] =?UTF-8?q?Ern=C3=A4hrungsbutton=20in=20Nav=20Bar=20?= =?UTF-8?q?bearbeitet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/navigationsbar/navigation.html | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/components/navigationsbar/navigation.html b/src/components/navigationsbar/navigation.html index 00be4db..71a3ea8 100644 --- a/src/components/navigationsbar/navigation.html +++ b/src/components/navigationsbar/navigation.html @@ -32,31 +32,19 @@ >
  • - Ernährung + Ernährung
  • - Beraterprofil + Beraterprofil
  • - FAQ + FAQ
  • Rezensionen
  • - Kontakt + Kontakt