diff --git a/.env.sample b/.env.sample index e94748a2e..65926e57f 100644 --- a/.env.sample +++ b/.env.sample @@ -15,7 +15,7 @@ POSTGRES_HOST=localhost POSTGRES_PORT=5432 POSTGRES_DB=db POSTGRES_USER=pguser -POSTGRES_PASSWORD= +POSTGRES_PASSWORD=pgpassword ANTHROPIC_API_KEY=none ANTHROPIC_MAX_TOKENS=0 diff --git a/.forgejo/workflows/buildAndTest.yml b/.forgejo/workflows/buildAndTest.yml index 7cf15ce77..67b743fb5 100644 --- a/.forgejo/workflows/buildAndTest.yml +++ b/.forgejo/workflows/buildAndTest.yml @@ -33,7 +33,7 @@ jobs: run: | set -e cd /src - git clone "${FORGEJO_SERVER_URL}/${FORGEJO_REPOSITORY}.git" _src + git clone https://forgejo.pschneider.fr/notazof/yavsc.git _src cd _src if [ -n "${GITHUB_REF:-}" ]; then git fetch origin "$GITHUB_REF" @@ -53,6 +53,5 @@ jobs: run: | echo "🚀 Lancement des tests..." cd /src/_src && dotnet test \ - --report-xunit \ - --report-xunit-html \ - --report-xunit-html-filename "test-results.html" + --verbosity normal \ + --filter="Category!=Platform-Android" diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml index 7d0670a17..7b17bba42 100644 --- a/.forgejo/workflows/release.yml +++ b/.forgejo/workflows/release.yml @@ -69,7 +69,7 @@ jobs: cd /src if [[ ! -d _src/.git ]]; then - git clone --depth=1 "${FORGEJO_SERVER_URL}/${FORGEJO_REPOSITORY}.git" _src + git clone --depth=1 https://forgejo.pschneider.fr/notazof/yavsc.git _src fi cd _src diff --git a/.vscode/tasks.json b/.vscode/tasks.json index cbd3a744d..71384cd8c 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,13 +1,27 @@ { "version": "2.0.0", "isRoot": true, - + "problemMatcher": [ + { + "owner": "dotnet", + "fileLocation": ["relative", "${workspaceFolder}"], + "source": "dotnet", + "pattern": { + "regexp": "^\\s*(.*)\\((\\d+),(\\d+)\\):\\s+(error|warning) (.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "code": 5, + "message": 6 + } + } + ], "tasks": [ { "label": "run-debug-android", "command": "dotnet", "type": "shell", - "problemMatcher": ["$msCompile"], "options": { "cwd": "${workspaceFolder}/src/PostIt/PostIt.Android", "env": { @@ -29,7 +43,6 @@ "label": "build", "command": "dotnet", "type": "process", - "problemMatcher": ["$msCompile"], "args": ["build", "/property:GenerateFullPaths=true"], "group": "build", "isBuildCommand": true, @@ -96,7 +109,6 @@ "isTestCommand": false, "isBackground": true, "command": "dotnet", - "problemMatcher": ["$msCompile"], "args": ["build", "/property:GenerateFullPaths=true"], "options": { "cwd": "src/PostIt/PostIt.Desktop" diff --git a/CHANGELOG.md b/CHANGELOG.md index 85139db1b..3f8253c70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,81 +1,5 @@ # Changelog -## [1.0.8-rc16] - unstable - -Flux de signature et de génération de devis : un devis peut désormais être -signé par les deux parties (fournisseur et client) puis téléchargé en PDF -ou TeX depuis PostIt, avec les signatures dessinées en vecteur. - -### Added - -* [PostIt] Pad de signature sur la page de validation d'un devis : on valide - ou on refuse en joignant une signature (côté fournisseur ou client), capturée - au format filaire PostIt (strokes) et envoyée sur l'acceptation. -* [Yavsc.Api] Finalisation de `POST /api/v1/front/query/accept` et `.../reject` : - autorisation selon le rôle (fournisseur/client/admin), signature optionnelle - persistée sur l'estimate avec horodatage `ProviderValidationDate` / - `ClientValidationDate` selon le côté signataire. -* [Yavsc.Api] `QueryStatus.Accepted` éclaté en `ProAccepted` / `ClientAccepted` - pour distinguer l'acceptation de chaque partie. -* [Yavsc.Api] Endpoints de devis `GET /api/v1/front/query/{id}/estimate.tex` et - `estimate.pdf` (clé par id de query). Le flux de génération TeX/PDF est - rapatrié de Yavsc.Org vers Yavsc.Api (`TeXHelpers`, `PdfGenerationViewModel`, - `BillViewComponent`, modèles Bill/Estimate, `_ViewImports` ; `Program.cs` - câble `AddControllersWithViews` + `SiteSettings`). -* [Yavsc.Api] Signatures du devis dessinées en TikZ vectoriel - (`TeXHelpers.SignatureToTikz`) en bas du document, fournisseur et client - côte à côte dans deux minipages ; plus de dépendance à un PNG de signature. -* [Yavsc.Api.Client] `DownloadAsync` sur le client + `GetEstimateTexAsync` / - `GetEstimatePdfAsync` ; boutons « Télécharger PDF » / « Télécharger TeX » - dans PostIt sur la page de validation du devis et le détail de commande, - avec un helper `StorageProvider` d'enregistrement de fichier. -* [PostIt] La liste des devis se rafraîchit au retour de navigation (pop-back), - de sorte qu'un devis validé quitte immédiatement la liste du signataire. -* [PostIt] Renommage des titres de liste : « Devis en attente de signature - fournisseur » / « Devis en attente de signature client ». -* [Yavsc.Abstract] `SignatureType` déplacé dans `Yavsc.Abstract.Billing`. -* [CLI] Option `-c configuration-file.json` au lancement. -* [Tests] `EstimatePdfGenerationTests` : tests unitaires du pipeline - signature → TikZ → PDF (devis signé des deux côtés), assertant que le PDF - existe au chemin de sortie attendu et que seul le PDF persiste (aux/log - nettoyés) ; skip propre si `lualatex` est absent. -* [README] Dépendances système au runtime pour la génération PDF : - `texlive-binaries` (lualatex), `texlive-luatex` (luaotfload), - `texlive-pictures` (tikz), `texlive-fonts-extra` (bera). - -### Changed - -* [Yavsc.Api] `GetOngoingEstimatesAsProvider` filtre désormais sur - `ProviderValidationDate` : un devis signé par le fournisseur quitte sa liste - « en attente ». -* [Yavsc.Api] La génération PDF compile via le `lualatex` système, le TeX fourni - sur l'entrée standard (`/dev/stdin`), le PDF écrit directement dans le - répertoire des factures (`Site.Bills`), les artefacts `.aux`/`.log` nettoyés - afin que seul le `.pdf` persiste. - -### Fixed - -* [Yavsc.Api] `Emergency stop` à la génération PDF : `lualatex` sans argument - fichier lit stdin en mode terminal et s'arrête après la première ligne d'un - document multi-lignes. Corrigé en passant `/dev/stdin` comme nom de fichier - d'entrée — stdin est alors traité comme un fichier régulier et le document - multi-lignes compile. C'était la cause racine de l'échec en production, pas - `bera` ni `luaotfload` (problèmes réels mais séparés, comblés côté paquets). -* [Yavsc.Api] `I can't write on file 'estimate-1.log'` : le chemin relatif - `Site.Bills` (`"bills"`) était imbriqué quand il servait à la fois de - `-output-directory` et de répertoire de travail ; résolu en chemin absolu. -* [Yavsc.Api] NRE dans `RenderViewToString` (RouteData / `ViewData.Model` - nuls) : reconstruit sur le `ControllerContext` du contrôleur ; `IViewEngine` - résolu via `IRazorViewEngine` (.NET 10 n'enregistre pas de `IViewEngine` - unique). -* [Yavsc.Api] `Layout = "null"` (chaîne) dans les modèles TeX cherchait - `null.cshtml` ; corrigé en `Layout = null` (null C#). -* [Yavsc.Api] `texi2pdf` (outil Texinfo, inadapté au LaTeX) remplacé par - `lualatex`. -* [Yavsc.Api] Les diagnostics lualatex étaient perdus (écrits dans le `.log` - qu'on supprimait) ; stdout/stderr sont désormais capturés dans le message - d'erreur, et le `.log` est conservé en cas d'échec. - ## [1.0.8-rc15] - unstable ### Added @@ -445,8 +369,6 @@ the ACL now comes along with the article, the same user-visible switch without a schema change. [Unreleased]: https://forgejo.pschneider.fr/notazof/yavsc/compare/HEAD -[1.0.8-rc16]: https://forgejo.pschneider.fr/notazof/yavsc/compare/1.0.8-rc15...1.0.8-rc16 -[1.0.8-rc15]: https://forgejo.pschneider.fr/notazof/yavsc/compare/1.0.8-rc14...1.0.8-rc15 [1.0.8-rc1]: https://forgejo.pschneider.fr/notazof/yavsc/compare/1.0.7...1.0.8-rc1 [1.0.7]: https://forgejo.pschneider.fr/notazof/yavsc/compare/1.0.6...1.0.7 [1.0.6]: https://forgejo.pschneider.fr/notazof/yavsc/compare/1.0.5...1.0.6 diff --git a/Directory.Packages.props b/Directory.Packages.props index b0b055b98..f1be93f91 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -4,71 +4,53 @@ 8.1.0-pazofrc007 - - - + + - + + + + + + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - - - - - - - + + + + - + + + + + - - - - - - - - - - - - - - - - - - - - - diff --git a/Makefile b/Makefile index e883088c2..d99fe5e5e 100644 --- a/Makefile +++ b/Makefile @@ -13,10 +13,6 @@ all: clean: dotnet clean -c $(CONFIG) -brutal_clean: - @find -name "obj" -exec rm -rf {} \; - @find -name "bin" -exec rm -rf {} \; - src/Yavsc/bin/output/wwwroot: dotnet --project src/Yavsc.Org/Yavsc.Org.csproj publish diff --git a/README.md b/README.md index fefbdd48d..549348f4a 100644 --- a/README.md +++ b/README.md @@ -61,40 +61,9 @@ make test ### les services et l'API -#### Dépendances système au runtime - -La génération des devis et factures en PDF (`/api/v1/front/query/{id}/estimate.pdf`, -`BillingController` → `BillViewComponent`) compile un source LaTeX via la commande -système `lualatex` : le TeX est fourni sur l'entrée standard et le PDF est écrit -directement dans le répertoire des factures (`Site.Bills`). `lualatex` doit donc -être installé et opérationnel sur l'hôte qui exécute l'API — typiquement le service -systemd `yavscApi`. - -Sous Debian/Ubuntu : - -```bash -sudo apt install texlive-binaries texlive-luatex texlive-pictures texlive-fonts-extra -``` - -- `texlive-binaries` fournit l'exécutable `lualatex` (collection basic). -- `texlive-luatex` fournit les paquets de support chargés au démarrage de lualatex, - notamment `luaotfload`. Sans lui, lualatex démarre mais s'arrête sur - `module 'luaotfload-main' not found … Emergency stop` et ne produit aucun PDF. - (Sur les Debian récents, `luaotfload` a migré de `texlive-latex-extra` vers - `texlive-luatex` — avoir `texlive-latex-extra` seul ne suffit plus.) -- `texlive-pictures` fournit `tikz`/`pgf`, utilisé pour dessiner les signatures - du client et du fournisseur en bas du devis (vecteur, pas de PNG). -- `texlive-fonts-extra` fournit la fonte `bera` chargée par le modèle de devis. - Sans elle, lualatex s'arrête sur `! LaTeX Error: File 'bera.sty' not found` - (`Emergency stop`, aucun PDF). - -Sur un hôte sans `lualatex`, les endpoints PDF renvoient une erreur 500 « PDF -generation failed » ; l'endpoint TeX (`estimate.tex`) reste utilisable et permet -de compiler le document hors-ligne. - ### La Prod -`cd srv/Yavsc/contrib` : `make reinstall CONFIGURATION=Release APP_INSTALL_ENV=production`. +`cd srv/Yavsc` : `make pushInProd CONFIGURATION=Release`. puis, pour une première installation `make install_service`. diff --git a/contrib/Makefile b/contrib/Makefile index 4ba6059df..4e4ef4df6 100644 --- a/contrib/Makefile +++ b/contrib/Makefile @@ -1,32 +1,23 @@ -DESTDIR= -DOTNET_FRAMEWORK=net10.0 APP_PROJECT_NAMES=Org Blogs Api SLNDIR=.. -APP_INSTALL_ENV=preprod -APP_INSTALL_PREFIX=preprod- -include .$(APP_INSTALL_ENV).env +include $(SLNDIR)/.env +include .env -reinstall: copy-binaries - @for project in $(APP_PROJECT_NAMES); do \ - echo "🚀 Starting $(APP_INSTALL_PREFIX)yavsc$${project}" ; \ - sudo systemctl start $(APP_INSTALL_PREFIX)yavsc$${project} ; done - @echo "💯 backend started" +generated/: + @mkdir -p $@ -reinstall-prod: - make reinstall APP_INSTALL_ENV= APP_INSTALL_PREFIX= APP_INSTALL_ENV=production +generated/yavscOrg.service: +generated/yavscBlogs.service: +generated/yavscApi.service: -generated/$(APP_INSTALL_PREFIX)yavscOrg.service: -generated/$(APP_INSTALL_PREFIX)yavscBlogs.service: -generated/$(APP_INSTALL_PREFIX)yavscApi.service: - -generated/$(APP_INSTALL_PREFIX)yavsc%.service: generated/ template.service $(SLNDIR)/.env +generated/yavsc%.service: generated/ template.service $(SLNDIR)/.env @cat template.service | APP_NAME="$*" \ DESTDIR="$(DESTDIR)" \ HTTP_HOST="$(HTTP_HOST)" \ HTTP_PORT="$*_$(HTTP_PORT)" \ - APPDIR="$(APPDIR)" \ - APP_SYSLOG_ID="$(APP_INSTALL_PREFIX)yavsc$*" \ + BASEAPPDIR="$(BASEAPPDIR)" \ + APP_SYSLOG_ID="yavsc$*" \ POSTGRES_HOST="$(POSTGRES_HOST)" \ POSTGRES_PORT="$(POSTGRES_PORT)" \ POSTGRES_DB="$(POSTGRES_DB)" \ @@ -43,13 +34,11 @@ generated/$(APP_INSTALL_PREFIX)yavsc%.service: generated/ template.service $(SLN envsubst > $@ @echo Created service file: $@ -generate-service-units: generated/$(APP_INSTALL_PREFIX)yavscOrg.service generated/$(APP_INSTALL_PREFIX)yavscBlogs.service generated/$(APP_INSTALL_PREFIX)yavscApi.service -install-service-%: /etc/systemd/system/$(APP_INSTALL_PREFIX)yavsc%.service - sudo systemctl daemon-reload - sudo systemctl enable $(APP_INSTALL_PREFIX)yavsc$* - -install-services: install-service-Org install-service-Blogs install-service-Api +copy-services: copy-service-Org copy-service-Blogs copy-service-Api +copy-service-Org: /etc/systemd/system/yavscOrg.service +copy-service-Blogs: /etc/systemd/system/yavscBlogs.service +copy-service-Api: /etc/systemd/system/yavscApi.service copy-binaries: build_publish_Org build_publish_Blogs build_publish_Api stop-services @for project in $(APP_PROJECT_NAMES); \ @@ -63,36 +52,39 @@ copy-binaries: build_publish_Org build_publish_Blogs build_publish_Api stop-serv rm -f $${BINDIR}/appsettings-$${LCAPI}.json ; \ sync ; \ ls $${BINDIR}/appsettings* 2>&1 ; \ - sudo cp -a $${BINDIR}/* $(DESTDIR)$(APPDIR) ; \ + sudo cp -a $${BINDIR}/* $(BASEAPPDIR) ; \ done - @sudo chown -R $(USER_AND_GROUP) $(DESTDIR)$(APPDIR) - @sync + @sudo chown -R $(USER_AND_GROUP) $(BASEAPPDIR) -$(DESTDIR)/etc/systemd/system/$(APP_INSTALL_PREFIX)yavsc%.service: generated/$(APP_INSTALL_PREFIX)yavsc%.service +/etc/systemd/system/yavsc%.service: generated/yavsc%.service sudo cp $^ $@ sudo chown root:root $@ build_publish_%: clean_publish_dir_% @ASPNETCORE_ENV=$(CONFIGURATION) dotnet publish $(SLNDIR)/src/Yavsc.$*/Yavsc.$*.csproj +build_publish: build_publish_Org build_publish_Blogs build_publish_Api + clean_publish_dir_%: @rm -rf $(SLNDIR)/src/Yavsc.$*/bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish -install: copy-binaries copy-services +install: build_publish copy-binaries copy-services @sudo systemctl daemon-reload @for project in $(APP_PROJECT_NAMES); \ do \ - echo "🖒 enable $(APP_INSTALL_PREFIX)yavsc$${project}" ; \ - sudo systemctl enable $(APP_INSTALL_PREFIX)yavsc$${project} ; \ - echo "🚀 start $(APP_INSTALL_PREFIX)yavsc$${project}" ; \ - sudo systemctl start $(APP_INSTALL_PREFIX)yavsc$${project} ; \ + sudo systemctl enable yavsc$${project} ; \ + sudo systemctl start yavsc$${project} ; \ done +reinstall: copy-binaries + @sync + @for project in $(APP_PROJECT_NAMES); do \ + sudo systemctl start yavsc$${project} ; done + stop-services: @for project in $(APP_PROJECT_NAMES); \ do \ - echo "🔥 Stopping $(APP_INSTALL_PREFIX)yavsc$${project}" ; \ - sudo systemctl stop $(APP_INSTALL_PREFIX)yavsc$${project} ; \ + sudo systemctl stop yavsc$${project} ; \ done $(SLNDIR)/src/Yavsc.Org/bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish: build_publish @@ -101,7 +93,7 @@ $(SLNDIR)/src/Yavsc.Api/bin/$(CONFIGURATION)/$(DOTNET_FRAMEWORK)/publish: build_ showConfig: @echo CONFIGURATION: $(CONFIGURATION) - @echo APPDIR: $(APPDIR) + @echo BASEAPPDIR: $(BASEAPPDIR) showApiLogs: @sudo journalctl -u yavscApi.service -S "2 min ago" | tee yavscApi.log @@ -115,5 +107,3 @@ showBlogsLogs: clean: @rm -rf generated -generated/: - @mkdir -p $@ diff --git a/contrib/template.service b/contrib/template.service index 3959c7151..b075c4da1 100644 --- a/contrib/template.service +++ b/contrib/template.service @@ -10,10 +10,10 @@ RestartSec=5s Type=simple User=yavsc Group=yavsc -WorkingDirectory=$APPDIR -ExecStart=$APPDIR/Yavsc.$APP_NAME +WorkingDirectory=$DESTDIR +ExecStart=$DESTDIR/Yavsc.$APP_NAME Restart=always -Environment="HOME=$APPDIR" +Environment="HOME=$DESTDIR" Environment="ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY" Environment="ANTHROPIC_MAX_TOKENS=$ANTHROPIC_MAX_TOKENS" Environment="ASPNETCORE_Environment=$ENV" @@ -29,6 +29,8 @@ Environment="ASPNETCORE_Smtp__Password=\"$ASPNETCORE_Smtp__Password\"" CapabilityBoundingSet=CAP_NET_BIND_SERVICE AmbientCapabilities=CAP_NET_BIND_SERVICE +StandardOutput=syslog +StandardError=syslog SyslogIdentifier=$APP_SYSLOG_ID [Install] diff --git a/contrib/yavsc-org b/contrib/yavsc-org new file mode 100644 index 000000000..dc65aa9fe --- /dev/null +++ b/contrib/yavsc-org @@ -0,0 +1,54 @@ + + +server { + if ($host = yavsc.pschneider.fr) { + return 301 https://$host$request_uri; + } # managed by Certbot + + + + listen 80; + server_name yavsc.pschneider.fr; + location / { + root /srv/www/yavsc/wwwroot; + dav_methods PUT; + + proxy_pass http://127.0.0.1:3003; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection keep-alive; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + +} + +server { + listen 443 ssl; + server_name yavsc.pschneider.fr; + ssl_certificate /etc/letsencrypt/live/yavsc.pschneider.fr-0001/fullchain.pem; # managed by Certbot + ssl_certificate_key /etc/letsencrypt/live/yavsc.pschneider.fr-0001/privkey.pem; # managed by Certbot + # Configure the SignalR Endpoint + location / { + # App server url + proxy_pass http://localhost:3003; + + proxy_cache off; + # WebSockets were implemented after http/1.0 + proxy_http_version 1.1; + + # Configuration for ServerSentEvents + proxy_buffering off; + + # Configuration for LongPolling or if your KeepAliveInterval is longer than 60 seconds + proxy_read_timeout 100s; + + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} + diff --git a/global.json b/global.json deleted file mode 100644 index 9009caf0b..000000000 --- a/global.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "test": { - "runner": "Microsoft.Testing.Platform" - } -} diff --git a/src/PostIt/Directory.Packages.props b/src/PostIt/Directory.Packages.props index 42d09cd55..e8804a8bf 100644 --- a/src/PostIt/Directory.Packages.props +++ b/src/PostIt/Directory.Packages.props @@ -4,31 +4,28 @@ true - 12.1.2 + 12.1.1 - - + - + - - - - - + + + - + \ No newline at end of file diff --git a/src/PostIt/PostIt.Android/PostIt.Android.csproj b/src/PostIt/PostIt.Android/PostIt.Android.csproj index 46702b3a8..a6d04cb33 100644 --- a/src/PostIt/PostIt.Android/PostIt.Android.csproj +++ b/src/PostIt/PostIt.Android/PostIt.Android.csproj @@ -11,8 +11,8 @@ false 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1 diff --git a/src/PostIt/PostIt.Browser/PostIt.Browser.csproj b/src/PostIt/PostIt.Browser/PostIt.Browser.csproj index bf967849b..ff2f3f2b8 100644 --- a/src/PostIt/PostIt.Browser/PostIt.Browser.csproj +++ b/src/PostIt/PostIt.Browser/PostIt.Browser.csproj @@ -6,8 +6,8 @@ enable 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1 diff --git a/src/PostIt/PostIt.Desktop/PostIt.Desktop.csproj b/src/PostIt/PostIt.Desktop/PostIt.Desktop.csproj index 12e9a8143..36ed1251e 100644 --- a/src/PostIt/PostIt.Desktop/PostIt.Desktop.csproj +++ b/src/PostIt/PostIt.Desktop/PostIt.Desktop.csproj @@ -7,8 +7,8 @@ enable 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1 app.manifest diff --git a/src/PostIt/PostIt.Desktop/Program.cs b/src/PostIt/PostIt.Desktop/Program.cs index a2b8cd66f..0de3bd69e 100644 --- a/src/PostIt/PostIt.Desktop/Program.cs +++ b/src/PostIt/PostIt.Desktop/Program.cs @@ -12,8 +12,6 @@ sealed class Program [STAThread] public static void Main(string[] args) { - CLIOptionHandler.HandleArgs(args); - // Short-circuit 2nd-instance launches (OS handing us the // postit://callback URL) BEFORE Avalonia spins up a window. // If we let Avalonia initialise, the new MainWindow flashes diff --git a/src/PostIt/PostIt.Tests/EstimateListPageViewModelTests.cs b/src/PostIt/PostIt.Tests/EstimateListPageViewModelTests.cs index eefd59f20..6158b4132 100644 --- a/src/PostIt/PostIt.Tests/EstimateListPageViewModelTests.cs +++ b/src/PostIt/PostIt.Tests/EstimateListPageViewModelTests.cs @@ -11,13 +11,12 @@ public class EstimateListPageViewModelTests { var api = new StubEstimateListApi(); var client = new EstimateApiClient(api, "https://business.example/api/v1/"); - var front = new FrontOfficeApiClient(api, "https://business.example/api/v1/"); - var vm = new EstimateListPageViewModel(client, EstimateListPerspective.Client, front); + var vm = new EstimateListPageViewModel(client, EstimateListPerspective.Client); await vm.InitializeAsync(); Assert.Contains("https://business.example/api/v1/estimate/asclient", api.Paths); - Assert.Equal("Devis en attente de signature client", vm.Title); + Assert.Equal("Mes devis à valider", vm.Title); Assert.Equal(2, vm.Estimates.Count); // Most recent first Assert.Equal(2, vm.Estimates[0].Id); @@ -29,13 +28,12 @@ public class EstimateListPageViewModelTests { var api = new StubEstimateListApi(); var client = new EstimateApiClient(api, "https://business.example/api/v1/"); - var front = new FrontOfficeApiClient(api, "https://business.example/api/v1/"); - var vm = new EstimateListPageViewModel(client, EstimateListPerspective.Provider, front); + var vm = new EstimateListPageViewModel(client, EstimateListPerspective.Provider); await vm.InitializeAsync(); Assert.Contains("https://business.example/api/v1/estimate/asprovider", api.Paths); - Assert.Equal("Devis en attente de signature fournisseur", vm.Title); + Assert.Equal("Mes devis en attente", vm.Title); Assert.Equal(2, vm.Estimates.Count); } @@ -44,8 +42,7 @@ public class EstimateListPageViewModelTests { var api = new StubEstimateListApi(); var client = new EstimateApiClient(api, "https://business.example/api/v1/"); - var front = new FrontOfficeApiClient(api, "https://business.example/api/v1/"); - var vm = new EstimateListPageViewModel(client, EstimateListPerspective.Provider, front); + var vm = new EstimateListPageViewModel(client, EstimateListPerspective.Provider); await vm.InitializeAsync(); @@ -66,8 +63,7 @@ public class EstimateListPageViewModelTests { var api = new StubEstimateListApi { Empty = true }; var client = new EstimateApiClient(api, "https://business.example/api/v1/"); - var front = new FrontOfficeApiClient(api, "https://business.example/api/v1/"); - var vm = new EstimateListPageViewModel(client, EstimateListPerspective.Client, front); + var vm = new EstimateListPageViewModel(client, EstimateListPerspective.Client); await vm.InitializeAsync(); diff --git a/src/PostIt/PostIt.Tests/EstimateValidationPageViewModelTests.cs b/src/PostIt/PostIt.Tests/EstimateValidationPageViewModelTests.cs deleted file mode 100644 index 02a6c64bd..000000000 --- a/src/PostIt/PostIt.Tests/EstimateValidationPageViewModelTests.cs +++ /dev/null @@ -1,255 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using PostIt.Controls; -using PostIt.ViewModels; -using Yavsc.Api.Client; -using Yavsc.Models.Billing; - -namespace PostIt.Tests; - -/// -/// Verifies wires the two -/// signature pads (provider + client) into the right -/// call. The view (XAML, rendering) -/// is not tested here; the VM is driven through the same -/// test hooks the signature tests use, -/// and the API client through a recording fake -/// . The test host's -/// TestApp makes GoBackAsync a no-op, so the success paths -/// run to completion without navigation side effects. -/// -public class EstimateValidationPageViewModelTests -{ - private static EstimateDto SampleEstimate(long? commandId = 42) => new() - { - Id = 7, - CommandId = commandId, - CommandType = "Rdv", - Title = "Devis plomberie", - Description = "Réparation", - ClientId = "bob", - OwnerId = "alice", - }; - - private static void DrawOneStroke(SignaturePadControl pad) - { - pad.AppendPointForTest(100, 200); - pad.AppendPointForTest(300, 400); - pad.SealStrokeForTest(); - } - - [Fact] - public async Task Validate_calls_accept_with_strokes_and_command_id() - { - var api = new RecordingApi(); - var front = new FrontOfficeApiClient(api, "https://business.example/api/v1/"); - var vm = new EstimateValidationPageViewModel( - SampleEstimate(), EstimateListPerspective.Provider, front); - - var proPad = new SignaturePadControl(); - var clientPad = new SignaturePadControl(); - vm.Attach(proPad, clientPad); - DrawOneStroke(proPad); // provider signs on their writable pad - - await vm.ValidateAsync(); - - Assert.Equal(HttpMethod.Post, api.LastMethod); - Assert.Contains("front/query/accept", api.LastPath); - Assert.Contains("billingCode=Rdv", api.LastPath); - Assert.Contains("queryId=42", api.LastPath); - var body = Assert.IsType(api.LastBody); - Assert.NotNull(body.Strokes); - Assert.NotEmpty(body.Strokes); - Assert.Equal(10_000, body.CoordinateMax); - // The provider perspective declares the Pro side, so the - // server stores the signature under the provider's author - // regardless of role inference. - Assert.Equal(SignatureType.Pro, body.SignatureType); - } - - [Fact] - public async Task Validate_from_client_perspective_declares_client_side() - { - var api = new RecordingApi(); - var front = new FrontOfficeApiClient(api, "https://business.example/api/v1/"); - var vm = new EstimateValidationPageViewModel( - SampleEstimate(), EstimateListPerspective.Client, front); - - var cliPad = new SignaturePadControl(); - vm.Attach(new SignaturePadControl(), cliPad); - DrawOneStroke(cliPad); // client signs on their writable pad - - await vm.ValidateAsync(); - - var body = Assert.IsType(api.LastBody); - Assert.Equal(SignatureType.Client, body.SignatureType); // Client side - } - - [Fact] - public async Task Reject_calls_reject_endpoint_with_no_body() - { - var api = new RecordingApi(); - var front = new FrontOfficeApiClient(api, "https://business.example/api/v1/"); - var vm = new EstimateValidationPageViewModel( - SampleEstimate(), EstimateListPerspective.Client, front); - - vm.Attach(new SignaturePadControl(), new SignaturePadControl()); - - await vm.RejectAsync(); - - Assert.Equal(HttpMethod.Post, api.LastMethod); - Assert.Contains("front/query/reject", api.LastPath); - Assert.Contains("queryId=42", api.LastPath); - Assert.Null(api.LastBody); - } - - [Fact] - public async Task Validate_without_signature_warns_and_does_not_call() - { - var api = new RecordingApi(); - var front = new FrontOfficeApiClient(api, "https://business.example/api/v1/"); - var vm = new EstimateValidationPageViewModel( - SampleEstimate(), EstimateListPerspective.Provider, front); - - vm.Attach(new SignaturePadControl(), new SignaturePadControl()); // empty pads - - await vm.ValidateAsync(); // no throw: returns before GoBack - - Assert.Null(api.LastMethod); - Assert.Contains("signer", vm.StatusMessage, StringComparison.OrdinalIgnoreCase); - } - - [Fact] - public void Validate_command_gates_on_signature_and_command() - { - var api = new RecordingApi(); - var front = new FrontOfficeApiClient(api, "https://business.example/api/v1/"); - var vm = new EstimateValidationPageViewModel( - SampleEstimate(commandId: null), EstimateListPerspective.Provider, front); - - var proPad = new SignaturePadControl(); - vm.Attach(proPad, new SignaturePadControl()); - - // No CommandId ⇒ cannot validate even after signing. - DrawOneStroke(proPad); - Assert.False(vm.ValidateCommand.CanExecute(null)); - - var vm2 = new EstimateValidationPageViewModel( - SampleEstimate(), EstimateListPerspective.Provider, front); - var proPad2 = new SignaturePadControl(); - vm2.Attach(proPad2, new SignaturePadControl()); - Assert.False(vm2.ValidateCommand.CanExecute(null)); // no signature yet - DrawOneStroke(proPad2); - Assert.True(vm2.ValidateCommand.CanExecute(null)); - } - - [Fact] - public void RoleLabel_reflects_perspective() - { - var front = new FrontOfficeApiClient(new RecordingApi(), "https://business.example/api/v1/"); - var pro = new EstimateValidationPageViewModel( - SampleEstimate(), EstimateListPerspective.Provider, front); - var cli = new EstimateValidationPageViewModel( - SampleEstimate(), EstimateListPerspective.Client, front); - - Assert.Contains("prestataire", pro.RoleLabel, StringComparison.OrdinalIgnoreCase); - Assert.Contains("client", cli.RoleLabel, StringComparison.OrdinalIgnoreCase); - } - - [Fact] - public void Only_the_author_pad_is_writable() - { - var front = new FrontOfficeApiClient(new RecordingApi(), "https://business.example/api/v1/"); - - var proVm = new EstimateValidationPageViewModel( - SampleEstimate(), EstimateListPerspective.Provider, front); - var proPad = new SignaturePadControl(); - var cliPad = new SignaturePadControl(); - proVm.Attach(proPad, cliPad); - Assert.False(proPad.IsReadOnly); // provider writes the Pro pad - Assert.True(cliPad.IsReadOnly); - - var cliVm = new EstimateValidationPageViewModel( - SampleEstimate(), EstimateListPerspective.Client, front); - var proPad2 = new SignaturePadControl(); - var cliPad2 = new SignaturePadControl(); - cliVm.Attach(proPad2, cliPad2); - Assert.True(proPad2.IsReadOnly); - Assert.False(cliPad2.IsReadOnly); // client writes the Client pad - } - - [Fact] - public void Load_existing_signatures_renders_both_sides_in_their_pads() - { - var proStrokes = new[] { 1, 1000, 1000 }; - var cliStrokes = new[] { 1, 9000, 9000 }; - var front = new FrontOfficeApiClient(new RecordingApi(), "https://business.example/api/v1/"); - var estimate = SampleEstimate(); - estimate.SignaturePro = new EstimateSignatureDto { Strokes = proStrokes }; - estimate.SignatureClient = new EstimateSignatureDto { Strokes = cliStrokes }; - - // Client perspective: both pads show the stored signatures, but - // only the client pad is writable. - var vm = new EstimateValidationPageViewModel(estimate, EstimateListPerspective.Client, front); - var proPad = new SignaturePadControl(); - var cliPad = new SignaturePadControl(); - vm.Attach(proPad, cliPad); - - Assert.Equal(proStrokes, proPad.Snapshot().Strokes); - Assert.Equal(cliStrokes, cliPad.Snapshot().Strokes); - Assert.True(vm.HasProSignature); - Assert.True(vm.HasClientSignature); - } - - [Fact] - public void Load_existing_pro_signature_renders_in_pro_pad_only() - { - var strokes = new[] { 1, 5000, 5000 }; - var front = new FrontOfficeApiClient(new RecordingApi(), "https://business.example/api/v1/"); - var estimate = SampleEstimate(); - estimate.SignaturePro = new EstimateSignatureDto { Strokes = strokes, CoordinateMax = 10_000 }; - var vm = new EstimateValidationPageViewModel(estimate, EstimateListPerspective.Provider, front); - - var proPad = new SignaturePadControl(); - var cliPad = new SignaturePadControl(); - vm.Attach(proPad, cliPad); - - Assert.Equal(strokes, proPad.Snapshot().Strokes); - Assert.True(cliPad.Snapshot().IsEmpty); - Assert.True(vm.HasProSignature); - Assert.False(vm.HasClientSignature); - } - - private sealed class RecordingApi : IYavscApiClient - { - public HttpClient Http { get; } = new(); - public HttpMethod? LastMethod { get; private set; } - public string? LastPath { get; private set; } - public object? LastBody { get; private set; } - - public Task CallAsync(HttpMethod method, string path, object? body = null, CancellationToken ct = default) - { - LastMethod = method; - LastPath = path; - LastBody = body; - return Task.FromResult(default(T)!); - } - - public Task CallAsync(HttpMethod method, string path, object? body = null, CancellationToken ct = default) - { - LastMethod = method; - LastPath = path; - LastBody = body; - return Task.CompletedTask; - } - - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - - public ValueTask DisposeAsync() => ValueTask.CompletedTask; - } -} \ No newline at end of file diff --git a/src/PostIt/PostIt.Tests/FrontOfficeApiClientTests.cs b/src/PostIt/PostIt.Tests/FrontOfficeApiClientTests.cs deleted file mode 100644 index 02f6ae149..000000000 --- a/src/PostIt/PostIt.Tests/FrontOfficeApiClientTests.cs +++ /dev/null @@ -1,157 +0,0 @@ -using System.Net.Http; -using Yavsc.Api.Client; - -namespace PostIt.Tests; - -/// -/// Verifies issues the right -/// api/v1/front/query/accept / .../reject requests with -/// the PostIt signature payload, using a recording fake -/// (no network). -/// -public class FrontOfficeApiClientTests -{ - private static QueryAcceptanceRequestDto SampleBody() => new() - { - Strokes = new[] { 2, 100, 200, 300, 400 }, - CoordinateMax = 10_000, - CapturedAtUtc = new DateTime(2026, 9, 21, 12, 0, 0, DateTimeKind.Utc), - }; - - [Fact] - public async Task Accept_posts_to_accept_endpoint_with_signature_body() - { - var api = new RecordingApi(); - var client = new FrontOfficeApiClient(api, "https://business.example/api/v1/"); - - var body = SampleBody(); - await client.AcceptQueryAsync("Rdv", 42, body); - - Assert.Equal(HttpMethod.Post, api.LastMethod); - Assert.Equal( - "https://business.example/api/v1/front/query/accept?billingCode=Rdv&queryId=42", - api.LastPath); - Assert.Same(body, api.LastBody); - } - - [Fact] - public async Task Reject_posts_to_reject_endpoint_with_no_body() - { - var api = new RecordingApi(); - var client = new FrontOfficeApiClient(api, "https://business.example/api/v1/"); - - await client.RejectQueryAsync("Rdv", 42); - - Assert.Equal(HttpMethod.Post, api.LastMethod); - Assert.Equal( - "https://business.example/api/v1/front/query/reject?billingCode=Rdv&queryId=42", - api.LastPath); - Assert.Null(api.LastBody); - } - - [Fact] - public async Task Accept_returns_deserialized_response() - { - var api = new RecordingApi - { - Response = new QueryAcceptanceResponseDto - { - QueryId = 42, - Status = "Accepted", - Signature = new QuerySignatureRefDto { Id = 9, EstimateId = 5, Type = "Pro" }, - }, - }; - var client = new FrontOfficeApiClient(api, "https://business.example/api/v1/"); - - var response = await client.AcceptQueryAsync("Rdv", 42, SampleBody()); - - Assert.Equal(42, response.QueryId); - Assert.Equal("Accepted", response.Status); - Assert.NotNull(response.Signature); - Assert.Equal("Pro", response.Signature!.Type); - } - - [Fact] - public async Task Accept_rejects_invalid_arguments() - { - var api = new RecordingApi(); - var client = new FrontOfficeApiClient(api, "https://business.example/api/v1/"); - - await Assert.ThrowsAsync(() => client.AcceptQueryAsync("", 42, SampleBody())); - await Assert.ThrowsAsync(() => client.AcceptQueryAsync("Rdv", 0, SampleBody())); - await Assert.ThrowsAsync(() => client.AcceptQueryAsync("Rdv", 42, null!)); - await Assert.ThrowsAsync(() => client.RejectQueryAsync("", 42)); - await Assert.ThrowsAsync(() => client.RejectQueryAsync("Rdv", 0)); - } - - [Fact] - public async Task GetEstimatePdf_gets_estimate_pdf_endpoint() - { - var api = new RecordingApi(); - var client = new FrontOfficeApiClient(api, "https://business.example/api/v1/"); - - await client.GetEstimatePdfAsync(42); - - Assert.Equal(HttpMethod.Get, api.LastMethod); - Assert.Equal( - "https://business.example/api/v1/front/query/42/estimate.pdf", - api.LastPath); - } - - [Fact] - public async Task GetEstimateTex_gets_estimate_tex_endpoint() - { - var api = new RecordingApi(); - var client = new FrontOfficeApiClient(api, "https://business.example/api/v1/"); - - await client.GetEstimateTexAsync(42); - - Assert.Equal(HttpMethod.Get, api.LastMethod); - Assert.Equal( - "https://business.example/api/v1/front/query/42/estimate.tex", - api.LastPath); - } - - private sealed class RecordingApi : IYavscApiClient - { - public HttpClient Http { get; } = new(); - public HttpMethod? LastMethod { get; private set; } - public string? LastPath { get; private set; } - public object? LastBody { get; private set; } - public QueryAcceptanceResponseDto? Response { get; set; } - - public Task CallAsync(HttpMethod method, string path, object? body = null, CancellationToken ct = default) - { - LastMethod = method; - LastPath = path; - LastBody = body; - - var response = Response ?? new QueryAcceptanceResponseDto(); - return Task.FromResult((T)(object)response); - } - - public Task CallAsync(HttpMethod method, string path, object? body = null, CancellationToken ct = default) - { - LastMethod = method; - LastPath = path; - LastBody = body; - return Task.CompletedTask; - } - - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - - public Task CallAsync(HttpMethod method, string path, Func contentFactory, CancellationToken ct = default) - => CallAsync(method, path, (object?)null, ct); - - public Task DownloadAsync(HttpMethod method, string path, CancellationToken ct = default) - { - LastMethod = method; - LastPath = path; - LastBody = null; - return Task.FromResult(Array.Empty()); - } - - public ValueTask DisposeAsync() => ValueTask.CompletedTask; - } -} \ No newline at end of file diff --git a/src/PostIt/PostIt.Tests/PostIt.Tests.csproj b/src/PostIt/PostIt.Tests/PostIt.Tests.csproj index d4d16c29e..bbe2096e7 100644 --- a/src/PostIt/PostIt.Tests/PostIt.Tests.csproj +++ b/src/PostIt/PostIt.Tests/PostIt.Tests.csproj @@ -8,13 +8,14 @@ true 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1 + diff --git a/src/PostIt/PostIt/App.axaml b/src/PostIt/PostIt/App.axaml index b89a4aef4..85e94ebc9 100644 --- a/src/PostIt/PostIt/App.axaml +++ b/src/PostIt/PostIt/App.axaml @@ -11,6 +11,5 @@ - diff --git a/src/PostIt/PostIt/App.axaml.cs b/src/PostIt/PostIt/App.axaml.cs index 9a04ffd03..b80351e17 100644 --- a/src/PostIt/PostIt/App.axaml.cs +++ b/src/PostIt/PostIt/App.axaml.cs @@ -17,7 +17,6 @@ namespace PostIt; public partial class App : Application { private int _bootStarted; - public string? CliConfigFileSpecification {  get; private set; } /// /// DI container the platform entry points hand to ViewModels so @@ -204,9 +203,4 @@ public partial class App : Application { await View!.NavRoot.PopAsync(); } - - internal void UseConfigFileWhenLoading(string configFile) - { - this.CliConfigFileSpecification = configFile; - } } diff --git a/src/PostIt/PostIt/CLIOptions/CLIOptions.cs b/src/PostIt/PostIt/CLIOptions/CLIOptions.cs deleted file mode 100644 index c0bea85ff..000000000 --- a/src/PostIt/PostIt/CLIOptions/CLIOptions.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Collections.Generic; -using Mono.Options; -using PostIt; -using PostIt.ViewModels; - -public static class CLIOptionHandler -{ - public static void HandleArgs(string[] args) - { - bool showHelp = false; - string configFile = null; - - var options = new OptionSet { - { "c|configuration=", "Configuration file to load", (string v) => configFile = v }, - { "h|help", "Show this message and exit", v => showHelp = true } - }; - - List extra = options.Parse(args); - - if (showHelp) - { - Console.WriteLine("Usage: app [OPTIONS]+"); - options.WriteOptionDescriptions(Console.Out); - return; - } - if (configFile != null) - { - var app = App.Current as App; - app.UseConfigFileWhenLoading(configFile); - } - } -} diff --git a/src/PostIt/PostIt/Controls/SignaturePadControl.cs b/src/PostIt/PostIt/Controls/SignaturePadControl.cs index bacb52486..dee8af36e 100644 --- a/src/PostIt/PostIt/Controls/SignaturePadControl.cs +++ b/src/PostIt/PostIt/Controls/SignaturePadControl.cs @@ -46,21 +46,6 @@ public class SignaturePadControl : TemplatedControl set => SetValue(CaptureAreaProperty, value); } - /// - /// When true the control ignores pointer input — it only displays - /// strokes loaded into its buffer (e.g. the other party's - /// signature, which must be shown but not editable). - /// and still work. - /// - public static readonly StyledProperty IsReadOnlyProperty = - AvaloniaProperty.Register(nameof(IsReadOnly)); - - public bool IsReadOnly - { - get => GetValue(IsReadOnlyProperty); - set => SetValue(IsReadOnlyProperty, value); - } - /// /// Captured strokes in wire form. Exposed as a read-only view /// over the internal buffer. The buffer only mutates on the UI @@ -124,7 +109,6 @@ public class SignaturePadControl : TemplatedControl private void OnCapturePressed(object? sender, PointerPressedEventArgs e) { - if (IsReadOnly) return; if (!e.GetCurrentPoint(CaptureArea).Properties.IsLeftButtonPressed) return; e.Pointer.Capture(CaptureArea); _capturing = true; @@ -197,38 +181,6 @@ public class SignaturePadControl : TemplatedControl RedrawRequested?.Invoke(this, EventArgs.Empty); } - /// - /// Replace the buffer with the given wire-format strokes — e.g. a - /// signature loaded back from the server — and raise - /// so the host view repaints the ink. - /// Loaded strokes are all sealed (there is no pending stroke). - /// Malformed input (a non-positive length prefix, or a stroke that - /// runs past the end of the array) is truncated at the offending - /// point so the buffer never holds an inconsistent state. - /// - public void Load(int[] strokes) - { - if (strokes is null) throw new ArgumentNullException(nameof(strokes)); - - _strokes.Clear(); - _pendingPoints = 0; - _capturing = false; - - int i = 0; - while (i < strokes.Length) - { - int k = strokes[i]; - if (k <= 0) break; - if (i + 1 + 2 * k > strokes.Length) break; - _strokes.Add(k); - for (int p = 0; p < 2 * k; p++) - _strokes.Add(strokes[i + 1 + p]); - i += 1 + 2 * k; - } - - RedrawRequested?.Invoke(this, EventArgs.Empty); - } - /// /// Defensive copy of the current buffer wrapped in a /// . Cheap; call only when the diff --git a/src/PostIt/PostIt/Controls/SignaturePadView.axaml b/src/PostIt/PostIt/Controls/SignaturePadView.axaml deleted file mode 100644 index cb0a99de1..000000000 --- a/src/PostIt/PostIt/Controls/SignaturePadView.axaml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/src/PostIt/PostIt/Controls/SignaturePadView.axaml.cs b/src/PostIt/PostIt/Controls/SignaturePadView.axaml.cs deleted file mode 100644 index ff75bf663..000000000 --- a/src/PostIt/PostIt/Controls/SignaturePadView.axaml.cs +++ /dev/null @@ -1,206 +0,0 @@ -using System; -using System.Collections.Generic; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Controls.Shapes; -using Avalonia.Media; -using PostIt.Models; - -namespace PostIt.Controls; - -/// -/// Reusable signature-capture surface: wraps the render-agnostic -/// in a sized -/// (the hit-test region) with an overlay that -/// the code-behind repaints from the control's stroke buffer. Owns -/// the ink-rendering logic so every page that captures a signature -/// (the dev-only SignaturePage, the estimate validation page, -/// ...) shares one implementation. -/// -/// Hosts bind / -/// (DIPs, defaults 600×200) to size the surface. View models attach -/// to the inner control (exposed here) to -/// read snapshots, drive Clear, and subscribe to -/// / -/// — the same -/// contract the headless tests use, since a -/// needs its XAML loaded to be drivable. -/// -public partial class SignaturePadView : UserControl -{ - private static readonly IBrush StrokeBrush = new SolidColorBrush(Color.FromRgb(0x10, 0x10, 0x10)); - private const double StrokeThickness = 2.0; - private const double CoordinateMax = 10_000.0; - - // Last size the ink layer was painted at. LayoutUpdated repaints - // only when this changes, so adding/removing polyline children - // (which invalidates layout) does not loop. - private Size _lastPaintedSize; - - public static readonly StyledProperty CaptureWidthProperty = - AvaloniaProperty.Register(nameof(CaptureWidth), 600); - - public static readonly StyledProperty CaptureHeightProperty = - AvaloniaProperty.Register(nameof(CaptureHeight), 200); - - public double CaptureWidth - { - get => GetValue(CaptureWidthProperty); - set => SetValue(CaptureWidthProperty, value); - } - - public double CaptureHeight - { - get => GetValue(CaptureHeightProperty); - set => SetValue(CaptureHeightProperty, value); - } - - /// - /// The underlying capture control. View models attach to this to - /// read / call - /// and subscribe to its - /// events; this view repaints the ink layer from the same buffer. - /// - public SignaturePadControl SignaturePad => InnerPad; - - public SignaturePadView() - { - InitializeComponent(); - - // Wire the capture area: the Pad is the control, the - // surrounding Border (PadFrame) is the hit-test region. For an - // inline control with no template, capture wiring happens on - // first measure, which is guaranteed before the user can - // interact, so attaching here is safe. - InnerPad.CaptureArea = PadFrame; - ApplyFrameSize(); - - InnerPad.RedrawRequested += OnPadRedrawRequested; - // Repaint once layout completes so an ink buffer loaded before - // the first arrange (e.g. an existing signature on reopen) - // still renders without waiting for a pointer event. The - // handler repaints only on a size change to avoid a layout - // loop (child changes invalidate layout → LayoutUpdated). - LayoutUpdated += OnLayoutUpdated; - } - - private void OnLayoutUpdated(object? sender, EventArgs e) - { - var size = EffectivePadSize(); - if (size != _lastPaintedSize) - Repaint(); - } - - protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) - { - base.OnPropertyChanged(change); - if (change.Property == CaptureWidthProperty || change.Property == CaptureHeightProperty) - { - ApplyFrameSize(); - Repaint(); - } - } - - private void ApplyFrameSize() - { - if (PadFrame is null) return; - PadFrame.Width = Math.Max(1, CaptureWidth); - PadFrame.Height = Math.Max(1, CaptureHeight); - } - - private void OnPadRedrawRequested(object? sender, EventArgs e) => Repaint(); - - /// Defensive copy of the current buffer. - public SignaturePadData Snapshot() => InnerPad.Snapshot(); - - /// Forget every captured stroke. - public void Clear() => InnerPad.Clear(); - - /// Wire-format strokes, forwarded from the inner control. - public IReadOnlyList Strokes => InnerPad.Strokes; - - /// - /// The effective ink surface size: the arranged bounds when - /// available, otherwise the explicitly sized - /// /. The - /// latter covers a repaint triggered before the first layout pass - /// (e.g. loading an existing signature on reopen), when Bounds is - /// still empty. - /// - private Size EffectivePadSize() - { - var w = PadFrame.Bounds.Width > 0 ? PadFrame.Bounds.Width : PadFrame.Width; - var h = PadFrame.Bounds.Height > 0 ? PadFrame.Bounds.Height : PadFrame.Height; - if (w <= 0 || double.IsNaN(w) || h <= 0 || double.IsNaN(h)) - return default; - return new Size(w, h); - } - - private void Repaint() - { - if (InkLayer is null) return; - - var size = EffectivePadSize(); - if (size == default) return; - _lastPaintedSize = size; - - InkLayer.Children.Clear(); - var w = size.Width; - var h = size.Height; - - var pending = InnerPad.PendingStroke; - var strokes = InnerPad.Strokes; - int sealedCount = strokes.Count - pending.Count; - if (sealedCount < 0) - { - sealedCount = 0; - } - - int i = 0; - while (i < sealedCount) - { - int k = strokes[i]; - if (k <= 0) break; - i++; // skip the length prefix - - var poly = new Polyline - { - Stroke = StrokeBrush, - StrokeThickness = StrokeThickness, - StrokeLineCap = PenLineCap.Round, - StrokeJoin = PenLineJoin.Round, - }; - var pts = new List(k); - for (int p = 0; p < k; p++) - { - int nx = strokes[i++]; - int ny = strokes[i++]; - pts.Add(new Point(nx / CoordinateMax * w, ny / CoordinateMax * h)); - } - poly.Points = pts; - InkLayer.Children.Add(poly); - } - - if (pending.Count > 0) - { - var poly = new Polyline - { - Stroke = StrokeBrush, - StrokeThickness = StrokeThickness, - StrokeLineCap = PenLineCap.Round, - StrokeJoin = PenLineJoin.Round, - }; - - var pts = new List(pending.Count / 2); - for (int p = 0; p < pending.Count; p += 2) - { - int nx = pending[p]; - int ny = pending[p + 1]; - pts.Add(new Point(nx / CoordinateMax * w, ny / CoordinateMax * h)); - } - - poly.Points = pts; - InkLayer.Children.Add(poly); - } - } -} \ No newline at end of file diff --git a/src/PostIt/PostIt/Helpers/FileSaveHelpers.cs b/src/PostIt/PostIt/Helpers/FileSaveHelpers.cs deleted file mode 100644 index b99a33c87..000000000 --- a/src/PostIt/PostIt/Helpers/FileSaveHelpers.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using Avalonia; -using Avalonia.Controls; -using Avalonia.Platform.Storage; - -namespace PostIt.Helpers; - -/// -/// Saves a downloaded byte payload to a file chosen by the user through -/// the platform save picker. This is the first file-save (as opposed to -/// file-open) helper in PostIt; the open picker is already used for blog -/// attachment and avatar uploads -/// (, ). -/// -/// The TopLevel hosting the picker is resolved from the -/// application's root MainView (App.View), which works for -/// both the desktop and single-view lifetimes. -/// -public static class FileSaveHelpers -{ - /// - /// Prompt the user to choose a destination and write - /// to it. Returns false when the user - /// cancels the picker; throws on IO/platform errors so the caller - /// can surface them. - /// - public static async Task SaveAsync( - string suggestedName, - string defaultExtension, - byte[] content, - CancellationToken ct = default) - { - var view = (Application.Current as App)?.View; - var topLevel = view is null ? null : TopLevel.GetTopLevel(view); - if (topLevel is null) - throw new InvalidOperationException("Aucune fenêtre active pour enregistrer le fichier."); - - var file = await topLevel.StorageProvider.SaveFilePickerAsync(new FilePickerSaveOptions - { - Title = "Enregistrer sous", - DefaultExtension = defaultExtension, - SuggestedFileName = suggestedName, - FileTypeChoices = new[] - { - new FilePickerFileType($"{defaultExtension.ToUpper()} file") - { - Patterns = new[] { $"*.{defaultExtension}" } - } - } - }).ConfigureAwait(true); - - if (file is null) - return false; // user cancelled - - await using var stream = await file.OpenWriteAsync().ConfigureAwait(true); - await stream.WriteAsync(content, ct).ConfigureAwait(true); - return true; - } -} \ No newline at end of file diff --git a/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs b/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs index 2b243f2d5..bc5e7298f 100644 --- a/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs +++ b/src/PostIt/PostIt/Helpers/ServiceCollectionHelpers.cs @@ -31,7 +31,6 @@ public static class ServiceCollectionHelpers () => settings.Authentication?.Authority); var billingClient = new BillingApiClient(api, () => settings.ApiUrl); var estimateClient = new EstimateApiClient(api, () => settings.ApiUrl); - var frontClient = new FrontOfficeApiClient(api, () => settings.ApiUrl); var userDirectory = new UserDirectory(userSearchClient); var reverseGeocoding = new NominatimReverseGeocodingService(); @@ -64,7 +63,6 @@ public static class ServiceCollectionHelpers services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddTransient(); // ViewModels services.AddSingleton(settings); @@ -76,7 +74,6 @@ public static class ServiceCollectionHelpers services.AddSingleton(activityClient); services.AddSingleton(billingClient); services.AddSingleton(estimateClient); - services.AddSingleton(frontClient); services.AddSingleton(reverseGeocoding); services.AddSingleton(userDirectory); services.AddSingleton(); diff --git a/src/PostIt/PostIt/PostIt.csproj b/src/PostIt/PostIt/PostIt.csproj index 98dcbe480..a6f01cc31 100644 --- a/src/PostIt/PostIt/PostIt.csproj +++ b/src/PostIt/PostIt/PostIt.csproj @@ -5,8 +5,8 @@ latest 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1 @@ -23,7 +23,6 @@ - @@ -36,7 +35,6 @@ - diff --git a/src/PostIt/PostIt/Services/YavscApiClient.cs b/src/PostIt/PostIt/Services/YavscApiClient.cs index 555a7c675..04dbce1fd 100644 --- a/src/PostIt/PostIt/Services/YavscApiClient.cs +++ b/src/PostIt/PostIt/Services/YavscApiClient.cs @@ -478,41 +478,6 @@ public class YavscApiClient : IYavscApiClient, IAsyncDisposable return "Avatar mis à jour."; } - /// - /// Download a binary resource (PDF, TeX, …) from the API. Unlike - /// , - /// which JSON-decodes the response, this returns the raw response - /// bytes. Reuses the same Bearer-token attach + 401-refresh-retry-once - /// contract as . - /// - public virtual async Task DownloadAsync( - HttpMethod method, - string path, - CancellationToken ct = default) - { - if (_tokens is null) - throw new InvalidOperationException("Not logged in. Call LoginInteractiveAsync first."); - - await EnsureFreshTokenAsync(ct).ConfigureAwait(false); - - var sendOnce = async () => - { - using var request = new HttpRequestMessage(method, path); - return await Http.SendAsync(request, ct).ConfigureAwait(false); - }; - - var response = await sendOnce().ConfigureAwait(false); - if (response.StatusCode == HttpStatusCode.Unauthorized) - { - response.Dispose(); - await ForceRefreshAsync(ct).ConfigureAwait(false); - response = await sendOnce().ConfigureAwait(false); - } - - await EnsureSuccessOrThrowAsync(response, ct).ConfigureAwait(false); - return await response.Content.ReadAsByteArrayAsync(ct).ConfigureAwait(false); - } - public async Task LogoutAsync() { _store.Clear(); diff --git a/src/PostIt/PostIt/ViewLocator.cs b/src/PostIt/PostIt/ViewLocator.cs index ca9a7ff65..11797b714 100644 --- a/src/PostIt/PostIt/ViewLocator.cs +++ b/src/PostIt/PostIt/ViewLocator.cs @@ -55,7 +55,6 @@ public class ViewLocator : IDataTemplate ProviderOngoingRequestsPageViewModel => services.GetRequiredService(), EstimateEditionPageViewModel => services.GetRequiredService(), EstimateListPageViewModel => services.GetRequiredService(), - EstimateValidationPageViewModel => services.GetRequiredService(), null => new TextBlock { Text = "No view for " }, _ => new TextBlock { Text = $"No view for {data.GetType().Name}" } }; diff --git a/src/PostIt/PostIt/ViewModels/Activity/BillingQueriesPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/BillingQueriesPageViewModel.cs index dbd84e5c2..aab923d80 100644 --- a/src/PostIt/PostIt/ViewModels/Activity/BillingQueriesPageViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/Activity/BillingQueriesPageViewModel.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using Avalonia; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using Microsoft.Extensions.DependencyInjection; using PostIt.Helpers; using Yavsc; using Yavsc.Api.Client; @@ -131,13 +130,11 @@ public partial class BillingQueriesPageViewModel : ViewModelBase, IActionStatusV try { var details = await _billingClient.GetQueryAsync(Form.ActionName, SelectedQuery.Id).ConfigureAwait(true); - var frontClient = app.ServiceProvider?.GetRequiredService(); var vm = new BillingQueryDetailsPageViewModel( Activity, Performer, Form, _billingClient, - frontClient, details, IsReadOnly); await app.PushPageAsync(vm).ConfigureAwait(true); @@ -174,9 +171,5 @@ public partial class BillingQueriesPageViewModel : ViewModelBase, IActionStatusV } private static bool IsOngoingStatus(QueryStatus status) - => status is QueryStatus.Inserted - or QueryStatus.Accepted - or QueryStatus.ProAccepted - or QueryStatus.ClientAccepted - or QueryStatus.InProgress; + => status is QueryStatus.Inserted or QueryStatus.Accepted or QueryStatus.InProgress; } diff --git a/src/PostIt/PostIt/ViewModels/Activity/BillingQueryDetailsPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/BillingQueryDetailsPageViewModel.cs index 9ab1b42fd..cd13daaf1 100644 --- a/src/PostIt/PostIt/ViewModels/Activity/BillingQueryDetailsPageViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/Activity/BillingQueryDetailsPageViewModel.cs @@ -14,7 +14,6 @@ namespace PostIt.ViewModels; public partial class BillingQueryDetailsPageViewModel : ViewModelBase, IActionStatusViewModel { private readonly BillingApiClient _billingClient; - private readonly FrontOfficeApiClient _frontClient; private readonly BillingQueryDetailsDto _details; public ActivityInfo Activity { get; } @@ -69,7 +68,6 @@ public partial class BillingQueryDetailsPageViewModel : ViewModelBase, IActionSt ActivityUserDisplayItem performer, CommandFormSummary form, BillingApiClient billingClient, - FrontOfficeApiClient frontClient, BillingQueryDetailsDto details, bool isReadOnly) { @@ -77,7 +75,6 @@ public partial class BillingQueryDetailsPageViewModel : ViewModelBase, IActionSt Performer = performer ?? throw new ArgumentNullException(nameof(performer)); Form = form ?? throw new ArgumentNullException(nameof(form)); _billingClient = billingClient ?? throw new ArgumentNullException(nameof(billingClient)); - _frontClient = frontClient ?? throw new ArgumentNullException(nameof(frontClient)); _details = details ?? throw new ArgumentNullException(nameof(details)); IsReadOnly = isReadOnly; @@ -134,53 +131,6 @@ public partial class BillingQueryDetailsPageViewModel : ViewModelBase, IActionSt await app.GoBackAsync().ConfigureAwait(true); } - /// - /// Download the estimate linked to this command as a LaTeX source - /// (GET api/v1/front/query/{Id}/estimate.tex). The estimate is - /// resolved server-side from Estimate.CommandId == query Id. - /// - [RelayCommand] - public async Task DownloadTexAsync() - { - await DownloadAsync( - () => _frontClient.GetEstimateTexAsync(_details.Id), - $"devis-{_details.Id}", "tex").ConfigureAwait(true); - } - - /// - /// Download the estimate linked to this command as a compiled PDF - /// (GET api/v1/front/query/{Id}/estimate.pdf). Requires - /// texi2pdf on the server host. - /// - [RelayCommand] - public async Task DownloadPdfAsync() - { - await DownloadAsync( - () => _frontClient.GetEstimatePdfAsync(_details.Id), - $"devis-{_details.Id}", "pdf").ConfigureAwait(true); - } - - private async Task DownloadAsync(Func> fetch, string fileName, string ext) - { - IsBusy = true; - try - { - var bytes = await fetch().ConfigureAwait(true); - var saved = await FileSaveHelpers.SaveAsync(fileName, ext, bytes).ConfigureAwait(true); - this.SetInfoStatus(saved - ? $"Fichier .{ext} enregistré." - : "Téléchargement annulé."); - } - catch (Exception ex) - { - this.SetErrorStatus($"Échec du téléchargement: {ex.Message}"); - } - finally - { - IsBusy = false; - } - } - private static string EmptyAsPlaceholder(string? value, string placeholder) => string.IsNullOrWhiteSpace(value) ? placeholder : value; @@ -216,8 +166,6 @@ public partial class BillingQueryDetailsPageViewModel : ViewModelBase, IActionSt => status switch { QueryStatus.Accepted => "#E6F7EC", - QueryStatus.ProAccepted => "#E6F7EC", - QueryStatus.ClientAccepted => "#E1F5FE", QueryStatus.InProgress => "#FFF4D6", QueryStatus.Rejected => "#FDECEA", QueryStatus.Failed => "#ECEFF1", @@ -229,8 +177,6 @@ public partial class BillingQueryDetailsPageViewModel : ViewModelBase, IActionSt => status switch { QueryStatus.Accepted => "#2E7D32", - QueryStatus.ProAccepted => "#2E7D32", - QueryStatus.ClientAccepted => "#1565C0", QueryStatus.InProgress => "#B26A00", QueryStatus.Rejected => "#C62828", QueryStatus.Failed => "#607D8B", @@ -242,8 +188,6 @@ public partial class BillingQueryDetailsPageViewModel : ViewModelBase, IActionSt => status switch { QueryStatus.Accepted => "#1B5E20", - QueryStatus.ProAccepted => "#1B5E20", - QueryStatus.ClientAccepted => "#0D47A1", QueryStatus.InProgress => "#7A4A00", QueryStatus.Rejected => "#8E0000", QueryStatus.Failed => "#37474F", @@ -255,8 +199,6 @@ public partial class BillingQueryDetailsPageViewModel : ViewModelBase, IActionSt => status switch { QueryStatus.Accepted => "OK", - QueryStatus.ProAccepted => "P", - QueryStatus.ClientAccepted => "C", QueryStatus.InProgress => "~", QueryStatus.Rejected => "!", QueryStatus.Failed => "X", diff --git a/src/PostIt/PostIt/ViewModels/Activity/EstimateListPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/EstimateListPageViewModel.cs index c90a6d884..e5c4d355f 100644 --- a/src/PostIt/PostIt/ViewModels/Activity/EstimateListPageViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/Activity/EstimateListPageViewModel.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; -using Avalonia; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using PostIt.Helpers; @@ -19,10 +18,10 @@ namespace PostIt.ViewModels; /// public enum EstimateListPerspective { - /// « Devis en attente de signature client » — l'utilisateur courant est le client. + /// « Mes devis à valider » — l'utilisateur courant est le client. Client, - /// « Devis en attente de signature fournisseur » — l'utilisateur courant est le fournisseur. + /// « Mes devis en attente » — l'utilisateur courant est le fournisseur. Provider, } @@ -33,15 +32,14 @@ public enum EstimateListPerspective public partial class EstimateListPageViewModel : ViewModelBase, IActionStatusViewModel { private readonly EstimateApiClient _estimateClient; - private readonly FrontOfficeApiClient _frontClient; private readonly EstimateListPerspective _perspective; private List _allEstimates = new(); public EstimateListPerspective Perspective => _perspective; public string Title => _perspective == EstimateListPerspective.Client - ? "Devis en attente de signature client" - : "Devis en attente de signature fournisseur"; + ? "Mes devis à valider" + : "Mes devis en attente"; public string Subtitle => _perspective == EstimateListPerspective.Client ? "Point de vue client" @@ -85,14 +83,10 @@ public partial class EstimateListPageViewModel : ViewModelBase, IActionStatusVie protected set { _ = value; } } - public EstimateListPageViewModel( - EstimateApiClient estimateClient, - EstimateListPerspective perspective, - FrontOfficeApiClient frontClient) + public EstimateListPageViewModel(EstimateApiClient estimateClient, EstimateListPerspective perspective) { _estimateClient = estimateClient ?? throw new ArgumentNullException(nameof(estimateClient)); _perspective = perspective; - _frontClient = frontClient ?? throw new ArgumentNullException(nameof(frontClient)); } public Task InitializeAsync() => RefreshAsync(); @@ -159,25 +153,4 @@ public partial class EstimateListPageViewModel : ViewModelBase, IActionStatusVie private static bool ContainsInsensitive(string? value, string query) => !string.IsNullOrWhiteSpace(value) && value.Contains(query, StringComparison.OrdinalIgnoreCase); - - /// - /// Open the estimate validation (accept/reject with signature) - /// page for a specific estimate. The perspective the list was - /// opened in is forwarded as the actor-role UI hint; the server - /// independently enforces the real authorization. - /// - [RelayCommand] - private async Task OpenEstimateAsync(EstimateDto? estimate) - { - if (estimate is null) return; - - var app = (App?)Application.Current; - if (app is null) - { - throw new InvalidOperationException("Application PostIt indisponible."); - } - - var vm = new EstimateValidationPageViewModel(estimate, _perspective, _frontClient); - await app.PushPageAsync(vm).ConfigureAwait(true); - } } diff --git a/src/PostIt/PostIt/ViewModels/Activity/EstimateValidationPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/EstimateValidationPageViewModel.cs deleted file mode 100644 index 87347c2ce..000000000 --- a/src/PostIt/PostIt/ViewModels/Activity/EstimateValidationPageViewModel.cs +++ /dev/null @@ -1,399 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Threading.Tasks; -using Avalonia; -using CommunityToolkit.Mvvm.ComponentModel; -using CommunityToolkit.Mvvm.Input; -using PostIt.Controls; -using PostIt.Helpers; -using PostIt.Models; -using Yavsc.Api.Client; -using Yavsc.Models.Billing; - -namespace PostIt.ViewModels; - -/// -/// Validation (accept / reject) of a single estimate, with optional -/// signatures. Backs the estimate validation page opened from -/// for either perspective. -/// -/// Two signatures are shown, each in its own pad: the provider's -/// (Pro) and the client's (Client). Both are rendered from the estimate -/// payload when present, so reopening a partly-signed estimate shows -/// what was already captured. Only the pad matching the current -/// is writable — the server -/// (POST api/v1/front/query/accept) independently enforces the -/// same rule (it resolves the caller's role from the authenticated user -/// vs the query's PerformerId/ClientId), and the UI marks -/// the other pad read-only. The billing code sent to the server is the -/// estimate's and the query id is -/// its . -/// -public partial class EstimateValidationPageViewModel : ViewModelBase, IActionStatusViewModel -{ - private readonly EstimateDto _estimate; - private readonly EstimateListPerspective _perspective; - private readonly FrontOfficeApiClient _frontClient; - private SignaturePadControl? _proPad; - private SignaturePadControl? _clientPad; - - public string Title => _estimate.Title; - public string Description => _estimate.Description; - public long EstimateId => _estimate.Id; - public string CommandType => _estimate.CommandType; - public IReadOnlyList Bill => _estimate.Bill; - public DateTime ProviderValidationDate => _estimate.ProviderValidationDate; - public DateTime ClientValidationDate => _estimate.ClientValidationDate; - - public string RoleLabel => _perspective == EstimateListPerspective.Provider - ? "Vous validez en tant que prestataire" - : "Vous validez en tant que client"; - - /// - /// True when the current user may sign the provider's signature - /// (i.e. they opened the list as the provider). The other party's - /// pad is read-only. - /// - public bool CanWritePro => _perspective == EstimateListPerspective.Provider; - - /// - /// True when the current user may sign the client's signature - /// (i.e. they opened the list as the client). - /// - public bool CanWriteClient => _perspective == EstimateListPerspective.Client; - - /// - /// Human-readable summary of who has already validated this - /// estimate, shown above the signature pads. - /// - public string ValidationStateText - { - get - { - var parts = new List(); - if (ProviderValidationDate != default) - parts.Add($"Prestataire validé le {ProviderValidationDate.ToLocalTime():g}"); - if (ClientValidationDate != default) - parts.Add($"Client validé le {ClientValidationDate.ToLocalTime():g}"); - return parts.Count == 0 ? "Devis en attente de validation." : string.Join(" · ", parts); - } - } - - /// - /// True when the estimate has a linked query to accept/reject. - /// Estimates without a are - /// templates and cannot be validated through the front-office - /// endpoint. - /// - public bool HasCommand => _estimate.CommandId is not null; - - [ObservableProperty] - public partial bool IsBusy { get; set; } - - [ObservableProperty] - public partial string StatusMessage { get; set; } = "Prêt."; - - [ObservableProperty] - public partial StatusNotice ActionStatus { get; set; } = StatusNotice.Info("Prêt."); - - /// Stroke/point counts for the writable pad. - [ObservableProperty] - public partial int StrokeCount { get; set; } - - [ObservableProperty] - public partial int PointCount { get; set; } - - /// True when the writable pad holds a signature. - [ObservableProperty] - public partial bool HasSignature { get; set; } - - [ObservableProperty] - public partial bool HasProSignature { get; set; } - - [ObservableProperty] - public partial bool HasClientSignature { get; set; } - - [ObservableProperty] - public partial string ProSignatureHint { get; set; } = string.Empty; - - [ObservableProperty] - public partial string ClientSignatureHint { get; set; } = string.Empty; - - public override bool CanNavigateNext - { - get => false; - protected set { _ = value; } - } - - public override bool CanNavigatePrevious - { - get => true; - protected set { _ = value; } - } - - public EstimateValidationPageViewModel( - EstimateDto estimate, - EstimateListPerspective perspective, - FrontOfficeApiClient frontClient) - { - _estimate = estimate ?? throw new ArgumentNullException(nameof(estimate)); - _perspective = perspective; - _frontClient = frontClient ?? throw new ArgumentNullException(nameof(frontClient)); - } - - /// - /// Bind the page's two signature pads (the inner controls of the - /// page's s): the provider's pad and - /// the client's pad. Called from the view's code-behind once the - /// view is in the visual tree. Marks the non-author pad read-only, - /// then renders any signatures already carried on the estimate - /// payload. - /// - public void Attach(SignaturePadControl proPad, SignaturePadControl clientPad) - { - if (proPad is null) throw new ArgumentNullException(nameof(proPad)); - if (clientPad is null) throw new ArgumentNullException(nameof(clientPad)); - Detach(); - _proPad = proPad; - _clientPad = clientPad; - _proPad.IsReadOnly = !CanWritePro; - _clientPad.IsReadOnly = !CanWriteClient; - _proPad.RedrawRequested += OnPadRedraw; - _clientPad.RedrawRequested += OnPadRedraw; - LoadExistingSignatures(); - RefreshFromPad(); - } - - public void Detach() - { - if (_proPad is not null) - _proPad.RedrawRequested -= OnPadRedraw; - if (_clientPad is not null) - _clientPad.RedrawRequested -= OnPadRedraw; - _proPad = null; - _clientPad = null; - } - - private void OnPadRedraw(object? sender, EventArgs e) => RefreshFromPad(); - - /// The pad the current user may sign in. - private SignaturePadControl? WritablePad => CanWritePro ? _proPad : _clientPad; - - /// - /// Render the signatures already stored for this estimate back into - /// their pads. The strokes are carried on the estimate payload as - /// two dedicated slots — and - /// — so each pad loads - /// its own side directly, in the same wire format PostIt captured. - /// No network round-trip. - /// - public void LoadExistingSignatures() - { - LoadSide(_proPad, _estimate.SignaturePro); - LoadSide(_clientPad, _estimate.SignatureClient); - RefreshFromPad(); - } - - private static void LoadSide(SignaturePadControl? pad, EstimateSignatureDto? sig) - { - if (pad is null || sig is null) return; - if (sig.Strokes is { Length: > 0 } strokes) - pad.Load(strokes); - } - - private void RefreshFromPad() - { - HasProSignature = _proPad?.Snapshot().IsEmpty == false; - HasClientSignature = _clientPad?.Snapshot().IsEmpty == false; - - ProSignatureHint = CanWritePro - ? (HasProSignature ? "Signé — dessinez pour remplacer" : "Signez ici en tant que prestataire") - : (HasProSignature ? "Signature du prestataire (lecture seule)" : "En attente de signature du prestataire"); - ClientSignatureHint = CanWriteClient - ? (HasClientSignature ? "Signé — dessinez pour remplacer" : "Signez ici en tant que client") - : (HasClientSignature ? "Signature du client (lecture seule)" : "En attente de signature du client"); - - var pad = WritablePad; - if (pad is null) return; - var snap = pad.Snapshot(); - StrokeCount = snap.StrokeCount; - PointCount = snap.PointCount; - HasSignature = !snap.IsEmpty; - } - - partial void OnIsBusyChanged(bool value) - { - ValidateCommand.NotifyCanExecuteChanged(); - RejectCommand.NotifyCanExecuteChanged(); - } - - partial void OnHasSignatureChanged(bool value) - => ValidateCommand.NotifyCanExecuteChanged(); - - private bool CanValidate => !IsBusy && HasSignature && HasCommand; - - private bool CanReject => !IsBusy && HasCommand; - - [RelayCommand(CanExecute = nameof(CanValidate))] - public async Task ValidateAsync() - { - if (_estimate.CommandId is not long queryId) - { - this.SetWarningStatus("Ce devis n'est pas lié à une demande et ne peut être validé."); - return; - } - var pad = WritablePad; - if (pad is null || pad.Snapshot().IsEmpty) - { - this.SetWarningStatus("Veuillez signer avant de valider."); - return; - } - - var snap = pad.Snapshot(); - IsBusy = true; - try - { - var body = new QueryAcceptanceRequestDto - { - Strokes = snap.Strokes, - CoordinateMax = SignaturePadData.CoordinateMax, - CapturedAtUtc = DateTime.UtcNow, - // Declare which side the caller is signing as, so the - // server stores the signature under the right author - // (Pro/Client) even when one user is both parties. - SignatureType = CanWritePro ? SignatureType.Pro : SignatureType.Client, - }; - - await _frontClient.AcceptQueryAsync(_estimate.CommandType, queryId, body) - .ConfigureAwait(true); - - this.SetInfoStatus("Devis validé."); - await GoBackAsync().ConfigureAwait(true); - } - catch (HttpRequestException ex) when (ex.StatusCode is System.Net.HttpStatusCode.Forbidden) - { - this.SetWarningStatus("Vous n'êtes pas partie à ce devis."); - } - catch (HttpRequestException ex) when (ex.StatusCode is System.Net.HttpStatusCode.BadRequest) - { - this.SetWarningStatus($"Validation refusée: {ex.Message}"); - } - catch (Exception ex) - { - this.SetErrorStatus($"Erreur: {ex.Message}"); - } - finally - { - IsBusy = false; - } - } - - [RelayCommand(CanExecute = nameof(CanReject))] - public async Task RejectAsync() - { - if (_estimate.CommandId is not long queryId) - { - this.SetWarningStatus("Ce devis n'est pas lié à une demande et ne peut être refusé."); - return; - } - - IsBusy = true; - try - { - await _frontClient.RejectQueryAsync(_estimate.CommandType, queryId) - .ConfigureAwait(true); - - this.SetInfoStatus("Devis refusé."); - await GoBackAsync().ConfigureAwait(true); - } - catch (HttpRequestException ex) when (ex.StatusCode is System.Net.HttpStatusCode.Forbidden) - { - this.SetWarningStatus("Vous n'êtes pas partie à ce devis."); - } - catch (Exception ex) - { - this.SetErrorStatus($"Erreur: {ex.Message}"); - } - finally - { - IsBusy = false; - } - } - - [RelayCommand] - public void ClearSignature() - { - WritablePad?.Clear(); - RefreshFromPad(); - } - - /// - /// Download the estimate as a LaTeX source document. The server - /// renders the Estimate_tex template to a .tex byte stream - /// (GET api/v1/front/query/{CommandId}/estimate.tex); the - /// user picks a destination via the platform save picker. - /// - [RelayCommand] - public async Task DownloadTexAsync() - { - await DownloadAsync( - () => _frontClient.GetEstimateTexAsync(_estimate.CommandId!.Value), - $"devis-{EstimateId}", "tex").ConfigureAwait(true); - } - - /// - /// Download the estimate as a compiled PDF - /// (GET api/v1/front/query/{CommandId}/estimate.pdf). - /// Requires texi2pdf on the server host; a failure is - /// surfaced as a status message. - /// - [RelayCommand] - public async Task DownloadPdfAsync() - { - await DownloadAsync( - () => _frontClient.GetEstimatePdfAsync(_estimate.CommandId!.Value), - $"devis-{EstimateId}", "pdf").ConfigureAwait(true); - } - - private async Task DownloadAsync(Func> fetch, string fileName, string ext) - { - if (_estimate.CommandId is not long) - { - this.SetWarningStatus("Ce devis n'est pas lié à une demande."); - return; - } - - IsBusy = true; - try - { - var bytes = await fetch().ConfigureAwait(true); - var saved = await FileSaveHelpers.SaveAsync(fileName, ext, bytes).ConfigureAwait(true); - this.SetInfoStatus(saved - ? $"Fichier .{ext} enregistré." - : "Téléchargement annulé."); - } - catch (Exception ex) - { - this.SetErrorStatus($"Échec du téléchargement: {ex.Message}"); - } - finally - { - IsBusy = false; - } - } - - [RelayCommand] - private async Task BackAsync() => await GoBackAsync().ConfigureAwait(true); - - private static async Task GoBackAsync() - { - var app = (App?)Application.Current; - if (app is null) - { - throw new InvalidOperationException("Application PostIt indisponible."); - } - await app.GoBackAsync().ConfigureAwait(true); - } -} \ No newline at end of file diff --git a/src/PostIt/PostIt/ViewModels/Activity/ProviderOngoingRequestsPageViewModel.cs b/src/PostIt/PostIt/ViewModels/Activity/ProviderOngoingRequestsPageViewModel.cs index 75a5bdc9a..71e979e66 100644 --- a/src/PostIt/PostIt/ViewModels/Activity/ProviderOngoingRequestsPageViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/Activity/ProviderOngoingRequestsPageViewModel.cs @@ -8,7 +8,6 @@ using System.Threading.Tasks; using Avalonia; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; -using Microsoft.Extensions.DependencyInjection; using PostIt.Helpers; using Yavsc; using Yavsc.Abstract.Workflow; @@ -154,14 +153,12 @@ public partial class ProviderOngoingRequestsPageViewModel : ViewModelBase, IActi .GetQueryAsync(SelectedQuery.BillingCode, SelectedQuery.Id) .ConfigureAwait(true); var (activity, performer, form) = BuildNavigationContext(SelectedQuery); - var frontClient = app.ServiceProvider?.GetRequiredService(); var vm = new BillingQueryDetailsPageViewModel( activity, performer, form, _billingClient, - frontClient, details, isReadOnly: false); @@ -347,13 +344,11 @@ public partial class ProviderOngoingRequestsPageViewModel : ViewModelBase, IActi => status switch { QueryStatus.InProgress => 0, - QueryStatus.ClientAccepted => 1, - QueryStatus.ProAccepted => 2, - QueryStatus.Accepted => 2, - QueryStatus.Inserted => 3, - QueryStatus.Success => 4, - QueryStatus.Rejected => 5, - QueryStatus.Failed => 6, + QueryStatus.Accepted => 1, + QueryStatus.Inserted => 2, + QueryStatus.Success => 3, + QueryStatus.Rejected => 4, + QueryStatus.Failed => 5, _ => 99, }; diff --git a/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs b/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs index a1e016e7f..726129f12 100644 --- a/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/Blogs/BlogsViewModel.cs @@ -11,7 +11,6 @@ using Yavsc.Blogspot; using Yavsc.Api.Client; using Yavsc.Abstract.Files; using PostIt.Helpers; -using AvaloniaEdit.Document; namespace PostIt.ViewModels; @@ -37,19 +36,8 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel /// Editor buffer for the post body. Same pattern as /// . - - public string DraftArticle - { - get => DraftArticleDocument.Text; - set { - OnPropertyChanging(nameof(DraftArticle)); - DraftArticleDocument = new TextDocument(value); - this.OnPropertyChanged(nameof(DraftArticle)); - } - } - [ObservableProperty] - public partial TextDocument DraftArticleDocument {get; set;} + public partial string DraftArticle { get; set; } /// Editor buffer for the post's publication state. /// Reflects the server-side IsPublished flag (the @@ -393,7 +381,6 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel WindowTitle = "PostIt"; DraftTitle = string.Empty; DraftArticle = string.Empty; - DraftIsPublished = false; IsLoaded = false; // Production path: DI injects the canonical Settings singleton @@ -477,6 +464,7 @@ public partial class BlogsViewModel : ViewModelBase, IActionStatusViewModel // enable as soon as the user has typed a non-whitespace // title, regardless of whether a post is selected. partial void OnDraftTitleChanged(string value) => SaveCommand.NotifyCanExecuteChanged(); + partial void OnDraftArticleChanged(string value) => SaveCommand.NotifyCanExecuteChanged(); private async Task RefreshPostsAsync() diff --git a/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs b/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs index 9d466b26c..ea92f1d0a 100644 --- a/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/Layout/HomePageViewModel.cs @@ -90,13 +90,7 @@ public class HomePageViewModel : ViewModelBase throw new InvalidOperationException("Client devis indisponible."); } - var frontClient = app.ServiceProvider?.GetRequiredService(); - if (frontClient is null) - { - throw new InvalidOperationException("Client front-office indisponible."); - } - - var vm = new EstimateListPageViewModel(estimateClient, perspective, frontClient); + var vm = new EstimateListPageViewModel(estimateClient, perspective); await vm.InitializeAsync(); await app.PushPageAsync(vm); } diff --git a/src/PostIt/PostIt/ViewModels/Settings/Settings.cs b/src/PostIt/PostIt/ViewModels/Settings/Settings.cs index 5576f01cd..1c2802e33 100644 --- a/src/PostIt/PostIt/ViewModels/Settings/Settings.cs +++ b/src/PostIt/PostIt/ViewModels/Settings/Settings.cs @@ -259,13 +259,25 @@ public partial class Settings : ViewModelBase return; } } - - var app = App.Current as App; - if (app!=null) - if (app.CliConfigFileSpecification!=null) + if (Environment.GetEnvironmentVariable("POSTIT_SETTINGS_JSON") is string envJson + && !string.IsNullOrWhiteSpace(envJson)) { - SettingsFileFullName = app.CliConfigFileSpecification; + Console.WriteLine("🔎 Loading settings from POSTIT_SETTINGS_JSON environment variable."); + FileInfo configByEnvFileInfo = new FileInfo(envJson); + if (!configByEnvFileInfo.Exists) + { + throw new Exception($"🩎 Settings file not found at {configByEnvFileInfo.FullName}"); + } + string json = File.ReadAllText(configByEnvFileInfo.FullName); + ApplyJson(json, "POSTIT_SETTINGS_JSON"); + SettingsFileFullName = configByEnvFileInfo.FullName; + Loaded = true; + return; } + string configDir = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), + "PostIt" + ); if (SettingsFileFullName is not null) { @@ -275,18 +287,10 @@ public partial class Settings : ViewModelBase else if (Environment.GetEnvironmentVariable("POSTIT_SETTINGS_JSON") is string envPath && !string.IsNullOrWhiteSpace(envPath)) { - FileInfo configByEnvFileInfo = new FileInfo(envPath); - if (!configByEnvFileInfo.Exists) - { - throw new Exception($"🩎 Settings file not found at {configByEnvFileInfo.FullName}"); - } - SettingsFileFullName = configByEnvFileInfo.FullName; + SettingsFileFullName = envPath; } else { - string configDir = Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), - "PostIt"); SettingsFileFullName = Path.Combine(configDir, "postit-settings.json"); } @@ -532,7 +536,6 @@ public partial class Settings : ViewModelBase /// partial void OnIsDirtyChanged(bool value) => SaveCommand.NotifyCanExecuteChanged(); - public override bool CanNavigateNext { get => false; protected set => throw new System.NotImplementedException(); } public override bool CanNavigatePrevious { get => true; protected set => throw new System.NotImplementedException(); } } diff --git a/src/PostIt/PostIt/ViewModels/Signature/SignaturePageViewModel.cs b/src/PostIt/PostIt/ViewModels/Signature/SignaturePageViewModel.cs index 1e7df9c60..23ba70cf9 100644 --- a/src/PostIt/PostIt/ViewModels/Signature/SignaturePageViewModel.cs +++ b/src/PostIt/PostIt/ViewModels/Signature/SignaturePageViewModel.cs @@ -85,12 +85,10 @@ public partial class SignaturePageViewModel : ViewModelBase, IActionStatusViewMo } /// - /// Bind the inner of the page's - /// to this VM. Called from the - /// page's code-behind once the view is in the visual tree (so the - /// control's is - /// wired). Attaching to the control directly (rather than the - /// view) keeps the same contract the headless tests use. + /// Bind a freshly-constructed (or re-templated) control to this + /// VM. Called from the view's code-behind once the control has + /// been added to the visual tree and its template applied (so + /// is wired). /// public void Attach(SignaturePadControl control) { diff --git a/src/PostIt/PostIt/Views/Activity/BillingQueryDetailsPage.axaml b/src/PostIt/PostIt/Views/Activity/BillingQueryDetailsPage.axaml index 904f17350..82231ad83 100644 --- a/src/PostIt/PostIt/Views/Activity/BillingQueryDetailsPage.axaml +++ b/src/PostIt/PostIt/Views/Activity/BillingQueryDetailsPage.axaml @@ -14,7 +14,7 @@ - + public enum QueryStatus: int { - Inserted = 0, - Rejected = 1, - Accepted = 2, - InProgress = 3, + Inserted, + Rejected, + Accepted, + InProgress, // final states - Failed = 4, - Success = 5, - // party-specific acceptance (see summary) - ProAccepted = 6, - ClientAccepted = 7, + Failed, + Success } -} \ No newline at end of file +} diff --git a/src/Yavsc.Abstract/Yavsc.Abstract.csproj b/src/Yavsc.Abstract/Yavsc.Abstract.csproj index 0a9e481bf..4df6c53c2 100644 --- a/src/Yavsc.Abstract/Yavsc.Abstract.csproj +++ b/src/Yavsc.Abstract/Yavsc.Abstract.csproj @@ -11,7 +11,7 @@ latest 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1 \ No newline at end of file diff --git a/src/Yavsc.Api.Client/Dtos/EstimateDtos.cs b/src/Yavsc.Api.Client/Dtos/EstimateDtos.cs index 3e7a15052..43c09d94a 100644 --- a/src/Yavsc.Api.Client/Dtos/EstimateDtos.cs +++ b/src/Yavsc.Api.Client/Dtos/EstimateDtos.cs @@ -39,39 +39,6 @@ public sealed class EstimateDto public string CommandType { get; set; } = string.Empty; public DateTime ProviderValidationDate { get; set; } public DateTime ClientValidationDate { get; set; } - - /// - /// The provider's signature on this estimate, or null when the - /// provider has not signed. Carries the PostIt wire-format - /// so the pad can - /// repaint a previously drawn signature on reopen without a second - /// round-trip. - /// - public EstimateSignatureDto? SignaturePro { get; set; } - - /// - /// The client's signature on this estimate, or null when the - /// client has not signed. - /// - public EstimateSignatureDto? SignatureClient { get; set; } -} - -/// -/// One stored signature on an estimate, mirroring the JSON shape of -/// the server-side Yavsc.Models.Billing.Signature entity. The -/// are the same PostIt wire-format payload that -/// was captured and are loaded back into the pad unchanged. The side -/// (Pro/Client) is implicit in which property of -/// carries this DTO, so no Type -/// field is needed. -/// -public sealed class EstimateSignatureDto -{ - public long Id { get; set; } - public long EstimateId { get; set; } - public int CoordinateMax { get; set; } - public DateTime CapturedAtUtc { get; set; } - public int[]? Strokes { get; set; } } /// diff --git a/src/Yavsc.Api.Client/Dtos/QueryAcceptanceDto.cs b/src/Yavsc.Api.Client/Dtos/QueryAcceptanceDto.cs deleted file mode 100644 index dffbbf3ad..000000000 --- a/src/Yavsc.Api.Client/Dtos/QueryAcceptanceDto.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using Yavsc.Models.Billing; - -namespace Yavsc.Api.Client; - -/// -/// Body of POST api/v1/front/query/accept and -/// .../query/reject, mirroring the server-side -/// Yavsc.ViewModels.FrontOffice.QueryAcceptanceRequest. -/// is optional: when non-empty on accept it is -/// the PostIt wire-format signature captured with the acceptance; null -/// or empty means no signature (the only valid value on reject). -/// -public sealed class QueryAcceptanceRequestDto -{ - public int[]? Strokes { get; set; } - - public int CoordinateMax { get; set; } = 10_000; - - public DateTime? CapturedAtUtc { get; set; } - - /// - /// Which signature is being submitted: - /// (the provider) or - /// . The PostIt client sets this - /// from the perspective the page was opened in (provider signs Pro, - /// client signs Client), so the server stores the signature under - /// the right author even when one user is both parties. Null lets - /// the server infer the role from the caller's identity. - /// - public SignatureType? SignatureType { get; set; } -} - -/// -/// Response of POST .../query/accept / .../query/reject -/// (Ok(new { queryId, status, signature = ... })). The -/// ref is non-null only when a signature was -/// staged on accept. -/// -public sealed class QueryAcceptanceResponseDto -{ - public long QueryId { get; set; } - - public string? Status { get; set; } - - public QuerySignatureRefDto? Signature { get; set; } -} - -public sealed class QuerySignatureRefDto -{ - public long Id { get; set; } - - public long EstimateId { get; set; } - - public string? Type { get; set; } -} \ No newline at end of file diff --git a/src/Yavsc.Api.Client/FrontOfficeApiClient.cs b/src/Yavsc.Api.Client/FrontOfficeApiClient.cs deleted file mode 100644 index 84c7b8a0c..000000000 --- a/src/Yavsc.Api.Client/FrontOfficeApiClient.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - -namespace Yavsc.Api.Client; - -/// -/// HTTP client for the front-office query routes -/// (api/v1/front/query/accept / .../reject, served by -/// ). Same -/// DTO↔path mapper shape as : transport -/// (base URL, JSON, Bearer auth, silent refresh on 401) is delegated -/// to . -/// -/// The server resolves the caller's role (provider vs client vs admin) -/// from the authenticated user compared to the query's -/// PerformerId/ClientId; the client does not declare a -/// role. is the estimate's -/// and -/// is its . -/// -public sealed class FrontOfficeApiClient -{ - private const string PathPrefix = "front"; - - private readonly IYavscApiClient _api; - private readonly Func _businessBaseAddress; - - public FrontOfficeApiClient(IYavscApiClient api, string businessBaseAddress) - : this(api, () => businessBaseAddress) - { - } - - public FrontOfficeApiClient(IYavscApiClient api, Func businessBaseAddress) - { - _api = api ?? throw new ArgumentNullException(nameof(api)); - _businessBaseAddress = businessBaseAddress ?? throw new ArgumentNullException(nameof(businessBaseAddress)); - _ = ResolveBusinessBaseAddress(); - } - - /// - /// Accept (validate) the query under - /// billing code . When - /// carries a non-empty signature, the - /// server stages it on the query's linked estimate and stamps the - /// matching validation date. - /// - public async Task AcceptQueryAsync( - string billingCode, - long queryId, - QueryAcceptanceRequestDto body, - CancellationToken ct = default) - { - if (string.IsNullOrWhiteSpace(billingCode)) - throw new ArgumentException("billingCode is required.", nameof(billingCode)); - if (queryId <= 0) - throw new ArgumentOutOfRangeException(nameof(queryId)); - if (body is null) - throw new ArgumentNullException(nameof(body)); - - var path = $"{PathPrefix}/query/accept?billingCode={Uri.EscapeDataString(billingCode)}&queryId={queryId}"; - var response = await _api.CallAsync( - HttpMethod.Post, - Absolute(path), - body: body, - ct: ct).ConfigureAwait(false); - - return response ?? new QueryAcceptanceResponseDto { QueryId = queryId }; - } - - /// - /// Reject the query under billing code - /// . A signature is not meaningful on - /// a rejection; the server refuses one with 400, so this sends no - /// body. - /// - public async Task RejectQueryAsync( - string billingCode, - long queryId, - CancellationToken ct = default) - { - if (string.IsNullOrWhiteSpace(billingCode)) - throw new ArgumentException("billingCode is required.", nameof(billingCode)); - if (queryId <= 0) - throw new ArgumentOutOfRangeException(nameof(queryId)); - - var path = $"{PathPrefix}/query/reject?billingCode={Uri.EscapeDataString(billingCode)}&queryId={queryId}"; - var response = await _api.CallAsync( - HttpMethod.Post, - Absolute(path), - body: null, - ct: ct).ConfigureAwait(false); - - return response ?? new QueryAcceptanceResponseDto { QueryId = queryId }; - } - - /// - /// Download the estimate linked to query - /// as a LaTeX (.tex) source document - /// (GET api/v1/front/query/{queryId}/estimate.tex). Returns - /// the raw TeX bytes (UTF-8 text). - /// - public Task GetEstimateTexAsync(long queryId, CancellationToken ct = default) - { - if (queryId <= 0) throw new ArgumentOutOfRangeException(nameof(queryId)); - var path = $"{PathPrefix}/query/{queryId}/estimate.tex"; - return _api.DownloadAsync(HttpMethod.Get, Absolute(path), ct); - } - - /// - /// Download the estimate linked to query - /// as a compiled PDF document - /// (GET api/v1/front/query/{queryId}/estimate.pdf). Returns - /// the raw PDF bytes. Requires texi2pdf on the server host; - /// a non-2xx response throws . - /// - public Task GetEstimatePdfAsync(long queryId, CancellationToken ct = default) - { - if (queryId <= 0) throw new ArgumentOutOfRangeException(nameof(queryId)); - var path = $"{PathPrefix}/query/{queryId}/estimate.pdf"; - return _api.DownloadAsync(HttpMethod.Get, Absolute(path), ct); - } - - private string Absolute(string relativePath) => new Uri(ResolveBusinessBaseAddress(), relativePath).ToString(); - - private Uri ResolveBusinessBaseAddress() - { - var raw = _businessBaseAddress(); - if (string.IsNullOrWhiteSpace(raw)) - throw new InvalidOperationException("Business base address is required."); - - return new Uri(raw, UriKind.Absolute); - } -} \ No newline at end of file diff --git a/src/Yavsc.Api.Client/IYavscApiClient.cs b/src/Yavsc.Api.Client/IYavscApiClient.cs index e2dd56acd..1d470b682 100644 --- a/src/Yavsc.Api.Client/IYavscApiClient.cs +++ b/src/Yavsc.Api.Client/IYavscApiClient.cs @@ -73,23 +73,4 @@ public interface IYavscApiClient : IAsyncDisposable string path, Func contentFactory, CancellationToken ct = default); - - /// - /// Download a binary resource (PDF, TeX, image, …). Unlike - /// , - /// which JSON-decodes the response, this returns the raw response - /// bytes. The same Bearer-token attach + 401-refresh-retry-once - /// contract applies. - /// - /// - /// A default interface implementation is provided so that test - /// fakes that do not exercise binary download still compile without - /// declaring the member; the real - /// overrides it with the authenticated transport. - /// - Task DownloadAsync( - HttpMethod method, - string path, - CancellationToken ct = default) - => Task.FromResult(Array.Empty()); } diff --git a/src/Yavsc.Api.Client/Yavsc.Api.Client.csproj b/src/Yavsc.Api.Client/Yavsc.Api.Client.csproj index c53f9e888..f0cdb55c9 100644 --- a/src/Yavsc.Api.Client/Yavsc.Api.Client.csproj +++ b/src/Yavsc.Api.Client/Yavsc.Api.Client.csproj @@ -17,8 +17,8 @@ true 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1 diff --git a/src/Yavsc.Api.Test/Directory.Packages.props b/src/Yavsc.Api.Test/Directory.Packages.props new file mode 100644 index 000000000..c2edf9eae --- /dev/null +++ b/src/Yavsc.Api.Test/Directory.Packages.props @@ -0,0 +1,7 @@ + + + + diff --git a/src/Yavsc.Api.Test/EstimateApiControllerTests.cs b/src/Yavsc.Api.Test/EstimateApiControllerTests.cs index 568175610..cb945f51a 100644 --- a/src/Yavsc.Api.Test/EstimateApiControllerTests.cs +++ b/src/Yavsc.Api.Test/EstimateApiControllerTests.cs @@ -209,12 +209,9 @@ public sealed class EstimateApiControllerTests : IClassFixture /// Seeds an estimate owned by alice (the provider) for the given - /// client, optionally already validated by the client. By default the - /// estimate is also provider-validated (i.e. the provider has submitted - /// it); pass = false to seed a - /// freshly inserted request the provider hasn't worked on yet. + /// client, optionally already validated by the client. /// - private long SeedEstimate(string clientId, bool clientValidated, bool providerValidated = true) + private long SeedEstimate(string clientId, bool clientValidated) { using var scope = _fixture.Services.CreateScope(); var db = scope.ServiceProvider.GetRequiredService(); @@ -232,7 +229,6 @@ public sealed class EstimateApiControllerTests : IClassFixture.pdf into DestDir. The -// TeX source mirrors what the Estimate_tex template emits for a devis -// signed by both parties: two side-by-side minipages, each with a -// tikzpicture bounded by \useasboundingbox and the strokes drawn by -// SignatureToTikz. -// -// The PDF test requires the system `lualatex` (and the TeX Live packages -// listed in the README runtime-deps section) on the host running the -// tests. On hosts without lualatex it skips cleanly rather than failing. -public sealed class EstimatePdfGenerationTests -{ - // A provider signature: a downstroke, a cross, and a tap (dot). - // PostIt wire format — [k, x0,y0, x1,y1, …] per stroke, coords in - // [0, 10000], y-down. - private static readonly int[] ProStrokes = - { - 4, 2000, 8000, 2000, 5000, 2000, 2000, 2100, 1900, - 3, 2000, 5000, 3500, 4500, 3500, 5100, - 1, 4000, 3000, - }; - - // A client signature: a wavy stroke and a shorter one. - private static readonly int[] CliStrokes = - { - 5, 1000, 7000, 1500, 4000, 2200, 3000, 3000, 2500, 4000, 1500, - 2, 3000, 6000, 4200, 5500, - }; - - [Fact] - public void SignatureToTikz_RendersBothSignaturesAsVectorDraws() - { - var pro = MakeSig(SignatureType.Pro, ProStrokes); - var cli = MakeSig(SignatureType.Client, CliStrokes); - - var proTex = pro.SignatureToTikz(5, 2.5).ToString(); - var cliTex = cli.SignatureToTikz(5, 2.5).ToString(); - - Assert.Contains("\\draw[line cap=round, line join=round, line width=1.5pt]", proTex); - Assert.Contains("\\fill", proTex); // the single-point tap → dot - Assert.Contains("(1.000,0.500)", proTex); // first point (2000,8000), y flipped - Assert.Contains("\\draw[line cap=round, line join=round, line width=1.5pt]", cliTex); - Assert.Contains("--", cliTex); - } - - [Fact] - public void SignatureToTikz_EmptySignatureIsEmpty() - { - var empty = MakeSig(SignatureType.Pro, Array.Empty()); - Assert.Equal("", empty.SignatureToTikz(5, 2.5).ToString()); - } - - [Fact] - public void GenerateEstimatePdf_WithBothSignatures_ProducesPdfInDestDir() - { - if (!IsLualatexAvailable()) - throw Xunit.Sdk.SkipException.ForSkip( - "lualatex is not installed on this host; skipping PDF generation test. " + - "Install the TeX Live runtime deps documented in the README."); - - var destDir = Path.Combine(Path.GetTempPath(), - "yavsc-estimate-pdf-" + Guid.NewGuid().ToString("N")); - Directory.CreateDirectory(destDir); - - try - { - var pro = MakeSig(SignatureType.Pro, ProStrokes); - var cli = MakeSig(SignatureType.Client, CliStrokes); - var baseName = "estimate-test-both-signed"; - - var model = new PdfGenerationViewModel - { - TeXSource = BuildEstimateTeX(pro, cli), - BaseFileName = baseName, - DestDir = destDir, - }; - - var fo = model.GenerateEstimatePdf(); - - // The PDF must exist, at the expected path, and be non-empty. - var expectedPath = Path.Combine(destDir, baseName + ".pdf"); - Assert.True(fo is not null && fo.Exists, - "GenerateEstimatePdf returned a non-existent file. Error: " - + model.GenerationErrorMessage?.Value); - Assert.Equal(expectedPath, fo.FullName); - Assert.True(fo.Length > 0, "generated PDF is empty"); - - // Only the PDF should persist in DestDir — aux/log artifacts - // are cleaned up by GenerateEstimatePdf on success. - var leftovers = Directory.GetFiles(destDir) - .Where(p => !Path.GetFileName(p).Equals(baseName + ".pdf", StringComparison.Ordinal)) - .ToArray(); - Assert.Empty(leftovers); - } - finally - { - if (Directory.Exists(destDir)) - Directory.Delete(destDir, recursive: true); - } - } - - private static Signature MakeSig(SignatureType type, int[] strokes) - => new() - { - Type = type, - CoordinateMax = 10_000, - Strokes = strokes, - CapturedAtUtc = new DateTime(2026, 9, 22, 0, 0, 0, DateTimeKind.Utc), - }; - - // Builds the LaTeX source the Estimate_tex template would emit for a - // devis signed by both parties: same preamble (incl. tikz + bera) and - // the two-minipage signature foot, with the strokes drawn by - // SignatureToTikz. - private static string BuildEstimateTeX(Signature pro, Signature cli) - { - var fr = CultureInfo.CreateSpecificCulture("fr-FR"); - var date = new DateTime(2026, 9, 22).ToString("dddd dd MMMM yyyy", fr); - var proTikz = pro.SignatureToTikz(5, 2.5).ToString(); - var cliTikz = cli.SignatureToTikz(5, 2.5).ToString(); - - return $@"\documentclass[french,11pt]{{article}} -\usepackage{{eurosym}} -\usepackage{{babel}} -\usepackage[T1]{{fontenc}} -\usepackage[utf8]{{inputenc}} -\usepackage[a4paper]{{geometry}} -\usepackage{{units}} -\usepackage{{bera}} -\usepackage{{graphicx}} -\usepackage{{fancyhdr}} -\usepackage{{fp}} -\usepackage{{tikz}} -\geometry{{verbose,tmargin=4em,bmargin=8em,lmargin=6em,rmargin=6em}} -\setlength{{\parindent}}{{0pt}} -\begin{{document}} -Devis n°1 --- test signe par les deux parties - -\vspace{{2em}} -\noindent -\begin{{minipage}}[t]{{0.45\textwidth}} - \textbf{{Signature du fournisseur}}\\ -\small Valide le {date}\\ -\begin{{tikzpicture}} - \useasboundingbox (0,0) rectangle (5,2.5); - {proTikz} -\end{{tikzpicture}} -\end{{minipage}}\hfill -\begin{{minipage}}[t]{{0.45\textwidth}} - \textbf{{Signature du client}}\\ -\small Valide le {date}\\ -\begin{{tikzpicture}} - \useasboundingbox (0,0) rectangle (5,2.5); - {cliTikz} -\end{{tikzpicture}} -\end{{minipage}} -\end{{document}} -"; - } - - private static bool IsLualatexAvailable() - { - try - { - using var p = Process.Start(new ProcessStartInfo("lualatex", "--version") - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - }); - if (p is null) return false; - return p.WaitForExit(5000) && p.ExitCode == 0; - } - catch - { - return false; - } - } -} \ No newline at end of file diff --git a/src/Yavsc.Api.Test/FrontOfficeApiControllerTests.cs b/src/Yavsc.Api.Test/FrontOfficeApiControllerTests.cs index b2a8fdbee..97ed2e61c 100644 --- a/src/Yavsc.Api.Test/FrontOfficeApiControllerTests.cs +++ b/src/Yavsc.Api.Test/FrontOfficeApiControllerTests.cs @@ -1,14 +1,9 @@ -using System.Linq; using System.Net; using System.Net.Http.Headers; -using System.Net.Http.Json; -using System.Text.Json; using Microsoft.Extensions.DependencyInjection; using Yavsc.Api.Test.Fixtures; using Yavsc.Helpers; using Yavsc.Models; -using Yavsc.Models.Billing; -using Yavsc.Models.Workflow; using Yavsc.Tests.Shared; namespace Yavsc.Api.Test; @@ -39,95 +34,21 @@ public sealed class FrontOfficeApiControllerTests : IClassFixture JsonBody(new { }); - - private static HttpContent WithSignature(SignatureType? type = null) => - type is null - ? JsonBody(new { strokes = SampleStrokes, coordinateMax = 10_000 }) - : JsonBody(new { strokes = SampleStrokes, coordinateMax = 10_000, signatureType = type }); - - private static JsonContent JsonBody(object value) => - JsonContent.Create(value, mediaType: new MediaTypeHeaderValue("application/json")); - - /// - /// Seeds an Rdv query and, when is - /// true, an estimate linked to it via CommandId. The provider is - /// (default alice) and the client is - /// (default bob) — pass the same value - /// for both to model a user who is both parties. Returns the query - /// id and (when seeded) the estimate id. - /// - private (long queryId, long estimateId) SeedQuery( - bool withEstimate, - string performer = "alice", - string client = "bob") - { - WorkflowHelpers.ConfigureBillingService(); - _fixture.ResetAndSeedActivityGraph(); - - using var scope = _fixture.Services.CreateScope(); - var db = scope.ServiceProvider.GetRequiredService(); - - var location = db.Locations.Single(l => l.Address == "1 rue du Test"); - - var query = new RdvQuery - { - ActivityCode = "dev", - ClientId = client, - PerformerId = performer, - Consent = true, - UserCreated = performer, - UserModified = performer, - DateCreated = DateTime.UtcNow, - DateModified = DateTime.UtcNow, - EventDate = DateTime.UtcNow.AddDays(1), - Location = location, - Reason = "Initial rendez-vous", - Status = Yavsc.QueryStatus.Inserted, - }; - db.RdvQueries.Add(query); - db.SaveChanges(); - - long estimateId = 0; - if (withEstimate) - { - var estimate = new Estimate - { - CommandId = query.Id, - ClientId = client, - OwnerId = performer, - CommandType = BillingCodes.Rdv, - Title = "Devis prestation", - Description = "Devis de test", - AttachedFiles = new List(), - AttachedGraphics = new List(), - Bill = new List - { - new() { Name = "Prestation", Description = "Prestation de base", Count = 1, UnitaryCost = 120m, Currency = "EUR" }, - }, - }; - db.Estimates.Add(estimate); - db.SaveChanges(); - estimateId = estimate.Id; - } - - return (query.Id, estimateId); - } - [Fact] public async Task Front_accept_query_updates_status_without_server_error() { - var (queryId, _) = SeedQuery(withEstimate: false); + WorkflowHelpers.ConfigureBillingService(); + _fixture.ResetAndSeedRdvQueryGraph(); + + long queryId; + using (var scope = _fixture.Services.CreateScope()) + { + var db = scope.ServiceProvider.GetRequiredService(); + queryId = db.RdvQueries.Select(q => q.Id).Single(); + } using var http = NewClient(); - var response = await http.PostAsync( - $"/api/v1/front/query/accept?billingCode=Rdv&queryId={queryId}", - NoSignature(), - TestContext.Current.CancellationToken); + var response = await http.PostAsync($"/api/v1/front/query/accept?billingCode=Rdv&queryId={queryId}", content: null, TestContext.Current.CancellationToken); var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); Assert.True(response.StatusCode == HttpStatusCode.OK, $"Unexpected status {(int)response.StatusCode} ({response.StatusCode}): {body}"); @@ -136,207 +57,6 @@ public sealed class FrontOfficeApiControllerTests : IClassFixture(); var updated = assertDb.RdvQueries.Single(q => q.Id == queryId); - // Default caller is "alice" (the provider) → ProAccepted. - Assert.Equal(QueryStatus.ProAccepted, updated.Status); + Assert.Equal(QueryStatus.Accepted, updated.Status); } - - [Fact] - public async Task Provider_accept_with_signature_sets_ProValidationDate_and_Pro_signature() - { - var (queryId, estimateId) = SeedQuery(withEstimate: true); - - using var http = NewClient(subject: "alice"); - var response = await http.PostAsync( - $"/api/v1/front/query/accept?billingCode=Rdv&queryId={queryId}", - WithSignature(), - TestContext.Current.CancellationToken); - var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); - Assert.True(response.StatusCode == HttpStatusCode.OK, $"Unexpected status {(int)response.StatusCode} ({response.StatusCode}): {body}"); - - using var assertScope = _fixture.Services.CreateScope(); - var assertDb = assertScope.ServiceProvider.GetRequiredService(); - - Assert.Equal(QueryStatus.ProAccepted, assertDb.RdvQueries.Single(q => q.Id == queryId).Status); - - var estimate = assertDb.Estimates.Single(e => e.Id == estimateId); - Assert.True(estimate.ProviderValidationDate != default); - Assert.True(estimate.ClientValidationDate == default); - - var signature = assertDb.Signatures.Single(s => s.EstimateId == estimateId); - Assert.Equal(SignatureType.Pro, signature.Type); - Assert.Equal("alice", signature.SignerId); - } - - [Fact] - public async Task Client_accept_with_signature_sets_ClientValidationDate_and_Client_signature() - { - var (queryId, estimateId) = SeedQuery(withEstimate: true); - - using var http = NewClient(subject: "bob"); - var response = await http.PostAsync( - $"/api/v1/front/query/accept?billingCode=Rdv&queryId={queryId}", - WithSignature(), - TestContext.Current.CancellationToken); - var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); - Assert.True(response.StatusCode == HttpStatusCode.OK, $"Unexpected status {(int)response.StatusCode} ({response.StatusCode}): {body}"); - - using var assertScope = _fixture.Services.CreateScope(); - var assertDb = assertScope.ServiceProvider.GetRequiredService(); - - Assert.Equal(QueryStatus.ClientAccepted, assertDb.RdvQueries.Single(q => q.Id == queryId).Status); - - var estimate = assertDb.Estimates.Single(e => e.Id == estimateId); - Assert.True(estimate.ClientValidationDate != default); - Assert.True(estimate.ProviderValidationDate == default); - - var signature = assertDb.Signatures.Single(s => s.EstimateId == estimateId); - Assert.Equal(SignatureType.Client, signature.Type); - Assert.Equal("bob", signature.SignerId); - } - - [Fact] - public async Task Reject_sets_status_Rejected() - { - var (queryId, _) = SeedQuery(withEstimate: false); - - using var http = NewClient(subject: "alice"); - var response = await http.PostAsync( - $"/api/v1/front/query/reject?billingCode=Rdv&queryId={queryId}", - NoSignature(), - TestContext.Current.CancellationToken); - var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); - Assert.True(response.StatusCode == HttpStatusCode.OK, $"Unexpected status {(int)response.StatusCode} ({response.StatusCode}): {body}"); - - using var assertScope = _fixture.Services.CreateScope(); - var assertDb = assertScope.ServiceProvider.GetRequiredService(); - Assert.Equal(QueryStatus.Rejected, assertDb.RdvQueries.Single(q => q.Id == queryId).Status); - } - - [Fact] - public async Task Non_party_actor_is_forbidden() - { - var (queryId, _) = SeedQuery(withEstimate: false); - - using var http = NewClient(subject: "carol"); - var response = await http.PostAsync( - $"/api/v1/front/query/accept?billingCode=Rdv&queryId={queryId}", - NoSignature(), - TestContext.Current.CancellationToken); - - Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); - } - - [Fact] - public async Task Accept_with_signature_but_no_estimate_returns_400() - { - var (queryId, _) = SeedQuery(withEstimate: false); - - using var http = NewClient(subject: "alice"); - var response = await http.PostAsync( - $"/api/v1/front/query/accept?billingCode=Rdv&queryId={queryId}", - WithSignature(), - TestContext.Current.CancellationToken); - - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - } - - [Fact] - public async Task Reject_with_signature_returns_400() - { - var (queryId, _) = SeedQuery(withEstimate: true); - - using var http = NewClient(subject: "alice"); - var response = await http.PostAsync( - $"/api/v1/front/query/reject?billingCode=Rdv&queryId={queryId}", - WithSignature(), - TestContext.Current.CancellationToken); - - Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - } - - [Fact] - public async Task Estimate_payload_carries_existing_signature_strokes() - { - var (queryId, estimateId) = SeedQuery(withEstimate: true); - - // Provider accepts with a signature. - using (var pro = NewClient(subject: "alice")) - { - var r = await pro.PostAsync( - $"/api/v1/front/query/accept?billingCode=Rdv&queryId={queryId}", - WithSignature(), - TestContext.Current.CancellationToken); - Assert.True(r.IsSuccessStatusCode, await r.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); - } - - // The estimate read endpoint carries the signature strokes on - // the estimate payload, so PostIt can repaint them on reopen. - using var reader = NewClient(subject: "alice"); - var response = await reader.GetAsync( - $"/api/v1/estimate/{estimateId}", - TestContext.Current.CancellationToken); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - - var body = await response.Content.ReadFromJsonAsync(TestContext.Current.CancellationToken); - - // Signatures ride on the estimate payload as two dedicated - // slots, not a list: the provider signed here, so signaturePro - // is populated and signatureClient is absent/null. - var proSig = body.GetProperty("signaturePro"); - Assert.Equal(JsonValueKind.Object, proSig.ValueKind); - Assert.Equal(JsonValueKind.Array, proSig.GetProperty("strokes").ValueKind); - Assert.True(body.TryGetProperty("signatureClient", out var cli) - && cli.ValueKind == JsonValueKind.Null, - "signatureClient should be null when only the provider has signed"); - } - - // When one user is both the provider and the client of a query, the - // server cannot infer which side they are signing as from identity - // alone (it would always pick the provider). The caller declares the - // side (signatureType=1 here = Client); the server honors it because - // the caller is that party, and stores a Client signature. - [Fact] - public async Task Accept_declared_client_side_stores_client_signature_when_user_is_both_parties() - { - var (queryId, estimateId) = SeedQuery(withEstimate: true, performer: "alice", client: "alice"); - - using var http = NewClient(subject: "alice"); - var response = await http.PostAsync( - $"/api/v1/front/query/accept?billingCode=Rdv&queryId={queryId}", - WithSignature(SignatureType.Client), // signing as the client - TestContext.Current.CancellationToken); - var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); - Assert.True(response.StatusCode == HttpStatusCode.OK, $"Unexpected status {(int)response.StatusCode} ({response.StatusCode}): {body}"); - - using var assertScope = _fixture.Services.CreateScope(); - var assertDb = assertScope.ServiceProvider.GetRequiredService(); - - Assert.Equal(QueryStatus.ClientAccepted, assertDb.RdvQueries.Single(q => q.Id == queryId).Status); - - var estimate = assertDb.Estimates.Single(e => e.Id == estimateId); - Assert.True(estimate.ClientValidationDate != default); - Assert.True(estimate.ProviderValidationDate == default); - - var signature = assertDb.Signatures.Single(s => s.EstimateId == estimateId); - Assert.Equal(SignatureType.Client, signature.Type); - Assert.Equal("alice", signature.SignerId); - } - - // A provider may not sign as the client (and vice versa): declaring - // the other party's side is refused even though the caller is a - // party to the query. Write is authorized only for the signature's - // author. - [Fact] - public async Task Accept_declaring_other_party_side_is_forbidden() - { - var (queryId, _) = SeedQuery(withEstimate: true); // provider alice, client bob - - using var http = NewClient(subject: "alice"); // alice is the provider - var response = await http.PostAsync( - $"/api/v1/front/query/accept?billingCode=Rdv&queryId={queryId}", - WithSignature(SignatureType.Client), // alice tries to sign as the client - TestContext.Current.CancellationToken); - - Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode); - } -} \ No newline at end of file +} diff --git a/src/Yavsc.Api.Test/Yavsc.Api.Test.csproj b/src/Yavsc.Api.Test/Yavsc.Api.Test.csproj index 21cbdc32c..ebe96dfa8 100644 --- a/src/Yavsc.Api.Test/Yavsc.Api.Test.csproj +++ b/src/Yavsc.Api.Test/Yavsc.Api.Test.csproj @@ -8,8 +8,8 @@ true 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1 @@ -19,6 +19,7 @@ + diff --git a/src/Yavsc.Api/Controllers/Business/BillingController.cs b/src/Yavsc.Api/Controllers/Business/BillingController.cs index aa195712d..ba099e7c7 100644 --- a/src/Yavsc.Api/Controllers/Business/BillingController.cs +++ b/src/Yavsc.Api/Controllers/Business/BillingController.cs @@ -157,8 +157,6 @@ namespace Yavsc.ApiControllers .Where(q => allowedActivityCodes.Contains(q.ActivityCode)) .Where(q => q.Status == QueryStatus.Inserted || q.Status == QueryStatus.Accepted - || q.Status == QueryStatus.ProAccepted - || q.Status == QueryStatus.ClientAccepted || q.Status == QueryStatus.InProgress) .Cast() .ToList() @@ -171,8 +169,6 @@ namespace Yavsc.ApiControllers .Where(q => allowedActivityCodes.Contains(q.ActivityCode)) .Where(q => q.Status == QueryStatus.Inserted || q.Status == QueryStatus.Accepted - || q.Status == QueryStatus.ProAccepted - || q.Status == QueryStatus.ClientAccepted || q.Status == QueryStatus.InProgress) .Cast() .ToList() @@ -185,8 +181,6 @@ namespace Yavsc.ApiControllers .Where(q => allowedActivityCodes.Contains(q.ActivityCode)) .Where(q => q.Status == QueryStatus.Inserted || q.Status == QueryStatus.Accepted - || q.Status == QueryStatus.ProAccepted - || q.Status == QueryStatus.ClientAccepted || q.Status == QueryStatus.InProgress) .Cast() .ToList() @@ -335,9 +329,9 @@ namespace Yavsc.ApiControllers return new ChallengeResult(); } - var filename = AbstractFileSystemHelpers.SignFileNameFormat("cli", billingCode, id); + var filename = AbstractFileSystemHelpers.SignFileNameFormat("pro", billingCode, id); FileInfo fi = new FileInfo(Path.Combine(siteSettings.Bills, filename)); - if (!fi.Exists) return NotFound(new { Error = "Client signature not found" }); + if (!fi.Exists) return NotFound(new { Error = "Professional signature not found" }); return File(fi.OpenRead(), "application/x-pdf", filename); } @@ -407,12 +401,79 @@ namespace Yavsc.ApiControllers Strokes = body.Strokes, }; - // The Signature row is staged by the shared persister; we - // SaveChanges here so the signature write is one - // transaction. The Strokes column is the source of truth — - // there is no on-disk copy to roll back on failure. - var signature = await EstimateSignaturePersister.StageAsync( - dbContext, id, type, userId, payload, token); + // Disk write first: a disk failure shouldn't leave + // a Signature row pointing at a file that doesn't + // exist. The file helper throws on filesystem + // problems and propagates here. + FileReceivedInfo fi; + try + { + fi = await User.ReceiveEstimateSignatureAsync(id, type, payload, + siteSettings, token); + } + catch (Exception ex) + { + logger.LogError(ex, "estimate {Id}: signature file write failed", id); + return BadRequest(new { Error = "file write failed", Detail = ex.Message }); + } + + // Find-or-add: the (EstimateId, Type) pair is + // unique, so a second POST for the same side of the + // estimate replaces the previous signature. EF + // translates this into a single UPDATE when the + // row exists and an INSERT otherwise; the unique + // index in ApplicationDbContext is the + // database-level guarantee that the contract + // holds if two requests race. + var signature = await dbContext.Signatures + .FirstOrDefaultAsync(s => s.EstimateId == id && s.Type == type, token); + + if (signature is null) + { + signature = new Signature + { + EstimateId = id, + SignerId = userId, + Type = type, + }; + dbContext.Signatures.Add(signature); + } + else + { + // Roll the signer's quota back by the size of + // the file we're about to orphan: the old + // FilePath is no longer referenced once we + // overwrite FilePath below. + try + { + var orphan = new FileInfo(signature.FilePath); + if (orphan.Exists) + { + var signerForOrphan = await dbContext.Users + .FirstOrDefaultAsync(u => u.Id == userId, token); + if (signerForOrphan is not null) + signerForOrphan.DiskUsage = + Math.Max(0, signerForOrphan.DiskUsage - orphan.Length); + } + } + catch { /* best effort — the file is being replaced anyway */ } + } + + signature.SignerId = userId; + signature.CoordinateMax = payload.CoordinateMax; + signature.Strokes = payload.Strokes; + signature.CapturedAtUtc = payload.CapturedAtUtc; + signature.FilePath = Path.Combine(fi.DestDir, fi.FileName); + + // Bump the signer's quota. The Signature row's + // SignerId is the IdentityUser.Id (a string), so we + // look up by Id and not by username. + var signer = await dbContext.Users + .FirstOrDefaultAsync(u => u.Id == userId, token); + if (signer is not null) + { + signer.DiskUsage += new FileInfo(signature.FilePath).Length; + } try { @@ -421,6 +482,10 @@ namespace Yavsc.ApiControllers catch (Exception ex) { logger.LogError(ex, "estimate {Id}: signature db write failed", id); + // Best-effort rollback: remove the file we wrote + // so disk and db don't disagree. + try { System.IO.File.Delete(signature.FilePath); } + catch { /* swallow — the row will be re-orphaned, the user re-signs */ } return BadRequest(new { Error = "db write failed", Detail = ex.Message }); } diff --git a/src/Yavsc.Api/Controllers/Business/EstimateApiController.cs b/src/Yavsc.Api/Controllers/Business/EstimateApiController.cs index c1e25cc27..b72c6d7a2 100644 --- a/src/Yavsc.Api/Controllers/Business/EstimateApiController.cs +++ b/src/Yavsc.Api/Controllers/Business/EstimateApiController.cs @@ -40,7 +40,7 @@ namespace Yavsc.Controllers else if (!UserIsAdminOrThis(ownerId)) // throw new Exception("Not authorized") ; // or just do nothing return new StatusCodeResult(StatusCodes.Status403Forbidden); - return Ok(_context.Estimates.Include(e => e.Bill).Include(e => e.Signatures).Where(e => e.OwnerId == ownerId)); + return Ok(_context.Estimates.Include(e => e.Bill).Where(e => e.OwnerId == ownerId)); } // GET: api/Estimate/5 [HttpGet("{id}", Name = "GetEstimate")] @@ -51,7 +51,7 @@ namespace Yavsc.Controllers return BadRequest(ModelState); } - Estimate estimate = _context.Estimates.Include(e => e.Bill).Include(e => e.Signatures).Single(m => m.Id == id); + Estimate estimate = _context.Estimates.Include(e => e.Bill).Single(m => m.Id == id); if (estimate == null) { @@ -201,31 +201,23 @@ namespace Yavsc.Controllers // GET: api/estimate/asclient // Ongoing estimates (not yet validated by the client) where the // current user is the client who initiated the request. - // An estimate only becomes visible to the client once the provider - // has actually established and submitted it (ProviderValidationDate); - // a freshly inserted request the provider hasn't worked on yet is - // not something the client can validate, so it's excluded. [HttpGet("asclient")] public IActionResult GetOngoingEstimatesAsClient() { var uid = User.GetUserId(); - return Ok(_context.Estimates.Include(e => e.Bill).Include(e => e.Signatures) - .Where(e => e.ClientId == uid - && e.ProviderValidationDate != default - && e.ClientValidationDate == default)); + return Ok(_context.Estimates.Include(e => e.Bill) + .Where(e => e.ClientId == uid && e.ClientValidationDate == default)); } // GET: api/estimate/asprovider - // Estimates awaiting the provider's signature, established by the - // current user as the provider. Once the provider validates - // (ProviderValidationDate stamped), the estimate leaves this list - // and appears in the client's "awaiting client signature" list. + // Ongoing estimates (not yet validated by the client) established + // by the current user as the provider. [HttpGet("asprovider")] public IActionResult GetOngoingEstimatesAsProvider() { var uid = User.GetUserId(); - return Ok(_context.Estimates.Include(e => e.Bill).Include(e => e.Signatures) - .Where(e => e.OwnerId == uid && e.ProviderValidationDate == default)); + return Ok(_context.Estimates.Include(e => e.Bill) + .Where(e => e.OwnerId == uid && e.ClientValidationDate == default)); } protected override void Dispose(bool disposing) diff --git a/src/Yavsc.Api/Controllers/Business/FrontOfficeApiController.cs b/src/Yavsc.Api/Controllers/Business/FrontOfficeApiController.cs index eb7995f5e..3adac5b03 100644 --- a/src/Yavsc.Api/Controllers/Business/FrontOfficeApiController.cs +++ b/src/Yavsc.Api/Controllers/Business/FrontOfficeApiController.cs @@ -1,17 +1,10 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Razor; -using Microsoft.AspNetCore.Mvc.ViewEngines; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.Options; -using Yavsc.Abstract.Workflow; using Yavsc.Helpers; using Yavsc.Models; -using Yavsc.Models.Billing; using Yavsc.Services; using Yavsc.Server.Helpers; using Yavsc.ViewModels.FrontOffice; -using Yavsc.ViewModels.Gen; namespace Yavsc.ApiControllers { @@ -20,38 +13,13 @@ namespace Yavsc.ApiControllers public class FrontOfficeApiController : Controller { ApplicationDbContext dbContext; - private readonly IBillingService billing; - private readonly ILogger logger; - private readonly SiteSettings siteSettings; - private readonly IRazorViewEngine viewEngine; - // The view-rendering dependencies (siteSettings, viewEngine) are - // optional: only EstimateTex/EstimatePdf use them, and those run in - // the Api host where Program.cs wires AddControllersWithViews + - // SiteSettings. The accept/reject endpoints (and the Api integration - // test host, which composes with AddControllers alone) never touch - // them, so they must not be required for controller activation. - // - // .NET 10 does not register a single IViewEngine service (the view - // engine is registered as IRazorViewEngine and surfaced through - // MvcViewOptions.ViewEngines). IRazorViewEngine (RazorViewEngine) - // implements IViewEngine, so we inject that and hand it to - // TeXHelpers.RenderViewToString as an IViewEngine. RenderViewToString - // builds its ActionContext from the controller's own ControllerContext, - // so no IActionContextAccessor is needed (and that type is deprecated - // in .NET 10). - public FrontOfficeApiController( - ApplicationDbContext context, - ILoggerFactory loggerFactory, - IOptions siteSettings = null, - IRazorViewEngine viewEngine = null, - IBillingService billing = null) + private IBillingService billing; + + public FrontOfficeApiController(ApplicationDbContext context, IBillingService billing = null) { dbContext = context; this.billing = billing ?? new BillingService(context); - logger = loggerFactory.CreateLogger(); - this.siteSettings = siteSettings?.Value; - this.viewEngine = viewEngine; } [HttpGet("profiles/{actCode}")] @@ -60,313 +28,29 @@ namespace Yavsc.ApiControllers return await dbContext.ListPerformersAsync(billing, actCode); } - // GET: api/front/query/{queryId}/estimate.tex - // - // Renders the estimate linked to the query (Estimate.CommandId == - // queryId) as a LaTeX source document. The view is a generation - // template (Layout = "null"), not a web page: it is returned with - // a text/x-tex content type so the caller can save it as a .tex - // file. Either party (client or provider) or an admin may read it. - [HttpGet("query/{queryId}/estimate.tex")] - public async Task EstimateTex(long queryId, CancellationToken token) - { - if (viewEngine is null || siteSettings is null) - return Problem("TeX rendering is not configured on this host.", - statusCode: StatusCodes.Status503ServiceUnavailable); - - var estimate = await LoadEstimateForRenderAsync(queryId, token); - if (estimate is null) return NotFound(new { Error = "no estimate linked to this query" }); - - var uid = User.GetUserId(); - if (!User.IsInRole(Constants.AdminGroupName) - && uid != estimate.ClientId && uid != estimate.OwnerId) - return Forbid(); - - PrepareEstimateViewBag(estimate); - Response.ContentType = "text/x-tex"; - return View("Estimate_tex", estimate); - } - - // GET: api/front/query/{queryId}/estimate.pdf - // - // Renders the same LaTeX template to a string, compiles it to PDF - // via texi2pdf (see SiteSettings.GenerateEstimatePdf), and streams - // the generated PDF bytes back. Requires /usr/bin/texi2pdf on the - // server host; on hosts without it the endpoint returns a problem. - [HttpGet("query/{queryId}/estimate.pdf")] - public async Task EstimatePdf(long queryId, CancellationToken token) - { - if (viewEngine is null || siteSettings is null) - { - string msg = "PDF rendering is not configured on this host."; - logger.LogError(msg); - return Problem(msg, - statusCode: StatusCodes.Status503ServiceUnavailable); - - } - - var estimate = await LoadEstimateForRenderAsync(queryId, token); - if (estimate is null) return NotFound(new { Error = "no estimate linked to this query" }); - - var uid = User.GetUserId(); - if (!User.IsInRole(Constants.AdminGroupName) - && uid != estimate.ClientId && uid != estimate.OwnerId) - return Forbid(); - - PrepareEstimateViewBag(estimate); - - var baseFileName = $"estimate-{estimate.Id}"; - // Resolve the bills dir to an absolute path once: appsettings - // configures it as a relative path ("bills"), and both the - // generation (GenerateEstimatePdf) and the read-back below must - // agree on the same location regardless of the host's cwd. - var billsDir = new System.IO.DirectoryInfo(siteSettings.Bills).FullName; - var model = new PdfGenerationViewModel - { - BaseFileName = baseFileName, - DestDir = billsDir, - Temp = siteSettings.TempDir, - }; - - try - { - // 1. Render the LaTeX template (Estimate_tex) to a TeX source - // string, using the estimate as the view model. - model.TeXSource = this.RenderViewToString(viewEngine, "Estimate_tex", estimate); - // 2. Render the generation view (Estimate_pdf). Its @{} block - // calls SiteSettings.GenerateEstimatePdf(Model), which - // compiles the TeX to PDF on disk as a side effect of the - // render. The HTML the view emits is not what we return — - // we stream the generated PDF bytes — but driving the - // generation through the view keeps the template as the - // output producer, per the original design. - if (model.GenerateEstimatePdf() is FileInfo fo && fo.Exists) - { - return File(fo.OpenRead(), "application/pdf"); - } - string msg = "TeX render failed: " + model.GenerationErrorMessage; - logger.LogError(msg); - return Problem(msg); - - } - catch (Exception ex) - { - logger.LogError(ex, "estimate {QueryId}: TeX/PDF render failed", queryId); - return Problem("TeX render failed: " + ex.Message); - } - } - - // Loads the estimate linked to a query with the navigation the - // Estimate_tex template reads (client + performer profile + bill - // + the captured signatures, drawn as tikz at the foot of the devis). - private Task LoadEstimateForRenderAsync(long queryId, CancellationToken token) - => dbContext.Estimates - .Where(e => e.CommandId == queryId) - .OrderByDescending(e => e.Id) - .Include(e => e.Bill) - .Include(e => e.Signatures) - .Include(e => e.Query).ThenInclude(q => q!.Client).ThenInclude(c => c!.PostalAddress) - .Include(e => e.Query).ThenInclude(q => q!.PerformerProfile).ThenInclude(p => p!.OrganizationAddress) - .Include(e => e.Query).ThenInclude(q => q!.PerformerProfile).ThenInclude(p => p!.Performer) - .FirstOrDefaultAsync(token); - - // Sets the ViewBag keys the Estimate_tex template reads. A devis - // is never a bill (AsBill=false) and is never acquitted. - private void PrepareEstimateViewBag(Estimate estimate) - { - ViewBag.AsBill = false; - ViewBag.Acquitted = false; - ViewBag.BillsDir = new System.IO.DirectoryInfo(siteSettings.Bills).FullName; - ViewBag.AvatarsDir = new System.IO.DirectoryInfo(siteSettings.Avatars).FullName; - } - - // POST: api/front/query/accept - // - // Either party of the query may accept: the provider - // (PerformerId) accepts the client's request, or the client - // (ClientId) accepts the provider's estimate. An optional - // signature (the PostIt wire format) may be sent along; it is - // persisted on the estimate linked to the query - // (Estimate.CommandId == queryId), and the matching validation - // date is stamped — ProviderValidationDate when the provider - // accepts, ClientValidationDate when the client accepts. - [HttpPost("query/accept")] - public async Task AcceptQuery( - string billingCode, - long queryId, - [FromBody] QueryAcceptanceRequest? body, - CancellationToken token) - { - var (query, role, error) = await ResolveAndAuthorizeAsync(billingCode, queryId, token); - if (error is not null) return error; - if (query is null) return BadRequest(new { Error = "query not found" }); - - Signature? signature = null; - // actingRole is the role the caller is signing / accepting - // as. It defaults to the role inferred from the caller's - // identity, but when a signature is submitted the caller - // declares the side (Pro/Client) and the server honors it - // only if the caller is that party. This disambiguates a - // user who is both parties (where inference would always - // pick the provider) and makes "write only for its author" - // explicit. - ActorRole actingRole = role; - if (HasSignature(body)) - { - // Admins bypass authorization but are neither party of - // the estimate, so they cannot sign. - if (role == ActorRole.Admin) - return BadRequest(new { Error = "admin cannot sign on behalf of a party" }); - - var estimate = await dbContext.Estimates - .Where(e => e.CommandId == queryId) - .OrderByDescending(e => e.Id) - .FirstOrDefaultAsync(token); - if (estimate is null) - return BadRequest(new { Error = "no estimate linked to this query" }); - - var uid = User.GetUserId(); - bool isProvider = query.PerformerId == uid; - bool isClient = query.ClientId == uid; - - // The caller declares which side they sign as. Null - // falls back to the role inferred from identity. - var declared = body!.SignatureType; - if (declared.HasValue) - { - if (declared.Value == SignatureType.Pro && !isProvider) - return Forbid(); - if (declared.Value == SignatureType.Client && !isClient) - return Forbid(); - actingRole = declared.Value == SignatureType.Pro - ? ActorRole.Provider - : ActorRole.Client; - } - - var type = actingRole == ActorRole.Provider - ? SignatureType.Pro - : SignatureType.Client; - - var payload = new SignaturePadPayload - { - CoordinateMax = body.CoordinateMax, - CapturedAtUtc = body.CapturedAtUtc ?? DateTime.UtcNow, - Strokes = body.Strokes!, - }; - - try - { - signature = await EstimateSignaturePersister.StageAsync( - dbContext, estimate.Id, type, uid, payload, token); - } - catch (Exception ex) - { - logger.LogError(ex, "query {QueryId}: signature stage failed", queryId); - return BadRequest(new { Error = "signature stage failed", Detail = ex.Message }); - } - - if (actingRole == ActorRole.Provider) - estimate.ProviderValidationDate = DateTime.UtcNow; - else - estimate.ClientValidationDate = DateTime.UtcNow; - } - - // The party-specific status records who accepted: the - // provider (ProAccepted) or the client (ClientAccepted). - // An admin accepting (neither party) falls back to the - // legacy generic Accepted. - query.Status = actingRole switch - { - ActorRole.Provider => QueryStatus.ProAccepted, - ActorRole.Client => QueryStatus.ClientAccepted, - _ => QueryStatus.Accepted, - }; - - try - { - await dbContext.SaveChangesAsync(User.GetUserId(), token); - } - catch (Exception ex) - { - logger.LogError(ex, "query {QueryId}: accept db write failed", queryId); - return BadRequest(new { Error = "db write failed", Detail = ex.Message }); - } - - return Ok(new - { - queryId, - status = query.Status.ToString(), - signature = signature is null - ? null - : new - { - id = signature.Id, - estimateId = signature.EstimateId, - type = signature.Type.ToString(), - }, - }); - } - - // POST: api/front/query/reject - // - // Either party may reject. A signature is not meaningful on a - // rejection, so one is refused with 400. [HttpPost("query/reject")] - public async Task RejectQuery( - string billingCode, - long queryId, - [FromBody] QueryAcceptanceRequest? body, - CancellationToken token) + public IActionResult RejectQuery(string billingCode, long queryId) { - if (HasSignature(body)) - return BadRequest(new { Error = "signature not allowed on reject" }); + if (billingCode == null) return BadRequest("billingCode"); + if (queryId == 0) return BadRequest("queryId"); + var billing = BillingService.GetBillable(dbContext, billingCode, queryId); + if (billing == null) return BadRequest(); - var (query, role, error) = await ResolveAndAuthorizeAsync(billingCode, queryId, token); - if (error is not null) return error; - if (query is null) return BadRequest(new { Error = "query not found" }); - - query.Status = QueryStatus.Rejected; - await dbContext.SaveChangesAsync(User.GetUserId(), token); - - return Ok(new - { - queryId, - status = query.Status.ToString(), - }); + billing.Status = QueryStatus.Rejected; + dbContext.SaveChanges(User.GetUserId()); + return Ok(); } - // Loads the query and checks that the caller is a party to it - // (provider or client) or an admin. Returns the role the caller - // is acting in; Admin is only returned when the caller is - // neither party but is an admin. - private async Task<(IQuery? Query, ActorRole Role, IActionResult? Error)> - ResolveAndAuthorizeAsync(string billingCode, long queryId, CancellationToken token) + [HttpPost("query/accept")] + public IActionResult AcceptQuery(string billingCode, long queryId) { - if (string.IsNullOrWhiteSpace(billingCode)) - return (null, ActorRole.None, BadRequest("billingCode")); - if (queryId == 0) - return (null, ActorRole.None, BadRequest("queryId")); - - var query = await billing.GetBillAsync(billingCode, queryId); - if (query is null) - return (null, ActorRole.None, BadRequest()); - - var uid = User.GetUserId(); - bool isProvider = query.PerformerId == uid; - bool isClient = query.ClientId == uid; - - if (isProvider) return (query, ActorRole.Provider, null); - if (isClient) return (query, ActorRole.Client, null); - - if (User.IsInRole(Constants.AdminGroupName)) - return (query, ActorRole.Admin, null); - - return (null, ActorRole.None, Forbid()); + if (billingCode == null) return BadRequest("billingCode"); + if (queryId == 0) return BadRequest("queryId"); + var billing = BillingService.GetBillable(dbContext, billingCode, queryId); + if (billing == null) return BadRequest(); + billing.Status = QueryStatus.Accepted; + dbContext.SaveChanges(User.GetUserId()); + return Ok(); } - - private static bool HasSignature(QueryAcceptanceRequest? body) - => body is not null && body.Strokes is not null && body.Strokes.Length > 0; } - - internal enum ActorRole { None, Provider, Client, Admin } } diff --git a/src/Yavsc.Api/Helpers/TeXHelpers.cs b/src/Yavsc.Api/Helpers/TeXHelpers.cs deleted file mode 100644 index c35806bda..000000000 --- a/src/Yavsc.Api/Helpers/TeXHelpers.cs +++ /dev/null @@ -1,325 +0,0 @@ -using System.Diagnostics; -using System.Globalization; -using System.Text; -using Microsoft.AspNetCore.Html; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Rendering; -using Microsoft.AspNetCore.Mvc.ViewEngines; -using Microsoft.AspNetCore.Mvc.ViewFeatures; -using Yavsc.Models.Billing; - -namespace Yavsc.Helpers -{ - using Microsoft.AspNetCore.Mvc.Razor; - using ViewModels.Gen; - public class TeXString : HtmlString - { - - public TeXString(TeXString teXString): base(teXString.ToString()) - { - } - - public TeXString(string str) : base(str) - { - - - } - - public static TeXString operator+ (TeXString a, TeXString b) { - return new TeXString(a.ToString()+b.ToString()); - } - - } - public class Replacement - { - string target; - string replacement; - public Replacement(string target, string replacement) - { - this.target = target; - this.replacement = replacement; - } - public string Execute(string source) - { - return source?.Replace(target, replacement) ?? null; - } - } - public static class TeXHelpers - { - public static readonly Replacement[] SpecialCharsDefaultRendering = - { - new Replacement("<","\\textless"), - new Replacement(">","\\textgreater"), - new Replacement("©","\\copyright"), - new Replacement("®","\\textregistered"), - new Replacement("\\","\\textbackslash"), - new Replacement("™","\\texttrademark"), - new Replacement("¶","\\P"), - new Replacement("|","\\textbar"), - new Replacement("%","\\%"), - new Replacement("{","\\{"), - new Replacement("}","\\}"), - new Replacement("_","\\_"), - new Replacement("#","\\#"), - new Replacement("$","\\$"), - new Replacement("_","\\_"), - new Replacement("¿","\\textquestiondown"), - new Replacement("§","\\S"), - new Replacement("£","\\pounds"), - new Replacement("&","\\&"), - new Replacement("¡","\\textexclamdown"), - new Replacement("†","\\dag"), - new Replacement("–","\\textendash"), - new Replacement("°","\\textdegree") - }; - - public static TeXString ToTeX(this string source, string defaultValue="\\textit{néant}") - { - if (source==null) return new TeXString(defaultValue); - string result=source; - foreach (var r in SpecialCharsDefaultRendering) - { - result = r.Execute(result); - } - return new TeXString(result); - } - - public static TeXString ToTeXCell(this string source, string defaultValue="\\textit{néant}") - { - if (source==null) return new TeXString(defaultValue); - string result=source; - foreach (var r in SpecialCharsDefaultRendering) - { - result = r.Execute(result); - } - result = result.Replace("\n","\\tabularnewline "); - return new TeXString(result); - } - - - public static string NewLinesWith(this string target, string separator) - { - var items = target.Split(new char[] { '\n' }).Where( - s => !string.IsNullOrWhiteSpace(s)); - - return string.Join(separator, items); - } - - public static TeXString ToTeXLines(this string source, string defaultValue, string lineSeparator = "\n\\\\") - { - if (source == null) return new TeXString(defaultValue); - return new TeXString( source.ToTeX().ToString().NewLinesWith(lineSeparator) ); - } - - public static TeXString SplitAddressToTeX (this string source, string lineSeparator = "\n\\\\", string defaultValue = "\\textit{pas d'adresse postale}") - { - if (string.IsNullOrWhiteSpace(source)) return new TeXString(defaultValue); - var alines = source.Split(','); - var texlines = alines.Select(l=>l.ToTeX().ToString()); - return new TeXString(string.Join(lineSeparator,texlines)); - } - - /// - /// Renders a captured signature as TikZ \draw/\fill - /// paths, suitable for embedding in a tikzpicture at the - /// foot of a devis. The signature is the PostIt wire format — a - /// length-prefixed sequence of strokes, each stroke being - /// [k, x0, y0, x1, y1, …] with coordinates normalised to - /// [0, CoordinateMax] and y growing downward (screen - /// orientation). We denormalise to a × - /// canvas and flip y so the signature - /// appears upright on paper. A single-point stroke (a pen-down - /// tap) is rendered as a filled dot, mirroring the round-capped - /// Polyline the Avalonia pad draws for it. - /// - /// The persisted signature. Null/empty yields - /// , so the caller can gate the - /// tikzpicture on a non-empty result. - public static HtmlString SignatureToTikz( - this Signature sig, double widthCm, double heightCm) - { - if (sig is null || sig.Strokes is null || sig.Strokes.Length == 0) - return HtmlString.Empty; - - double max = sig.CoordinateMax > 0 ? sig.CoordinateMax : 10_000.0; - var strokes = sig.Strokes; - var sb = new StringBuilder(); - - int i = 0; - while (i < strokes.Length) - { - int k = strokes[i]; - if (k <= 0) break; - if (i + 1 + 2 * k > strokes.Length) break; - i++; // skip the length prefix - - // A single-point stroke: a tap. Draw a round dot so it - // shows instead of a zero-length, invisible \draw. - if (k == 1) - { - double px = strokes[i] / max * widthCm; - double py = heightCm - strokes[i + 1] / max * heightCm; - sb.Append(CultureInfo.InvariantCulture, - $"\\fill ({px:F3},{py:F3}) circle (0.8pt); "); - i += 2; - continue; - } - - sb.Append("\\draw[line cap=round, line join=round, line width=1.5pt] "); - for (int p = 0; p < k; p++) - { - double px = strokes[i + 2 * p] / max * widthCm; - double py = heightCm - strokes[i + 2 * p + 1] / max * heightCm; - if (p == 0) sb.Append(CultureInfo.InvariantCulture, - $"({px:F3},{py:F3})"); - else sb.Append(CultureInfo.InvariantCulture, - $" -- ({px:F3},{py:F3})"); - } - sb.Append("; "); - i += 2 * k; - } - - return new HtmlString(sb.ToString()); - } - - public static FileInfo GenerateEstimatePdf(this PdfGenerationViewModel Model) - { - string errorMsg = null; - // Resolve to an absolute path: SiteSettings.Bills may be a - // relative path (e.g. "bills"), and lualatex resolves - // -output-directory against its working directory — passing a - // relative path to both would nest them (/bills/bills) and - // the log/pdf write would fail. - var billdir = new DirectoryInfo(Model.DestDir).FullName; - string name = Model.BaseFileName; - FileInfo fo = new FileInfo(System.IO.Path.Combine(billdir, name + ".pdf")); - System.IO.Directory.CreateDirectory(billdir); - - // Compile the LaTeX source with the system lualatex, feeding it - // the TeX on standard input (no intermediate .tex file) and - // directing the resulting .pdf straight into the bills - // directory. lualatex is a file-based engine — it cannot emit - // the PDF on stdout — so the pdf lands at its final destination - // and we only clean the transient aux/log artifacts beside it. - // - // We pass "/dev/stdin" as the input filename so lualatex treats - // stdin as a regular input *file* rather than the terminal: with - // no filename argument it reads stdin in terminal mode, which - // emergency-stops after the first line of a multi-line document. - // /dev/stdin makes the full multi-line TeX compile cleanly. (The - // API host is Linux — the systemd service and lualatex dep are - // Linux-only too.) - // - // We capture stdout/stderr so a LaTeX error (missing .sty, - // missing \includegraphics target, …) is surfaced in - // GenerationErrorMessage instead of being buried in the .log - // we then delete. - int exitCode; - string stdout = null, stderr = null; - try - { - using (Process p = new Process()) - { - p.StartInfo = new ProcessStartInfo - { - UseShellExecute = false, - WorkingDirectory = billdir, - FileName = "lualatex", - Arguments = $"-interaction=nonstopmode -halt-on-error" - + $" -jobname=\"{name}\" -output-directory=\"{billdir}\"" - + " /dev/stdin", - RedirectStandardInput = true, - RedirectStandardOutput = true, - RedirectStandardError = true, - }; - p.Start(); - // Write the TeX source, then drain stdout/stderr. Read - // them after WaitForExit — lualatex's output is small - // (a few KB), well under the pipe buffer, so no deadlock. - using (var stdin = p.StandardInput) - { - stdin.Write(Model.TeXSource); - } - p.WaitForExit(); - exitCode = p.ExitCode; - stdout = p.StandardOutput.ReadToEnd(); - stderr = p.StandardError.ReadToEnd(); - } - } - catch (Exception ex) - { - errorMsg = "lualatex invocation failed: " + ex.Message; - exitCode = -1; - } - - if (exitCode != 0 && errorMsg == null) - { - // Surface the engine's own diagnostics (stderr carries the - // "! Emergency stop" / "! LaTeX Error: …" lines; stdout has - // the transcript banner) so the caller can see why it failed - // without having to find the (soon-deleted) .log file. - var diag = string.IsNullOrWhiteSpace(stderr) ? stdout : stderr; - errorMsg = $"Pdf generation failed with exit code {exitCode}: {diag}"; - } - else if (!fo.Exists && errorMsg == null) - errorMsg = "Pdf generation produced no output"; - - // Remove the LaTeX build artifacts left beside the pdf so only - // .pdf persists in the bills directory. Keep the .log on - // failure so it can be inspected. - foreach (var ext in new[] { ".aux", ".log", ".out", ".fls", - ".fdb_latexmk", ".synctex.gz", ".toc" }) - { - if (ext == ".log" && !fo.Exists) continue; - var fileName = name + ext; - var f = new FileInfo(System.IO.Path.Combine(billdir, fileName)); - if (f.Exists) { try { f.Delete(); } catch { } } - } - - Model.Generated = fo.Exists; - Model.GenerationErrorMessage = new HtmlString(errorMsg); - return fo; - } - - public static string RenderViewToString( - this Controller controller, IRazorViewEngine engine, - string viewName, object model, bool isMainPage = true) - { - if (engine == null) - throw new InvalidOperationException("no engine"); - - // Build an ActionContext from the controller's own context — - // .NET 10 deprecated IActionContextAccessor, and the controller - // already holds the live request context on its ControllerContext. - var controllerContext = controller.ControllerContext; - var actionContext = new ActionContext( - controllerContext.HttpContext, - controllerContext.RouteData, - controllerContext.ActionDescriptor); - - ViewEngineResult viewResult = engine.FindView(actionContext, viewName, isMainPage); - if (!viewResult.Success) - throw new InvalidOperationException( - $"View '{viewName}' not found. Searched locations: " - + string.Join(", ", viewResult.SearchedLocations)); - - // Hand the model to the view through ViewData so the template's - // @model directive resolves it (the previous implementation never - // set ViewData.Model, leaving Model null inside the template). - controller.ViewData.Model = model; - - using (var sw = new StringWriter()) - { - var viewContext = new ViewContext( - actionContext, - viewResult.View, - controller.ViewData, - controller.TempData, - sw, - new HtmlHelperOptions()); - viewResult.View.RenderAsync(viewContext).GetAwaiter().GetResult(); - return sw.GetStringBuilder().ToString(); - } - } - } -} diff --git a/src/Yavsc.Api/Program.cs b/src/Yavsc.Api/Program.cs index 8daff34d9..be4ae6579 100644 --- a/src/Yavsc.Api/Program.cs +++ b/src/Yavsc.Api/Program.cs @@ -55,11 +55,7 @@ internal class Program }); }) .AddYavscCors(builder.Configuration) - .AddControllersWithViews(); - - // Binds the "Site" section (Bills, TempDir, Avatars, …) used by - // the document-generation flow and BillingController. - services.Configure(builder.Configuration.GetSection("Site")); + .AddControllers(); // accepts any access token issued by identity server services.AddAuthentication("Bearer") diff --git a/src/Yavsc.Api/ViewModels/FrontOffice/QueryAcceptanceRequest.cs b/src/Yavsc.Api/ViewModels/FrontOffice/QueryAcceptanceRequest.cs deleted file mode 100644 index 41e4dd650..000000000 --- a/src/Yavsc.Api/ViewModels/FrontOffice/QueryAcceptanceRequest.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Yavsc.Models.Billing; - -namespace Yavsc.ViewModels.FrontOffice -{ - /// - /// Body of POST /api/v1/front/query/accept and - /// .../reject. The field is optional: - /// when present (non-empty) on accept, it is the PostIt wire-format - /// signature captured with the acceptance. All fields are optional - /// so a caller may post {} to accept/reject without a - /// signature. - /// - public class QueryAcceptanceRequest - { - /// - /// Wire-format strokes. See - /// PostIt.Models.SignaturePadData. Null or empty means - /// no signature. - /// - public int[]? Strokes { get; set; } - - public int CoordinateMax { get; set; } = 10_000; - - public DateTime? CapturedAtUtc { get; set; } - - /// - /// Which signature is being submitted: - /// (the provider) or - /// . When - /// is present this declares the side the caller is signing as; - /// the server honors it only if the caller is that party - /// (query.PerformerId for Pro, query.ClientId - /// for Client). This disambiguates the case where one user is - /// both parties, and makes "write only for its author" - /// explicit. When null, the server falls back to the role - /// inferred from the caller's identity. - /// - public SignatureType? SignatureType { get; set; } - } -} diff --git a/src/Yavsc.Api/Views/_ViewImports.cshtml b/src/Yavsc.Api/Views/_ViewImports.cshtml deleted file mode 100644 index 788aed5ab..000000000 --- a/src/Yavsc.Api/Views/_ViewImports.cshtml +++ /dev/null @@ -1,12 +0,0 @@ -@using Yavsc -@using Yavsc.Helpers -@using Yavsc.Billing -@using Yavsc.Models -@using Yavsc.Models.Billing -@using Yavsc.ViewModels.Gen -@using Microsoft.AspNetCore.Mvc -@using Microsoft.AspNetCore.Html - -@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers - -@inject Microsoft.Extensions.Options.IOptions SiteSettings \ No newline at end of file diff --git a/src/Yavsc.Api/Yavsc.Api.csproj b/src/Yavsc.Api/Yavsc.Api.csproj index 72231b0af..1a2accfc8 100644 --- a/src/Yavsc.Api/Yavsc.Api.csproj +++ b/src/Yavsc.Api/Yavsc.Api.csproj @@ -7,8 +7,8 @@ true 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1 diff --git a/src/Yavsc.Blogs.Tests/BlogAclApiTests.cs b/src/Yavsc.Blogs.Tests/BlogAclApiTests.cs index e47ab4e57..174838173 100644 --- a/src/Yavsc.Blogs.Tests/BlogAclApiTests.cs +++ b/src/Yavsc.Blogs.Tests/BlogAclApiTests.cs @@ -13,7 +13,7 @@ using static Yavsc.Constants; namespace Yavsc.Blogs.Tests; /// -/// Behavioral tests for BlogAclApiController.PostCircleAuthorizationToBlogPost: +/// Behavioural tests for BlogAclApiController.PostCircleAuthorizationToBlogPost: /// POST /api/v1/blogacl with a JSON body of /// CircleAuthorizationToBlogPost (CircleId + BlogPostId). /// @@ -84,7 +84,7 @@ public sealed class BlogAclApiTests : IClassFixture // rather than being rewritten to ClaimTypes.NameIdentifier. // The controller, however, reads the user id via // User.FindFirstValue(ClaimTypes.NameIdentifier), so we add - // an explicit NameIdentifier claim to keep the legacy lookup happy. + // an explicit nameid claim to keep the legacy lookup happy. http.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue( "Bearer", @@ -106,7 +106,7 @@ public sealed class BlogAclApiTests : IClassFixture /// when POSTs the /// shape { "circleId": <id> } — the exact body the /// PostIt client builds from - /// (which only carries CircleId). The server deserializes + /// (which only carries CircleId). The server deserialises /// it into , leaves /// BlogPostId at its default(long) = 0, attaches /// no Target navigation, and EF Core refuses to INSERT diff --git a/src/Yavsc.Blogs.Tests/BlogSpotServiceIndexTests.cs b/src/Yavsc.Blogs.Tests/BlogSpotServiceIndexTests.cs deleted file mode 100644 index fa1b9c379..000000000 --- a/src/Yavsc.Blogs.Tests/BlogSpotServiceIndexTests.cs +++ /dev/null @@ -1,164 +0,0 @@ -using System.Security.Claims; -using Microsoft.Extensions.DependencyInjection; -using Yavsc.Blogspot; -using Yavsc.Models; -using Yavsc.Models.Blog; -using Yavsc.Blogs.Tests.Fixtures; - -namespace Yavsc.Blogs.Tests; - -/// -/// Direct service-level tests for . -/// -/// The HTTP tests in all drive the -/// [Authorize("BlogScope")]-protected BlogApiController, -/// so the request is always authenticated by the time it reaches the -/// service. The MVC BlogspotController.Index action, by -/// contrast, is [AllowAnonymous] — that is the one caller that -/// actually exercises the unauthenticated branch of -/// , the branch that reads from -/// blogSpotPublications and projects to p.Post. -/// -/// These tests pin that behaviour at the service layer: a -/// BlogPost that has a BlogSpotPublication row is -/// listed for an anonymous viewer, and one without is not. Seeding -/// runs against the same shared SQLite store as the rest of the -/// Yavsc Blogs collection, so the real -/// (registered by -/// ) is resolved from DI and run -/// end-to-end — no mocking. -/// -[Collection("Yavsc Blogs")] -public sealed class BlogSpotServiceIndexTests : IClassFixture -{ - private readonly BlogsWebServerFixture _fixture; - - public BlogSpotServiceIndexTests(BlogsWebServerFixture fixture) - { - _fixture = fixture; - } - - /// An unauthenticated principal: a - /// with no authentication type has - /// == false, so - /// takes its anonymous - /// branch. This mirrors what the JwtBearer middleware hands the - /// MVC [AllowAnonymous] action when no bearer token is - /// presented. - private static ClaimsPrincipal Anonymous() - => new(new ClaimsIdentity()); - - /// Resolve the real from the - /// fixture's DI container and run against it, - /// disposing the request scope (and its scoped DbContext) afterwards. - /// The service itself is not ; the scope is, - /// so the scope is what we must dispose to avoid leaking a DbContext - /// against the shared SQLite connection across tests. - private async Task WithServiceAsync( - Func> body) - { - using var scope = _fixture.Services.CreateScope(); - var service = scope.ServiceProvider.GetRequiredService(); - return await body(service); - } - - private void Publish(long postId) - { - using var scope = _fixture.Services.CreateScope(); - var db = scope.ServiceProvider.GetRequiredService(); - // The publication flag is the existence of a row in - // blogSpotPublications — same write SetPublishAsync performs - // in production. Inserting it directly is the most faithful - // way to seed "this post is published" without routing - // through the authorize-gated PUT/publish endpoint. - db.blogSpotPublications.Add(new BlogSpotPublication { PostId = postId }); - db.SaveChanges(); - } - - [Fact] - public async Task Index_lists_a_published_post_for_anonymous_access() - { - _fixture.ResetDatabase(); - _fixture.SeedUser("alice"); - var publishedId = _fixture.SeedBlogPost("alice", "Published for all"); - - Publish(publishedId); - - var posts = await WithServiceAsync(async svc => - { - var result = await svc.Index(Anonymous(), id: "", skip: 0, take: 25); - return result.OfType().ToArray(); - }); - - var listed = Assert.Single(posts); - Assert.Equal("Published for all", listed.Title); - // The service serves IsPublished from the - // blogSpotPublications table — the anonymous branch does - // not Include the Publication navigation, so this proves - // the value is hydrated by the service, not derived from - // the EF graph. - Assert.True(listed.IsPublished); - } - - [Fact] - public async Task Index_serves_IsPublished_false_for_a_draft_for_anonymous_access() - { - // Anonymous viewers never receive a draft (the branch - // queries blogSpotPublications), so this is checked through - // the authenticated branch: the service must still serve - // IsPublished == false for a post with no publication row, - // not the default of a freshly loaded entity. - _fixture.ResetDatabase(); - _fixture.SeedUser("alice"); - var draftId = _fixture.SeedBlogPost("alice", "Draft, not published"); - - var viewer = new ClaimsPrincipal(new ClaimsIdentity( - [new Claim("sub", "alice")], authenticationType: "Bearer")); - - var posts = await WithServiceAsync(async svc => - { - var result = await svc.Index(viewer, id: "", skip: 0, take: 25); - return result.OfType().ToArray(); - }); - - var draft = Assert.Single(posts); - Assert.Equal(draftId, draft.Id); - Assert.False(draft.IsPublished); - } - - [Fact] - public async Task Index_does_not_list_an_unpublished_post_for_anonymous_access() - { - _fixture.ResetDatabase(); - _fixture.SeedUser("alice"); - var publishedId = _fixture.SeedBlogPost("alice", "Published for all"); - _fixture.SeedBlogPost("alice", "Draft, not published"); - - // Only the first post is published; the draft has no - // BlogSpotPublication row. - Publish(publishedId); - - var titles = await WithServiceAsync(async svc => - { - var result = await svc.Index(Anonymous(), id: "", skip: 0, take: 25); - return result.Select(p => p.Title).ToArray(); - }); - - Assert.Contains("Published for all", titles); - Assert.DoesNotContain("Draft, not published", titles); - } - - [Fact] - public async Task Index_returns_an_empty_list_for_anonymous_access_when_nothing_is_published() - { - _fixture.ResetDatabase(); - _fixture.SeedUser("alice"); - // A draft with no publication row: invisible to anonymous. - _fixture.SeedBlogPost("alice", "Draft, not published"); - - var result = await WithServiceAsync(svc => - svc.Index(Anonymous(), id: "", skip: 0, take: 25)); - - Assert.Empty(result); - } -} \ No newline at end of file diff --git a/src/Yavsc.Blogs.Tests/Directory.Packages.props b/src/Yavsc.Blogs.Tests/Directory.Packages.props new file mode 100644 index 000000000..eea5d34f9 --- /dev/null +++ b/src/Yavsc.Blogs.Tests/Directory.Packages.props @@ -0,0 +1,7 @@ + + + + diff --git a/src/Yavsc.Blogs.Tests/Fixtures/MappedClaimsBlogsWebServerFixture.cs b/src/Yavsc.Blogs.Tests/Fixtures/MappedClaimsBlogsWebServerFixture.cs index df977dc4e..ab4418c57 100644 --- a/src/Yavsc.Blogs.Tests/Fixtures/MappedClaimsBlogsWebServerFixture.cs +++ b/src/Yavsc.Blogs.Tests/Fixtures/MappedClaimsBlogsWebServerFixture.cs @@ -33,7 +33,7 @@ public sealed class MappedClaimsBlogsWebServerFixture : IDisposable, IBackendFix JwtSecurityTokenHandler.DefaultInboundClaimTypeMap["sub"] = ClaimTypes.NameIdentifier; var builder = WebApplication.CreateBuilder(); - builder.WebHost.UseUrls("http://127.0.0.1:5105"); + builder.WebHost.UseUrls("http://127.0.0.1:5104"); builder.Services.AddDbContext(opt => opt.UseInMemoryDatabase("Yavsc.Blogs.Tests.MappedClaims", _inMemoryRoot)); @@ -88,7 +88,7 @@ public sealed class MappedClaimsBlogsWebServerFixture : IDisposable, IBackendFix _app.MapControllers(); _app.StartAsync().GetAwaiter().GetResult(); - Addresses = ["http://127.0.0.1:5105"]; + Addresses = ["http://127.0.0.1:5104"]; Services = _app.Services; } diff --git a/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj b/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj index 4b3abb75c..590f27867 100644 --- a/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj +++ b/src/Yavsc.Blogs.Tests/Yavsc.Blogs.Tests.csproj @@ -9,8 +9,8 @@ true 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1 @@ -21,6 +21,7 @@ + diff --git a/src/Yavsc.Blogs/Yavsc.Blogs.csproj b/src/Yavsc.Blogs/Yavsc.Blogs.csproj index 204ae6c73..28b5841c9 100644 --- a/src/Yavsc.Blogs/Yavsc.Blogs.csproj +++ b/src/Yavsc.Blogs/Yavsc.Blogs.csproj @@ -8,8 +8,8 @@ true 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1 diff --git a/src/Yavsc.Org.Tests/Directory.Packages.props b/src/Yavsc.Org.Tests/Directory.Packages.props new file mode 100644 index 000000000..af2ef948c --- /dev/null +++ b/src/Yavsc.Org.Tests/Directory.Packages.props @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/Yavsc.Org.Tests/EstimateSignatureFileHelperTests.cs b/src/Yavsc.Org.Tests/EstimateSignatureFileHelperTests.cs new file mode 100644 index 000000000..b7b4d3591 --- /dev/null +++ b/src/Yavsc.Org.Tests/EstimateSignatureFileHelperTests.cs @@ -0,0 +1,136 @@ +using System.Security.Claims; +using System.Text.Json; +using Xunit; +using Yavsc.Models.Billing; +using Yavsc.Server.Helpers; +using Yavsc.Server.Models.FileSystem; + +namespace Yavsc.Org.Tests; + +/// +/// Tests for the static +/// helper. Scope is intentionally narrow: the file-naming format, +/// the strokes counter, and the on-disk write path. The controller +/// (authz, db persistence, signalR notification) is out of scope +/// for this commit and will get a dedicated integration test once +/// the Yavsc.Api test project is set up. +/// +public class EstimateSignatureFileHelperTests : IDisposable +{ + private readonly string _tempRoot; + private SiteSettings siteSettings; + + public EstimateSignatureFileHelperTests() + { + // UserFilesDirName is a process-wide static; we redirect + // it to a per-test temp dir so concurrent tests don't + // collide and the host filesystem is not littered. + _tempRoot = Path.Combine( + Path.GetTempPath(), + "yavsc-sig-tests-" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(_tempRoot); + this.siteSettings = new SiteSettings { Blog = _tempRoot }; + } + + public void Dispose() + { + try { Directory.Delete(_tempRoot, recursive: true); } + catch { /* best effort — the OS will clean Temp eventually */ } + } + + [Fact] + public void FileNameFormat_lowercases_type_and_includes_estimateId_and_ticks() + { + var name = EstimateSignatureFileHelper.FileNameFormat( + SignatureType.Pro, 42, 638_000_000_000_000_000L); + Assert.Equal("sign-pro-42-638000000000000000.json", name); + + var cli = EstimateSignatureFileHelper.FileNameFormat( + SignatureType.Client, 7, 1L); + Assert.Equal("sign-client-7-1.json", cli); + } + + [Theory] + [InlineData(new int[] { }, 0)] + [InlineData(new[] { 1, 100, 200 }, 1)] + [InlineData(new[] { 2, 1, 2, 3, 4 }, 1)] + [InlineData(new[] { 1, 1, 1, 2, 2, 3, 3 }, 2)] + [InlineData(new[] { 0, 1, 2, 3 }, 0)] // malformed k=0: short-circuit + public void ReceiveEstimateSignatureAsync_writes_a_v1_envelope(int[] strokes, int expectedStrokeCount) + { + // We don't read the count back from the helper (it's a + // private method), but the JSON envelope must reflect + // it; this verifies the public behaviour end-to-end. + _ = expectedStrokeCount; + // Arrange + var user = MakeUser("alice"); + var payload = new SignaturePadPayload + { + CoordinateMax = 10_000, + CapturedAtUtc = new DateTime(2026, 7, 4, 12, 0, 0, DateTimeKind.Utc), + Strokes = strokes, + }; + + // Act + var fi = Run(user, 123L, SignatureType.Pro, payload, this.siteSettings); + + // Assert: file exists, sits under the user's root, and + // parses as a yavsc.signature/v1 envelope. + var fullPath = Path.Combine(fi.DestDir, fi.FileName); + Assert.True(File.Exists(fullPath), $"missing: {fullPath}"); + + using var doc = JsonDocument.Parse(File.ReadAllText(fullPath)); + var root = doc.RootElement; + Assert.Equal("yavsc.signature/v1", root.GetProperty("format").GetString()); + Assert.Equal(10_000, root.GetProperty("coordinateMax").GetInt32()); + Assert.Equal(123L, root.GetProperty("estimateId").GetInt64()); + Assert.Equal("Pro", root.GetProperty("type").GetString()); + Assert.Equal("alice", root.GetProperty("signerName").GetString()); + Assert.Equal(expectedStrokeCount, root.GetProperty("strokeCount").GetInt32()); + } + + [Fact] + public async Task ReceiveEstimateSignatureAsync_rejects_null_payload() + { + var user = MakeUser("bob"); + // Capture TestContext.Current.CancellationToken outside the + // lambda so xUnit1051 sees a real CancellationToken argument + // (the lambda body runs on a different stack frame). + var ct = TestContext.Current.CancellationToken; + await Assert.ThrowsAsync(() => + EstimateSignatureFileHelper.ReceiveEstimateSignatureAsync( + user, 1L, SignatureType.Pro, payload: null!, + siteSettings, token: ct)); + } + + [Fact] + public async Task ReceiveEstimateSignatureAsync_rejects_non_positive_estimateId() + { + var user = MakeUser("bob"); + var payload = new SignaturePadPayload { Strokes = new[] { 1, 100, 100 } }; + var ct = TestContext.Current.CancellationToken; + await Assert.ThrowsAsync(() => + EstimateSignatureFileHelper.ReceiveEstimateSignatureAsync( + user, 0L, SignatureType.Pro, payload, this.siteSettings, token: ct)); + } + + // --- helpers ---------------------------------------------------- + + private static FileReceivedInfo Run( + ClaimsPrincipal user, long estimateId, SignatureType type, SignaturePadPayload payload, SiteSettings siteSettings) + { + // The helper is async; tests that don't care about the + // result can call it sync via .GetAwaiter().GetResult() + // because we know it never throws in the happy path. + return EstimateSignatureFileHelper + .ReceiveEstimateSignatureAsync(user, estimateId, type, payload, siteSettings, CancellationToken.None) + .GetAwaiter().GetResult(); + } + + private static ClaimsPrincipal MakeUser(string username) + { + return new ClaimsPrincipal(new ClaimsIdentity( + new[] { new Claim(ClaimTypes.Name, username) }, + authenticationType: "test")); + } +} diff --git a/src/Yavsc.Org.Tests/Smoke/AccountSmokeTests.cs b/src/Yavsc.Org.Tests/Smoke/AccountSmokeTests.cs index 4a7600596..61d774c57 100644 --- a/src/Yavsc.Org.Tests/Smoke/AccountSmokeTests.cs +++ b/src/Yavsc.Org.Tests/Smoke/AccountSmokeTests.cs @@ -1,9 +1,5 @@ -using System.Net; -using System.Net.Http; using IdentityServer8.Stores; -using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.Extensions.DependencyInjection; -using Yavsc.Tests.Shared; using Xunit; namespace Yavsc.Org.Tests.Smoke; @@ -44,48 +40,4 @@ public class AccountSmokeTests : IClassFixture Assert.Null(exception); } - - // Regression: anonymous access to a restricted page used to redirect to - // the ASP.NET Identity default LoginPath "/Account/Login", which has no - // controller here. The application cookie must redirect to the real - // sign-in endpoint at /signin instead. - [Fact] - public async Task Anonymous_access_to_protected_page_redirects_to_signin() - { - var client = _factory.CreateClient(new WebApplicationFactoryClientOptions - { - AllowAutoRedirect = false, - }); - - // EstimateController is [Authorize]; GET /Estimate hits Index(). - var response = await client.GetAsync("/Estimate", TestContext.Current.CancellationToken); - - Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); - var location = response.Headers.Location; - Assert.NotNull(location); - Assert.Equal("/signin", location.AbsolutePath); - Assert.Contains("ReturnUrl=%2FEstimate", location.Query); - } - - // Regression: the POST Delete action existed, but neither the GET - // Delete view nor a rendered confirmation page was reachable — - // Views/Account/Delete.cshtml was missing. An authenticated GET - // must now return the confirmation page (200). - [Fact] - public async Task Authenticated_get_account_delete_returns_confirmation_page() - { - var client = _factory.CreateClient(new WebApplicationFactoryClientOptions - { - AllowAutoRedirect = false, - }); - client.DefaultRequestHeaders.Add( - TestAuthPolicyProvider.HeaderName, TestAuthPolicyProvider.AdminRole); - - var response = await client.GetAsync("/Account/Delete", TestContext.Current.CancellationToken); - - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - var body = await response.Content.ReadAsStringAsync( - TestContext.Current.CancellationToken); - Assert.Contains("Supprimer mon compte", body); - } } diff --git a/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj b/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj index 51d3660a9..bdacafc5c 100644 --- a/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj +++ b/src/Yavsc.Org.Tests/Yavsc.Org.Tests.csproj @@ -11,8 +11,8 @@ $(MSBuildProjectDirectory)\test.runsettings 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1 @@ -30,6 +30,7 @@ + diff --git a/src/Yavsc.Org/Controllers/Accounting/AccountController.cs b/src/Yavsc.Org/Controllers/Accounting/AccountController.cs index f8fa7f0d2..04f60aa1d 100644 --- a/src/Yavsc.Org/Controllers/Accounting/AccountController.cs +++ b/src/Yavsc.Org/Controllers/Accounting/AccountController.cs @@ -1061,7 +1061,6 @@ IHtmlLocalizerFactory htmlLocalizerFactory, } return View(); } - [HttpPost, Authorize] public async Task Delete(UnregisterViewModel model) { diff --git a/src/Yavsc.Org/Controllers/Communicating/BlogspotController.cs b/src/Yavsc.Org/Controllers/Communicating/BlogspotController.cs index 31142cd60..b55171879 100644 --- a/src/Yavsc.Org/Controllers/Communicating/BlogspotController.cs +++ b/src/Yavsc.Org/Controllers/Communicating/BlogspotController.cs @@ -90,6 +90,73 @@ namespace Yavsc.Org.Controllers ); } + // GET: Blog/Create + [Authorize()] + public IActionResult Create(string title) + { + var result = new BlogPostEditViewModel + (new Models.Blog.BlogPost + { + Title = title + }, true); + SetLangItems(); + return View(result); + } + + // POST: Blog/Create + [HttpPost, Authorize, ValidateAntiForgeryToken] + public IActionResult Create(Models.Blog.BlogPost blogInput) + { + if (ModelState.IsValid) + { + var uid = User.GetUserId(); + Models.Blog.BlogPost post = blogSpotService.Create(uid, blogInput); + blogSpotService.AttachFiles(Request.Form.Files, uid, post.Id); + return RedirectToAction("Index"); + } + return View("Edit", blogInput); + } + + [Authorize()] + // GET: Blog/Edit/5 + public async Task Edit(long? id) + { + if (id == null) + { + return NotFound(); + } + try + { + var blog = await blogSpotService.GetPostForEdition(User, id.Value); + if (blog == null) + { + return NotFound(); + } + + + SetLangItems(); + return View(blog); + + } + catch (AuthorizationFailureException) + { + return new ChallengeResult(); + } + } + + // POST: Blog/Edit/5 + [HttpPost] + [ValidateAntiForgeryToken, Authorize()] + public async Task Edit(BlogPostEditViewModel blogEdit) + { + if (ModelState.IsValid) + { + await blogSpotService.Modify(User, blogEdit); + ViewBag.StatusMessage = "Post modified"; + return RedirectToAction("Index"); + } + return View(blogEdit); + } // GET: Blog/Delete/5 [ActionName("Delete"), Authorize()] diff --git a/src/Yavsc.Org/Controllers/Contracting/FrontOfficeController.cs b/src/Yavsc.Org/Controllers/Contracting/FrontOfficeController.cs index ac97193d0..549d80942 100644 --- a/src/Yavsc.Org/Controllers/Contracting/FrontOfficeController.cs +++ b/src/Yavsc.Org/Controllers/Contracting/FrontOfficeController.cs @@ -8,6 +8,7 @@ namespace Yavsc.Controllers using Helpers; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Localization; + using Microsoft.Extensions.Options; using Models; using ViewModels.FrontOffice; using Yavsc.Server.Helpers; @@ -20,20 +21,22 @@ namespace Yavsc.Controllers readonly ILogger _logger; readonly IStringLocalizer _SR; private readonly IBillingService _billing; + private readonly SiteSettings _siteSettings; public FrontOfficeController(ApplicationDbContext context, UserManager userManager, IBillingService billing, ILoggerFactory loggerFactory, - IStringLocalizer SR) + IStringLocalizer SR, + IOptions siteSettings) { _context = context; _userManager = userManager; _logger = loggerFactory.CreateLogger(); _SR = SR; _billing = billing; + _siteSettings = siteSettings.Value; } - public ActionResult Index() { var uid = User.FindFirstValue(ClaimTypes.NameIdentifier); @@ -43,9 +46,9 @@ namespace Yavsc.Controllers { EstimateToProduceCount = _context.RdvQueries.Where(c => c.PerformerId == uid && c.EventDate > now && c.Status == QueryStatus.Inserted && c.ValidationDate == null && !_context.Estimates.Any(e => e.CommandId == c.Id)).Count(), - EstimateToHonorAsProCount = _context.RdvQueries.Where(c => c.PerformerId == uid && c.EventDate > now && (c.Status == QueryStatus.ProAccepted || c.Status == QueryStatus.Accepted) + EstimateToHonorAsProCount = _context.RdvQueries.Where(c => c.PerformerId == uid && c.EventDate > now && c.Status == QueryStatus.Accepted && c.ValidationDate == null && _context.Estimates.Any(e => e.CommandId == c.Id )).Count(), - EstimateToSignAsCliCount = _context.Estimates.Where(e => e.ClientId == uid && (e.Query.Status == QueryStatus.ProAccepted || e.Query.Status == QueryStatus.Accepted)).Count(), + EstimateToSignAsCliCount = _context.Estimates.Where(e => e.ClientId == uid && e.Query.Status == QueryStatus.Accepted).Count(), BillToSignAsCliCount = 0, NewPayementsCount = 0 @@ -77,5 +80,61 @@ namespace Yavsc.Controllers return View(result); } + + [Produces("text/x-tex"), Authorize, Route("estimate-{id}.tex")] + [HttpGet] + public ViewResult EstimateTex(long id) + { + var estimate = _context.Estimates.Include(x => x.Query) + .Include(x => x.Query.Client) + .Include(x => x.Query.PerformerProfile) + .Include(x => x.Query.PerformerProfile.OrganizationAddress) + .Include(x => x.Query.PerformerProfile.Performer) + .Include(e => e.Bill).FirstOrDefault(x => x.Id == id); + Response.ContentType = "text/x-tex"; + return View("Estimate.tex", estimate); + } + + [Authorize, Route("Estimate-{id}.pdf")] + [HttpGet] + public IActionResult EstimatePdf(long id) + { + ViewBag.TempDir = _siteSettings.TempDir; + ViewBag.BillsDir = _siteSettings.Bills; + var estimate = _context.Estimates.Include(x => x.Query) + .Include(x => x.Query.Client) + .Include(x => x.Query.PerformerProfile) + .Include(x => x.Query.PerformerProfile.OrganizationAddress) + .Include(x => x.Query.PerformerProfile.Performer) + .Include(e => e.Bill).FirstOrDefault(x => x.Id == id); + if (estimate == null) + throw new Exception("No data"); + return View("Estimate.pdf", estimate); + } + + [Authorize] + public IActionResult EstimateProValidation() + { + throw new NotImplementedException(); + + } + + [Authorize] + public IActionResult EstimateClientValidation() + { + throw new NotImplementedException(); + + } + [Authorize] + public IActionResult BillValidation() + { + throw new NotImplementedException(); + + } + [Authorize] + public IActionResult BillAcquitment() + { + throw new NotImplementedException(); + } } } diff --git a/src/Yavsc.Org/Extensions/HostingExtensions.cs b/src/Yavsc.Org/Extensions/HostingExtensions.cs index ea035001c..8c1db2a73 100644 --- a/src/Yavsc.Org/Extensions/HostingExtensions.cs +++ b/src/Yavsc.Org/Extensions/HostingExtensions.cs @@ -215,18 +215,6 @@ public static class HostingExtensions services.AddScoped, UserClaimsPrincipalFactory>(); - // The default application cookie LoginPath is "/Account/Login", which - // has no controller here — anonymous access to a restricted page must - // redirect to the actual sign-in endpoint instead. The paths mirror - // the route attributes on AccountController (Constants.SigninPath etc., - // which carry a "~/" prefix that the cookie middleware does not want). - services.ConfigureApplicationCookie(options => - { - options.LoginPath = "/signin"; - options.LogoutPath = "/signout"; - options.AccessDeniedPath = "/Account/AccessDenied"; - }); - // Dev-only: Chromium rejects SameSite=None without Secure on http:// // (e.g. http://localhost:5000). The default Identity cookie policy // sets SameSite=None, which is invalid without Secure. Force Lax in diff --git a/src/Yavsc.Org/Helpers/AsciiDocTagHelper.cs b/src/Yavsc.Org/Helpers/AsciiDocTagHelper.cs index 778cf5cfe..f5ce87c5a 100644 --- a/src/Yavsc.Org/Helpers/AsciiDocTagHelper.cs +++ b/src/Yavsc.Org/Helpers/AsciiDocTagHelper.cs @@ -1,12 +1,13 @@ using System.Web; -using Markdig; +using AsciiDocSharp; +using AsciiDocSharp.Converters.Html; using Microsoft.AspNetCore.Html; using Microsoft.AspNetCore.Razor.TagHelpers; namespace Yavsc.Helpers { - public class MarkdownTagHelper : TagHelper + public class AsciidocTagHelper : TagHelper { public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output) { @@ -18,7 +19,6 @@ namespace Yavsc.Helpers if (string.IsNullOrWhiteSpace(text)) return; try - { if (context.AllAttributes.ContainsName("summary")) { @@ -34,10 +34,16 @@ namespace Yavsc.Helpers } } } - var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build(); - var result = Markdown.ToHtml(text, pipeline); + var processor = new AsciiDocProcessor( + + ); + var htmlConverter = new HtmlDocumentConverter(); - output.Content.AppendHtml(result); + var document = processor.ParseFromText(text); + var htmlResult = processor.ConvertDocument(document, htmlConverter); + + + output.Content.AppendHtml(htmlResult); } catch (ArgumentException ex) { diff --git a/src/Yavsc.Org/Helpers/TeXHelpers.cs b/src/Yavsc.Org/Helpers/TeXHelpers.cs new file mode 100644 index 000000000..f7cc812fb --- /dev/null +++ b/src/Yavsc.Org/Helpers/TeXHelpers.cs @@ -0,0 +1,198 @@ +using System.Diagnostics; +using Microsoft.AspNetCore.Html; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.AspNetCore.Mvc.ViewEngines; + +namespace Yavsc.Helpers +{ + using Microsoft.AspNetCore.Mvc.Infrastructure; + using ViewModels.Gen; + public class TeXString : HtmlString + { + + public TeXString(TeXString teXString): base(teXString.ToString()) + { + } + + public TeXString(string str) : base(str) + { + + + } + + public static TeXString operator+ (TeXString a, TeXString b) { + return new TeXString(a.ToString()+b.ToString()); + } + + } + public class Replacement + { + string target; + string replacement; + public Replacement(string target, string replacement) + { + this.target = target; + this.replacement = replacement; + } + public string Execute(string source) + { + return source?.Replace(target, replacement) ?? null; + } + } + public static class TeXHelpers + { + public static readonly Replacement[] SpecialCharsDefaultRendering = + { + new Replacement("<","\\textless"), + new Replacement(">","\\textgreater"), + new Replacement("©","\\copyright"), + new Replacement("®","\\textregistered"), + new Replacement("\\","\\textbackslash"), + new Replacement("™","\\texttrademark"), + new Replacement("¶","\\P"), + new Replacement("|","\\textbar"), + new Replacement("%","\\%"), + new Replacement("{","\\{"), + new Replacement("}","\\}"), + new Replacement("_","\\_"), + new Replacement("#","\\#"), + new Replacement("$","\\$"), + new Replacement("_","\\_"), + new Replacement("¿","\\textquestiondown"), + new Replacement("§","\\S"), + new Replacement("£","\\pounds"), + new Replacement("&","\\&"), + new Replacement("¡","\\textexclamdown"), + new Replacement("†","\\dag"), + new Replacement("–","\\textendash"), + new Replacement("°","\\textdegree") + }; + + public static TeXString ToTeX(this string source, string defaultValue="\\textit{néant}") + { + if (source==null) return new TeXString(defaultValue); + string result=source; + foreach (var r in SpecialCharsDefaultRendering) + { + result = r.Execute(result); + } + return new TeXString(result); + } + + public static TeXString ToTeXCell(this string source, string defaultValue="\\textit{néant}") + { + if (source==null) return new TeXString(defaultValue); + string result=source; + foreach (var r in SpecialCharsDefaultRendering) + { + result = r.Execute(result); + } + result = result.Replace("\n","\\tabularnewline "); + return new TeXString(result); + } + + + public static string NewLinesWith(this string target, string separator) + { + var items = target.Split(new char[] { '\n' }).Where( + s => !string.IsNullOrWhiteSpace(s)); + + return string.Join(separator, items); + } + + public static TeXString ToTeXLines(this string source, string defaultValue, string lineSeparator = "\n\\\\") + { + if (source == null) return new TeXString(defaultValue); + return new TeXString( source.ToTeX().ToString().NewLinesWith(lineSeparator) ); + } + + public static TeXString SplitAddressToTeX (this string source, string lineSeparator = "\n\\\\", string defaultValue = "\\textit{pas d'adresse postale}") + { + if (string.IsNullOrWhiteSpace(source)) return new TeXString(defaultValue); + var alines = source.Split(','); + var texlines = alines.Select(l=>l.ToTeX().ToString()); + return new TeXString(string.Join(lineSeparator,texlines)); + } + + public static bool GenerateEstimatePdf(this SiteSettings settings, PdfGenerationViewModel Model) + { + string errorMsg = null; + var billdir = Model.DestDir; + var tempdir = settings.TempDir; + string name = Model.BaseFileName; + string fullname = new FileInfo( + System.IO.Path.Combine(tempdir, name)).FullName; + string ofullname = new FileInfo( + System.IO.Path.Combine(billdir, name)).FullName; + + FileInfo fi = new FileInfo(fullname + ".tex"); + FileInfo fo = new FileInfo(ofullname + ".pdf"); + using (StreamWriter sw = new StreamWriter(fi.FullName)) + { + sw.Write(Model.TeXSource); + } + if (!fi.Exists) + { + errorMsg = "Source write failed"; + } + else + { + using (Process p = new Process()) + { + p.StartInfo.WorkingDirectory = tempdir; + p.StartInfo = new ProcessStartInfo + { + UseShellExecute = false, + WorkingDirectory = tempdir, + FileName = "/usr/bin/texi2pdf", + Arguments = $"--batch --build-dir=. -o {fo.FullName} {fi.FullName}" + }; + p.Start(); + p.WaitForExit(); + if (p.ExitCode != 0) + { + errorMsg = $"Pdf generation failed with exit code: {p.ExitCode}"; + } + else + { + fi.Delete(); + var di = new DirectoryInfo(Path.Combine(tempdir,$"{Model.BaseFileName}.t2d")); + di.Delete(true); + + } + } + } + Model.Generated = fo.Exists; + Model.GenerationErrorMessage = new HtmlString(errorMsg); + return fo.Exists; + } + + public static string RenderViewToString( + this Controller controller, IViewEngine engine, + IActionContextAccessor contextAccessor, + string viewName, object model, bool isMainPage = true) + { + using (var sw = new StringWriter()) + { + if (engine == null) + throw new InvalidOperationException("no engine"); + + // try to find the specified view + controller.TryValidateModel(model); + ViewEngineResult viewResult = engine.FindView(contextAccessor.ActionContext, viewName, isMainPage); + + // create the associated context + ViewContext viewContext = new ViewContext(); + viewContext.ActionDescriptor = contextAccessor.ActionContext.ActionDescriptor; + viewContext.HttpContext = contextAccessor.ActionContext.HttpContext; + viewContext.TempData = controller.TempData; + viewContext.View = viewResult.View; + viewContext.Writer = sw; + // write the render view with the given context to the stringwriter + viewResult.View.RenderAsync(viewContext).Wait(); + return sw.GetStringBuilder().ToString(); + } + } + } +} diff --git a/src/Yavsc.Org/Migrations/20260919195608_blogPostId.Designer.cs b/src/Yavsc.Org/Migrations/20260919195608_blogPostId.Designer.cs deleted file mode 100644 index 903838f1d..000000000 --- a/src/Yavsc.Org/Migrations/20260919195608_blogPostId.Designer.cs +++ /dev/null @@ -1,4714 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using Yavsc.Models; - -#nullable disable - -namespace Yavsc.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20260919195608_blogPostId")] - partial class blogPostId - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.9") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResource", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("AllowedAccessTokenSigningAlgorithms") - .HasColumnType("text"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("DisplayName") - .HasColumnType("text"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("LastAccessed") - .HasColumnType("timestamp with time zone"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("NonEditable") - .HasColumnType("boolean"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("boolean"); - - b.Property("Updated") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("ApiResources"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("ApiResourceId1") - .HasColumnType("integer"); - - b.Property("Type") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.HasIndex("ApiResourceId1"); - - b.ToTable("ApiResourceClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("ApiResourceId1") - .HasColumnType("integer"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.HasIndex("ApiResourceId1"); - - b.ToTable("ApiResourceProperties"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("ApiResourceId1") - .HasColumnType("integer"); - - b.Property("Scope") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.HasIndex("ApiResourceId1"); - - b.ToTable("ApiResourceScopes"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("ApiResourceId1") - .HasColumnType("integer"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Expiration") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.HasIndex("ApiResourceId1"); - - b.ToTable("ApiResourceSecrets"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("DisplayName") - .HasColumnType("text"); - - b.Property("Emphasize") - .HasColumnType("boolean"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Required") - .HasColumnType("boolean"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.ToTable("ApiScopes"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ScopeId") - .HasColumnType("integer"); - - b.Property("ScopeId1") - .HasColumnType("integer"); - - b.Property("Type") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ScopeId"); - - b.HasIndex("ScopeId1"); - - b.ToTable("ApiScopeClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("ScopeId") - .HasColumnType("integer"); - - b.Property("ScopeId1") - .HasColumnType("integer"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ScopeId"); - - b.HasIndex("ScopeId1"); - - b.ToTable("ApiScopeProperties"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.Client", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("AbsoluteRefreshTokenLifetime") - .HasColumnType("integer"); - - b.Property("AccessTokenLifetime") - .HasColumnType("integer"); - - b.Property("AccessTokenType") - .HasColumnType("integer"); - - b.Property("AllowAccessTokensViaBrowser") - .HasColumnType("boolean"); - - b.Property("AllowOfflineAccess") - .HasColumnType("boolean"); - - b.Property("AllowPlainTextPkce") - .HasColumnType("boolean"); - - b.Property("AllowRememberConsent") - .HasColumnType("boolean"); - - b.Property("AllowedIdentityTokenSigningAlgorithms") - .HasColumnType("text"); - - b.Property("AlwaysIncludeUserClaimsInIdToken") - .HasColumnType("boolean"); - - b.Property("AlwaysSendClientClaims") - .HasColumnType("boolean"); - - b.Property("AuthorizationCodeLifetime") - .HasColumnType("integer"); - - b.Property("BackChannelLogoutSessionRequired") - .HasColumnType("boolean"); - - b.Property("BackChannelLogoutUri") - .HasColumnType("text"); - - b.Property("ClientClaimsPrefix") - .HasColumnType("text"); - - b.Property("ClientId") - .HasColumnType("text"); - - b.Property("ClientName") - .HasColumnType("text"); - - b.Property("ClientUri") - .HasColumnType("text"); - - b.Property("ConsentLifetime") - .HasColumnType("integer"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("DeviceCodeLifetime") - .HasColumnType("integer"); - - b.Property("EnableLocalLogin") - .HasColumnType("boolean"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("FrontChannelLogoutSessionRequired") - .HasColumnType("boolean"); - - b.Property("FrontChannelLogoutUri") - .HasColumnType("text"); - - b.Property("IdentityTokenLifetime") - .HasColumnType("integer"); - - b.Property("IncludeJwtId") - .HasColumnType("boolean"); - - b.Property("LastAccessed") - .HasColumnType("timestamp with time zone"); - - b.Property("LogoUri") - .HasColumnType("text"); - - b.Property("NonEditable") - .HasColumnType("boolean"); - - b.Property("PairWiseSubjectSalt") - .HasColumnType("text"); - - b.Property("ProtocolType") - .HasColumnType("text"); - - b.Property("RefreshTokenExpiration") - .HasColumnType("integer"); - - b.Property("RefreshTokenUsage") - .HasColumnType("integer"); - - b.Property("RequireClientSecret") - .HasColumnType("boolean"); - - b.Property("RequireConsent") - .HasColumnType("boolean"); - - b.Property("RequirePkce") - .HasColumnType("boolean"); - - b.Property("RequireRequestObject") - .HasColumnType("boolean"); - - b.Property("SlidingRefreshTokenLifetime") - .HasColumnType("integer"); - - b.Property("UpdateAccessTokenClaimsOnRefresh") - .HasColumnType("boolean"); - - b.Property("Updated") - .HasColumnType("timestamp with time zone"); - - b.Property("UserCodeType") - .HasColumnType("text"); - - b.Property("UserSsoLifetime") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.ToTable("Clients"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("Type") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientCorsOrigin", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("ClientId1") - .HasColumnType("integer"); - - b.Property("Origin") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ClientId1"); - - b.ToTable("ClientCorsOrigins"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("GrantType") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientGrantTypes"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientIdPRestriction", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("ClientId1") - .HasColumnType("integer"); - - b.Property("Provider") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ClientId1"); - - b.ToTable("ClientIdPRestrictions"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("ClientId1") - .HasColumnType("integer"); - - b.Property("PostLogoutRedirectUri") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ClientId1"); - - b.ToTable("ClientPostLogoutRedirectUris"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("ClientId1") - .HasColumnType("integer"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ClientId1"); - - b.ToTable("ClientProperties"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("RedirectUri") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientRedirectUris"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("Scope") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientScopes"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("ClientId1") - .HasColumnType("integer"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Expiration") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ClientId1"); - - b.ToTable("ClientSecrets"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.DeviceFlowCodes", b => - { - b.Property("UserCode") - .HasColumnType("text"); - - b.Property("DeviceCode") - .HasColumnType("text"); - - b.Property("ClientId") - .HasColumnType("text"); - - b.Property("CreationTime") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Expiration") - .HasColumnType("timestamp with time zone"); - - b.Property("SessionId") - .HasColumnType("text"); - - b.Property("SubjectId") - .HasColumnType("text"); - - b.HasKey("UserCode", "DeviceCode"); - - b.ToTable("DeviceFlowCodes"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResource", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("DisplayName") - .HasColumnType("text"); - - b.Property("Emphasize") - .HasColumnType("boolean"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("NonEditable") - .HasColumnType("boolean"); - - b.Property("Required") - .HasColumnType("boolean"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("boolean"); - - b.Property("Updated") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("IdentityResources"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("IdentityResourceId") - .HasColumnType("integer"); - - b.Property("Type") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("IdentityResourceId"); - - b.ToTable("IdentityResourceClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("IdentityResourceId") - .HasColumnType("integer"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("IdentityResourceId"); - - b.ToTable("IdentityResourceProperties"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.PersistedGrant", b => - { - b.Property("Key") - .HasColumnType("text"); - - b.Property("ClientId") - .HasColumnType("text"); - - b.Property("ConsumedTime") - .HasColumnType("timestamp with time zone"); - - b.Property("CreationTime") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Expiration") - .HasColumnType("timestamp with time zone"); - - b.Property("SessionId") - .HasColumnType("text"); - - b.Property("SubjectId") - .HasColumnType("text"); - - b.Property("Type") - .HasColumnType("text"); - - b.HasKey("Key"); - - b.ToTable("PersistedGrants"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("AspNetRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("ProviderKey") - .HasColumnType("text"); - - b.Property("ProviderDisplayName") - .HasColumnType("text"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("text"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens", (string)null); - }); - - modelBuilder.Entity("Yavsc.Abstract.Identity.ClientProviderInfo", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("Avatar") - .HasColumnType("text"); - - b.Property("BillingAddressId") - .HasColumnType("bigint"); - - b.Property("EMail") - .HasColumnType("text"); - - b.Property("Phone") - .HasColumnType("text"); - - b.Property("UserName") - .HasColumnType("text"); - - b.HasKey("UserId"); - - b.ToTable("ClientProviderInfo"); - }); - - modelBuilder.Entity("Yavsc.Abstract.Models.Messaging.Notification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Target") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("body") - .IsRequired() - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("click_action") - .IsRequired() - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("color") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("icon") - .ValueGeneratedOnAdd() - .HasMaxLength(512) - .HasColumnType("character varying(512)") - .HasDefaultValue("exclam"); - - b.Property("sound") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("tag") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("title") - .IsRequired() - .HasMaxLength(1024) - .HasColumnType("character varying(1024)"); - - b.HasKey("Id"); - - b.ToTable("Notification"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.Ban", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text"); - - b.Property("TargetId") - .IsRequired() - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("TargetId"); - - b.ToTable("Ban"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.BlackListed", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("OwnerId"); - - b.HasIndex("UserId"); - - b.ToTable("BlackListed"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.CircleAuthorizationToBlogPost", b => - { - b.Property("CircleId") - .HasColumnType("bigint"); - - b.Property("BlogPostId") - .HasColumnType("bigint"); - - b.HasKey("CircleId", "BlogPostId"); - - b.HasIndex("BlogPostId"); - - b.ToTable("CircleAuthorizationToBlogPost"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.CircleAuthorizationToFile", b => - { - b.Property("CircleId") - .HasColumnType("bigint"); - - b.Property("Path") - .HasColumnType("text"); - - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("Access") - .HasColumnType("smallint"); - - b.HasKey("CircleId", "Path", "OwnerId"); - - b.HasIndex("OwnerId"); - - b.ToTable("CircleAuthorizationToFile"); - }); - - modelBuilder.Entity("Yavsc.Models.AccountBalance", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("ContactCredits") - .HasColumnType("bigint"); - - b.Property("Credits") - .HasColumnType("numeric"); - - b.HasKey("UserId"); - - b.ToTable("BankStatus"); - }); - - modelBuilder.Entity("Yavsc.Models.ApplicationUser", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("AccessFailedCount") - .HasColumnType("integer"); - - b.Property("AllowMonthlyEmail") - .HasColumnType("boolean"); - - b.Property("Avatar") - .ValueGeneratedOnAdd() - .HasMaxLength(512) - .HasColumnType("character varying(512)") - .HasDefaultValue("/images/Users/icon_user.png"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("DedicatedGoogleCalendar") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("DiskQuota") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(524288000L); - - b.Property("DiskUsage") - .HasColumnType("bigint"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property("FullName") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("MaxFileSize") - .HasColumnType("bigint"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PasswordHash") - .HasColumnType("text"); - - b.Property("PhoneNumber") - .HasColumnType("text"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property("PostalAddressId") - .HasColumnType("bigint"); - - b.Property("SecurityStamp") - .HasColumnType("text"); - - b.Property("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasAlternateKey("Email"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PostalAddressId"); - - b.ToTable("AspNetUsers", (string)null); - }); - - modelBuilder.Entity("Yavsc.Models.BalanceImpact", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BalanceId") - .IsRequired() - .HasColumnType("text"); - - b.Property("ExecDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Impact") - .HasColumnType("numeric"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("BalanceId"); - - b.ToTable("BalanceImpact"); - }); - - modelBuilder.Entity("Yavsc.Models.Bank.BankIdentity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AccountNumber") - .IsRequired() - .HasColumnType("text"); - - b.Property("BIC") - .IsRequired() - .HasColumnType("text"); - - b.Property("BankCode") - .IsRequired() - .HasColumnType("text"); - - b.Property("BankedKey") - .HasColumnType("integer"); - - b.Property("IBAN") - .IsRequired() - .HasColumnType("text"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.Property("WicketCode") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("BankIdentity"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.CommandLine", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Count") - .HasColumnType("integer"); - - b.Property("Currency") - .HasColumnType("text"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("EstimateId") - .HasColumnType("bigint"); - - b.Property("EstimateTemplateId") - .HasColumnType("bigint"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("UnitaryCost") - .HasColumnType("numeric"); - - b.HasKey("Id"); - - b.HasIndex("EstimateId"); - - b.HasIndex("EstimateTemplateId"); - - b.ToTable("CommandLine"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.Estimate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AttachedFilesString") - .IsRequired() - .HasColumnType("text"); - - b.Property("AttachedGraphicsString") - .IsRequired() - .HasColumnType("text"); - - b.Property("ClientId") - .IsRequired() - .HasColumnType("text"); - - b.Property("ClientValidationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("CommandId") - .HasColumnType("bigint"); - - b.Property("CommandType") - .IsRequired() - .HasColumnType("text"); - - b.Property("Description") - .IsRequired() - .HasColumnType("text"); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("ProviderValidationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Title") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("CommandId"); - - b.HasIndex("OwnerId"); - - b.ToTable("Estimates"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.EstimateTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Title") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("EstimateTemplates"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.ExceptionSIREN", b => - { - b.Property("SIREN") - .HasColumnType("text"); - - b.HasKey("SIREN"); - - b.ToTable("ExceptionsSIREN"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.NominativeServiceCommand", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ActivityCode") - .IsRequired() - .HasColumnType("text"); - - b.Property("ClientId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Consent") - .HasColumnType("boolean"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .IsRequired() - .HasColumnType("text"); - - b.Property("Discriminator") - .IsRequired() - .HasMaxLength(34) - .HasColumnType("character varying(34)"); - - b.Property("PaymentId") - .HasColumnType("text"); - - b.Property("PerformerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Provisional") - .HasColumnType("numeric"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("UserCreated") - .IsRequired() - .HasColumnType("text"); - - b.Property("UserModified") - .IsRequired() - .HasColumnType("text"); - - b.Property("ValidationDate") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ActivityCode"); - - b.HasIndex("ClientId"); - - b.HasIndex("PaymentId"); - - b.HasIndex("PerformerId"); - - b.ToTable("NominativeServiceCommands"); - - b.HasDiscriminator("Discriminator").HasValue("NominativeServiceCommand"); - - b.UseTphMappingStrategy(); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.Signature", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CapturedAtUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("CoordinateMax") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(10000); - - b.Property("EstimateId") - .HasColumnType("bigint"); - - b.Property("FilePath") - .IsRequired() - .HasColumnType("text"); - - b.Property("SignerId") - .IsRequired() - .HasColumnType("text"); - - b.PrimitiveCollection("Strokes") - .IsRequired() - .HasColumnType("integer[]"); - - b.Property("Type") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("SignerId"); - - b.HasIndex("EstimateId", "Type") - .IsUnique(); - - b.ToTable("Signatures"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogAttachedFile", b => - { - b.Property("FileId") - .HasColumnType("bigint"); - - b.Property("PostId") - .HasColumnType("bigint"); - - b.HasKey("FileId", "PostId"); - - b.HasIndex("PostId"); - - b.ToTable("BlogAttachedFiles"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogPost", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Article") - .HasMaxLength(56224) - .HasColumnType("character varying(56224)"); - - b.Property("AuthorId") - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("Photo") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(1024) - .HasColumnType("character varying(1024)"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("AuthorId"); - - b.ToTable("BlogSpot"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogTag", b => - { - b.Property("PostId") - .HasColumnType("bigint"); - - b.Property("TagId") - .HasColumnType("bigint"); - - b.HasKey("PostId", "TagId"); - - b.HasIndex("TagId"); - - b.ToTable("BlogTag"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.Comment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Article") - .IsRequired() - .HasColumnType("text"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("ParentId") - .HasColumnType("bigint"); - - b.Property("ReceiverId") - .HasColumnType("bigint"); - - b.Property("UserCreated") - .IsRequired() - .HasColumnType("text"); - - b.Property("UserModified") - .IsRequired() - .HasColumnType("text"); - - b.Property("Visible") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("AuthorId"); - - b.HasIndex("ParentId"); - - b.HasIndex("ReceiverId"); - - b.ToTable("Comment"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.UploadedFile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ContentType") - .HasColumnType("text"); - - b.Property("Length") - .HasColumnType("bigint"); - - b.Property("Path") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("UploadedFiles"); - }); - - modelBuilder.Entity("Yavsc.Models.BlogSpotPublication", b => - { - b.Property("PostId") - .HasColumnType("bigint"); - - b.HasKey("PostId"); - - b.ToTable("blogSpotPublications"); - }); - - modelBuilder.Entity("Yavsc.Models.Calendar.Schedule", b => - { - b.Property("OwnerId") - .HasColumnType("text"); - - b.HasKey("OwnerId"); - - b.ToTable("Schedule"); - }); - - modelBuilder.Entity("Yavsc.Models.Calendar.ScheduledEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("PeriodEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("PeriodStart") - .HasColumnType("timestamp with time zone"); - - b.Property("Reccurence") - .HasColumnType("integer"); - - b.Property("ScheduleOwnerId") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ScheduleOwnerId"); - - b.HasIndex("PeriodStart", "PeriodEnd"); - - b.ToTable("ScheduledEvent"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatConnection", b => - { - b.Property("ConnectionId") - .HasColumnType("text"); - - b.Property("ApplicationUserId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Connected") - .HasColumnType("boolean"); - - b.Property("UserAgent") - .HasColumnType("text"); - - b.HasKey("ConnectionId"); - - b.HasIndex("ApplicationUserId"); - - b.ToTable("ChatConnection"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatRoom", b => - { - b.Property("Name") - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("LatestJoinPart") - .HasColumnType("timestamp with time zone"); - - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("Topic") - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Name"); - - b.HasIndex("OwnerId"); - - b.ToTable("ChatRoom"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatRoomAccess", b => - { - b.Property("ChannelName") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("text"); - - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Level") - .HasColumnType("integer"); - - b.HasKey("ChannelName", "UserId"); - - b.HasIndex("UserId"); - - b.ToTable("ChatRoomAccess"); - }); - - modelBuilder.Entity("Yavsc.Models.Cratie.Option", b => - { - b.Property("Code") - .HasColumnType("text"); - - b.Property("CodeScrutin") - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Code", "CodeScrutin"); - - b.ToTable("Option"); - }); - - modelBuilder.Entity("Yavsc.Models.Drawing.Color", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Blue") - .HasColumnType("smallint"); - - b.Property("Green") - .HasColumnType("smallint"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Red") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.ToTable("Color"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.BrusherProfile", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("ActionDistance") - .HasColumnType("integer"); - - b.Property("CarePrice") - .HasColumnType("numeric"); - - b.Property("FlatFeeDiscount") - .HasColumnType("numeric"); - - b.Property("HalfBalayagePrice") - .HasColumnType("numeric"); - - b.Property("HalfBrushingPrice") - .HasColumnType("numeric"); - - b.Property("HalfColorPrice") - .HasColumnType("numeric"); - - b.Property("HalfDefrisPrice") - .HasColumnType("numeric"); - - b.Property("HalfFoldingPrice") - .HasColumnType("numeric"); - - b.Property("HalfMechPrice") - .HasColumnType("numeric"); - - b.Property("HalfMultiColorPrice") - .HasColumnType("numeric"); - - b.Property("HalfPermanentPrice") - .HasColumnType("numeric"); - - b.Property("KidCutPrice") - .HasColumnType("numeric"); - - b.Property("LongBalayagePrice") - .HasColumnType("numeric"); - - b.Property("LongBrushingPrice") - .HasColumnType("numeric"); - - b.Property("LongColorPrice") - .HasColumnType("numeric"); - - b.Property("LongDefrisPrice") - .HasColumnType("numeric"); - - b.Property("LongFoldingPrice") - .HasColumnType("numeric"); - - b.Property("LongMechPrice") - .HasColumnType("numeric"); - - b.Property("LongMultiColorPrice") - .HasColumnType("numeric"); - - b.Property("LongPermanentPrice") - .HasColumnType("numeric"); - - b.Property("ManBrushPrice") - .HasColumnType("numeric"); - - b.Property("ManCutPrice") - .HasColumnType("numeric"); - - b.Property("ScheduleOwnerId") - .HasColumnType("text"); - - b.Property("ShampooPrice") - .HasColumnType("numeric"); - - b.Property("ShortBalayagePrice") - .HasColumnType("numeric"); - - b.Property("ShortBrushingPrice") - .HasColumnType("numeric"); - - b.Property("ShortColorPrice") - .HasColumnType("numeric"); - - b.Property("ShortDefrisPrice") - .HasColumnType("numeric"); - - b.Property("ShortFoldingPrice") - .HasColumnType("numeric"); - - b.Property("ShortMechPrice") - .HasColumnType("numeric"); - - b.Property("ShortMultiColorPrice") - .HasColumnType("numeric"); - - b.Property("ShortPermanentPrice") - .HasColumnType("numeric"); - - b.Property("WomenHalfCutPrice") - .HasColumnType("numeric"); - - b.Property("WomenLongCutPrice") - .HasColumnType("numeric"); - - b.Property("WomenShortCutPrice") - .HasColumnType("numeric"); - - b.HasKey("UserId"); - - b.HasIndex("ScheduleOwnerId"); - - b.ToTable("BrusherProfile"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairPrestation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Cares") - .HasColumnType("boolean"); - - b.Property("Cut") - .HasColumnType("boolean"); - - b.Property("Dressing") - .HasColumnType("integer"); - - b.Property("Gender") - .HasColumnType("integer"); - - b.Property("Length") - .HasColumnType("integer"); - - b.Property("Shampoo") - .HasColumnType("boolean"); - - b.Property("Tech") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.ToTable("HairPrestation"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairPrestationCollectionItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("PrestationId") - .HasColumnType("bigint"); - - b.Property("QueryId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("PrestationId"); - - b.HasIndex("QueryId"); - - b.ToTable("HairPrestationCollectionItem"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairTaint", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Brand") - .HasColumnType("text"); - - b.Property("ColorId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("ColorId"); - - b.ToTable("HairTaint"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairTaintInstance", b => - { - b.Property("TaintId") - .HasColumnType("bigint"); - - b.Property("PrestationId") - .HasColumnType("bigint"); - - b.HasKey("TaintId", "PrestationId"); - - b.HasIndex("PrestationId"); - - b.ToTable("HairTaintInstance"); - }); - - modelBuilder.Entity("Yavsc.Models.IT.Evolution.Feature", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("ShortName") - .HasColumnType("text"); - - b.Property("Status") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.ToTable("Feature"); - }); - - modelBuilder.Entity("Yavsc.Models.IT.Fixing.Bug", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .IsRequired() - .HasMaxLength(10240) - .HasColumnType("character varying(10240)"); - - b.Property("FeatureId") - .HasColumnType("bigint"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("Title") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("FeatureId"); - - b.ToTable("Bug"); - }); - - modelBuilder.Entity("Yavsc.Models.Identity.DeviceDeclaration", b => - { - b.Property("DeviceId") - .HasColumnType("text"); - - b.Property("DeclarationDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("LOCALTIMESTAMP"); - - b.Property("DeviceOwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("LatestActivityUpdate") - .HasColumnType("timestamp with time zone"); - - b.Property("Model") - .IsRequired() - .HasColumnType("text"); - - b.Property("Platform") - .IsRequired() - .HasColumnType("text"); - - b.Property("Version") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("DeviceId"); - - b.HasIndex("DeviceOwnerId"); - - b.ToTable("DeviceDeclaration"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.DeclarationFlag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DeclarationId") - .HasColumnType("bigint"); - - b.Property("MatchExcerpt") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("PatternId") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("DeclarationId"); - - b.HasIndex("PatternId"); - - b.ToTable("DeclarationFlag"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.ModerationLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Action") - .HasColumnType("integer"); - - b.Property("DeclarationId") - .HasColumnType("bigint"); - - b.Property("ModeratorId") - .HasColumnType("text"); - - b.Property("ScoreDelta") - .HasColumnType("integer"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("DeclarationId"); - - b.HasIndex("ModeratorId"); - - b.HasIndex("Timestamp"); - - b.ToTable("ModerationLogs", t => - { - t.HasCheckConstraint("CK_ModerationLog_Immutable", "1=1"); - }); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.RegexAlertPattern", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("IsActive") - .HasColumnType("boolean"); - - b.Property("Pattern") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)"); - - b.Property("Severity") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("IsActive"); - - b.ToTable("RegexAlertPatterns"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.TrustDeclaration", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Content") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.Property("DeclarantTokenId") - .HasColumnType("uuid"); - - b.Property("ScoreDelta") - .HasColumnType("integer"); - - b.Property("Sentiment") - .HasColumnType("integer"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("SubmittedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("TrustTokenId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("Status"); - - b.HasIndex("SubmittedAt"); - - b.HasIndex("TrustTokenId"); - - b.ToTable("TrustDeclarations"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.TrustToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("TokenHash") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("TokenSource") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b.Property("TrustScore") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("TokenHash") - .IsUnique(); - - b.ToTable("TrustTokens"); - }); - - modelBuilder.Entity("Yavsc.Models.Market.Product", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Depth") - .HasColumnType("numeric"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Height") - .HasColumnType("numeric"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Price") - .HasColumnType("numeric"); - - b.Property("Public") - .HasColumnType("boolean"); - - b.Property("Weight") - .HasColumnType("numeric"); - - b.Property("Width") - .HasColumnType("numeric"); - - b.HasKey("Id"); - - b.ToTable("Products"); - }); - - modelBuilder.Entity("Yavsc.Models.Market.Service", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ContextId") - .HasColumnType("text"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Public") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("ContextId"); - - b.ToTable("Services"); - }); - - modelBuilder.Entity("Yavsc.Models.Messaging.Announce", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("For") - .HasColumnType("smallint"); - - b.Property("Message") - .HasColumnType("text"); - - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("Sender") - .HasColumnType("text"); - - b.Property("Topic") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("OwnerId"); - - b.ToTable("Announce"); - }); - - modelBuilder.Entity("Yavsc.Models.Messaging.DismissClicked", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("NotificationId") - .HasColumnType("bigint"); - - b.HasKey("UserId", "NotificationId"); - - b.HasIndex("NotificationId"); - - b.ToTable("DismissClicked"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Instrument", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)"); - - b.HasKey("Id"); - - b.ToTable("Instrument"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.InstrumentRating", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("InstrumentId") - .HasColumnType("bigint"); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Rate") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasAlternateKey("InstrumentId", "OwnerId"); - - b.HasIndex("OwnerId"); - - b.ToTable("InstrumentRating"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.MusicalPreference", b => - { - b.Property("OwnerProfileId") - .HasColumnType("text"); - - b.Property("DjSettingsUserId") - .HasColumnType("text"); - - b.Property("MusicLoverSettingsUserId") - .HasColumnType("text"); - - b.Property("Rate") - .HasColumnType("integer"); - - b.Property("TendencyId") - .HasColumnType("bigint"); - - b.HasKey("OwnerProfileId"); - - b.HasIndex("DjSettingsUserId"); - - b.HasIndex("MusicLoverSettingsUserId"); - - b.HasIndex("TendencyId"); - - b.ToTable("MusicalPreference"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.MusicalTendency", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)"); - - b.HasKey("Id"); - - b.ToTable("MusicalTendency"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.DjSettings", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("SoundCloudId") - .HasColumnType("text"); - - b.HasKey("UserId"); - - b.ToTable("DjSettings"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.Instrumentation", b => - { - b.Property("InstrumentId") - .HasColumnType("bigint"); - - b.Property("UserId") - .HasColumnType("text"); - - b.HasKey("InstrumentId", "UserId"); - - b.HasIndex("UserId"); - - b.ToTable("Instrumentation"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.MusicLoverSettings", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.HasKey("UserId"); - - b.ToTable("MusicLoverSettings"); - }); - - modelBuilder.Entity("Yavsc.Models.Payment.PayPalPayment", b => - { - b.Property("CreationToken") - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("ExecutorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("OrderReference") - .HasColumnType("text"); - - b.Property("PaypalPayerId") - .HasColumnType("text"); - - b.Property("State") - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("CreationToken"); - - b.HasIndex("ExecutorId"); - - b.ToTable("PayPalPayment"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Circle", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApplicationUserId") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Public") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("ApplicationUserId"); - - b.ToTable("Circle"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.CircleMember", b => - { - b.Property("MemberId") - .HasColumnType("text"); - - b.Property("CircleId") - .HasColumnType("bigint"); - - b.HasKey("MemberId", "CircleId"); - - b.HasIndex("CircleId"); - - b.ToTable("CircleMembers"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Contact", b => - { - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("text"); - - b.Property("AddressId") - .HasColumnType("bigint"); - - b.Property("ApplicationUserId") - .HasColumnType("text"); - - b.Property("EMail") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.HasKey("OwnerId", "UserId"); - - b.HasIndex("AddressId"); - - b.HasIndex("ApplicationUserId"); - - b.ToTable("Contact"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.HyperLink", b => - { - b.Property("HRef") - .HasColumnType("text"); - - b.Property("Method") - .HasColumnType("text"); - - b.Property("BrusherProfileUserId") - .HasColumnType("text"); - - b.Property("ContentType") - .HasColumnType("text"); - - b.Property("PayPalPaymentCreationToken") - .HasColumnType("text"); - - b.Property("Rel") - .HasColumnType("text"); - - b.HasKey("HRef", "Method"); - - b.HasIndex("BrusherProfileUserId"); - - b.HasIndex("PayPalPaymentCreationToken"); - - b.ToTable("HyperLink"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Location", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Address") - .IsRequired() - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Latitude") - .HasColumnType("double precision"); - - b.Property("Longitude") - .HasColumnType("double precision"); - - b.HasKey("Id"); - - b.ToTable("Locations"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.PostalAddress", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("City") - .HasColumnType("text"); - - b.Property("Country") - .HasColumnType("text"); - - b.Property("PostalCode") - .HasColumnType("text"); - - b.Property("Province") - .HasColumnType("text"); - - b.Property("State") - .HasColumnType("text"); - - b.Property("Street1") - .HasColumnType("text"); - - b.Property("Street2") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("PostalAddress"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Tag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Tags"); - }); - - modelBuilder.Entity("Yavsc.Models.Skill", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Rate") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.ToTable("SiteSkills"); - }); - - modelBuilder.Entity("Yavsc.Models.Streaming.LiveFlow", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DifferedFileName") - .HasColumnType("text"); - - b.Property("MediaType") - .HasColumnType("text"); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Pitch") - .HasColumnType("text"); - - b.Property("SequenceNumber") - .HasColumnType("integer"); - - b.Property("Title") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("OwnerId"); - - b.ToTable("LiveFlow"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Activity", b => - { - b.Property("Code") - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Hidden") - .HasColumnType("boolean"); - - b.Property("Moderated") - .HasColumnType("boolean"); - - b.Property("ModeratorGroupName") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("ParentCode") - .HasColumnType("text"); - - b.Property("Photo") - .HasColumnType("text"); - - b.Property("Rate") - .HasColumnType("integer"); - - b.Property("SettingsClassName") - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Code"); - - b.HasIndex("ParentCode"); - - b.ToTable("Activities"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.CoWorking", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("FormationSettingsUserId") - .HasColumnType("text"); - - b.Property("PerformerId") - .HasColumnType("text"); - - b.Property("WorkingForId") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("FormationSettingsUserId"); - - b.HasIndex("PerformerId"); - - b.HasIndex("WorkingForId"); - - b.ToTable("CoWorking"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.CommandForm", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ActionName") - .HasColumnType("text"); - - b.Property("ActivityCode") - .IsRequired() - .HasColumnType("text"); - - b.Property("Title") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ActivityCode"); - - b.ToTable("CommandForm"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Country", b => - { - b.Property("Code") - .HasMaxLength(2) - .HasColumnType("character varying(2)"); - - b.Property("DisplayName") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.HasKey("Code"); - - b.ToTable("Countries"); - - b.HasData( - new - { - Code = "fr", - DisplayName = "France" - }, - new - { - Code = "en", - DisplayName = "England" - }, - new - { - Code = "pt", - DisplayName = "Portugal" - }); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.DictionnaireMetier", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DomaineActiviteCode") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("Langue") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Nom") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.HasKey("Id"); - - b.HasIndex("DomaineActiviteCode", "Langue", "Nom") - .IsUnique(); - - b.ToTable("DictionnaireMetier"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.PerformerCodeInputValidation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CountryCode") - .IsRequired() - .HasMaxLength(2) - .HasColumnType("character varying(2)"); - - b.Property("ErrorMessage") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("RegularExpression") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("CountryCode"); - - b.ToTable("PerformerCodeInputValidations"); - - b.HasData( - new - { - Id = 1L, - CountryCode = "fr", - ErrorMessage = "Le code FR doit contenir entre 9 et 14 chiffres.", - RegularExpression = "^[0-9]{9,14}$" - }, - new - { - Id = 2L, - CountryCode = "en", - ErrorMessage = "Le code EN doit contenir entre 8 et 14 caracteres alphanumeriques.", - RegularExpression = "^[A-Za-z0-9]{8,14}$" - }, - new - { - Id = 3L, - CountryCode = "pt", - ErrorMessage = "Le code PT doit contenir exactement 9 chiffres.", - RegularExpression = "^[0-9]{9}$" - }); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.PerformerProfile", b => - { - b.Property("PerformerId") - .HasColumnType("text"); - - b.Property("AcceptNotifications") - .HasColumnType("boolean"); - - b.Property("AcceptPublicContact") - .HasColumnType("boolean"); - - b.Property("Active") - .HasColumnType("boolean"); - - b.Property("ExerciseCountryCode") - .IsRequired() - .HasColumnType("text"); - - b.Property("MaxDailyCost") - .HasColumnType("integer"); - - b.Property("MinDailyCost") - .HasColumnType("integer"); - - b.Property("OrganizationAddressId") - .HasColumnType("bigint"); - - b.Property("Rate") - .HasColumnType("integer"); - - b.Property("SIREN") - .IsRequired() - .HasColumnType("text"); - - b.Property("UseGeoLocalizationToReduceDistanceWithClients") - .HasColumnType("boolean"); - - b.Property("WebSite") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("PerformerId"); - - b.HasIndex("OrganizationAddressId"); - - b.ToTable("Performers"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Profiles.FormationSettings", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("DisplayName") - .HasColumnType("text"); - - b.HasKey("UserId"); - - b.ToTable("FormationSettings"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.TermeMetier", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DateSoumission") - .HasColumnType("timestamp with time zone"); - - b.Property("DateValidation") - .HasColumnType("timestamp with time zone"); - - b.Property("Definition") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.Property("DictionnaireMetierId") - .HasColumnType("bigint"); - - b.Property("Langue") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Mot") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ProposeParId") - .HasMaxLength(450) - .HasColumnType("character varying(450)"); - - b.Property("StatutValidation") - .HasColumnType("integer"); - - b.Property("ValideParId") - .HasMaxLength(450) - .HasColumnType("character varying(450)"); - - b.HasKey("Id"); - - b.HasIndex("ProposeParId"); - - b.HasIndex("ValideParId"); - - b.HasIndex("DictionnaireMetierId", "Langue", "Mot") - .IsUnique(); - - b.ToTable("TermeMetier"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.UserActivity", b => - { - b.Property("DoesCode") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("text"); - - b.Property("Weight") - .HasColumnType("integer"); - - b.HasKey("DoesCode", "UserId"); - - b.HasIndex("UserId"); - - b.ToTable("UserActivities"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.Calendar.Period", b => - { - b.Property("Start") - .HasColumnType("timestamp with time zone"); - - b.Property("End") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Start", "End"); - - b.ToTable("Period"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.EMailing.MailingTemplate", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Body") - .HasMaxLength(65536) - .HasColumnType("character varying(65536)"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("ReplyToAddress") - .HasColumnType("text"); - - b.Property("ToSend") - .HasColumnType("integer"); - - b.Property("Topic") - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("MailingTemplate"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.ProjectBuildConfiguration", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("ProjectId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("ProjectId"); - - b.ToTable("ProjectBuildConfiguration"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.SourceCode.GitRepositoryReference", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Branch") - .HasColumnType("text"); - - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text"); - - b.Property("Url") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("OwnerId"); - - b.ToTable("GitRepositoryReference"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairCutQuery", b => - { - b.HasBaseType("Yavsc.Models.Billing.NominativeServiceCommand"); - - b.Property("AdditionalInfo") - .IsRequired() - .HasColumnType("text"); - - b.Property("EventDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LocationId") - .HasColumnType("bigint"); - - b.Property("PrestationId") - .HasColumnType("bigint"); - - b.Property("SelectedProfileUserId") - .HasColumnType("text"); - - b.HasIndex("LocationId"); - - b.HasIndex("PrestationId"); - - b.HasIndex("SelectedProfileUserId"); - - b.HasDiscriminator().HasValue("HairCutQuery"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairMultiCutQuery", b => - { - b.HasBaseType("Yavsc.Models.Billing.NominativeServiceCommand"); - - b.Property("EventDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LocationId") - .HasColumnType("bigint"); - - b.HasIndex("LocationId"); - - b.ToTable("NominativeServiceCommands", t => - { - t.Property("EventDate") - .HasColumnName("HairMultiCutQuery_EventDate"); - - t.Property("LocationId") - .HasColumnName("HairMultiCutQuery_LocationId"); - }); - - b.HasDiscriminator().HasValue("HairMultiCutQuery"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.RdvQuery", b => - { - b.HasBaseType("Yavsc.Models.Billing.NominativeServiceCommand"); - - b.Property("EventDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LocationId") - .HasColumnType("bigint"); - - b.Property("LocationType") - .HasColumnType("integer"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text"); - - b.HasIndex("LocationId"); - - b.ToTable("NominativeServiceCommands", t => - { - t.Property("EventDate") - .HasColumnName("RdvQuery_EventDate"); - - t.Property("LocationId") - .HasColumnName("RdvQuery_LocationId"); - }); - - b.HasDiscriminator().HasValue("RdvQuery"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.Project", b => - { - b.HasBaseType("Yavsc.Models.Billing.NominativeServiceCommand"); - - b.Property("GitId") - .HasColumnType("bigint"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("Version") - .HasColumnType("text"); - - b.HasIndex("GitId"); - - b.HasDiscriminator().HasValue("Project"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceClaim", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", null) - .WithMany("UserClaims") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany() - .HasForeignKey("ApiResourceId1"); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceProperty", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", null) - .WithMany("Properties") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany() - .HasForeignKey("ApiResourceId1"); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceScope", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", null) - .WithMany("Scopes") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany() - .HasForeignKey("ApiResourceId1"); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceSecret", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", null) - .WithMany("Secrets") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany() - .HasForeignKey("ApiResourceId1"); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeClaim", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", null) - .WithMany("UserClaims") - .HasForeignKey("ScopeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", "Scope") - .WithMany() - .HasForeignKey("ScopeId1"); - - b.Navigation("Scope"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeProperty", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", null) - .WithMany("Properties") - .HasForeignKey("ScopeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", "Scope") - .WithMany() - .HasForeignKey("ScopeId1"); - - b.Navigation("Scope"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientClaim", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany("Claims") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientCorsOrigin", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null) - .WithMany("AllowedCorsOrigins") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId1"); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany("AllowedGrantTypes") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientIdPRestriction", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null) - .WithMany("IdentityProviderRestrictions") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId1"); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null) - .WithMany("PostLogoutRedirectUris") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId1"); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientProperty", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null) - .WithMany("Properties") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId1"); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany("RedirectUris") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany("AllowedScopes") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientSecret", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null) - .WithMany("ClientSecrets") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId1"); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceClaim", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.IdentityResource", "IdentityResource") - .WithMany("UserClaims") - .HasForeignKey("IdentityResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityResource"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceProperty", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.IdentityResource", "IdentityResource") - .WithMany("Properties") - .HasForeignKey("IdentityResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityResource"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Yavsc.Models.Access.Ban", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "TargetUser") - .WithMany() - .HasForeignKey("TargetId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("TargetUser"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.BlackListed", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany("BlackList") - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.CircleAuthorizationToBlogPost", b => - { - b.HasOne("Yavsc.Models.Blog.BlogPost", "Target") - .WithMany("ACL") - .HasForeignKey("BlogPostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Relationship.Circle", "Allowed") - .WithMany() - .HasForeignKey("CircleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Allowed"); - - b.Navigation("Target"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.CircleAuthorizationToFile", b => - { - b.HasOne("Yavsc.Models.Relationship.Circle", "Allowed") - .WithMany() - .HasForeignKey("CircleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany() - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Allowed"); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.AccountBalance", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithOne("AccountBalance") - .HasForeignKey("Yavsc.Models.AccountBalance", "UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.ApplicationUser", b => - { - b.HasOne("Yavsc.Models.Relationship.Location", "PostalAddress") - .WithMany() - .HasForeignKey("PostalAddressId"); - - b.Navigation("PostalAddress"); - }); - - modelBuilder.Entity("Yavsc.Models.BalanceImpact", b => - { - b.HasOne("Yavsc.Models.AccountBalance", "Balance") - .WithMany() - .HasForeignKey("BalanceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Balance"); - }); - - modelBuilder.Entity("Yavsc.Models.Bank.BankIdentity", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "User") - .WithMany("BankInfo") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.CommandLine", b => - { - b.HasOne("Yavsc.Models.Billing.Estimate", null) - .WithMany("Bill") - .HasForeignKey("EstimateId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Billing.EstimateTemplate", null) - .WithMany("Bill") - .HasForeignKey("EstimateTemplateId"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.Estimate", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Billing.NominativeServiceCommand", "Query") - .WithMany() - .HasForeignKey("CommandId"); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "Owner") - .WithMany() - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - - b.Navigation("Owner"); - - b.Navigation("Query"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.NominativeServiceCommand", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "Context") - .WithMany() - .HasForeignKey("ActivityCode") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Payment.PayPalPayment", "Regularization") - .WithMany() - .HasForeignKey("PaymentId"); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "PerformerProfile") - .WithMany() - .HasForeignKey("PerformerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - - b.Navigation("Context"); - - b.Navigation("PerformerProfile"); - - b.Navigation("Regularization"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.Signature", b => - { - b.HasOne("Yavsc.Models.Billing.Estimate", "Estimate") - .WithMany("Signatures") - .HasForeignKey("EstimateId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "Signer") - .WithMany() - .HasForeignKey("SignerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Estimate"); - - b.Navigation("Signer"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogAttachedFile", b => - { - b.HasOne("Yavsc.Models.Blog.UploadedFile", "File") - .WithMany() - .HasForeignKey("FileId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Blog.BlogPost", "Post") - .WithMany() - .HasForeignKey("PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("File"); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogPost", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Author") - .WithMany("Posts") - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Restrict); - - b.Navigation("Author"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogTag", b => - { - b.HasOne("Yavsc.Models.Blog.BlogPost", "Post") - .WithMany("Tags") - .HasForeignKey("PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Relationship.Tag", "Tag") - .WithMany() - .HasForeignKey("TagId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Post"); - - b.Navigation("Tag"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.Comment", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Author") - .WithMany("BlogComments") - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.HasOne("Yavsc.Models.Blog.Comment", "Parent") - .WithMany("Children") - .HasForeignKey("ParentId"); - - b.HasOne("Yavsc.Models.Blog.BlogPost", "Post") - .WithMany("Comments") - .HasForeignKey("ReceiverId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Author"); - - b.Navigation("Parent"); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("Yavsc.Models.BlogSpotPublication", b => - { - b.HasOne("Yavsc.Models.Blog.BlogPost", "Post") - .WithMany() - .HasForeignKey("PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("Yavsc.Models.Calendar.Schedule", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany() - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Calendar.ScheduledEvent", b => - { - b.HasOne("Yavsc.Models.Calendar.Schedule", null) - .WithMany("Events") - .HasForeignKey("ScheduleOwnerId"); - - b.HasOne("Yavsc.Server.Models.Calendar.Period", "Period") - .WithMany() - .HasForeignKey("PeriodStart", "PeriodEnd"); - - b.Navigation("Period"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatConnection", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany("Connections") - .HasForeignKey("ApplicationUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatRoom", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany("Rooms") - .HasForeignKey("OwnerId"); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatRoomAccess", b => - { - b.HasOne("Yavsc.Models.Chat.ChatRoom", "Room") - .WithMany("Moderation") - .HasForeignKey("ChannelName") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "User") - .WithMany("RoomAccess") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Room"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.BrusherProfile", b => - { - b.HasOne("Yavsc.Models.Calendar.Schedule", "Schedule") - .WithMany() - .HasForeignKey("ScheduleOwnerId"); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "BaseProfile") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("BaseProfile"); - - b.Navigation("Schedule"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairPrestationCollectionItem", b => - { - b.HasOne("Yavsc.Models.Haircut.HairPrestation", "Prestation") - .WithMany() - .HasForeignKey("PrestationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Haircut.HairMultiCutQuery", "Query") - .WithMany("Prestations") - .HasForeignKey("QueryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Prestation"); - - b.Navigation("Query"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairTaint", b => - { - b.HasOne("Yavsc.Models.Drawing.Color", "Color") - .WithMany() - .HasForeignKey("ColorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Color"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairTaintInstance", b => - { - b.HasOne("Yavsc.Models.Haircut.HairPrestation", "Prestation") - .WithMany("Taints") - .HasForeignKey("PrestationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Haircut.HairTaint", "Taint") - .WithMany() - .HasForeignKey("TaintId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Prestation"); - - b.Navigation("Taint"); - }); - - modelBuilder.Entity("Yavsc.Models.IT.Fixing.Bug", b => - { - b.HasOne("Yavsc.Models.IT.Evolution.Feature", "False") - .WithMany() - .HasForeignKey("FeatureId"); - - b.Navigation("False"); - }); - - modelBuilder.Entity("Yavsc.Models.Identity.DeviceDeclaration", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "DeviceOwner") - .WithMany("DeviceDeclaration") - .HasForeignKey("DeviceOwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("DeviceOwner"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.DeclarationFlag", b => - { - b.HasOne("Yavsc.Models.Kyc.TrustDeclaration", "Declaration") - .WithMany("Flags") - .HasForeignKey("DeclarationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Kyc.RegexAlertPattern", "Pattern") - .WithMany() - .HasForeignKey("PatternId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Declaration"); - - b.Navigation("Pattern"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.ModerationLog", b => - { - b.HasOne("Yavsc.Models.Kyc.TrustDeclaration", "Declaration") - .WithMany() - .HasForeignKey("DeclarationId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Declaration"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.TrustDeclaration", b => - { - b.HasOne("Yavsc.Models.Kyc.TrustToken", "Subject") - .WithMany("Declarations") - .HasForeignKey("TrustTokenId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Subject"); - }); - - modelBuilder.Entity("Yavsc.Models.Market.Service", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "Context") - .WithMany("Services") - .HasForeignKey("ContextId"); - - b.Navigation("Context"); - }); - - modelBuilder.Entity("Yavsc.Models.Messaging.Announce", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany() - .HasForeignKey("OwnerId"); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Messaging.DismissClicked", b => - { - b.HasOne("Yavsc.Abstract.Models.Messaging.Notification", "Notified") - .WithMany() - .HasForeignKey("NotificationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Notified"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.InstrumentRating", b => - { - b.HasOne("Yavsc.Models.Musical.Instrument", "Instrument") - .WithMany() - .HasForeignKey("InstrumentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "Profile") - .WithMany() - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Instrument"); - - b.Navigation("Profile"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.MusicalPreference", b => - { - b.HasOne("Yavsc.Models.Musical.Profiles.DjSettings", null) - .WithMany("SoundColor") - .HasForeignKey("DjSettingsUserId"); - - b.HasOne("Yavsc.Models.Musical.Profiles.MusicLoverSettings", null) - .WithMany("SoundColor") - .HasForeignKey("MusicLoverSettingsUserId"); - - b.HasOne("Yavsc.Models.Musical.MusicalTendency", "MusicalTendency") - .WithMany() - .HasForeignKey("TendencyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MusicalTendency"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.Instrumentation", b => - { - b.HasOne("Yavsc.Models.Musical.Instrument", "Tool") - .WithMany() - .HasForeignKey("InstrumentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Tool"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Models.Payment.PayPalPayment", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Executor") - .WithMany() - .HasForeignKey("ExecutorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Executor"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Circle", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany("Circles") - .HasForeignKey("ApplicationUserId"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.CircleMember", b => - { - b.HasOne("Yavsc.Models.Relationship.Circle", "Circle") - .WithMany("Members") - .HasForeignKey("CircleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "Member") - .WithMany("Membership") - .HasForeignKey("MemberId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Circle"); - - b.Navigation("Member"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Contact", b => - { - b.HasOne("Yavsc.Models.Relationship.PostalAddress", "PostalAddress") - .WithMany() - .HasForeignKey("AddressId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany("Book") - .HasForeignKey("ApplicationUserId"); - - b.Navigation("PostalAddress"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.HyperLink", b => - { - b.HasOne("Yavsc.Models.Haircut.BrusherProfile", null) - .WithMany("Links") - .HasForeignKey("BrusherProfileUserId"); - - b.HasOne("Yavsc.Models.Payment.PayPalPayment", null) - .WithMany("Links") - .HasForeignKey("PayPalPaymentCreationToken"); - }); - - modelBuilder.Entity("Yavsc.Models.Streaming.LiveFlow", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany() - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Activity", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "Parent") - .WithMany("Children") - .HasForeignKey("ParentCode"); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.CoWorking", b => - { - b.HasOne("Yavsc.Models.Workflow.Profiles.FormationSettings", null) - .WithMany("CoWorking") - .HasForeignKey("FormationSettingsUserId"); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "Performer") - .WithMany() - .HasForeignKey("PerformerId"); - - b.HasOne("Yavsc.Models.ApplicationUser", "WorkingFor") - .WithMany() - .HasForeignKey("WorkingForId"); - - b.Navigation("Performer"); - - b.Navigation("WorkingFor"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.CommandForm", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "Context") - .WithMany("Forms") - .HasForeignKey("ActivityCode") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Context"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.DictionnaireMetier", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "DomaineActivite") - .WithMany() - .HasForeignKey("DomaineActiviteCode") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("DomaineActivite"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.PerformerCodeInputValidation", b => - { - b.HasOne("Yavsc.Models.Workflow.Country", "Country") - .WithMany() - .HasForeignKey("CountryCode") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Country"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.PerformerProfile", b => - { - b.HasOne("Yavsc.Models.Relationship.Location", "OrganizationAddress") - .WithMany() - .HasForeignKey("OrganizationAddressId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "Performer") - .WithMany() - .HasForeignKey("PerformerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("OrganizationAddress"); - - b.Navigation("Performer"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.TermeMetier", b => - { - b.HasOne("Yavsc.Models.Workflow.DictionnaireMetier", "DictionnaireMetier") - .WithMany("Termes") - .HasForeignKey("DictionnaireMetierId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "ProposePar") - .WithMany() - .HasForeignKey("ProposeParId"); - - b.HasOne("Yavsc.Models.ApplicationUser", "ValidePar") - .WithMany() - .HasForeignKey("ValideParId"); - - b.Navigation("DictionnaireMetier"); - - b.Navigation("ProposePar"); - - b.Navigation("ValidePar"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.UserActivity", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "Does") - .WithMany() - .HasForeignKey("DoesCode") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "User") - .WithMany("Activity") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Does"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.ProjectBuildConfiguration", b => - { - b.HasOne("Yavsc.Server.Models.IT.Project", "TargetProject") - .WithMany("Configurations") - .HasForeignKey("ProjectId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("TargetProject"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.SourceCode.GitRepositoryReference", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany() - .HasForeignKey("OwnerId"); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairCutQuery", b => - { - b.HasOne("Yavsc.Models.Relationship.Location", "Location") - .WithMany() - .HasForeignKey("LocationId"); - - b.HasOne("Yavsc.Models.Haircut.HairPrestation", "Prestation") - .WithMany() - .HasForeignKey("PrestationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Haircut.BrusherProfile", "SelectedProfile") - .WithMany() - .HasForeignKey("SelectedProfileUserId"); - - b.Navigation("Location"); - - b.Navigation("Prestation"); - - b.Navigation("SelectedProfile"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairMultiCutQuery", b => - { - b.HasOne("Yavsc.Models.Relationship.Location", "Location") - .WithMany() - .HasForeignKey("LocationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Location"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.RdvQuery", b => - { - b.HasOne("Yavsc.Models.Relationship.Location", "Location") - .WithMany() - .HasForeignKey("LocationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Location"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.Project", b => - { - b.HasOne("Yavsc.Server.Models.IT.SourceCode.GitRepositoryReference", "Repository") - .WithMany() - .HasForeignKey("GitId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Repository"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResource", b => - { - b.Navigation("Properties"); - - b.Navigation("Scopes"); - - b.Navigation("Secrets"); - - b.Navigation("UserClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScope", b => - { - b.Navigation("Properties"); - - b.Navigation("UserClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.Client", b => - { - b.Navigation("AllowedCorsOrigins"); - - b.Navigation("AllowedGrantTypes"); - - b.Navigation("AllowedScopes"); - - b.Navigation("Claims"); - - b.Navigation("ClientSecrets"); - - b.Navigation("IdentityProviderRestrictions"); - - b.Navigation("PostLogoutRedirectUris"); - - b.Navigation("Properties"); - - b.Navigation("RedirectUris"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResource", b => - { - b.Navigation("Properties"); - - b.Navigation("UserClaims"); - }); - - modelBuilder.Entity("Yavsc.Models.ApplicationUser", b => - { - b.Navigation("AccountBalance"); - - b.Navigation("BankInfo"); - - b.Navigation("BlackList"); - - b.Navigation("BlogComments"); - - b.Navigation("Book"); - - b.Navigation("Circles"); - - b.Navigation("Connections"); - - b.Navigation("DeviceDeclaration"); - - b.Navigation("Membership"); - - b.Navigation("Posts"); - - b.Navigation("RoomAccess"); - - b.Navigation("Rooms"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.Estimate", b => - { - b.Navigation("Bill"); - - b.Navigation("Signatures"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.EstimateTemplate", b => - { - b.Navigation("Bill"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogPost", b => - { - b.Navigation("ACL"); - - b.Navigation("Comments"); - - b.Navigation("Tags"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.Comment", b => - { - b.Navigation("Children"); - }); - - modelBuilder.Entity("Yavsc.Models.Calendar.Schedule", b => - { - b.Navigation("Events"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatRoom", b => - { - b.Navigation("Moderation"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.BrusherProfile", b => - { - b.Navigation("Links"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairPrestation", b => - { - b.Navigation("Taints"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.TrustDeclaration", b => - { - b.Navigation("Flags"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.TrustToken", b => - { - b.Navigation("Declarations"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.DjSettings", b => - { - b.Navigation("SoundColor"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.MusicLoverSettings", b => - { - b.Navigation("SoundColor"); - }); - - modelBuilder.Entity("Yavsc.Models.Payment.PayPalPayment", b => - { - b.Navigation("Links"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Circle", b => - { - b.Navigation("Members"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Activity", b => - { - b.Navigation("Children"); - - b.Navigation("Forms"); - - b.Navigation("Services"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.DictionnaireMetier", b => - { - b.Navigation("Termes"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.PerformerProfile", b => - { - b.Navigation("Activity"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Profiles.FormationSettings", b => - { - b.Navigation("CoWorking"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairMultiCutQuery", b => - { - b.Navigation("Prestations"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.Project", b => - { - b.Navigation("Configurations"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/Yavsc.Org/Migrations/20260919195608_blogPostId.cs b/src/Yavsc.Org/Migrations/20260919195608_blogPostId.cs deleted file mode 100644 index 624571209..000000000 --- a/src/Yavsc.Org/Migrations/20260919195608_blogPostId.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Yavsc.Migrations -{ - /// - public partial class blogPostId : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_blogSpotPublications_BlogSpot_BlogpostId", - table: "blogSpotPublications"); - - migrationBuilder.RenameColumn( - name: "BlogpostId", - table: "blogSpotPublications", - newName: "PostId"); - - migrationBuilder.AddForeignKey( - name: "FK_blogSpotPublications_BlogSpot_PostId", - table: "blogSpotPublications", - column: "PostId", - principalTable: "BlogSpot", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropForeignKey( - name: "FK_blogSpotPublications_BlogSpot_PostId", - table: "blogSpotPublications"); - - migrationBuilder.RenameColumn( - name: "PostId", - table: "blogSpotPublications", - newName: "BlogpostId"); - - migrationBuilder.AddForeignKey( - name: "FK_blogSpotPublications_BlogSpot_BlogpostId", - table: "blogSpotPublications", - column: "BlogpostId", - principalTable: "BlogSpot", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - } - } -} diff --git a/src/Yavsc.Org/Migrations/20260921145454_DropSignatureFilePath.Designer.cs b/src/Yavsc.Org/Migrations/20260921145454_DropSignatureFilePath.Designer.cs deleted file mode 100644 index 04b9fdc68..000000000 --- a/src/Yavsc.Org/Migrations/20260921145454_DropSignatureFilePath.Designer.cs +++ /dev/null @@ -1,4713 +0,0 @@ -// -using System; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using Yavsc.Models; - -#nullable disable - -namespace Yavsc.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20260921145454_DropSignatureFilePath")] - partial class DropSignatureFilePath - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "10.0.12") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResource", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("AllowedAccessTokenSigningAlgorithms") - .HasColumnType("text"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("DisplayName") - .HasColumnType("text"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("LastAccessed") - .HasColumnType("timestamp with time zone"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("NonEditable") - .HasColumnType("boolean"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("boolean"); - - b.Property("Updated") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("ApiResources"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("ApiResourceId1") - .HasColumnType("integer"); - - b.Property("Type") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.HasIndex("ApiResourceId1"); - - b.ToTable("ApiResourceClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("ApiResourceId1") - .HasColumnType("integer"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.HasIndex("ApiResourceId1"); - - b.ToTable("ApiResourceProperties"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("ApiResourceId1") - .HasColumnType("integer"); - - b.Property("Scope") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.HasIndex("ApiResourceId1"); - - b.ToTable("ApiResourceScopes"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApiResourceId") - .HasColumnType("integer"); - - b.Property("ApiResourceId1") - .HasColumnType("integer"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Expiration") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ApiResourceId"); - - b.HasIndex("ApiResourceId1"); - - b.ToTable("ApiResourceSecrets"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("DisplayName") - .HasColumnType("text"); - - b.Property("Emphasize") - .HasColumnType("boolean"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Required") - .HasColumnType("boolean"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.ToTable("ApiScopes"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ScopeId") - .HasColumnType("integer"); - - b.Property("ScopeId1") - .HasColumnType("integer"); - - b.Property("Type") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ScopeId"); - - b.HasIndex("ScopeId1"); - - b.ToTable("ApiScopeClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("ScopeId") - .HasColumnType("integer"); - - b.Property("ScopeId1") - .HasColumnType("integer"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ScopeId"); - - b.HasIndex("ScopeId1"); - - b.ToTable("ApiScopeProperties"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.Client", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("AbsoluteRefreshTokenLifetime") - .HasColumnType("integer"); - - b.Property("AccessTokenLifetime") - .HasColumnType("integer"); - - b.Property("AccessTokenType") - .HasColumnType("integer"); - - b.Property("AllowAccessTokensViaBrowser") - .HasColumnType("boolean"); - - b.Property("AllowOfflineAccess") - .HasColumnType("boolean"); - - b.Property("AllowPlainTextPkce") - .HasColumnType("boolean"); - - b.Property("AllowRememberConsent") - .HasColumnType("boolean"); - - b.Property("AllowedIdentityTokenSigningAlgorithms") - .HasColumnType("text"); - - b.Property("AlwaysIncludeUserClaimsInIdToken") - .HasColumnType("boolean"); - - b.Property("AlwaysSendClientClaims") - .HasColumnType("boolean"); - - b.Property("AuthorizationCodeLifetime") - .HasColumnType("integer"); - - b.Property("BackChannelLogoutSessionRequired") - .HasColumnType("boolean"); - - b.Property("BackChannelLogoutUri") - .HasColumnType("text"); - - b.Property("ClientClaimsPrefix") - .HasColumnType("text"); - - b.Property("ClientId") - .HasColumnType("text"); - - b.Property("ClientName") - .HasColumnType("text"); - - b.Property("ClientUri") - .HasColumnType("text"); - - b.Property("ConsentLifetime") - .HasColumnType("integer"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("DeviceCodeLifetime") - .HasColumnType("integer"); - - b.Property("EnableLocalLogin") - .HasColumnType("boolean"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("FrontChannelLogoutSessionRequired") - .HasColumnType("boolean"); - - b.Property("FrontChannelLogoutUri") - .HasColumnType("text"); - - b.Property("IdentityTokenLifetime") - .HasColumnType("integer"); - - b.Property("IncludeJwtId") - .HasColumnType("boolean"); - - b.Property("LastAccessed") - .HasColumnType("timestamp with time zone"); - - b.Property("LogoUri") - .HasColumnType("text"); - - b.Property("NonEditable") - .HasColumnType("boolean"); - - b.Property("PairWiseSubjectSalt") - .HasColumnType("text"); - - b.Property("ProtocolType") - .HasColumnType("text"); - - b.Property("RefreshTokenExpiration") - .HasColumnType("integer"); - - b.Property("RefreshTokenUsage") - .HasColumnType("integer"); - - b.Property("RequireClientSecret") - .HasColumnType("boolean"); - - b.Property("RequireConsent") - .HasColumnType("boolean"); - - b.Property("RequirePkce") - .HasColumnType("boolean"); - - b.Property("RequireRequestObject") - .HasColumnType("boolean"); - - b.Property("SlidingRefreshTokenLifetime") - .HasColumnType("integer"); - - b.Property("UpdateAccessTokenClaimsOnRefresh") - .HasColumnType("boolean"); - - b.Property("Updated") - .HasColumnType("timestamp with time zone"); - - b.Property("UserCodeType") - .HasColumnType("text"); - - b.Property("UserSsoLifetime") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.ToTable("Clients"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("Type") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientCorsOrigin", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("ClientId1") - .HasColumnType("integer"); - - b.Property("Origin") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ClientId1"); - - b.ToTable("ClientCorsOrigins"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("GrantType") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientGrantTypes"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientIdPRestriction", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("ClientId1") - .HasColumnType("integer"); - - b.Property("Provider") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ClientId1"); - - b.ToTable("ClientIdPRestrictions"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("ClientId1") - .HasColumnType("integer"); - - b.Property("PostLogoutRedirectUri") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ClientId1"); - - b.ToTable("ClientPostLogoutRedirectUris"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("ClientId1") - .HasColumnType("integer"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ClientId1"); - - b.ToTable("ClientProperties"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("RedirectUri") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientRedirectUris"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("Scope") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.ToTable("ClientScopes"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientSecret", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityAlwaysColumn(b.Property("Id")); - - b.Property("ClientId") - .HasColumnType("integer"); - - b.Property("ClientId1") - .HasColumnType("integer"); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Expiration") - .HasColumnType("timestamp with time zone"); - - b.Property("Type") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("ClientId1"); - - b.ToTable("ClientSecrets"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.DeviceFlowCodes", b => - { - b.Property("UserCode") - .HasColumnType("text"); - - b.Property("DeviceCode") - .HasColumnType("text"); - - b.Property("ClientId") - .HasColumnType("text"); - - b.Property("CreationTime") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Expiration") - .HasColumnType("timestamp with time zone"); - - b.Property("SessionId") - .HasColumnType("text"); - - b.Property("SubjectId") - .HasColumnType("text"); - - b.HasKey("UserCode", "DeviceCode"); - - b.ToTable("DeviceFlowCodes"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResource", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Created") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("DisplayName") - .HasColumnType("text"); - - b.Property("Emphasize") - .HasColumnType("boolean"); - - b.Property("Enabled") - .HasColumnType("boolean"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("NonEditable") - .HasColumnType("boolean"); - - b.Property("Required") - .HasColumnType("boolean"); - - b.Property("ShowInDiscoveryDocument") - .HasColumnType("boolean"); - - b.Property("Updated") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.ToTable("IdentityResources"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("IdentityResourceId") - .HasColumnType("integer"); - - b.Property("Type") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("IdentityResourceId"); - - b.ToTable("IdentityResourceClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceProperty", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("IdentityResourceId") - .HasColumnType("integer"); - - b.Property("Key") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("IdentityResourceId"); - - b.ToTable("IdentityResourceProperties"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.PersistedGrant", b => - { - b.Property("Key") - .HasColumnType("text"); - - b.Property("ClientId") - .HasColumnType("text"); - - b.Property("ConsumedTime") - .HasColumnType("timestamp with time zone"); - - b.Property("CreationTime") - .HasColumnType("timestamp with time zone"); - - b.Property("Data") - .HasColumnType("text"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Expiration") - .HasColumnType("timestamp with time zone"); - - b.Property("SessionId") - .HasColumnType("text"); - - b.Property("SubjectId") - .HasColumnType("text"); - - b.Property("Type") - .HasColumnType("text"); - - b.HasKey("Key"); - - b.ToTable("PersistedGrants"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("Name") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("NormalizedName") - .IsUnique() - .HasDatabaseName("RoleNameIndex"); - - b.ToTable("AspNetRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("RoleId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetRoleClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ClaimType") - .HasColumnType("text"); - - b.Property("ClaimValue") - .HasColumnType("text"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserClaims", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("ProviderKey") - .HasColumnType("text"); - - b.Property("ProviderDisplayName") - .HasColumnType("text"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("LoginProvider", "ProviderKey"); - - b.HasIndex("UserId"); - - b.ToTable("AspNetUserLogins", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("RoleId") - .HasColumnType("text"); - - b.HasKey("UserId", "RoleId"); - - b.HasIndex("RoleId"); - - b.ToTable("AspNetUserRoles", (string)null); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("LoginProvider") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Value") - .HasColumnType("text"); - - b.HasKey("UserId", "LoginProvider", "Name"); - - b.ToTable("AspNetUserTokens", (string)null); - }); - - modelBuilder.Entity("Yavsc.Abstract.Identity.ClientProviderInfo", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("Avatar") - .HasColumnType("text"); - - b.Property("BillingAddressId") - .HasColumnType("bigint"); - - b.Property("EMail") - .HasColumnType("text"); - - b.Property("Phone") - .HasColumnType("text"); - - b.Property("UserName") - .HasColumnType("text"); - - b.HasKey("UserId"); - - b.ToTable("ClientProviderInfo"); - }); - - modelBuilder.Entity("Yavsc.Abstract.Models.Messaging.Notification", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Target") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("body") - .IsRequired() - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("click_action") - .IsRequired() - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("color") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("icon") - .ValueGeneratedOnAdd() - .HasMaxLength(512) - .HasColumnType("character varying(512)") - .HasDefaultValue("exclam"); - - b.Property("sound") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("tag") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("title") - .IsRequired() - .HasMaxLength(1024) - .HasColumnType("character varying(1024)"); - - b.HasKey("Id"); - - b.ToTable("Notification"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.Ban", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text"); - - b.Property("TargetId") - .IsRequired() - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("TargetId"); - - b.ToTable("Ban"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.BlackListed", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("OwnerId"); - - b.HasIndex("UserId"); - - b.ToTable("BlackListed"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.CircleAuthorizationToBlogPost", b => - { - b.Property("CircleId") - .HasColumnType("bigint"); - - b.Property("BlogPostId") - .HasColumnType("bigint"); - - b.HasKey("CircleId", "BlogPostId"); - - b.HasIndex("BlogPostId"); - - b.ToTable("CircleAuthorizationToBlogPost"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.CircleAuthorizationToFile", b => - { - b.Property("CircleId") - .HasColumnType("bigint"); - - b.Property("Path") - .HasColumnType("text"); - - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("Access") - .HasColumnType("smallint"); - - b.HasKey("CircleId", "Path", "OwnerId"); - - b.HasIndex("OwnerId"); - - b.ToTable("CircleAuthorizationToFile"); - }); - - modelBuilder.Entity("Yavsc.Models.AccountBalance", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("ContactCredits") - .HasColumnType("bigint"); - - b.Property("Credits") - .HasColumnType("numeric"); - - b.HasKey("UserId"); - - b.ToTable("BankStatus"); - }); - - modelBuilder.Entity("Yavsc.Models.ApplicationUser", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("AccessFailedCount") - .HasColumnType("integer"); - - b.Property("AllowMonthlyEmail") - .HasColumnType("boolean"); - - b.Property("Avatar") - .ValueGeneratedOnAdd() - .HasMaxLength(512) - .HasColumnType("character varying(512)") - .HasDefaultValue("/images/Users/icon_user.png"); - - b.Property("ConcurrencyStamp") - .IsConcurrencyToken() - .HasColumnType("text"); - - b.Property("DedicatedGoogleCalendar") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("DiskQuota") - .ValueGeneratedOnAdd() - .HasColumnType("bigint") - .HasDefaultValue(524288000L); - - b.Property("DiskUsage") - .HasColumnType("bigint"); - - b.Property("Email") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("EmailConfirmed") - .HasColumnType("boolean"); - - b.Property("FullName") - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("LockoutEnabled") - .HasColumnType("boolean"); - - b.Property("LockoutEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("MaxFileSize") - .HasColumnType("bigint"); - - b.Property("NormalizedEmail") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("NormalizedUserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("PasswordHash") - .HasColumnType("text"); - - b.Property("PhoneNumber") - .HasColumnType("text"); - - b.Property("PhoneNumberConfirmed") - .HasColumnType("boolean"); - - b.Property("PostalAddressId") - .HasColumnType("bigint"); - - b.Property("SecurityStamp") - .HasColumnType("text"); - - b.Property("TwoFactorEnabled") - .HasColumnType("boolean"); - - b.Property("UserName") - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasAlternateKey("Email"); - - b.HasIndex("NormalizedEmail") - .HasDatabaseName("EmailIndex"); - - b.HasIndex("NormalizedUserName") - .IsUnique() - .HasDatabaseName("UserNameIndex"); - - b.HasIndex("PostalAddressId"); - - b.ToTable("AspNetUsers", (string)null); - }); - - modelBuilder.Entity("Yavsc.Models.BalanceImpact", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("BalanceId") - .IsRequired() - .HasColumnType("text"); - - b.Property("ExecDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Impact") - .HasColumnType("numeric"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("BalanceId"); - - b.ToTable("BalanceImpact"); - }); - - modelBuilder.Entity("Yavsc.Models.Bank.BankIdentity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AccountNumber") - .IsRequired() - .HasColumnType("text"); - - b.Property("BIC") - .IsRequired() - .HasColumnType("text"); - - b.Property("BankCode") - .IsRequired() - .HasColumnType("text"); - - b.Property("BankedKey") - .HasColumnType("integer"); - - b.Property("IBAN") - .IsRequired() - .HasColumnType("text"); - - b.Property("UserId") - .IsRequired() - .HasColumnType("text"); - - b.Property("WicketCode") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("BankIdentity"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.CommandLine", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Count") - .HasColumnType("integer"); - - b.Property("Currency") - .HasColumnType("text"); - - b.Property("Description") - .IsRequired() - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("EstimateId") - .HasColumnType("bigint"); - - b.Property("EstimateTemplateId") - .HasColumnType("bigint"); - - b.Property("Name") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.Property("UnitaryCost") - .HasColumnType("numeric"); - - b.HasKey("Id"); - - b.HasIndex("EstimateId"); - - b.HasIndex("EstimateTemplateId"); - - b.ToTable("CommandLine"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.Estimate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("AttachedFilesString") - .IsRequired() - .HasColumnType("text"); - - b.Property("AttachedGraphicsString") - .IsRequired() - .HasColumnType("text"); - - b.Property("ClientId") - .IsRequired() - .HasColumnType("text"); - - b.Property("ClientValidationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("CommandId") - .HasColumnType("bigint"); - - b.Property("CommandType") - .IsRequired() - .HasColumnType("text"); - - b.Property("Description") - .IsRequired() - .HasColumnType("text"); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("ProviderValidationDate") - .HasColumnType("timestamp with time zone"); - - b.Property("Title") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ClientId"); - - b.HasIndex("CommandId"); - - b.HasIndex("OwnerId"); - - b.ToTable("Estimates"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.EstimateTemplate", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Title") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("EstimateTemplates"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.ExceptionSIREN", b => - { - b.Property("SIREN") - .HasColumnType("text"); - - b.HasKey("SIREN"); - - b.ToTable("ExceptionsSIREN"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.NominativeServiceCommand", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ActivityCode") - .IsRequired() - .HasColumnType("text"); - - b.Property("ClientId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Consent") - .HasColumnType("boolean"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .IsRequired() - .HasColumnType("text"); - - b.Property("Discriminator") - .IsRequired() - .HasMaxLength(34) - .HasColumnType("character varying(34)"); - - b.Property("PaymentId") - .HasColumnType("text"); - - b.Property("PerformerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Provisional") - .HasColumnType("numeric"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("UserCreated") - .IsRequired() - .HasColumnType("text"); - - b.Property("UserModified") - .IsRequired() - .HasColumnType("text"); - - b.Property("ValidationDate") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("ActivityCode"); - - b.HasIndex("ClientId"); - - b.HasIndex("PaymentId"); - - b.HasIndex("PerformerId"); - - b.ToTable("NominativeServiceCommands"); - - b.HasDiscriminator("Discriminator").HasValue("NominativeServiceCommand"); - - b.UseTphMappingStrategy(); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.Signature", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CapturedAtUtc") - .HasColumnType("timestamp with time zone"); - - b.Property("CoordinateMax") - .ValueGeneratedOnAdd() - .HasColumnType("integer") - .HasDefaultValue(10000); - - b.Property("EstimateId") - .HasColumnType("bigint"); - - b.Property("SignerId") - .IsRequired() - .HasColumnType("text"); - - b.PrimitiveCollection("Strokes") - .IsRequired() - .HasColumnType("integer[]"); - - b.Property("Type") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("SignerId"); - - b.HasIndex("EstimateId", "Type") - .IsUnique(); - - b.ToTable("Signatures"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogAttachedFile", b => - { - b.Property("FileId") - .HasColumnType("bigint"); - - b.Property("PostId") - .HasColumnType("bigint"); - - b.HasKey("FileId", "PostId"); - - b.HasIndex("PostId"); - - b.ToTable("BlogAttachedFiles"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogPost", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Article") - .HasMaxLength(56224) - .HasColumnType("character varying(56224)"); - - b.Property("AuthorId") - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("Photo") - .HasMaxLength(1024) - .HasColumnType("character varying(1024)"); - - b.Property("Title") - .IsRequired() - .HasMaxLength(1024) - .HasColumnType("character varying(1024)"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("AuthorId"); - - b.ToTable("BlogSpot"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogTag", b => - { - b.Property("PostId") - .HasColumnType("bigint"); - - b.Property("TagId") - .HasColumnType("bigint"); - - b.HasKey("PostId", "TagId"); - - b.HasIndex("TagId"); - - b.ToTable("BlogTag"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.Comment", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Article") - .IsRequired() - .HasColumnType("text"); - - b.Property("AuthorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("ParentId") - .HasColumnType("bigint"); - - b.Property("ReceiverId") - .HasColumnType("bigint"); - - b.Property("UserCreated") - .IsRequired() - .HasColumnType("text"); - - b.Property("UserModified") - .IsRequired() - .HasColumnType("text"); - - b.Property("Visible") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("AuthorId"); - - b.HasIndex("ParentId"); - - b.HasIndex("ReceiverId"); - - b.ToTable("Comment"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.UploadedFile", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ContentType") - .HasColumnType("text"); - - b.Property("Length") - .HasColumnType("bigint"); - - b.Property("Path") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("UploadedFiles"); - }); - - modelBuilder.Entity("Yavsc.Models.BlogSpotPublication", b => - { - b.Property("PostId") - .HasColumnType("bigint"); - - b.HasKey("PostId"); - - b.ToTable("blogSpotPublications"); - }); - - modelBuilder.Entity("Yavsc.Models.Calendar.Schedule", b => - { - b.Property("OwnerId") - .HasColumnType("text"); - - b.HasKey("OwnerId"); - - b.ToTable("Schedule"); - }); - - modelBuilder.Entity("Yavsc.Models.Calendar.ScheduledEvent", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("PeriodEnd") - .HasColumnType("timestamp with time zone"); - - b.Property("PeriodStart") - .HasColumnType("timestamp with time zone"); - - b.Property("Reccurence") - .HasColumnType("integer"); - - b.Property("ScheduleOwnerId") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ScheduleOwnerId"); - - b.HasIndex("PeriodStart", "PeriodEnd"); - - b.ToTable("ScheduledEvent"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatConnection", b => - { - b.Property("ConnectionId") - .HasColumnType("text"); - - b.Property("ApplicationUserId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Connected") - .HasColumnType("boolean"); - - b.Property("UserAgent") - .HasColumnType("text"); - - b.HasKey("ConnectionId"); - - b.HasIndex("ApplicationUserId"); - - b.ToTable("ChatConnection"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatRoom", b => - { - b.Property("Name") - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("LatestJoinPart") - .HasColumnType("timestamp with time zone"); - - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("Topic") - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Name"); - - b.HasIndex("OwnerId"); - - b.ToTable("ChatRoom"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatRoomAccess", b => - { - b.Property("ChannelName") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("text"); - - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Level") - .HasColumnType("integer"); - - b.HasKey("ChannelName", "UserId"); - - b.HasIndex("UserId"); - - b.ToTable("ChatRoomAccess"); - }); - - modelBuilder.Entity("Yavsc.Models.Cratie.Option", b => - { - b.Property("Code") - .HasColumnType("text"); - - b.Property("CodeScrutin") - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Code", "CodeScrutin"); - - b.ToTable("Option"); - }); - - modelBuilder.Entity("Yavsc.Models.Drawing.Color", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Blue") - .HasColumnType("smallint"); - - b.Property("Green") - .HasColumnType("smallint"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Red") - .HasColumnType("smallint"); - - b.HasKey("Id"); - - b.ToTable("Color"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.BrusherProfile", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("ActionDistance") - .HasColumnType("integer"); - - b.Property("CarePrice") - .HasColumnType("numeric"); - - b.Property("FlatFeeDiscount") - .HasColumnType("numeric"); - - b.Property("HalfBalayagePrice") - .HasColumnType("numeric"); - - b.Property("HalfBrushingPrice") - .HasColumnType("numeric"); - - b.Property("HalfColorPrice") - .HasColumnType("numeric"); - - b.Property("HalfDefrisPrice") - .HasColumnType("numeric"); - - b.Property("HalfFoldingPrice") - .HasColumnType("numeric"); - - b.Property("HalfMechPrice") - .HasColumnType("numeric"); - - b.Property("HalfMultiColorPrice") - .HasColumnType("numeric"); - - b.Property("HalfPermanentPrice") - .HasColumnType("numeric"); - - b.Property("KidCutPrice") - .HasColumnType("numeric"); - - b.Property("LongBalayagePrice") - .HasColumnType("numeric"); - - b.Property("LongBrushingPrice") - .HasColumnType("numeric"); - - b.Property("LongColorPrice") - .HasColumnType("numeric"); - - b.Property("LongDefrisPrice") - .HasColumnType("numeric"); - - b.Property("LongFoldingPrice") - .HasColumnType("numeric"); - - b.Property("LongMechPrice") - .HasColumnType("numeric"); - - b.Property("LongMultiColorPrice") - .HasColumnType("numeric"); - - b.Property("LongPermanentPrice") - .HasColumnType("numeric"); - - b.Property("ManBrushPrice") - .HasColumnType("numeric"); - - b.Property("ManCutPrice") - .HasColumnType("numeric"); - - b.Property("ScheduleOwnerId") - .HasColumnType("text"); - - b.Property("ShampooPrice") - .HasColumnType("numeric"); - - b.Property("ShortBalayagePrice") - .HasColumnType("numeric"); - - b.Property("ShortBrushingPrice") - .HasColumnType("numeric"); - - b.Property("ShortColorPrice") - .HasColumnType("numeric"); - - b.Property("ShortDefrisPrice") - .HasColumnType("numeric"); - - b.Property("ShortFoldingPrice") - .HasColumnType("numeric"); - - b.Property("ShortMechPrice") - .HasColumnType("numeric"); - - b.Property("ShortMultiColorPrice") - .HasColumnType("numeric"); - - b.Property("ShortPermanentPrice") - .HasColumnType("numeric"); - - b.Property("WomenHalfCutPrice") - .HasColumnType("numeric"); - - b.Property("WomenLongCutPrice") - .HasColumnType("numeric"); - - b.Property("WomenShortCutPrice") - .HasColumnType("numeric"); - - b.HasKey("UserId"); - - b.HasIndex("ScheduleOwnerId"); - - b.ToTable("BrusherProfile"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairPrestation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Cares") - .HasColumnType("boolean"); - - b.Property("Cut") - .HasColumnType("boolean"); - - b.Property("Dressing") - .HasColumnType("integer"); - - b.Property("Gender") - .HasColumnType("integer"); - - b.Property("Length") - .HasColumnType("integer"); - - b.Property("Shampoo") - .HasColumnType("boolean"); - - b.Property("Tech") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.ToTable("HairPrestation"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairPrestationCollectionItem", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("PrestationId") - .HasColumnType("bigint"); - - b.Property("QueryId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("PrestationId"); - - b.HasIndex("QueryId"); - - b.ToTable("HairPrestationCollectionItem"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairTaint", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Brand") - .HasColumnType("text"); - - b.Property("ColorId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("ColorId"); - - b.ToTable("HairTaint"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairTaintInstance", b => - { - b.Property("TaintId") - .HasColumnType("bigint"); - - b.Property("PrestationId") - .HasColumnType("bigint"); - - b.HasKey("TaintId", "PrestationId"); - - b.HasIndex("PrestationId"); - - b.ToTable("HairTaintInstance"); - }); - - modelBuilder.Entity("Yavsc.Models.IT.Evolution.Feature", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("ShortName") - .HasColumnType("text"); - - b.Property("Status") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.ToTable("Feature"); - }); - - modelBuilder.Entity("Yavsc.Models.IT.Fixing.Bug", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .IsRequired() - .HasMaxLength(10240) - .HasColumnType("character varying(10240)"); - - b.Property("FeatureId") - .HasColumnType("bigint"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("Title") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("FeatureId"); - - b.ToTable("Bug"); - }); - - modelBuilder.Entity("Yavsc.Models.Identity.DeviceDeclaration", b => - { - b.Property("DeviceId") - .HasColumnType("text"); - - b.Property("DeclarationDate") - .ValueGeneratedOnAdd() - .HasColumnType("timestamp with time zone") - .HasDefaultValueSql("LOCALTIMESTAMP"); - - b.Property("DeviceOwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("LatestActivityUpdate") - .HasColumnType("timestamp with time zone"); - - b.Property("Model") - .IsRequired() - .HasColumnType("text"); - - b.Property("Platform") - .IsRequired() - .HasColumnType("text"); - - b.Property("Version") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("DeviceId"); - - b.HasIndex("DeviceOwnerId"); - - b.ToTable("DeviceDeclaration"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.DeclarationFlag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DeclarationId") - .HasColumnType("bigint"); - - b.Property("MatchExcerpt") - .HasMaxLength(100) - .HasColumnType("character varying(100)"); - - b.Property("PatternId") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("DeclarationId"); - - b.HasIndex("PatternId"); - - b.ToTable("DeclarationFlag"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.ModerationLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Action") - .HasColumnType("integer"); - - b.Property("DeclarationId") - .HasColumnType("bigint"); - - b.Property("ModeratorId") - .HasColumnType("text"); - - b.Property("ScoreDelta") - .HasColumnType("integer"); - - b.Property("Timestamp") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Id"); - - b.HasIndex("DeclarationId"); - - b.HasIndex("ModeratorId"); - - b.HasIndex("Timestamp"); - - b.ToTable("ModerationLogs", t => - { - t.HasCheckConstraint("CK_ModerationLog_Immutable", "1=1"); - }); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.RegexAlertPattern", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Description") - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("IsActive") - .HasColumnType("boolean"); - - b.Property("Pattern") - .IsRequired() - .HasMaxLength(500) - .HasColumnType("character varying(500)"); - - b.Property("Severity") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("IsActive"); - - b.ToTable("RegexAlertPatterns"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.TrustDeclaration", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Content") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.Property("DeclarantTokenId") - .HasColumnType("uuid"); - - b.Property("ScoreDelta") - .HasColumnType("integer"); - - b.Property("Sentiment") - .HasColumnType("integer"); - - b.Property("Status") - .HasColumnType("integer"); - - b.Property("SubmittedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("TrustTokenId") - .HasColumnType("uuid"); - - b.HasKey("Id"); - - b.HasIndex("Status"); - - b.HasIndex("SubmittedAt"); - - b.HasIndex("TrustTokenId"); - - b.ToTable("TrustDeclarations"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.TrustToken", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("uuid"); - - b.Property("CreatedAt") - .HasColumnType("timestamp with time zone"); - - b.Property("TokenHash") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("TokenSource") - .IsRequired() - .HasMaxLength(32) - .HasColumnType("character varying(32)"); - - b.Property("TrustScore") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasIndex("TokenHash") - .IsUnique(); - - b.ToTable("TrustTokens"); - }); - - modelBuilder.Entity("Yavsc.Models.Market.Product", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Depth") - .HasColumnType("numeric"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Height") - .HasColumnType("numeric"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Price") - .HasColumnType("numeric"); - - b.Property("Public") - .HasColumnType("boolean"); - - b.Property("Weight") - .HasColumnType("numeric"); - - b.Property("Width") - .HasColumnType("numeric"); - - b.HasKey("Id"); - - b.ToTable("Products"); - }); - - modelBuilder.Entity("Yavsc.Models.Market.Service", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ContextId") - .HasColumnType("text"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Public") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("ContextId"); - - b.ToTable("Services"); - }); - - modelBuilder.Entity("Yavsc.Models.Messaging.Announce", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("For") - .HasColumnType("smallint"); - - b.Property("Message") - .HasColumnType("text"); - - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("Sender") - .HasColumnType("text"); - - b.Property("Topic") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("OwnerId"); - - b.ToTable("Announce"); - }); - - modelBuilder.Entity("Yavsc.Models.Messaging.DismissClicked", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("NotificationId") - .HasColumnType("bigint"); - - b.HasKey("UserId", "NotificationId"); - - b.HasIndex("NotificationId"); - - b.ToTable("DismissClicked"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Instrument", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)"); - - b.HasKey("Id"); - - b.ToTable("Instrument"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.InstrumentRating", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("InstrumentId") - .HasColumnType("bigint"); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Rate") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.HasAlternateKey("InstrumentId", "OwnerId"); - - b.HasIndex("OwnerId"); - - b.ToTable("InstrumentRating"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.MusicalPreference", b => - { - b.Property("OwnerProfileId") - .HasColumnType("text"); - - b.Property("DjSettingsUserId") - .HasColumnType("text"); - - b.Property("MusicLoverSettingsUserId") - .HasColumnType("text"); - - b.Property("Rate") - .HasColumnType("integer"); - - b.Property("TendencyId") - .HasColumnType("bigint"); - - b.HasKey("OwnerProfileId"); - - b.HasIndex("DjSettingsUserId"); - - b.HasIndex("MusicLoverSettingsUserId"); - - b.HasIndex("TendencyId"); - - b.ToTable("MusicalPreference"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.MusicalTendency", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)"); - - b.HasKey("Id"); - - b.ToTable("MusicalTendency"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.DjSettings", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("SoundCloudId") - .HasColumnType("text"); - - b.HasKey("UserId"); - - b.ToTable("DjSettings"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.Instrumentation", b => - { - b.Property("InstrumentId") - .HasColumnType("bigint"); - - b.Property("UserId") - .HasColumnType("text"); - - b.HasKey("InstrumentId", "UserId"); - - b.HasIndex("UserId"); - - b.ToTable("Instrumentation"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.MusicLoverSettings", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.HasKey("UserId"); - - b.ToTable("MusicLoverSettings"); - }); - - modelBuilder.Entity("Yavsc.Models.Payment.PayPalPayment", b => - { - b.Property("CreationToken") - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("ExecutorId") - .IsRequired() - .HasColumnType("text"); - - b.Property("OrderReference") - .HasColumnType("text"); - - b.Property("PaypalPayerId") - .HasColumnType("text"); - - b.Property("State") - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("CreationToken"); - - b.HasIndex("ExecutorId"); - - b.ToTable("PayPalPayment"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Circle", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ApplicationUserId") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Public") - .HasColumnType("boolean"); - - b.HasKey("Id"); - - b.HasIndex("ApplicationUserId"); - - b.ToTable("Circle"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.CircleMember", b => - { - b.Property("MemberId") - .HasColumnType("text"); - - b.Property("CircleId") - .HasColumnType("bigint"); - - b.HasKey("MemberId", "CircleId"); - - b.HasIndex("CircleId"); - - b.ToTable("CircleMembers"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Contact", b => - { - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("text"); - - b.Property("AddressId") - .HasColumnType("bigint"); - - b.Property("ApplicationUserId") - .HasColumnType("text"); - - b.Property("EMail") - .HasColumnType("text"); - - b.Property("Name") - .HasColumnType("text"); - - b.HasKey("OwnerId", "UserId"); - - b.HasIndex("AddressId"); - - b.HasIndex("ApplicationUserId"); - - b.ToTable("Contact"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.HyperLink", b => - { - b.Property("HRef") - .HasColumnType("text"); - - b.Property("Method") - .HasColumnType("text"); - - b.Property("BrusherProfileUserId") - .HasColumnType("text"); - - b.Property("ContentType") - .HasColumnType("text"); - - b.Property("PayPalPaymentCreationToken") - .HasColumnType("text"); - - b.Property("Rel") - .HasColumnType("text"); - - b.HasKey("HRef", "Method"); - - b.HasIndex("BrusherProfileUserId"); - - b.HasIndex("PayPalPaymentCreationToken"); - - b.ToTable("HyperLink"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Location", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Address") - .IsRequired() - .HasMaxLength(512) - .HasColumnType("character varying(512)"); - - b.Property("Latitude") - .HasColumnType("double precision"); - - b.Property("Longitude") - .HasColumnType("double precision"); - - b.HasKey("Id"); - - b.ToTable("Locations"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.PostalAddress", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("City") - .HasColumnType("text"); - - b.Property("Country") - .HasColumnType("text"); - - b.Property("PostalCode") - .HasColumnType("text"); - - b.Property("Province") - .HasColumnType("text"); - - b.Property("State") - .HasColumnType("text"); - - b.Property("Street1") - .HasColumnType("text"); - - b.Property("Street2") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("PostalAddress"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Tag", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Tags"); - }); - - modelBuilder.Entity("Yavsc.Models.Skill", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .HasColumnType("text"); - - b.Property("Rate") - .HasColumnType("integer"); - - b.HasKey("Id"); - - b.ToTable("SiteSkills"); - }); - - modelBuilder.Entity("Yavsc.Models.Streaming.LiveFlow", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DifferedFileName") - .HasColumnType("text"); - - b.Property("MediaType") - .HasColumnType("text"); - - b.Property("OwnerId") - .IsRequired() - .HasColumnType("text"); - - b.Property("Pitch") - .HasColumnType("text"); - - b.Property("SequenceNumber") - .HasColumnType("integer"); - - b.Property("Title") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("OwnerId"); - - b.ToTable("LiveFlow"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Activity", b => - { - b.Property("Code") - .HasColumnType("text"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("Description") - .HasColumnType("text"); - - b.Property("Hidden") - .HasColumnType("boolean"); - - b.Property("Moderated") - .HasColumnType("boolean"); - - b.Property("ModeratorGroupName") - .HasColumnType("text"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("ParentCode") - .HasColumnType("text"); - - b.Property("Photo") - .HasColumnType("text"); - - b.Property("Rate") - .HasColumnType("integer"); - - b.Property("SettingsClassName") - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Code"); - - b.HasIndex("ParentCode"); - - b.ToTable("Activities"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.CoWorking", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("FormationSettingsUserId") - .HasColumnType("text"); - - b.Property("PerformerId") - .HasColumnType("text"); - - b.Property("WorkingForId") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("FormationSettingsUserId"); - - b.HasIndex("PerformerId"); - - b.HasIndex("WorkingForId"); - - b.ToTable("CoWorking"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.CommandForm", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("ActionName") - .HasColumnType("text"); - - b.Property("ActivityCode") - .IsRequired() - .HasColumnType("text"); - - b.Property("Title") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("ActivityCode"); - - b.ToTable("CommandForm"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Country", b => - { - b.Property("Code") - .HasMaxLength(2) - .HasColumnType("character varying(2)"); - - b.Property("DisplayName") - .IsRequired() - .HasMaxLength(64) - .HasColumnType("character varying(64)"); - - b.HasKey("Code"); - - b.ToTable("Countries"); - - b.HasData( - new - { - Code = "fr", - DisplayName = "France" - }, - new - { - Code = "en", - DisplayName = "England" - }, - new - { - Code = "pt", - DisplayName = "Portugal" - }); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.DictionnaireMetier", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DomaineActiviteCode") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("Langue") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Nom") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.HasKey("Id"); - - b.HasIndex("DomaineActiviteCode", "Langue", "Nom") - .IsUnique(); - - b.ToTable("DictionnaireMetier"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.PerformerCodeInputValidation", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("CountryCode") - .IsRequired() - .HasMaxLength(2) - .HasColumnType("character varying(2)"); - - b.Property("ErrorMessage") - .IsRequired() - .HasMaxLength(128) - .HasColumnType("character varying(128)"); - - b.Property("RegularExpression") - .IsRequired() - .HasMaxLength(256) - .HasColumnType("character varying(256)"); - - b.HasKey("Id"); - - b.HasIndex("CountryCode"); - - b.ToTable("PerformerCodeInputValidations"); - - b.HasData( - new - { - Id = 1L, - CountryCode = "fr", - ErrorMessage = "Le code FR doit contenir entre 9 et 14 chiffres.", - RegularExpression = "^[0-9]{9,14}$" - }, - new - { - Id = 2L, - CountryCode = "en", - ErrorMessage = "Le code EN doit contenir entre 8 et 14 caracteres alphanumeriques.", - RegularExpression = "^[A-Za-z0-9]{8,14}$" - }, - new - { - Id = 3L, - CountryCode = "pt", - ErrorMessage = "Le code PT doit contenir exactement 9 chiffres.", - RegularExpression = "^[0-9]{9}$" - }); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.PerformerProfile", b => - { - b.Property("PerformerId") - .HasColumnType("text"); - - b.Property("AcceptNotifications") - .HasColumnType("boolean"); - - b.Property("AcceptPublicContact") - .HasColumnType("boolean"); - - b.Property("Active") - .HasColumnType("boolean"); - - b.Property("ExerciseCountryCode") - .IsRequired() - .HasColumnType("text"); - - b.Property("MaxDailyCost") - .HasColumnType("integer"); - - b.Property("MinDailyCost") - .HasColumnType("integer"); - - b.Property("OrganizationAddressId") - .HasColumnType("bigint"); - - b.Property("Rate") - .HasColumnType("integer"); - - b.Property("SIREN") - .IsRequired() - .HasColumnType("text"); - - b.Property("UseGeoLocalizationToReduceDistanceWithClients") - .HasColumnType("boolean"); - - b.Property("WebSite") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("PerformerId"); - - b.HasIndex("OrganizationAddressId"); - - b.ToTable("Performers"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Profiles.FormationSettings", b => - { - b.Property("UserId") - .HasColumnType("text"); - - b.Property("DisplayName") - .HasColumnType("text"); - - b.HasKey("UserId"); - - b.ToTable("FormationSettings"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.TermeMetier", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("DateSoumission") - .HasColumnType("timestamp with time zone"); - - b.Property("DateValidation") - .HasColumnType("timestamp with time zone"); - - b.Property("Definition") - .IsRequired() - .HasMaxLength(2000) - .HasColumnType("character varying(2000)"); - - b.Property("DictionnaireMetierId") - .HasColumnType("bigint"); - - b.Property("Langue") - .IsRequired() - .HasMaxLength(20) - .HasColumnType("character varying(20)"); - - b.Property("Mot") - .IsRequired() - .HasMaxLength(200) - .HasColumnType("character varying(200)"); - - b.Property("ProposeParId") - .HasMaxLength(450) - .HasColumnType("character varying(450)"); - - b.Property("StatutValidation") - .HasColumnType("integer"); - - b.Property("ValideParId") - .HasMaxLength(450) - .HasColumnType("character varying(450)"); - - b.HasKey("Id"); - - b.HasIndex("ProposeParId"); - - b.HasIndex("ValideParId"); - - b.HasIndex("DictionnaireMetierId", "Langue", "Mot") - .IsUnique(); - - b.ToTable("TermeMetier"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.UserActivity", b => - { - b.Property("DoesCode") - .HasColumnType("text"); - - b.Property("UserId") - .HasColumnType("text"); - - b.Property("Weight") - .HasColumnType("integer"); - - b.HasKey("DoesCode", "UserId"); - - b.HasIndex("UserId"); - - b.ToTable("UserActivities"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.Calendar.Period", b => - { - b.Property("Start") - .HasColumnType("timestamp with time zone"); - - b.Property("End") - .HasColumnType("timestamp with time zone"); - - b.HasKey("Start", "End"); - - b.ToTable("Period"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.EMailing.MailingTemplate", b => - { - b.Property("Id") - .HasColumnType("text"); - - b.Property("Body") - .HasMaxLength(65536) - .HasColumnType("character varying(65536)"); - - b.Property("DateCreated") - .HasColumnType("timestamp with time zone"); - - b.Property("DateModified") - .HasColumnType("timestamp with time zone"); - - b.Property("ReplyToAddress") - .HasColumnType("text"); - - b.Property("ToSend") - .HasColumnType("integer"); - - b.Property("Topic") - .HasColumnType("text"); - - b.Property("UserCreated") - .HasColumnType("text"); - - b.Property("UserModified") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("MailingTemplate"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.ProjectBuildConfiguration", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("ProjectId") - .HasColumnType("bigint"); - - b.HasKey("Id"); - - b.HasIndex("ProjectId"); - - b.ToTable("ProjectBuildConfiguration"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.SourceCode.GitRepositoryReference", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("bigint"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Branch") - .HasColumnType("text"); - - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("Path") - .IsRequired() - .HasColumnType("text"); - - b.Property("Url") - .HasColumnType("text"); - - b.HasKey("Id"); - - b.HasIndex("OwnerId"); - - b.ToTable("GitRepositoryReference"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairCutQuery", b => - { - b.HasBaseType("Yavsc.Models.Billing.NominativeServiceCommand"); - - b.Property("AdditionalInfo") - .IsRequired() - .HasColumnType("text"); - - b.Property("EventDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LocationId") - .HasColumnType("bigint"); - - b.Property("PrestationId") - .HasColumnType("bigint"); - - b.Property("SelectedProfileUserId") - .HasColumnType("text"); - - b.HasIndex("LocationId"); - - b.HasIndex("PrestationId"); - - b.HasIndex("SelectedProfileUserId"); - - b.HasDiscriminator().HasValue("HairCutQuery"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairMultiCutQuery", b => - { - b.HasBaseType("Yavsc.Models.Billing.NominativeServiceCommand"); - - b.Property("EventDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LocationId") - .HasColumnType("bigint"); - - b.HasIndex("LocationId"); - - b.ToTable("NominativeServiceCommands", t => - { - t.Property("EventDate") - .HasColumnName("HairMultiCutQuery_EventDate"); - - t.Property("LocationId") - .HasColumnName("HairMultiCutQuery_LocationId"); - }); - - b.HasDiscriminator().HasValue("HairMultiCutQuery"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.RdvQuery", b => - { - b.HasBaseType("Yavsc.Models.Billing.NominativeServiceCommand"); - - b.Property("EventDate") - .HasColumnType("timestamp with time zone"); - - b.Property("LocationId") - .HasColumnType("bigint"); - - b.Property("LocationType") - .HasColumnType("integer"); - - b.Property("Reason") - .IsRequired() - .HasColumnType("text"); - - b.HasIndex("LocationId"); - - b.ToTable("NominativeServiceCommands", t => - { - t.Property("EventDate") - .HasColumnName("RdvQuery_EventDate"); - - t.Property("LocationId") - .HasColumnName("RdvQuery_LocationId"); - }); - - b.HasDiscriminator().HasValue("RdvQuery"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.Project", b => - { - b.HasBaseType("Yavsc.Models.Billing.NominativeServiceCommand"); - - b.Property("GitId") - .HasColumnType("bigint"); - - b.Property("Name") - .IsRequired() - .HasColumnType("text"); - - b.Property("OwnerId") - .HasColumnType("text"); - - b.Property("Version") - .HasColumnType("text"); - - b.HasIndex("GitId"); - - b.HasDiscriminator().HasValue("Project"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceClaim", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", null) - .WithMany("UserClaims") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany() - .HasForeignKey("ApiResourceId1"); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceProperty", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", null) - .WithMany("Properties") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany() - .HasForeignKey("ApiResourceId1"); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceScope", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", null) - .WithMany("Scopes") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany() - .HasForeignKey("ApiResourceId1"); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceSecret", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", null) - .WithMany("Secrets") - .HasForeignKey("ApiResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") - .WithMany() - .HasForeignKey("ApiResourceId1"); - - b.Navigation("ApiResource"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeClaim", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", null) - .WithMany("UserClaims") - .HasForeignKey("ScopeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", "Scope") - .WithMany() - .HasForeignKey("ScopeId1"); - - b.Navigation("Scope"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeProperty", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", null) - .WithMany("Properties") - .HasForeignKey("ScopeId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", "Scope") - .WithMany() - .HasForeignKey("ScopeId1"); - - b.Navigation("Scope"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientClaim", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany("Claims") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientCorsOrigin", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null) - .WithMany("AllowedCorsOrigins") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId1"); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany("AllowedGrantTypes") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientIdPRestriction", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null) - .WithMany("IdentityProviderRestrictions") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId1"); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null) - .WithMany("PostLogoutRedirectUris") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId1"); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientProperty", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null) - .WithMany("Properties") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId1"); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany("RedirectUris") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany("AllowedScopes") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientSecret", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", null) - .WithMany("ClientSecrets") - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") - .WithMany() - .HasForeignKey("ClientId1"); - - b.Navigation("Client"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceClaim", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.IdentityResource", "IdentityResource") - .WithMany("UserClaims") - .HasForeignKey("IdentityResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityResource"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceProperty", b => - { - b.HasOne("IdentityServer8.EntityFramework.Entities.IdentityResource", "IdentityResource") - .WithMany("Properties") - .HasForeignKey("IdentityResourceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("IdentityResource"); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => - { - b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) - .WithMany() - .HasForeignKey("RoleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Yavsc.Models.Access.Ban", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "TargetUser") - .WithMany() - .HasForeignKey("TargetId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("TargetUser"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.BlackListed", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany("BlackList") - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.CircleAuthorizationToBlogPost", b => - { - b.HasOne("Yavsc.Models.Blog.BlogPost", "Target") - .WithMany("ACL") - .HasForeignKey("BlogPostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Relationship.Circle", "Allowed") - .WithMany() - .HasForeignKey("CircleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Allowed"); - - b.Navigation("Target"); - }); - - modelBuilder.Entity("Yavsc.Models.Access.CircleAuthorizationToFile", b => - { - b.HasOne("Yavsc.Models.Relationship.Circle", "Allowed") - .WithMany() - .HasForeignKey("CircleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany() - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Allowed"); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.AccountBalance", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithOne("AccountBalance") - .HasForeignKey("Yavsc.Models.AccountBalance", "UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.ApplicationUser", b => - { - b.HasOne("Yavsc.Models.Relationship.Location", "PostalAddress") - .WithMany() - .HasForeignKey("PostalAddressId"); - - b.Navigation("PostalAddress"); - }); - - modelBuilder.Entity("Yavsc.Models.BalanceImpact", b => - { - b.HasOne("Yavsc.Models.AccountBalance", "Balance") - .WithMany() - .HasForeignKey("BalanceId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Balance"); - }); - - modelBuilder.Entity("Yavsc.Models.Bank.BankIdentity", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "User") - .WithMany("BankInfo") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.CommandLine", b => - { - b.HasOne("Yavsc.Models.Billing.Estimate", null) - .WithMany("Bill") - .HasForeignKey("EstimateId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Billing.EstimateTemplate", null) - .WithMany("Bill") - .HasForeignKey("EstimateTemplateId"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.Estimate", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Billing.NominativeServiceCommand", "Query") - .WithMany() - .HasForeignKey("CommandId"); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "Owner") - .WithMany() - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - - b.Navigation("Owner"); - - b.Navigation("Query"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.NominativeServiceCommand", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "Context") - .WithMany() - .HasForeignKey("ActivityCode") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "Client") - .WithMany() - .HasForeignKey("ClientId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Payment.PayPalPayment", "Regularization") - .WithMany() - .HasForeignKey("PaymentId"); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "PerformerProfile") - .WithMany() - .HasForeignKey("PerformerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Client"); - - b.Navigation("Context"); - - b.Navigation("PerformerProfile"); - - b.Navigation("Regularization"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.Signature", b => - { - b.HasOne("Yavsc.Models.Billing.Estimate", "Estimate") - .WithMany("Signatures") - .HasForeignKey("EstimateId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "Signer") - .WithMany() - .HasForeignKey("SignerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Estimate"); - - b.Navigation("Signer"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogAttachedFile", b => - { - b.HasOne("Yavsc.Models.Blog.UploadedFile", "File") - .WithMany() - .HasForeignKey("FileId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Blog.BlogPost", "Post") - .WithMany() - .HasForeignKey("PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("File"); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogPost", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Author") - .WithMany("Posts") - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Restrict); - - b.Navigation("Author"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogTag", b => - { - b.HasOne("Yavsc.Models.Blog.BlogPost", "Post") - .WithMany("Tags") - .HasForeignKey("PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Relationship.Tag", "Tag") - .WithMany() - .HasForeignKey("TagId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Post"); - - b.Navigation("Tag"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.Comment", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Author") - .WithMany("BlogComments") - .HasForeignKey("AuthorId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.HasOne("Yavsc.Models.Blog.Comment", "Parent") - .WithMany("Children") - .HasForeignKey("ParentId"); - - b.HasOne("Yavsc.Models.Blog.BlogPost", "Post") - .WithMany("Comments") - .HasForeignKey("ReceiverId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Author"); - - b.Navigation("Parent"); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("Yavsc.Models.BlogSpotPublication", b => - { - b.HasOne("Yavsc.Models.Blog.BlogPost", "Post") - .WithOne("Publication") - .HasForeignKey("Yavsc.Models.BlogSpotPublication", "PostId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Post"); - }); - - modelBuilder.Entity("Yavsc.Models.Calendar.Schedule", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany() - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Calendar.ScheduledEvent", b => - { - b.HasOne("Yavsc.Models.Calendar.Schedule", null) - .WithMany("Events") - .HasForeignKey("ScheduleOwnerId"); - - b.HasOne("Yavsc.Server.Models.Calendar.Period", "Period") - .WithMany() - .HasForeignKey("PeriodStart", "PeriodEnd"); - - b.Navigation("Period"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatConnection", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany("Connections") - .HasForeignKey("ApplicationUserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatRoom", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany("Rooms") - .HasForeignKey("OwnerId"); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatRoomAccess", b => - { - b.HasOne("Yavsc.Models.Chat.ChatRoom", "Room") - .WithMany("Moderation") - .HasForeignKey("ChannelName") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "User") - .WithMany("RoomAccess") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Room"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.BrusherProfile", b => - { - b.HasOne("Yavsc.Models.Calendar.Schedule", "Schedule") - .WithMany() - .HasForeignKey("ScheduleOwnerId"); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "BaseProfile") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("BaseProfile"); - - b.Navigation("Schedule"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairPrestationCollectionItem", b => - { - b.HasOne("Yavsc.Models.Haircut.HairPrestation", "Prestation") - .WithMany() - .HasForeignKey("PrestationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Haircut.HairMultiCutQuery", "Query") - .WithMany("Prestations") - .HasForeignKey("QueryId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Prestation"); - - b.Navigation("Query"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairTaint", b => - { - b.HasOne("Yavsc.Models.Drawing.Color", "Color") - .WithMany() - .HasForeignKey("ColorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Color"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairTaintInstance", b => - { - b.HasOne("Yavsc.Models.Haircut.HairPrestation", "Prestation") - .WithMany("Taints") - .HasForeignKey("PrestationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Haircut.HairTaint", "Taint") - .WithMany() - .HasForeignKey("TaintId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Prestation"); - - b.Navigation("Taint"); - }); - - modelBuilder.Entity("Yavsc.Models.IT.Fixing.Bug", b => - { - b.HasOne("Yavsc.Models.IT.Evolution.Feature", "False") - .WithMany() - .HasForeignKey("FeatureId"); - - b.Navigation("False"); - }); - - modelBuilder.Entity("Yavsc.Models.Identity.DeviceDeclaration", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "DeviceOwner") - .WithMany("DeviceDeclaration") - .HasForeignKey("DeviceOwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("DeviceOwner"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.DeclarationFlag", b => - { - b.HasOne("Yavsc.Models.Kyc.TrustDeclaration", "Declaration") - .WithMany("Flags") - .HasForeignKey("DeclarationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Kyc.RegexAlertPattern", "Pattern") - .WithMany() - .HasForeignKey("PatternId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Declaration"); - - b.Navigation("Pattern"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.ModerationLog", b => - { - b.HasOne("Yavsc.Models.Kyc.TrustDeclaration", "Declaration") - .WithMany() - .HasForeignKey("DeclarationId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Declaration"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.TrustDeclaration", b => - { - b.HasOne("Yavsc.Models.Kyc.TrustToken", "Subject") - .WithMany("Declarations") - .HasForeignKey("TrustTokenId") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Subject"); - }); - - modelBuilder.Entity("Yavsc.Models.Market.Service", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "Context") - .WithMany("Services") - .HasForeignKey("ContextId"); - - b.Navigation("Context"); - }); - - modelBuilder.Entity("Yavsc.Models.Messaging.Announce", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany() - .HasForeignKey("OwnerId"); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Messaging.DismissClicked", b => - { - b.HasOne("Yavsc.Abstract.Models.Messaging.Notification", "Notified") - .WithMany() - .HasForeignKey("NotificationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Notified"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.InstrumentRating", b => - { - b.HasOne("Yavsc.Models.Musical.Instrument", "Instrument") - .WithMany() - .HasForeignKey("InstrumentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "Profile") - .WithMany() - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Instrument"); - - b.Navigation("Profile"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.MusicalPreference", b => - { - b.HasOne("Yavsc.Models.Musical.Profiles.DjSettings", null) - .WithMany("SoundColor") - .HasForeignKey("DjSettingsUserId"); - - b.HasOne("Yavsc.Models.Musical.Profiles.MusicLoverSettings", null) - .WithMany("SoundColor") - .HasForeignKey("MusicLoverSettingsUserId"); - - b.HasOne("Yavsc.Models.Musical.MusicalTendency", "MusicalTendency") - .WithMany() - .HasForeignKey("TendencyId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("MusicalTendency"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.Instrumentation", b => - { - b.HasOne("Yavsc.Models.Musical.Instrument", "Tool") - .WithMany() - .HasForeignKey("InstrumentId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "User") - .WithMany() - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Tool"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Models.Payment.PayPalPayment", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Executor") - .WithMany() - .HasForeignKey("ExecutorId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Executor"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Circle", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany("Circles") - .HasForeignKey("ApplicationUserId"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.CircleMember", b => - { - b.HasOne("Yavsc.Models.Relationship.Circle", "Circle") - .WithMany("Members") - .HasForeignKey("CircleId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "Member") - .WithMany("Membership") - .HasForeignKey("MemberId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Circle"); - - b.Navigation("Member"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Contact", b => - { - b.HasOne("Yavsc.Models.Relationship.PostalAddress", "PostalAddress") - .WithMany() - .HasForeignKey("AddressId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", null) - .WithMany("Book") - .HasForeignKey("ApplicationUserId"); - - b.Navigation("PostalAddress"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.HyperLink", b => - { - b.HasOne("Yavsc.Models.Haircut.BrusherProfile", null) - .WithMany("Links") - .HasForeignKey("BrusherProfileUserId"); - - b.HasOne("Yavsc.Models.Payment.PayPalPayment", null) - .WithMany("Links") - .HasForeignKey("PayPalPaymentCreationToken"); - }); - - modelBuilder.Entity("Yavsc.Models.Streaming.LiveFlow", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany() - .HasForeignKey("OwnerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Activity", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "Parent") - .WithMany("Children") - .HasForeignKey("ParentCode"); - - b.Navigation("Parent"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.CoWorking", b => - { - b.HasOne("Yavsc.Models.Workflow.Profiles.FormationSettings", null) - .WithMany("CoWorking") - .HasForeignKey("FormationSettingsUserId"); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "Performer") - .WithMany() - .HasForeignKey("PerformerId"); - - b.HasOne("Yavsc.Models.ApplicationUser", "WorkingFor") - .WithMany() - .HasForeignKey("WorkingForId"); - - b.Navigation("Performer"); - - b.Navigation("WorkingFor"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.CommandForm", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "Context") - .WithMany("Forms") - .HasForeignKey("ActivityCode") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Context"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.DictionnaireMetier", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "DomaineActivite") - .WithMany() - .HasForeignKey("DomaineActiviteCode") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("DomaineActivite"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.PerformerCodeInputValidation", b => - { - b.HasOne("Yavsc.Models.Workflow.Country", "Country") - .WithMany() - .HasForeignKey("CountryCode") - .OnDelete(DeleteBehavior.Restrict) - .IsRequired(); - - b.Navigation("Country"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.PerformerProfile", b => - { - b.HasOne("Yavsc.Models.Relationship.Location", "OrganizationAddress") - .WithMany() - .HasForeignKey("OrganizationAddressId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "Performer") - .WithMany() - .HasForeignKey("PerformerId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("OrganizationAddress"); - - b.Navigation("Performer"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.TermeMetier", b => - { - b.HasOne("Yavsc.Models.Workflow.DictionnaireMetier", "DictionnaireMetier") - .WithMany("Termes") - .HasForeignKey("DictionnaireMetierId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.ApplicationUser", "ProposePar") - .WithMany() - .HasForeignKey("ProposeParId"); - - b.HasOne("Yavsc.Models.ApplicationUser", "ValidePar") - .WithMany() - .HasForeignKey("ValideParId"); - - b.Navigation("DictionnaireMetier"); - - b.Navigation("ProposePar"); - - b.Navigation("ValidePar"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.UserActivity", b => - { - b.HasOne("Yavsc.Models.Workflow.Activity", "Does") - .WithMany() - .HasForeignKey("DoesCode") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Workflow.PerformerProfile", "User") - .WithMany("Activity") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Does"); - - b.Navigation("User"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.ProjectBuildConfiguration", b => - { - b.HasOne("Yavsc.Server.Models.IT.Project", "TargetProject") - .WithMany("Configurations") - .HasForeignKey("ProjectId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("TargetProject"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.SourceCode.GitRepositoryReference", b => - { - b.HasOne("Yavsc.Models.ApplicationUser", "Owner") - .WithMany() - .HasForeignKey("OwnerId"); - - b.Navigation("Owner"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairCutQuery", b => - { - b.HasOne("Yavsc.Models.Relationship.Location", "Location") - .WithMany() - .HasForeignKey("LocationId"); - - b.HasOne("Yavsc.Models.Haircut.HairPrestation", "Prestation") - .WithMany() - .HasForeignKey("PrestationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.HasOne("Yavsc.Models.Haircut.BrusherProfile", "SelectedProfile") - .WithMany() - .HasForeignKey("SelectedProfileUserId"); - - b.Navigation("Location"); - - b.Navigation("Prestation"); - - b.Navigation("SelectedProfile"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairMultiCutQuery", b => - { - b.HasOne("Yavsc.Models.Relationship.Location", "Location") - .WithMany() - .HasForeignKey("LocationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Location"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.RdvQuery", b => - { - b.HasOne("Yavsc.Models.Relationship.Location", "Location") - .WithMany() - .HasForeignKey("LocationId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Location"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.Project", b => - { - b.HasOne("Yavsc.Server.Models.IT.SourceCode.GitRepositoryReference", "Repository") - .WithMany() - .HasForeignKey("GitId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - - b.Navigation("Repository"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResource", b => - { - b.Navigation("Properties"); - - b.Navigation("Scopes"); - - b.Navigation("Secrets"); - - b.Navigation("UserClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScope", b => - { - b.Navigation("Properties"); - - b.Navigation("UserClaims"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.Client", b => - { - b.Navigation("AllowedCorsOrigins"); - - b.Navigation("AllowedGrantTypes"); - - b.Navigation("AllowedScopes"); - - b.Navigation("Claims"); - - b.Navigation("ClientSecrets"); - - b.Navigation("IdentityProviderRestrictions"); - - b.Navigation("PostLogoutRedirectUris"); - - b.Navigation("Properties"); - - b.Navigation("RedirectUris"); - }); - - modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResource", b => - { - b.Navigation("Properties"); - - b.Navigation("UserClaims"); - }); - - modelBuilder.Entity("Yavsc.Models.ApplicationUser", b => - { - b.Navigation("AccountBalance"); - - b.Navigation("BankInfo"); - - b.Navigation("BlackList"); - - b.Navigation("BlogComments"); - - b.Navigation("Book"); - - b.Navigation("Circles"); - - b.Navigation("Connections"); - - b.Navigation("DeviceDeclaration"); - - b.Navigation("Membership"); - - b.Navigation("Posts"); - - b.Navigation("RoomAccess"); - - b.Navigation("Rooms"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.Estimate", b => - { - b.Navigation("Bill"); - - b.Navigation("Signatures"); - }); - - modelBuilder.Entity("Yavsc.Models.Billing.EstimateTemplate", b => - { - b.Navigation("Bill"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.BlogPost", b => - { - b.Navigation("ACL"); - - b.Navigation("Comments"); - - b.Navigation("Publication") - .IsRequired(); - - b.Navigation("Tags"); - }); - - modelBuilder.Entity("Yavsc.Models.Blog.Comment", b => - { - b.Navigation("Children"); - }); - - modelBuilder.Entity("Yavsc.Models.Calendar.Schedule", b => - { - b.Navigation("Events"); - }); - - modelBuilder.Entity("Yavsc.Models.Chat.ChatRoom", b => - { - b.Navigation("Moderation"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.BrusherProfile", b => - { - b.Navigation("Links"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairPrestation", b => - { - b.Navigation("Taints"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.TrustDeclaration", b => - { - b.Navigation("Flags"); - }); - - modelBuilder.Entity("Yavsc.Models.Kyc.TrustToken", b => - { - b.Navigation("Declarations"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.DjSettings", b => - { - b.Navigation("SoundColor"); - }); - - modelBuilder.Entity("Yavsc.Models.Musical.Profiles.MusicLoverSettings", b => - { - b.Navigation("SoundColor"); - }); - - modelBuilder.Entity("Yavsc.Models.Payment.PayPalPayment", b => - { - b.Navigation("Links"); - }); - - modelBuilder.Entity("Yavsc.Models.Relationship.Circle", b => - { - b.Navigation("Members"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Activity", b => - { - b.Navigation("Children"); - - b.Navigation("Forms"); - - b.Navigation("Services"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.DictionnaireMetier", b => - { - b.Navigation("Termes"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.PerformerProfile", b => - { - b.Navigation("Activity"); - }); - - modelBuilder.Entity("Yavsc.Models.Workflow.Profiles.FormationSettings", b => - { - b.Navigation("CoWorking"); - }); - - modelBuilder.Entity("Yavsc.Models.Haircut.HairMultiCutQuery", b => - { - b.Navigation("Prestations"); - }); - - modelBuilder.Entity("Yavsc.Server.Models.IT.Project", b => - { - b.Navigation("Configurations"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/src/Yavsc.Org/Migrations/20260921145454_DropSignatureFilePath.cs b/src/Yavsc.Org/Migrations/20260921145454_DropSignatureFilePath.cs deleted file mode 100644 index d29c153cb..000000000 --- a/src/Yavsc.Org/Migrations/20260921145454_DropSignatureFilePath.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace Yavsc.Migrations -{ - /// - public partial class DropSignatureFilePath : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropColumn( - name: "FilePath", - table: "Signatures"); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.AddColumn( - name: "FilePath", - table: "Signatures", - type: "text", - nullable: false, - defaultValue: ""); - } - } -} diff --git a/src/Yavsc.Org/Migrations/ApplicationDbContextModelSnapshot.cs b/src/Yavsc.Org/Migrations/ApplicationDbContextModelSnapshot.cs index 2b8b98354..84960c5bc 100644 --- a/src/Yavsc.Org/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/src/Yavsc.Org/Migrations/ApplicationDbContextModelSnapshot.cs @@ -17,7 +17,7 @@ namespace Yavsc.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "10.0.12") + .HasAnnotation("ProductVersion", "10.0.9") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); @@ -1521,6 +1521,10 @@ namespace Yavsc.Migrations b.Property("EstimateId") .HasColumnType("bigint"); + b.Property("FilePath") + .IsRequired() + .HasColumnType("text"); + b.Property("SignerId") .IsRequired() .HasColumnType("text"); @@ -1689,10 +1693,10 @@ namespace Yavsc.Migrations modelBuilder.Entity("Yavsc.Models.BlogSpotPublication", b => { - b.Property("PostId") + b.Property("BlogpostId") .HasColumnType("bigint"); - b.HasKey("PostId"); + b.HasKey("BlogpostId"); b.ToTable("blogSpotPublications"); }); @@ -3954,13 +3958,13 @@ namespace Yavsc.Migrations modelBuilder.Entity("Yavsc.Models.BlogSpotPublication", b => { - b.HasOne("Yavsc.Models.Blog.BlogPost", "Post") - .WithOne("Publication") - .HasForeignKey("Yavsc.Models.BlogSpotPublication", "PostId") + b.HasOne("Yavsc.Models.Blog.BlogPost", "BlogPost") + .WithMany() + .HasForeignKey("BlogpostId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); - b.Navigation("Post"); + b.Navigation("BlogPost"); }); modelBuilder.Entity("Yavsc.Models.Calendar.Schedule", b => @@ -4610,9 +4614,6 @@ namespace Yavsc.Migrations b.Navigation("Comments"); - b.Navigation("Publication") - .IsRequired(); - b.Navigation("Tags"); }); diff --git a/src/Yavsc.Org/ViewComponents/BillViewComponent.cs b/src/Yavsc.Org/ViewComponents/BillViewComponent.cs new file mode 100644 index 000000000..0217ebcf2 --- /dev/null +++ b/src/Yavsc.Org/ViewComponents/BillViewComponent.cs @@ -0,0 +1,108 @@ + +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Localization; +using Yavsc.Billing; +using Yavsc.Helpers; +using Yavsc.Models; +using Yavsc.ViewModels; +using Yavsc.ViewModels.Gen; +using Yavsc.Services; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; + +namespace Yavsc.ViewComponents +{ + public class BillViewComponent : ViewComponent + { + readonly ApplicationDbContext dbContext; + readonly IBillingService billing; + readonly IStringLocalizer localizer; + private readonly SiteSettings siteSettings; + + public BillViewComponent(ApplicationDbContext dbContext, + IStringLocalizer localizer, + IBillingService billing, + IOptions siteSettings) + { + this.billing = billing; + this.dbContext = dbContext; + this.localizer = localizer; + this.siteSettings = siteSettings.Value; + } + + public async Task InvokeAsync( + string code, + IBillable billable, + OutputFormat format, + bool asBill, + SiteSettings settings + ) + { + var di = new DirectoryInfo(settings.Bills); + var dia = new DirectoryInfo(settings.Avatars); + ViewBag.BillsDir = di.FullName; + ViewBag.AvatarsDir = dia.FullName; + ViewBag.AsBill = asBill; // vrai pour une facture, sinon, c'est un devis + ViewBag.Acquitted = billable.GetIsAcquitted(); + + ViewBag.BillingCode = code; + var client = await dbContext.Users + .Include(u=>u.PostalAddress) + .SingleAsync(u=>u.Id == billable.ClientId); + ViewBag.Client = client; + var performer = await dbContext.Users + .Include(u=>u.BankInfo) + .Include(u=>u.PostalAddress) + .SingleAsync(u=>u.Id == billable.PerformerId); + ViewBag.Performer = performer; + string clientAddress = client.PostalAddress?.Address ?? null; + ViewBag.ClientAddress = clientAddress.SplitAddressToTeX(); + + + var profile = await dbContext.Performers + .Include(p=>p.OrganizationAddress) + .SingleAsync(p=>p.PerformerId == billable.PerformerId); + ViewBag.PerformerProfile = profile; + ViewBag.ActivityLabel = (await dbContext.Activities.SingleAsync(a => a.Code == billable.ActivityCode)).Name; + + var proAddr = profile.OrganizationAddress.Address; + ViewBag.PerformerOrganizationAddress = proAddr.SplitAddressToTeX() ; + ViewBag.FooterPerformerOrganizationAddress = proAddr.SplitAddressToTeX(", "); + + ViewBag.PerformerAddress = performer.PostalAddress?.Address.SplitAddressToTeX() ; + switch (format) { + case OutputFormat.LaTeX : + return this.View("Bill_tex", billable); + case OutputFormat.Pdf : + string tex = null; + var oldWriter = ViewComponentContext.ViewContext.Writer; + using (var writer = new StringWriter()) + { + this.ViewComponentContext.ViewContext.Writer = writer; + var resultTex = View("Bill_tex", billable); + await resultTex.ExecuteAsync(this.ViewComponentContext); + tex = writer.ToString(); + } + ViewComponentContext.ViewContext.Writer = oldWriter; + + var pdfGenerationViewModel = new PdfGenerationViewModel + { + Temp = siteSettings.TempDir, + TeXSource = tex, + DestDir = siteSettings.Bills, + BaseFileName = billable.GetFileBaseName(billing) + }; + if (settings.GenerateEstimatePdf(pdfGenerationViewModel)) { + return this.View(new { Generated = pdfGenerationViewModel.BaseFileName+".pdf" }); + } else { + return View(new { Error = pdfGenerationViewModel.GenerationErrorMessage } ); + } + } + ViewBag.BillFileInfo = billable.GetBillInfo(billing, siteSettings); + ViewBag.Settings = settings; + return View("Default",billable); + + } + + } +} diff --git a/src/Yavsc.Org/ViewComponents/CommentViewComponent.cs b/src/Yavsc.Org/ViewComponents/CommentViewComponent.cs index 32b7a14b2..6f85a953f 100644 --- a/src/Yavsc.Org/ViewComponents/CommentViewComponent.cs +++ b/src/Yavsc.Org/ViewComponents/CommentViewComponent.cs @@ -18,10 +18,7 @@ namespace Yavsc.ViewComponents } public async Task InvokeAsync(long id) { - var comment = await context.Comment - .Include(c => c.Children) - .Include(c => c.Author) - .FirstOrDefaultAsync(c => c.Id==id); + var comment = await context.Comment.Include(c=>c.Children).FirstOrDefaultAsync(c => c.Id==id); if (comment == null) throw new InvalidOperationException(); ViewBag.apictlr = "/api/v1/blogcomments"; diff --git a/src/Yavsc.Api/ViewModels/Gen/PdfGenerationViewModel.cs b/src/Yavsc.Org/ViewModels/Gen/PdfGenerationViewModel.cs similarity index 98% rename from src/Yavsc.Api/ViewModels/Gen/PdfGenerationViewModel.cs rename to src/Yavsc.Org/ViewModels/Gen/PdfGenerationViewModel.cs index 841e8d5dd..fe934e3ac 100644 --- a/src/Yavsc.Api/ViewModels/Gen/PdfGenerationViewModel.cs +++ b/src/Yavsc.Org/ViewModels/Gen/PdfGenerationViewModel.cs @@ -5,7 +5,7 @@ namespace Yavsc.ViewModels.Gen { public class PdfGenerationViewModel { - + [Required] public string TeXSource { get; set; } [Required] diff --git a/src/Yavsc.Org/Views/Account/Delete.cshtml b/src/Yavsc.Org/Views/Account/Delete.cshtml deleted file mode 100644 index 7443d3d2a..000000000 --- a/src/Yavsc.Org/Views/Account/Delete.cshtml +++ /dev/null @@ -1,21 +0,0 @@ -@model UnregisterViewModel -@{ - ViewBag.Title = "Supprimer mon compte"; -} - -

@ViewBag.Title

- -
-
-

- Cette action est immédiate et irréversible. Toutes les données - associées à votre compte seront supprimées, et vous serez déconnecté. -

- -
- @Html.Hidden("UserId") - - Annuler -
-
-
\ No newline at end of file diff --git a/src/Yavsc.Org/Views/Blogspot/Create.cshtml b/src/Yavsc.Org/Views/Blogspot/Create.cshtml new file mode 100644 index 000000000..6831b0461 --- /dev/null +++ b/src/Yavsc.Org/Views/Blogspot/Create.cshtml @@ -0,0 +1,88 @@ +@model BlogPostEditViewModel + +@{ + ViewBag.Title = "Blog post edition"; +} + +@section header { + + + + + + @{ await Html.RenderPartialAsync("_FSScriptsPartial"); } + +} + +

Blog post

+ + +
+ + +

@Model.Title

+ +
@Model.Article
+ +
+
+
+ +
+ +
+ +
+ + + +
+
+ +
+ +
+ + + +
+
+ +
+ +
+ + + +
+
+ +
+ +
+ @await Component.InvokeAsync("CirclesControl", Model) +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+
+
+@await Component.InvokeAsync(name: "Directory",arguments: new {subdir="."}) +
+ @{ await Html.RenderPartialAsync("_PostFilesPartial"); } +
+ diff --git a/src/Yavsc.Org/Views/Blogspot/Details.cshtml b/src/Yavsc.Org/Views/Blogspot/Details.cshtml index 0b07b0d94..dc0bea8a8 100644 --- a/src/Yavsc.Org/Views/Blogspot/Details.cshtml +++ b/src/Yavsc.Org/Views/Blogspot/Details.cshtml @@ -1,235 +1,105 @@ @model BlogPost @{ - ViewBag.Title = Model.Title; + ViewBag.Title=Model.Title; } -@section header { +@section scripts { + + + } -@section scripts { - -} - -
-
- @if (!string.IsNullOrEmpty(Model.Photo)) - { - @Model.Title - } -
-

@Model.Title

- -
-
- @Model.Article +
+
+
+ +

@Model.Title

+ + @Html.DisplayFor(m=>m.Author) + @Html.DisplayFor(m=>m.Article) +
- @if (Model.Comments != null) - { - foreach (var comment in Model.Comments.Where(c => c.ParentId == null)) - { + @if (Model.Comments!=null) { + foreach (var comment in Model.Comments.Where(c=>c.ParentId==null)) { @await Component.InvokeAsync("Comment", new { id = comment.Id }) } }
- - @if (User.GetUserId() != null) - { -
-
- - - - - -
+@if (User.GetUserId()!=null) { +
+
+ + + + +
+ +
} - else - { -

Vous devez être identifié pour commenter.

+ else { +

Vous devez être identifié pour commenter.

+ } + + @if ((await AuthorizationService.AuthorizeAsync(User, Model, new EditPermission())).Succeeded) { + Edit } - Back to List -
\ No newline at end of file + diff --git a/src/Yavsc.Org/Views/Blogspot/Edit.cshtml b/src/Yavsc.Org/Views/Blogspot/Edit.cshtml new file mode 100644 index 000000000..4645d45db --- /dev/null +++ b/src/Yavsc.Org/Views/Blogspot/Edit.cshtml @@ -0,0 +1,120 @@ +@model BlogPostEditViewModel +@{ + ViewBag.Title = "Blog post edition"; +} + +@section header { + + + + + + + + @{ await Html.RenderPartialAsync("_FSScriptsPartial"); } + +} + +

Blog post

+ + +
+ + + + + +

@Model.Title

+ +
+
+ +
+ @Html.HiddenFor(m=>m.Id) + +
+ +
+ + + +
+
+ +
+ +
+ + + +
+
+
+ +
+ + + +
+
+
+ +
+ @await Component.InvokeAsync("CirclesControl", Model) +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+
+
+@await Component.InvokeAsync("Directory","") + + +
+ +
+ @{ await Html.RenderPartialAsync("_PostFilesPartial"); } +
+ diff --git a/src/Yavsc.Org/Views/Blogspot/Index.cshtml b/src/Yavsc.Org/Views/Blogspot/Index.cshtml index 9f29a9417..1582041cf 100644 --- a/src/Yavsc.Org/Views/Blogspot/Index.cshtml +++ b/src/Yavsc.Org/Views/Blogspot/Index.cshtml @@ -1,155 +1,92 @@ + @model IEnumerable @{ ViewBag.Title = "Blogs, l'index"; - - // Pagination reflects the Index(string id, int skip, int take) - // call profile. The service applies Skip/Take server-side, so - // the view only needs the current page params to build Prev/Next - // links — it does not receive a total count. - int currentSkip = Context.Request.Query.TryGetValue("skip", out var skipVal) && int.TryParse(skipVal, out var s) ? s : 0; - int currentTake = Context.Request.Query.TryGetValue("take", out var takeVal) && int.TryParse(takeVal, out var t) ? t : 25; - - var posts = Model.ToList(); - bool hasPrev = currentSkip > 0; - bool hasNext = posts.Count >= currentTake; - - int prevSkip = Math.Max(0, currentSkip - currentTake); - int nextSkip = currentSkip + currentTake; - - int maxSummaryLen = 256; } @section header { + +} +@section scripts { + }

@ViewBag.Title

@ViewBag.StatusMessage

- -@if (!posts.Any()) -{ -

Néant

+@if (User.IsSignedIn()) { +

+ Create a new article +

} -else -{ -
- @foreach (var post in posts) - { - var canEdit = (await AuthorizationService.AuthorizeAsync(User, post, new EditPermission())).Succeeded; -
- @if (!string.IsNullOrEmpty(post.Photo)) - { - - @post.Title - - } -
-

- @post.Title -

-
- - @post.Article - -
- -
- Details - @if (canEdit) - { - Delete - } -
-
-
- } -
- -} \ No newline at end of file +
+@{ + int maxTextLen = 75; + foreach (var post in Model) { +
+ + + +
+

@post.Title

+
+
+ + @post.Article + @Html.DisplayFor(m => post.Author) + + posté le @post.DateCreated.ToString("dddd d MMM yyyy à H:mm") + @if ((post.DateModified - post.DateCreated).Minutes > 0){  + @:- Modifié le @post.DateModified.ToString("dddd d MMM yyyy à H:mm") + } + +
+
+ @if ((await AuthorizationService.AuthorizeAsync(User, post, new ReadPermission())).Succeeded) + { + Details + } + else + { + Details + } + @if ((await AuthorizationService.AuthorizeAsync(User, post, new EditPermission())).Succeeded) + { + Edit + + Delete + + } +
+
+ } +} +
+ +@if(Model.Count()==0){

Néant

} diff --git a/src/Yavsc.Org/Views/Blogspot/Title.cshtml b/src/Yavsc.Org/Views/Blogspot/Title.cshtml index 9421a5b01..7bdf0468c 100644 --- a/src/Yavsc.Org/Views/Blogspot/Title.cshtml +++ b/src/Yavsc.Org/Views/Blogspot/Title.cshtml @@ -3,24 +3,28 @@

@ViewBag.StatusMessage

- +

+ Poster au même titre +

+
+ @foreach (var item in Model) { -
- @((item.Article?.Length > 256) ? item.Article.Substring(0, 256) + " ..." : item.Article) + @((item.Article?.Length > 256) ? item.Article.Substring(0, 256) + " ..." : item.Article) (@item.Author.UserName , posté le @item.DateCreated.ToString("dddd d MMM yyyy à H:mm") - @if ((item.DateModified - item.DateCreated).Minutes > 0){ + @if ((item.DateModified - item.DateCreated).Minutes > 0){  @:- Modifié le @item.DateModified.ToString("dddd d MMM yyyy à H:mm") }) +
    @if ((await AuthorizationService.AuthorizeAsync(User, item, new ReadPermission())).Succeeded) {
  • @@ -28,7 +32,8 @@
  • } @if ((await AuthorizationService.AuthorizeAsync(User, item, new EditPermission())).Succeeded) { - +
  • Edit +
  • Delete
  • } diff --git a/src/Yavsc.Org/Views/HairCutCommand/CGV.cshtml b/src/Yavsc.Org/Views/HairCutCommand/CGV.cshtml index 08d01cf63..1428ee674 100644 --- a/src/Yavsc.Org/Views/HairCutCommand/CGV.cshtml +++ b/src/Yavsc.Org/Views/HairCutCommand/CGV.cshtml @@ -1,13 +1,13 @@ -@{ +@{ ViewBag.Title = "Conditions Générales de Vente"; }

    @ViewBag.Title

    - + Le paiement intervient à la commande, quelque soit la date d'intervention. -Vous pouvez annuler votre commande depuis la [liste de vos commande en cours](/HairCutCommand/), - -Vous pouvez demander le remboursement d'un paiement, dans le cadre d'une réclamation sur l'execution +Vous pouvez annuler votre commande depuis la [liste de vos commande en cours](/HairCutCommand/), + +Vous pouvez demander le remboursement d'un paiement, dans le cadre d'une réclamation sur l'execution de votre commande, en nous contactant via courrier éléctronique ou postal, [que vour retrouverez sur la page de contact](/Home/Contact), votre demande sera rapidement traitée. - + diff --git a/src/Yavsc.Org/Views/Home/About.cshtml b/src/Yavsc.Org/Views/Home/About.cshtml index 746968be8..39062cb22 100755 --- a/src/Yavsc.Org/Views/Home/About.cshtml +++ b/src/Yavsc.Org/Views/Home/About.cshtml @@ -7,12 +7,12 @@ **Version de Development** - -## L'objectif + +== L'objectif Cette application est construite pour mettre en relation des artistes du domaine musical avec leur public. -## Le fonctionnement +== Le fonctionnement Les utilisateurs du site sont soit artiste, soit client, soit administrateur. ils ont tous droit à leur blog. Pour les artistes, c'est un moyen de promouvoir leur activité. @@ -45,7 +45,7 @@ Une fois sa prestation associée exécutée, les paiements relatifs sont effectu Pour un contrat exécuté et non honoré par le client, le processus de poursuite en recouvrement est engagé, sinon, le contrat est archivé, des attestations de paiement sont disponibles pour l'artiste et la facture est marquée payée, puis repostée au client. -## Pour l'artiste +=== Pour l'artiste L'artiste choisit plusieurs paramètres qui vont faire son profil : @@ -55,7 +55,7 @@ L'artiste choisit plusieurs paramètres qui vont faire son profil : * Un agenda Google optionnel, à consulter pour établir sa disponibilité * Des paramètres supplémentaires en fonctions de son type d'activité par exemple, pour les ensembles, leur taille, le cas échéant, leur répertoire ou des indications sur le style de leur musique) -## Pour le client +=== Pour le client Il choisit un lieu et une date pour déclarer un événement avenir (il peut bien-sûr en programmer autant qu'il le veut). @@ -63,19 +63,11 @@ Il peut, parcourir les publications des artistes, et entamer, sur la base d'un d Il a accès à la connaissance des journées connues comme libres des artistes par le système. -## La confidentialité +== La confidentialité -À l'exception d'adresses postales pour rendez-vous, -transmises avec accord explicite, -et dans le cadre d'établissement d'un devis, -aucune adresse postale, -aucune adresse e-mail, -ni aucun numéro de téléphone ne sont transmis, -ni aux clients, ni aux artistes. -Seul le serveur a accès à ces informations. +À aucun moment, aucune adresse postale, aucune adresse e-mail ni aucun numéro de téléphone ne sont transmis ni aux clients, ni aux artistes. Seul le système a accès à ces informations. De plus, le droit de retrait est permanent et sa mise en oeuvre immédiate. -Les artistes comme les clients peuvent demander leur désinscription, qui désactive immédiatement les publications associées à leurs informations, -et programme la suppression complète de ces dites informations dans les quinze jours à compter de la demande, sauf demande contradictoire. +Les artistes comme les clients peuvent demander leur désinscription, qui désactive immédiatement les publications associées à leurs informations, et programme la suppression complète de ces dites informations dans les quinze jours à compter de la demande, sauf demande contradictoire. L'opération est annulable, jusqu'à deux semaines après sa programmation. - + \ No newline at end of file diff --git a/src/Yavsc.Org/Views/Home/About.pt.cshtml b/src/Yavsc.Org/Views/Home/About.pt.cshtml index a805999ce..487a1ab36 100755 --- a/src/Yavsc.Org/Views/Home/About.pt.cshtml +++ b/src/Yavsc.Org/Views/Home/About.pt.cshtml @@ -5,7 +5,7 @@

    @SiteSettings.Value.Title - objetivo

    - + = O objetivo Esta aplicação é construída para conectar artistas @@ -87,51 +87,51 @@ que imediatamente desativa as publicações associadas às suas informações, e planeia a eliminação completa desta informação no prazo de quinze dias do aplicativo, a menos que seja um pedido contraditório. A operação é anulável até duas semanas após a sua programação. - + -Este é o meu site perso, uma configuração de _Yavsc_ (outro negócio muito pequeno). +Este é o meu site perso, uma configuração de _Yavsc_ (outro negócio muito pequeno). * [README](https://forgejo.pschneider.fr/notazof/yavsc/blob/vnext/README.md) * [licença: GNU GPL v3](https://forgejo.pschneider.fr/notazof/yavsc/blob/vnext/LICENSE) Outras instalações: - - + + * [Coiffure](http://coiffure.pschneider.fr) * [ZicMoove](http://linkmuse.pschneider.fr) * [Yavsc](http://yavsc.pschneider.fr) - + - + Yet Another Very Small Company ... * [README](https://forgejo.pschneider.fr/notazof/yavsc/blob/vnext/README.md) * [license: GNU FPL v3](https://forgejo.pschneider.fr/notazof/yavsc/blob/vnext/LICENSE) - + - -## Yet Another Very Small Company : + +## Yet Another Very Small Company : * [README](https://forgejo.pschneider.fr/notazof/yavsc/blob/vnext/README.md) * [license: GNU FPL v3](https://forgejo.pschneider.fr/notazof/yavsc/blob/vnext/LICENSE) En production: - + * [Lua](https://lua.pschneider.fr) * [Yavsc](https://yavsc.pschneider.fr) - + - + Você está no site da ordem em cabeleireiro em casa de Soraya Boudjouraf, um ás de cabeleireiro, que trabalha na região de Paris. @@ -139,11 +139,11 @@ Ao validar um formulário de pedido aqui, é para ela que você notifica sua sol Você pode [Deixe-lhe o seu número de telefone] (/ HairCutCommand / HairCut? ActivityCode = Brush & performerId = 1bd841ab-c305-4971-940d-7ddca818310c) e / ou detalhes sobre o seu pedido, -ela vai ligar de volta. +ela vai ligar de volta. - + ## Este é um site de desenvolvimento. Este recurso só diz respeito ao desenvolvimento do software que o implementa. @@ -156,7 +156,7 @@ A "pré-produção" exibe os seguintes sites: * [Yavsc](https://yavsc.pschneider.fr) * [Lua](https://lua.pschneider.fr) - +

    @Model

    diff --git a/src/Yavsc.Org/Views/Home/AboutAccess.cshtml b/src/Yavsc.Org/Views/Home/AboutAccess.cshtml index 6aaec7ff9..05331032b 100644 --- a/src/Yavsc.Org/Views/Home/AboutAccess.cshtml +++ b/src/Yavsc.Org/Views/Home/AboutAccess.cshtml @@ -3,13 +3,13 @@ }

    @ViewBag.Title

    - + # Blog et fichiers utilisateurs Par défaut, un envoi à destination du serveur est privé, visible seulement pour son possesseur (et l'administration). -L'utilisateur peu publier chacun de ces derniers à l'adresse : +L'utilisateur peu publier chacun de ces derniers à l'adresse : * de tout le monde, * ou d'une liste de cercles d'utilisateurs, @@ -17,8 +17,8 @@ L'utilisateur peu publier chacun de ces derniers à l'adresse : Quand il s'agit d'un fichier, son adresse permanente relative à la racine du serveur web est: `/UserFiles/(Nom Utilisateur)/(cheminVersLeFichier)` -Les restrictions d'accès à ce fichier sont celles associées à tous les fichiers du même dossier. -Il n'y a pas de regle assiciée à chaque fichier individuellement, +Les restrictions d'accès à ce fichier sont celles associées à tous les fichiers du même dossier. +Il n'y a pas de regle assiciée à chaque fichier individuellement, elles appartiennent au dossier qui les contient. Les billet de blog sont regroupés par utilisateur, à l'Url @@ -35,6 +35,6 @@ Et ils possèdent une addresse permanente de la forme : - + diff --git a/src/Yavsc.Org/Views/Home/AboutMarkdown.cshtml b/src/Yavsc.Org/Views/Home/AboutMarkdown.cshtml index f39aaf536..f025c111e 100644 --- a/src/Yavsc.Org/Views/Home/AboutMarkdown.cshtml +++ b/src/Yavsc.Org/Views/Home/AboutMarkdown.cshtml @@ -3,7 +3,7 @@ }

    @ViewBag.Title

    - + Quelques extensions à un Markdown de base : * les video et audio: `![video: Ep. 28 - La France en commun - Une invitation à écrire un nouveau projet d'émancipation humaine](/images/testVideo.mp4)` @@ -16,4 +16,4 @@ Quelques extensions à un Markdown de base : * le tag "Titre d'article": "#1_great_title" * le tag "User" = "@@(John Doe)" ou "@@jdoe" - + diff --git a/src/Yavsc.Org/Views/Home/CGV.cshtml b/src/Yavsc.Org/Views/Home/CGV.cshtml index 08d01cf63..1428ee674 100644 --- a/src/Yavsc.Org/Views/Home/CGV.cshtml +++ b/src/Yavsc.Org/Views/Home/CGV.cshtml @@ -1,13 +1,13 @@ -@{ +@{ ViewBag.Title = "Conditions Générales de Vente"; }

    @ViewBag.Title

    - + Le paiement intervient à la commande, quelque soit la date d'intervention. -Vous pouvez annuler votre commande depuis la [liste de vos commande en cours](/HairCutCommand/), - -Vous pouvez demander le remboursement d'un paiement, dans le cadre d'une réclamation sur l'execution +Vous pouvez annuler votre commande depuis la [liste de vos commande en cours](/HairCutCommand/), + +Vous pouvez demander le remboursement d'un paiement, dans le cadre d'une réclamation sur l'execution de votre commande, en nous contactant via courrier éléctronique ou postal, [que vour retrouverez sur la page de contact](/Home/Contact), votre demande sera rapidement traitée. - + diff --git a/src/Yavsc.Org/Views/Home/Index.cshtml b/src/Yavsc.Org/Views/Home/Index.cshtml index a18b0727b..f867b2bfc 100755 --- a/src/Yavsc.Org/Views/Home/Index.cshtml +++ b/src/Yavsc.Org/Views/Home/Index.cshtml @@ -2,6 +2,8 @@ @{ ViewBag.Title = @Localizer["Page d'accueil"]; + int i=0; + bool multipleact = Model.Count()>1; } diff --git a/src/Yavsc.Org/Views/Home/Privacy.cshtml b/src/Yavsc.Org/Views/Home/Privacy.cshtml index a9a6e7cf4..34ae18037 100644 --- a/src/Yavsc.Org/Views/Home/Privacy.cshtml +++ b/src/Yavsc.Org/Views/Home/Privacy.cshtml @@ -2,15 +2,15 @@ ViewBag.Title = Localizer["Home"]; } - -# La confidentialité + += La confidentialité +:hide-uri-scheme: -À aucun moment, aucune adresse postale, -aucune adresse e-mail ni aucun numéro de téléphone -ne sont transmis à personne. Seul le système - et son link:Contact[possesseur] ont accès - à ces informations. +xref:Contact[Get Report] -De plus, le droit de retrait est permanent -et sa [mise en oeuvre est immédiate](/Account/Delete). - +À aucun moment, aucune adresse postale, aucune adresse e-mail ni aucun numéro de téléphone +ne sont transmis à personne. Seul le système et son link:Contact[possesseur] ont accès à ces informations. + +De plus, le droit de retrait est permanent et sa mise en oeuvre link:/Account/Delete[immédiate]. + + \ No newline at end of file diff --git a/src/Yavsc.Org/Views/Home/Todo.cshtml b/src/Yavsc.Org/Views/Home/Todo.cshtml index c63500479..448251a48 100755 --- a/src/Yavsc.Org/Views/Home/Todo.cshtml +++ b/src/Yavsc.Org/Views/Home/Todo.cshtml @@ -4,14 +4,14 @@

    @ViewBag.Title

    Linkmuse(trox) - + Les tags. La librairie, les lives. - + @{ ViewBag.Title = @Localizer["TODO"]; }

    @ViewBag.Title

    Faster, stronger, shorter - - + + diff --git a/src/Yavsc.Org/Views/Shared/Components/Bill/Bill_pdf.cshtml b/src/Yavsc.Org/Views/Shared/Components/Bill/Bill_pdf.cshtml new file mode 100644 index 000000000..f49fad802 --- /dev/null +++ b/src/Yavsc.Org/Views/Shared/Components/Bill/Bill_pdf.cshtml @@ -0,0 +1,21 @@ +@using System.Reflection +@using System.IO +@using Microsoft.Extensions.WebEncoders +@using System.Diagnostics +@using System.Text +@using Yavsc.Helpers +@model Yavsc.ViewModels.Gen.PdfGenerationViewModel + +@{ + ViewBag.Settings.GenerateEstimatePdf(Model); +} +@if (Model.Generated) { +
    + @(Model.BaseFileName).pdf was generated +
    +} else { +
    + @Model.GenerationErrorMessage +

    Something went wrong ...

    +
    +} diff --git a/src/Yavsc.Api/Views/Shared/Bill_tex.cshtml b/src/Yavsc.Org/Views/Shared/Components/Bill/Bill_tex.cshtml similarity index 99% rename from src/Yavsc.Api/Views/Shared/Bill_tex.cshtml rename to src/Yavsc.Org/Views/Shared/Components/Bill/Bill_tex.cshtml index f0a3d2ad3..353d124e0 100644 --- a/src/Yavsc.Api/Views/Shared/Bill_tex.cshtml +++ b/src/Yavsc.Org/Views/Shared/Components/Bill/Bill_tex.cshtml @@ -3,7 +3,7 @@ @using System.Globalization @model IBillable @{ - Layout = null; + Layout = "null"; var pro = ViewBag.PerformerProfile; var from = ViewBag.Performer; var to = ViewBag.Client; diff --git a/src/Yavsc.Org/Views/Shared/Components/Bill/Default.cshtml b/src/Yavsc.Org/Views/Shared/Components/Bill/Default.cshtml new file mode 100644 index 000000000..f2d0e0163 --- /dev/null +++ b/src/Yavsc.Org/Views/Shared/Components/Bill/Default.cshtml @@ -0,0 +1,33 @@ + +@model IBillable +
    +@if (ViewBag.BillFileInfo.Exists) { +La facture au format Pdf +} else { + +
    +
    + + +
    +
    +
    +
    +} +
    diff --git a/src/Yavsc.Org/Views/Shared/Components/Comment/Default.cshtml b/src/Yavsc.Org/Views/Shared/Components/Comment/Default.cshtml index e83ea14c6..6dfec029c 100644 --- a/src/Yavsc.Org/Views/Shared/Components/Comment/Default.cshtml +++ b/src/Yavsc.Org/Views/Shared/Components/Comment/Default.cshtml @@ -1,30 +1,16 @@ @model Comment -
    -
    - @Model.Article -
    - - — @(Model.Author?.UserName ?? "Anonyme"), - posté le @Model.DateCreated.ToString("dddd d MMM yyyy à H:mm") - - @if (User.GetUserId() != null) - { - - } +
    + @Model.Article + + @if (Model.Children!=null) {
    - @if (Model.Children != null) - { - foreach (var comment in Model.Children) + @foreach (var comment in Model.Children) { - @await this.Component.InvokeAsync("Comment", new { id = comment.Id }) + @await this.Component.InvokeAsync("Comment", new { id = comment.Id}); } - }
    -
    \ No newline at end of file +} +
    + diff --git a/src/Yavsc.Org/Views/Shared/Components/Estimate/Default.cshtml b/src/Yavsc.Org/Views/Shared/Components/Estimate/Default.cshtml new file mode 100644 index 000000000..b9a1ab26d --- /dev/null +++ b/src/Yavsc.Org/Views/Shared/Components/Estimate/Default.cshtml @@ -0,0 +1,13 @@ + +@model Estimate + diff --git a/src/Yavsc.Org/Views/Shared/Components/Estimate/Estimate_pdf.cshtml b/src/Yavsc.Org/Views/Shared/Components/Estimate/Estimate_pdf.cshtml new file mode 100644 index 000000000..bef19b35a --- /dev/null +++ b/src/Yavsc.Org/Views/Shared/Components/Estimate/Estimate_pdf.cshtml @@ -0,0 +1,21 @@ +@using System.Reflection +@using System.IO +@using Microsoft.Extensions.WebEncoders +@using System.Diagnostics +@using System.Text +@using Yavsc.Helpers +@model Yavsc.ViewModels.Gen.PdfGenerationViewModel + +@{ + SiteSettings.Value.GenerateEstimatePdf(Model); +} +@if (Model.Generated) { +
    + @(Model.BaseFileName).pdf was generated +
    +} else { +
    + @Model.GenerationErrorMessage +

    Something went wrong ...

    +
    +} diff --git a/src/Yavsc.Api/Views/Shared/Estimate_tex.cshtml b/src/Yavsc.Org/Views/Shared/Components/Estimate/Estimate_tex.cshtml similarity index 72% rename from src/Yavsc.Api/Views/Shared/Estimate_tex.cshtml rename to src/Yavsc.Org/Views/Shared/Components/Estimate/Estimate_tex.cshtml index bbe4326b4..4c35d4bdd 100644 --- a/src/Yavsc.Api/Views/Shared/Estimate_tex.cshtml +++ b/src/Yavsc.Org/Views/Shared/Components/Estimate/Estimate_tex.cshtml @@ -3,7 +3,7 @@ @using System.Globalization @model Estimate @{ - Layout = null; + Layout = "null"; var pro = Model.Query.PerformerProfile; var from = Model.Query.PerformerProfile.Performer; var to = Model.Query.Client; @@ -11,6 +11,7 @@ var proaddr = Model.Query.PerformerProfile.OrganizationAddress.Address.SplitAddressToTeX(); var proaddrm = new HtmlString(Model.Query.PerformerProfile.OrganizationAddress.Address); var isestimate = !ViewBag.AsBill; + var prosign = new FileInfo($"{ViewBag.BillsDir}/estimate-prosign-{Model.Id}.png"); } \documentclass[french,11pt]{article} @@ -24,7 +25,6 @@ \usepackage{graphicx} \usepackage{fancyhdr} \usepackage{fp} -\usepackage{tikz} \def\TVA{20} % Taux de la TVA @@ -49,7 +49,7 @@ \global\let\TotalHT\TotalHT \global\let\TotalTVA\TotalTVA \global\let\TotalTTC\TotalTTC - \cr + \cr \hline \textbf{Total} & & & \TotalHT } @@ -77,12 +77,12 @@ % Infos Client \def\ClientNom{@TeXHelpers.ToTeX(to.UserName)} % Nom du client -\def\ClientAdresse{ +\def\ClientAdresse{ % Adresse du client @PostalAddress @if (PostalAddress!=null) {@PostalAddress} @if (!string.IsNullOrWhiteSpace(to.PhoneNumber)) {@TeXHelpers.ToTeX(to.PhoneNumber)\\ - } + } E-mail: @TeXHelpers.ToTeX(to.Email) } @@ -105,7 +105,7 @@ \renewcommand{\headrulewidth}{0pt} \cfoot{ @TeXHelpers.ToTeX(from.UserName) @if (proaddrm!=null) { - @proaddrm } \newline - \small{ E-mail: @TeXHelpers.ToTeX(from.Email) @if (!string.IsNullOrWhiteSpace(from.PhoneNumber)) { - Téléphone fixe: @TeXHelpers.ToTeX(from.PhoneNumber) } + \small{ E-mail: @TeXHelpers.ToTeX(from.Email) @if (!string.IsNullOrWhiteSpace(from.PhoneNumber)) { - Téléphone fixe: @TeXHelpers.ToTeX(from.PhoneNumber) } } } @@ -113,10 +113,10 @@ % Logo de la société @if (from.Avatar != null) { -\includegraphics[height=60pt, viewport=0 0 128 128]{@(ViewBag.AvatarsDir)/@(from.UserName).png} +\includegraphics[height=60pt]{@(ViewBag.AvatarsDir)/@(from.UserName).png} -} else { -%\includegraphics[height=60pt, viewport=0 0 128 128]{logo.png} +} else { +%\includegraphics{logo.png} } % Nom et adresse de la société @@ -170,12 +170,12 @@ \begin{center} \begin{tabular}{|c c c c|} @if (!string.IsNullOrWhiteSpace(bi.BankCode) && !string.IsNullOrWhiteSpace(bi.WicketCode) - && !string.IsNullOrWhiteSpace(bi.AccountNumber) ) { + && !string.IsNullOrWhiteSpace(bi.AccountNumber) ) { \hline \textbf{Code banque} & \textbf{Code guichet} & \textbf{N° de Compte} & \textbf{Clé RIB} \\ @bi.BankCode & @bi.WicketCode & @bi.AccountNumber & @bi.BankedKey \\ } - @if (!string.IsNullOrWhiteSpace(@bi.IBAN) && !string.IsNullOrWhiteSpace(@bi.BIC)) { + @if (!string.IsNullOrWhiteSpace(@bi.IBAN) && !string.IsNullOrWhiteSpace(@bi.BIC)) { \hline \textbf{IBAN N°} & \multicolumn{3}{|l|}{ @bi.IBAN } \\ \hline \textbf{Code BIC} & \multicolumn{3}{|l|}{ @bi.BIC } @@ -188,46 +188,19 @@ } -@{ - // Captured signatures, if any. The Estimate.Signatures collection - // is loaded by the controller; SignaturePro/SignatureClient project - // the row by type. We draw them as vector TikZ at the foot of the - // devis (see TeXHelpers.SignatureToTikz), so there is no PNG to - // produce or \includegraphics to fail on. - var fr = CultureInfo.CreateSpecificCulture("fr-FR"); - var proSig = Model.SignaturePro; - var cliSig = Model.SignatureClient; +@if (!(Model.ProviderValidationDate==null)) { + +\begin{flushright} + @(new HtmlString(isestimate?"Devis validé":"Facture validée")) le @TeXHelpers.ToTeX(Model.ProviderValidationDate.ToString("dddd dd MMMM yyyy")) +\end{flushright} +@if (prosign.Exists) { + +\begin{center} + \hspace{263pt} + \includegraphics[height=60pt]{@(ViewBag.BillsDir)/estimate-prosign-@(Model.Id).png} +\end{center} + } - -\vspace{2em} -\noindent -\begin{minipage}[t]{0.45\textwidth} - \textbf{Signature du fournisseur}\\ -@if (Model.ProviderValidationDate != default(DateTime)) { -\small Validé le @TeXHelpers.ToTeX(Model.ProviderValidationDate.ToString("dddd dd MMMM yyyy", fr))\\ + } -@if (proSig != null && proSig.Strokes != null && proSig.Strokes.Length > 0) { -\begin{tikzpicture} - \useasboundingbox (0,0) rectangle (5,2.5); - @proSig.SignatureToTikz(5, 2.5) -\end{tikzpicture} -} else { -\rule[-2.2cm]{5cm}{0.4pt} -} -\end{minipage}\hfill -\begin{minipage}[t]{0.45\textwidth} - \textbf{Signature du client}\\ -@if (Model.ClientValidationDate != default(DateTime)) { -\small Validé le @TeXHelpers.ToTeX(Model.ClientValidationDate.ToString("dddd dd MMMM yyyy", fr))\\ -} -@if (cliSig != null && cliSig.Strokes != null && cliSig.Strokes.Length > 0) { -\begin{tikzpicture} - \useasboundingbox (0,0) rectangle (5,2.5); - @cliSig.SignatureToTikz(5, 2.5) -\end{tikzpicture} -} else { -\rule[-2.2cm]{5cm}{0.4pt} -} -\end{minipage} - \end{document} diff --git a/src/Yavsc.Org/Views/Shared/_Layout.cshtml b/src/Yavsc.Org/Views/Shared/_Layout.cshtml index 653ffae0a..9515ac2b4 100644 --- a/src/Yavsc.Org/Views/Shared/_Layout.cshtml +++ b/src/Yavsc.Org/Views/Shared/_Layout.cshtml @@ -31,7 +31,7 @@

    @((n.click_action == null) ? Localizer["Fermer"] : Localizer[n.click_action]) - @n.body + @n.body } } diff --git a/src/Yavsc.Org/Yavsc.Org.csproj b/src/Yavsc.Org/Yavsc.Org.csproj index 99e0cf29c..c713ad2ba 100644 --- a/src/Yavsc.Org/Yavsc.Org.csproj +++ b/src/Yavsc.Org/Yavsc.Org.csproj @@ -9,18 +9,16 @@ https://github.com/pazof/yavsc 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1 - - @@ -39,6 +37,8 @@ + + diff --git a/src/Yavsc.Org/wwwroot/lib/bootstrap.quartz.css b/src/Yavsc.Org/wwwroot/lib/bootstrap.quartz.css index 41ed0f2ed..84fb5efe4 100644 --- a/src/Yavsc.Org/wwwroot/lib/bootstrap.quartz.css +++ b/src/Yavsc.Org/wwwroot/lib/bootstrap.quartz.css @@ -3924,7 +3924,7 @@ fieldset:disabled .btn { --bs-dropdown-box-shadow: var(--bs-box-shadow); --bs-dropdown-link-color: var(--bs-body-color); --bs-dropdown-link-hover-color: #fff; - --bs-dropdown-link-hover-bg: rgba(0, 0, 0, 0.4); + --bs-dropdown-link-hover-bg: rgba(255, 255, 255, 0.4); --bs-dropdown-link-active-color: #fff; --bs-dropdown-link-active-bg: #e83283; --bs-dropdown-link-disabled-color: var(--bs-tertiary-color); diff --git a/src/Yavsc.Org/wwwroot/lib/bootstrap.quartz.min.css b/src/Yavsc.Org/wwwroot/lib/bootstrap.quartz.min.css index 32df6b5e2..390637ef1 100644 --- a/src/Yavsc.Org/wwwroot/lib/bootstrap.quartz.min.css +++ b/src/Yavsc.Org/wwwroot/lib/bootstrap.quartz.min.css @@ -4,4 +4,4 @@ * Licensed under MIT * Based on Bootstrap*//*!* Bootstrap v5.3.8 (https://getbootstrap.com/) * Copyright 2011-2025 The Bootstrap Authors -* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)*/:root,[data-bs-theme=light]{--bs-blue:#3a8fd9;--bs-indigo:#6610f2;--bs-purple:#686dc3;--bs-pink:#e83283;--bs-red:#fc346f;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#41d7a7;--bs-teal:#528fb3;--bs-cyan:#39cbfb;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9e9e8;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#e83283;--bs-secondary:rgba(255, 255, 255, 0.4);--bs-success:#41d7a7;--bs-info:#39cbfb;--bs-warning:#ffc107;--bs-danger:#fd7e14;--bs-light:#e9e9e8;--bs-dark:#212529;--bs-primary-rgb:232, 50, 131;--bs-secondary-rgb:255, 255, 255;--bs-success-rgb:65, 215, 167;--bs-info-rgb:57, 203, 251;--bs-warning-rgb:255, 193, 7;--bs-danger-rgb:253, 126, 20;--bs-light-rgb:233, 233, 232;--bs-dark-rgb:33, 37, 41;--bs-primary-text-emphasis:#5d1434;--bs-secondary-text-emphasis:rgba(36, 36, 36, 0.76);--bs-success-text-emphasis:#1a5643;--bs-info-text-emphasis:#175164;--bs-warning-text-emphasis:#664d03;--bs-danger-text-emphasis:#653208;--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:#fad6e6;--bs-secondary-bg-subtle:rgba(255, 255, 255, 0.88);--bs-success-bg-subtle:#d9f7ed;--bs-info-bg-subtle:#d7f5fe;--bs-warning-bg-subtle:#fff3cd;--bs-danger-bg-subtle:#ffe5d0;--bs-light-bg-subtle:#fcfcfd;--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:#f6adcd;--bs-secondary-border-subtle:rgba(255, 255, 255, 0.76);--bs-success-border-subtle:#b3efdc;--bs-info-border-subtle:#b0eafd;--bs-warning-border-subtle:#ffe69c;--bs-danger-border-subtle:#fecba1;--bs-light-border-subtle:#e9e9e8;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255, 255, 255;--bs-black-rgb:0, 0, 0;--bs-font-sans-serif:system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--bs-font-monospace:SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#fff;--bs-body-color-rgb:255, 255, 255;--bs-body-bg:#686dc3;--bs-body-bg-rgb:104, 109, 195;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0, 0, 0;--bs-secondary-color:rgba(255, 255, 255, 0.75);--bs-secondary-color-rgb:255, 255, 255;--bs-secondary-bg:#e9e9e8;--bs-secondary-bg-rgb:233, 233, 232;--bs-tertiary-color:rgba(255, 255, 255, 0.5);--bs-tertiary-color-rgb:255, 255, 255;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248, 249, 250;--bs-heading-color:inherit;--bs-link-color:#fff;--bs-link-color-rgb:255, 255, 255;--bs-link-decoration:underline;--bs-link-hover-color:#cccccc;--bs-link-hover-color-rgb:204, 204, 204;--bs-code-color:#e83283;--bs-highlight-color:#fff;--bs-highlight-bg:#fff3cd;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:rgba(255, 255, 255, 0.2);--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.5rem;--bs-border-radius-sm:0.6rem;--bs-border-radius-lg:0.7rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:1px 3px 24px -1px rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 1px 1px rgba(0, 0, 0, 0.1);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(232, 50, 131, 0.25);--bs-form-valid-color:#41d7a7;--bs-form-valid-border-color:#41d7a7;--bs-form-invalid-color:#fd7e14;--bs-form-invalid-border-color:#fd7e14}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#dee2e6;--bs-body-color-rgb:222, 226, 230;--bs-body-bg:#212529;--bs-body-bg-rgb:33, 37, 41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255, 255, 255;--bs-secondary-color:rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb:222, 226, 230;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52, 58, 64;--bs-tertiary-color:rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb:222, 226, 230;--bs-tertiary-bg:#2b3035;--bs-tertiary-bg-rgb:43, 48, 53;--bs-primary-text-emphasis:#f184b5;--bs-secondary-text-emphasis:rgba(255, 255, 255, 0.64);--bs-success-text-emphasis:#8de7ca;--bs-info-text-emphasis:#88e0fd;--bs-warning-text-emphasis:#ffda6a;--bs-danger-text-emphasis:#feb272;--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-primary-bg-subtle:#2e0a1a;--bs-secondary-bg-subtle:rgba(15, 15, 15, 0.88);--bs-success-bg-subtle:#0d2b21;--bs-info-bg-subtle:#0b2932;--bs-warning-bg-subtle:#332701;--bs-danger-bg-subtle:#331904;--bs-light-bg-subtle:#343a40;--bs-dark-bg-subtle:#1a1d20;--bs-primary-border-subtle:#8b1e4f;--bs-secondary-border-subtle:rgba(70, 70, 70, 0.64);--bs-success-border-subtle:#278164;--bs-info-border-subtle:#227a97;--bs-warning-border-subtle:#997404;--bs-danger-border-subtle:#984c0c;--bs-light-border-subtle:#495057;--bs-dark-border-subtle:#343a40;--bs-heading-color:inherit;--bs-link-color:#f184b5;--bs-link-hover-color:#f49dc4;--bs-link-color-rgb:241, 132, 181;--bs-link-hover-color-rgb:244, 157, 196;--bs-code-color:#f184b5;--bs-highlight-color:#dee2e6;--bs-highlight-bg:#664d03;--bs-border-color:#495057;--bs-border-color-translucent:rgba(255, 255, 255, 0.15);--bs-form-valid-color:#8de7ca;--bs-form-valid-border-color:#8de7ca;--bs-form-invalid-color:#fd85a9;--bs-form-invalid-border-color:#fd85a9}*,::after,::before{box-sizing:border-box}@media(prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:2rem 0;color:inherit;border:0;border-top:var(--bs-border-width)solid;opacity:.25}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:1rem;font-weight:700;line-height:1.2;color:var(--bs-heading-color)}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media(min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media(min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media(min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media(min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.1875em;color:var(--bs-highlight-color);background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1));text-decoration:underline}a:hover{--bs-link-color-rgb:var(--bs-link-hover-color-rgb)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.6rem}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-secondary-color);text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;line-height:inherit;font-size:calc(1.275rem + .3vw)}@media(min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button{cursor:pointer;filter:grayscale(1)}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-weight:300;line-height:1.2;font-size:calc(1.625rem + 4.5vw)}@media(min-width:1200px){.display-1{font-size:5rem}}.display-2{font-weight:300;line-height:1.2;font-size:calc(1.575rem + 3.9vw)}@media(min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-weight:300;line-height:1.2;font-size:calc(1.525rem + 3.3vw)}@media(min-width:1200px){.display-3{font-size:4rem}}.display-4{font-weight:300;line-height:1.2;font-size:calc(1.475rem + 2.7vw)}@media(min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-weight:300;line-height:1.2;font-size:calc(1.425rem + 2.1vw)}@media(min-width:1200px){.display-5{font-size:3rem}}.display-6{font-weight:300;line-height:1.2;font-size:calc(1.375rem + 1.5vw)}@media(min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:2rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-2rem;margin-bottom:2rem;font-size:.875em;color:rgba(255,255,255,.7)}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:var(--bs-body-bg);border:var(--bs-border-width)solid var(--bs-border-color);border-radius:var(--bs-border-radius);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:1rem;line-height:1}.figure-caption{font-size:.875em;color:var(--bs-secondary-color)}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media(min-width:576px){.container,.container-sm{max-width:540px}}@media(min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media(min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media(min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media(min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0}.row-cols-auto>*{flex:none;width:auto}.row-cols-1>*{flex:none;width:100%}.row-cols-2>*{flex:none;width:50%}.row-cols-3>*{flex:none;width:33.33333333%}.row-cols-4>*{flex:none;width:25%}.row-cols-5>*{flex:none;width:20%}.row-cols-6>*{flex:none;width:16.66666667%}.col-auto{flex:none;width:auto}.col-1{flex:none;width:8.33333333%}.col-2{flex:none;width:16.66666667%}.col-3{flex:none;width:25%}.col-4{flex:none;width:33.33333333%}.col-5{flex:none;width:41.66666667%}.col-6{flex:none;width:50%}.col-7{flex:none;width:58.33333333%}.col-8{flex:none;width:66.66666667%}.col-9{flex:none;width:75%}.col-10{flex:none;width:83.33333333%}.col-11{flex:none;width:91.66666667%}.col-12{flex:none;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.5rem}.g-1,.gy-1{--bs-gutter-y:0.5rem}.g-2,.gx-2{--bs-gutter-x:1rem}.g-2,.gy-2{--bs-gutter-y:1rem}.g-3,.gx-3{--bs-gutter-x:2rem}.g-3,.gy-3{--bs-gutter-y:2rem}.g-4,.gx-4{--bs-gutter-x:3rem}.g-4,.gy-4{--bs-gutter-y:3rem}.g-5,.gx-5{--bs-gutter-x:6rem}.g-5,.gy-5{--bs-gutter-y:6rem}@media(min-width:576px){.col-sm{flex:1 0}.row-cols-sm-auto>*{flex:none;width:auto}.row-cols-sm-1>*{flex:none;width:100%}.row-cols-sm-2>*{flex:none;width:50%}.row-cols-sm-3>*{flex:none;width:33.33333333%}.row-cols-sm-4>*{flex:none;width:25%}.row-cols-sm-5>*{flex:none;width:20%}.row-cols-sm-6>*{flex:none;width:16.66666667%}.col-sm-auto{flex:none;width:auto}.col-sm-1{flex:none;width:8.33333333%}.col-sm-2{flex:none;width:16.66666667%}.col-sm-3{flex:none;width:25%}.col-sm-4{flex:none;width:33.33333333%}.col-sm-5{flex:none;width:41.66666667%}.col-sm-6{flex:none;width:50%}.col-sm-7{flex:none;width:58.33333333%}.col-sm-8{flex:none;width:66.66666667%}.col-sm-9{flex:none;width:75%}.col-sm-10{flex:none;width:83.33333333%}.col-sm-11{flex:none;width:91.66666667%}.col-sm-12{flex:none;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.5rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.5rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:1rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:1rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:2rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:2rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:3rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:3rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:6rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:6rem}}@media(min-width:768px){.col-md{flex:1 0}.row-cols-md-auto>*{flex:none;width:auto}.row-cols-md-1>*{flex:none;width:100%}.row-cols-md-2>*{flex:none;width:50%}.row-cols-md-3>*{flex:none;width:33.33333333%}.row-cols-md-4>*{flex:none;width:25%}.row-cols-md-5>*{flex:none;width:20%}.row-cols-md-6>*{flex:none;width:16.66666667%}.col-md-auto{flex:none;width:auto}.col-md-1{flex:none;width:8.33333333%}.col-md-2{flex:none;width:16.66666667%}.col-md-3{flex:none;width:25%}.col-md-4{flex:none;width:33.33333333%}.col-md-5{flex:none;width:41.66666667%}.col-md-6{flex:none;width:50%}.col-md-7{flex:none;width:58.33333333%}.col-md-8{flex:none;width:66.66666667%}.col-md-9{flex:none;width:75%}.col-md-10{flex:none;width:83.33333333%}.col-md-11{flex:none;width:91.66666667%}.col-md-12{flex:none;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.5rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.5rem}.g-md-2,.gx-md-2{--bs-gutter-x:1rem}.g-md-2,.gy-md-2{--bs-gutter-y:1rem}.g-md-3,.gx-md-3{--bs-gutter-x:2rem}.g-md-3,.gy-md-3{--bs-gutter-y:2rem}.g-md-4,.gx-md-4{--bs-gutter-x:3rem}.g-md-4,.gy-md-4{--bs-gutter-y:3rem}.g-md-5,.gx-md-5{--bs-gutter-x:6rem}.g-md-5,.gy-md-5{--bs-gutter-y:6rem}}@media(min-width:992px){.col-lg{flex:1 0}.row-cols-lg-auto>*{flex:none;width:auto}.row-cols-lg-1>*{flex:none;width:100%}.row-cols-lg-2>*{flex:none;width:50%}.row-cols-lg-3>*{flex:none;width:33.33333333%}.row-cols-lg-4>*{flex:none;width:25%}.row-cols-lg-5>*{flex:none;width:20%}.row-cols-lg-6>*{flex:none;width:16.66666667%}.col-lg-auto{flex:none;width:auto}.col-lg-1{flex:none;width:8.33333333%}.col-lg-2{flex:none;width:16.66666667%}.col-lg-3{flex:none;width:25%}.col-lg-4{flex:none;width:33.33333333%}.col-lg-5{flex:none;width:41.66666667%}.col-lg-6{flex:none;width:50%}.col-lg-7{flex:none;width:58.33333333%}.col-lg-8{flex:none;width:66.66666667%}.col-lg-9{flex:none;width:75%}.col-lg-10{flex:none;width:83.33333333%}.col-lg-11{flex:none;width:91.66666667%}.col-lg-12{flex:none;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.5rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.5rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:1rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:1rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:2rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:2rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:3rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:3rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:6rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:6rem}}@media(min-width:1200px){.col-xl{flex:1 0}.row-cols-xl-auto>*{flex:none;width:auto}.row-cols-xl-1>*{flex:none;width:100%}.row-cols-xl-2>*{flex:none;width:50%}.row-cols-xl-3>*{flex:none;width:33.33333333%}.row-cols-xl-4>*{flex:none;width:25%}.row-cols-xl-5>*{flex:none;width:20%}.row-cols-xl-6>*{flex:none;width:16.66666667%}.col-xl-auto{flex:none;width:auto}.col-xl-1{flex:none;width:8.33333333%}.col-xl-2{flex:none;width:16.66666667%}.col-xl-3{flex:none;width:25%}.col-xl-4{flex:none;width:33.33333333%}.col-xl-5{flex:none;width:41.66666667%}.col-xl-6{flex:none;width:50%}.col-xl-7{flex:none;width:58.33333333%}.col-xl-8{flex:none;width:66.66666667%}.col-xl-9{flex:none;width:75%}.col-xl-10{flex:none;width:83.33333333%}.col-xl-11{flex:none;width:91.66666667%}.col-xl-12{flex:none;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.5rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.5rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:1rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:1rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:2rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:2rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:3rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:3rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:6rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:6rem}}@media(min-width:1400px){.col-xxl{flex:1 0}.row-cols-xxl-auto>*{flex:none;width:auto}.row-cols-xxl-1>*{flex:none;width:100%}.row-cols-xxl-2>*{flex:none;width:50%}.row-cols-xxl-3>*{flex:none;width:33.33333333%}.row-cols-xxl-4>*{flex:none;width:25%}.row-cols-xxl-5>*{flex:none;width:20%}.row-cols-xxl-6>*{flex:none;width:16.66666667%}.col-xxl-auto{flex:none;width:auto}.col-xxl-1{flex:none;width:8.33333333%}.col-xxl-2{flex:none;width:16.66666667%}.col-xxl-3{flex:none;width:25%}.col-xxl-4{flex:none;width:33.33333333%}.col-xxl-5{flex:none;width:41.66666667%}.col-xxl-6{flex:none;width:50%}.col-xxl-7{flex:none;width:58.33333333%}.col-xxl-8{flex:none;width:66.66666667%}.col-xxl-9{flex:none;width:75%}.col-xxl-10{flex:none;width:83.33333333%}.col-xxl-11{flex:none;width:91.66666667%}.col-xxl-12{flex:none;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.5rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.5rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:1rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:1rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:2rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:2rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:3rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:3rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:6rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:6rem}}.table{--bs-table-color-type:initial;--bs-table-bg-type:initial;--bs-table-color-state:initial;--bs-table-bg-state:initial;--bs-table-color:initial;--bs-table-bg:var(--bs-body-bg);--bs-table-border-color:var(--bs-border-color);--bs-table-accent-bg:transparent;--bs-table-striped-color:initial;--bs-table-striped-bg:rgba(var(--bs-emphasis-color-rgb), 0.05);--bs-table-active-color:initial;--bs-table-active-bg:rgba(var(--bs-emphasis-color-rgb), 0.1);--bs-table-hover-color:initial;--bs-table-hover-bg:rgba(var(--bs-emphasis-color-rgb), 0.075);width:100%;margin-bottom:2rem;vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*{padding:.5rem;color:var(--bs-table-color-state,var(--bs-table-color-type,var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:var(--bs-border-width);box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state,var(--bs-table-bg-type,var(--bs-table-accent-bg)))}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(var(--bs-border-width) * 2)solid}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem}.table-bordered>:not(caption)>*{border-width:var(--bs-border-width)0}.table-bordered>:not(caption)>*>*{border-width:0 var(--bs-border-width)}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(2n){--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-active{--bs-table-color-state:var(--bs-table-active-color);--bs-table-bg-state:var(--bs-table-active-bg)}.table-hover>tbody>tr:hover>*{--bs-table-color-state:var(--bs-table-hover-color);--bs-table-bg-state:var(--bs-table-hover-bg)}.table-primary{--bs-table-color:#fff;--bs-table-bg:#e83283;--bs-table-border-color:#ed5b9c;--bs-table-striped-bg:#e93c89;--bs-table-striped-color:#fff;--bs-table-active-bg:#ea478f;--bs-table-active-color:#fff;--bs-table-hover-bg:#ea418c;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color:#fff;--bs-table-bg:rgba(255, 255, 255, 0.4);--bs-table-border-color:rgba(255, 255, 255, 0.52);--bs-table-striped-bg:rgba(255, 255, 255, 0.43);--bs-table-striped-color:#000;--bs-table-active-bg:rgba(255, 255, 255, 0.46);--bs-table-active-color:#000;--bs-table-hover-bg:rgba(255, 255, 255, 0.445);--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color:#fff;--bs-table-bg:#41d7a7;--bs-table-border-color:#67dfb9;--bs-table-striped-bg:#4bd9ab;--bs-table-striped-color:#fff;--bs-table-active-bg:#54dbb0;--bs-table-active-color:#fff;--bs-table-hover-bg:#4fdaae;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color:#fff;--bs-table-bg:#39cbfb;--bs-table-border-color:#61d5fc;--bs-table-striped-bg:#43cefb;--bs-table-striped-color:#fff;--bs-table-active-bg:#4dd0fb;--bs-table-active-color:#fff;--bs-table-hover-bg:#48cffb;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color:#fff;--bs-table-bg:#ffc107;--bs-table-border-color:#ffcd39;--bs-table-striped-bg:#ffc413;--bs-table-striped-color:#fff;--bs-table-active-bg:#ffc720;--bs-table-active-color:#fff;--bs-table-hover-bg:#ffc61a;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color:#fff;--bs-table-bg:#fd7e14;--bs-table-border-color:#fd9843;--bs-table-striped-bg:#fd8420;--bs-table-striped-color:#fff;--bs-table-active-bg:#fd8b2c;--bs-table-active-color:#fff;--bs-table-hover-bg:#fd8826;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color:#000;--bs-table-bg:#e9e9e8;--bs-table-border-color:#bababa;--bs-table-striped-bg:#dddddc;--bs-table-striped-color:#000;--bs-table-active-bg:#d2d2d1;--bs-table-active-color:#fff;--bs-table-hover-bg:#d8d8d7;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color:#fff;--bs-table-bg:#212529;--bs-table-border-color:#4d5154;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.75rem + 1px);padding-bottom:calc(.75rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:var(--bs-secondary-color)}.form-control{display:block;width:100%;padding:.75rem 1.5rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:initial;background-clip:padding-box;border:1px solid rgba(255,255,255,.4);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:var(--bs-body-color);background-color:initial;border-color:rgba(255,255,255,.4);outline:0;box-shadow:none}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::-moz-placeholder{color:rgba(255,255,255,.7);opacity:1}.form-control::placeholder{color:rgba(255,255,255,.7);opacity:1}.form-control:disabled{background-color:rgba(255,255,255,.1);opacity:1}.form-control::-webkit-file-upload-button{padding:.75rem 1.5rem;margin:-.75rem -1.5rem;-webkit-margin-end:1.5rem;margin-inline-end:1.5rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::file-selector-button{padding:.75rem 1.5rem;margin:-.75rem -1.5rem;-webkit-margin-end:1.5rem;margin-inline-end:1.5rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:var(--bs-secondary-bg)}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.75rem 0;margin-bottom:0;line-height:1.5;color:var(--bs-body-color);background-color:initial;border:solid transparent;border-width:1px 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + calc(1px * 2));padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + calc(1px * 2));padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + 1.5rem + calc(1px * 2))}textarea.form-control-sm{min-height:calc(1.5em + .5rem + calc(1px * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(1px * 2))}.form-control-color{width:3rem;height:calc(1.5em + 1.5rem + calc(1px * 2));padding:.75rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color::-webkit-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color.form-control-sm{height:calc(1.5em + .5rem + calc(1px * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(1px * 2))}.form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.75rem 4.5rem .75rem 1.5rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:initial;background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon,none);background-repeat:no-repeat;background-position:right 1.5rem center;background-size:16px 12px;border:1px solid rgba(255,255,255,.4);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:rgba(255,255,255,.4);outline:0;box-shadow:none}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:1.5rem;background-image:none}.form-select:disabled{color:rgba(255,255,255,.7);background-color:rgba(255,255,255,.1)}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--bs-body-color)}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{--bs-form-check-bg:rgba(255, 255, 255, 0.3);flex-shrink:0;width:1em;height:1em;margin-top:.25em;vertical-align:top;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:50%;background-size:contain;border:1px solid rgba(255,255,255,.2);-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:rgba(255,255,255,.4);outline:0;box-shadow:0 0 0 .25rem rgba(232,50,131,.25)}.form-check-input:checked{background-color:#e83283;border-color:#e83283}.form-check-input:checked[type=checkbox]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#e83283;border-color:#e83283;--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);background-position:0;border-radius:2em;transition:background-position .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:100%;--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.5rem;padding:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:initial}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #686dc3,none}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #686dc3,none}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;-webkit-appearance:none;appearance:none;background-color:#e83283;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#f8c2da}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:rgba(0,0,0,.2);border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;-moz-appearance:none;appearance:none;background-color:#e83283;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#f8c2da}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:rgba(0,0,0,.2);border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--bs-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--bs-secondary-color)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(1px * 2));min-height:calc(3.5rem + calc(1px * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;max-width:100%;height:100%;padding:1rem 1.5rem;overflow:hidden;color:rgba(var(--bs-body-color-rgb),.65);text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media(prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem 1.5rem}.form-floating>.form-control-plaintext::-moz-placeholder,.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control-plaintext::placeholder,.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control-plaintext:not(:-moz-placeholder-shown),.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown),.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:-webkit-autofill,.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem;padding-left:1.5rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{transform:scale(.85)translateY(-.5rem)translateX(.15rem)}.form-floating>.form-control-plaintext~label,.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{transform:scale(.85)translateY(-.5rem)translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{transform:scale(.85)translateY(-.5rem)translateX(.15rem)}.form-floating>textarea:not(:-moz-placeholder-shown)~label::after{position:absolute;inset:1rem .75rem;z-index:-1;height:1.5em;content:"";background-color:initial;border-radius:var(--bs-border-radius)}.form-floating>textarea:focus~label::after,.form-floating>textarea:not(:placeholder-shown)~label::after{position:absolute;inset:1rem .75rem;z-index:-1;height:1.5em;content:"";background-color:initial;border-radius:var(--bs-border-radius)}.form-floating>textarea:disabled~label::after{background-color:rgba(255,255,255,.1)}.form-floating>.form-control-plaintext~label{border-width:1px 0}.form-floating>.form-control:disabled~label,.form-floating>:disabled~label{color:#6c757d}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-floating,.input-group>.form-select{position:relative;flex:auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-floating:focus-within,.input-group>.form-select:focus{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.75rem 1.5rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);text-align:center;white-space:nowrap;background-color:initial;border:1px solid rgba(255,255,255,.4);border-radius:var(--bs-border-radius)}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:6rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select,.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select,.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(-1 * 1px);border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.5rem 1rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-success);border-radius:var(--bs-border-radius)}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:var(--bs-form-valid-border-color);padding-right:calc(1.5em + 1.5rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2341d7a7' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .375rem)center;background-size:calc(.75em + .75rem)calc(.75em + .75rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + 1.5rem);background-position:top calc(.375em + .375rem)right calc(.375em + .375rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:var(--bs-form-valid-border-color)}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2341d7a7' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1'/%3e%3c/svg%3e");padding-right:8.25rem;background-position:right 1.5rem center,center right 4.5rem;background-size:16px 12px,calc(.75em + .75rem)calc(.75em + .75rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-control-color.is-valid,.was-validated .form-control-color:valid{width:calc(3rem + calc(1.5em + 1.5rem))}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:var(--bs-form-valid-border-color)}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:var(--bs-form-valid-color)}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:var(--bs-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-valid,.input-group>.form-floating:not(:focus-within).is-valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-control:not(:focus):valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.was-validated .input-group>.form-select:not(:focus):valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.5rem 1rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-danger);border-radius:var(--bs-border-radius)}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:var(--bs-form-invalid-border-color);padding-right:calc(1.5em + 1.5rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23fd7e14'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23fd7e14' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .375rem)center;background-size:calc(.75em + .75rem)calc(.75em + .75rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + 1.5rem);background-position:top calc(.375em + .375rem)right calc(.375em + .375rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:var(--bs-form-invalid-border-color)}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23fd7e14'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23fd7e14' stroke='none'/%3e%3c/svg%3e");padding-right:8.25rem;background-position:right 1.5rem center,center right 4.5rem;background-size:16px 12px,calc(.75em + .75rem)calc(.75em + .75rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-control-color.is-invalid,.was-validated .form-control-color:invalid{width:calc(3rem + calc(1.5em + 1.5rem))}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:var(--bs-form-invalid-border-color)}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:var(--bs-form-invalid-color)}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:var(--bs-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-invalid,.input-group>.form-floating:not(:focus-within).is-invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-control:not(:focus):invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.was-validated .input-group>.form-select:not(:focus):invalid{z-index:4}.btn{--bs-btn-padding-x:1.5rem;--bs-btn-padding-y:0.75rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight:400;--bs-btn-line-height:1.5;--bs-btn-color:var(--bs-body-color);--bs-btn-bg:transparent;--bs-btn-border-width:var(--bs-border-width);--bs-btn-border-color:transparent;--bs-btn-border-radius:var(--bs-border-radius);--bs-btn-hover-border-color:transparent;--bs-btn-box-shadow:1px 3px 24px -1px rgba(0, 0, 0, 0.15);--bs-btn-disabled-opacity:0.65;--bs-btn-focus-box-shadow:0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y)var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width)solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,.btn.active,.btn.show,.btn:first-child:active,:not(.btn-check)+.btn:active{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible,.btn:first-child:active:focus-visible,:not(.btn-check)+.btn:active:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked:focus-visible+.btn{box-shadow:var(--bs-btn-focus-box-shadow)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-primary{--bs-btn-color:#fff;--bs-btn-bg:#e83283;--bs-btn-border-color:#e83283;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#c52b6f;--bs-btn-hover-border-color:#ba2869;--bs-btn-focus-shadow-rgb:235, 81, 150;--bs-btn-active-color:#fff;--bs-btn-active-bg:#ba2869;--bs-btn-active-border-color:#ae2662;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#e83283;--bs-btn-disabled-border-color:#e83283}.btn-secondary{--bs-btn-color:#000;--bs-btn-bg:rgba(255, 255, 255, 0.4);--bs-btn-border-color:rgba(255, 255, 255, 0.4);--bs-btn-hover-color:#000;--bs-btn-hover-bg:rgba(255, 255, 255, 0.49);--bs-btn-hover-border-color:rgba(255, 255, 255, 0.46);--bs-btn-focus-shadow-rgb:149, 149, 149;--bs-btn-active-color:#000;--bs-btn-active-bg:rgba(255, 255, 255, 0.52);--bs-btn-active-border-color:rgba(255, 255, 255, 0.46);--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:rgba(255, 255, 255, 0.4);--bs-btn-disabled-border-color:rgba(255, 255, 255, 0.4)}.btn-success{--bs-btn-color:#fff;--bs-btn-bg:#41d7a7;--bs-btn-border-color:#41d7a7;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#37b78e;--bs-btn-hover-border-color:#34ac86;--bs-btn-focus-shadow-rgb:94, 221, 180;--bs-btn-active-color:#fff;--bs-btn-active-bg:#34ac86;--bs-btn-active-border-color:#31a17d;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#41d7a7;--bs-btn-disabled-border-color:#41d7a7}.btn-info{--bs-btn-color:#fff;--bs-btn-bg:#39cbfb;--bs-btn-border-color:#39cbfb;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#30add5;--bs-btn-hover-border-color:#2ea2c9;--bs-btn-focus-shadow-rgb:87, 211, 252;--bs-btn-active-color:#fff;--bs-btn-active-bg:#2ea2c9;--bs-btn-active-border-color:#2b98bc;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#39cbfb;--bs-btn-disabled-border-color:#39cbfb}.btn-warning{--bs-btn-color:#fff;--bs-btn-bg:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#d9a406;--bs-btn-hover-border-color:#cc9a06;--bs-btn-focus-shadow-rgb:255, 202, 44;--bs-btn-active-color:#fff;--bs-btn-active-bg:#cc9a06;--bs-btn-active-border-color:#bf9105;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#ffc107;--bs-btn-disabled-border-color:#ffc107}.btn-danger{--bs-btn-color:#fff;--bs-btn-bg:#fd7e14;--bs-btn-border-color:#fd7e14;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#d76b11;--bs-btn-hover-border-color:#ca6510;--bs-btn-focus-shadow-rgb:253, 145, 55;--bs-btn-active-color:#fff;--bs-btn-active-bg:#ca6510;--bs-btn-active-border-color:#be5f0f;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#fd7e14;--bs-btn-disabled-border-color:#fd7e14}.btn-light{--bs-btn-color:#000;--bs-btn-bg:#e9e9e8;--bs-btn-border-color:#e9e9e8;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#c6c6c5;--bs-btn-hover-border-color:#bababa;--bs-btn-focus-shadow-rgb:198, 198, 197;--bs-btn-active-color:#fff;--bs-btn-active-bg:#bababa;--bs-btn-active-border-color:#afafae;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#e9e9e8;--bs-btn-disabled-border-color:#e9e9e8}.btn-dark{--bs-btn-color:#fff;--bs-btn-bg:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#424649;--bs-btn-hover-border-color:#373b3e;--bs-btn-focus-shadow-rgb:66, 70, 73;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4d5154;--bs-btn-active-border-color:#373b3e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#212529;--bs-btn-disabled-border-color:#212529}.btn-outline-primary{--bs-btn-color:#e83283;--bs-btn-border-color:#e83283;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#e83283;--bs-btn-hover-border-color:#e83283;--bs-btn-focus-shadow-rgb:232, 50, 131;--bs-btn-active-color:#fff;--bs-btn-active-bg:#e83283;--bs-btn-active-border-color:#e83283;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#e83283;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#e83283;--bs-gradient:none}.btn-outline-secondary{--bs-btn-color:rgba(255, 255, 255, 0.4);--bs-btn-border-color:rgba(255, 255, 255, 0.4);--bs-btn-hover-color:#000;--bs-btn-hover-bg:rgba(255, 255, 255, 0.4);--bs-btn-hover-border-color:rgba(255, 255, 255, 0.4);--bs-btn-focus-shadow-rgb:255, 255, 255;--bs-btn-active-color:#000;--bs-btn-active-bg:rgba(255, 255, 255, 0.4);--bs-btn-active-border-color:rgba(255, 255, 255, 0.4);--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:rgba(255, 255, 255, 0.4);--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:rgba(255, 255, 255, 0.4);--bs-gradient:none}.btn-outline-success{--bs-btn-color:#41d7a7;--bs-btn-border-color:#41d7a7;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#41d7a7;--bs-btn-hover-border-color:#41d7a7;--bs-btn-focus-shadow-rgb:65, 215, 167;--bs-btn-active-color:#fff;--bs-btn-active-bg:#41d7a7;--bs-btn-active-border-color:#41d7a7;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#41d7a7;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#41d7a7;--bs-gradient:none}.btn-outline-info{--bs-btn-color:#39cbfb;--bs-btn-border-color:#39cbfb;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#39cbfb;--bs-btn-hover-border-color:#39cbfb;--bs-btn-focus-shadow-rgb:57, 203, 251;--bs-btn-active-color:#fff;--bs-btn-active-bg:#39cbfb;--bs-btn-active-border-color:#39cbfb;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#39cbfb;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#39cbfb;--bs-gradient:none}.btn-outline-warning{--bs-btn-color:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#ffc107;--bs-btn-hover-border-color:#ffc107;--bs-btn-focus-shadow-rgb:255, 193, 7;--bs-btn-active-color:#fff;--bs-btn-active-bg:#ffc107;--bs-btn-active-border-color:#ffc107;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#ffc107;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#ffc107;--bs-gradient:none}.btn-outline-danger{--bs-btn-color:#fd7e14;--bs-btn-border-color:#fd7e14;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#fd7e14;--bs-btn-hover-border-color:#fd7e14;--bs-btn-focus-shadow-rgb:253, 126, 20;--bs-btn-active-color:#fff;--bs-btn-active-bg:#fd7e14;--bs-btn-active-border-color:#fd7e14;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fd7e14;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#fd7e14;--bs-gradient:none}.btn-outline-light{--bs-btn-color:#e9e9e8;--bs-btn-border-color:#e9e9e8;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#e9e9e8;--bs-btn-hover-border-color:#e9e9e8;--bs-btn-focus-shadow-rgb:233, 233, 232;--bs-btn-active-color:#000;--bs-btn-active-bg:#e9e9e8;--bs-btn-active-border-color:#e9e9e8;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#e9e9e8;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#e9e9e8;--bs-gradient:none}.btn-outline-dark{--bs-btn-color:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#212529;--bs-btn-hover-border-color:#212529;--bs-btn-focus-shadow-rgb:33, 37, 41;--bs-btn-active-color:#fff;--bs-btn-active-bg:#212529;--bs-btn-active-border-color:#212529;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#212529;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#212529;--bs-gradient:none}.btn-link{--bs-btn-font-weight:400;--bs-btn-color:var(--bs-link-color);--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-hover-color:var(--bs-link-hover-color);--bs-btn-hover-border-color:transparent;--bs-btn-active-color:var(--bs-link-hover-color);--bs-btn-active-border-color:transparent;--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-border-color:transparent;--bs-btn-box-shadow:0 0 0 #000;--bs-btn-focus-shadow-rgb:217, 217, 217;text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-group-lg>.btn,.btn-lg{--bs-btn-padding-y:0.5rem;--bs-btn-padding-x:1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius:var(--bs-border-radius-lg)}.btn-group-sm>.btn,.btn-sm{--bs-btn-padding-y:0.25rem;--bs-btn-padding-x:0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius:var(--bs-border-radius-sm)}.fade{transition:opacity .15s linear}@media(prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media(prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media(prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropdown-center,.dropend,.dropstart,.dropup,.dropup-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex:1000;--bs-dropdown-min-width:10rem;--bs-dropdown-padding-x:0;--bs-dropdown-padding-y:0.5rem;--bs-dropdown-spacer:0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color:var(--bs-body-color);--bs-dropdown-bg:var(--bs-body-bg);--bs-dropdown-border-color:rgba(255, 255, 255, 0.2);--bs-dropdown-border-radius:var(--bs-border-radius);--bs-dropdown-border-width:var(--bs-border-width);--bs-dropdown-inner-border-radius:calc(var(--bs-border-radius) - var(--bs-border-width));--bs-dropdown-divider-bg:rgba(255, 255, 255, 0.2);--bs-dropdown-divider-margin-y:1rem;--bs-dropdown-box-shadow:var(--bs-box-shadow);--bs-dropdown-link-color:var(--bs-body-color);--bs-dropdown-link-hover-color:#fff;--bs-dropdown-link-hover-bg:rgba(0, 0, 0, 0.4);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#e83283;--bs-dropdown-link-disabled-color:var(--bs-tertiary-color);--bs-dropdown-item-padding-x:2rem;--bs-dropdown-item-padding-y:0.5rem;--bs-dropdown-header-color:#6c757d;--bs-dropdown-header-padding-x:2rem;--bs-dropdown-header-padding-y:0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y)var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width)solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media(min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media(min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media(min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media(min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media(min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer)}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y)0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y)var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;text-decoration:none;white-space:nowrap;background-color:initial;border:0;border-radius:var(--bs-dropdown-item-border-radius,0)}.dropdown-item:focus,.dropdown-item:hover{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:initial}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y)var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y)var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color:#dee2e6;--bs-dropdown-bg:#343a40;--bs-dropdown-border-color:rgba(255, 255, 255, 0.2);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color:#dee2e6;--bs-dropdown-link-hover-color:#fff;--bs-dropdown-divider-bg:rgba(255, 255, 255, 0.2);--bs-dropdown-link-hover-bg:rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#e83283;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-header-color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:var(--bs-border-radius)}.btn-group>.btn-group:not(:first-child),.btn-group>:not(.btn-check:first-child)+.btn{margin-left:calc(-1 * var(--bs-border-width))}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:1.125rem;padding-left:1.125rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:calc(-1 * var(--bs-border-width))}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:nth-child(n+3),.btn-group-vertical>:not(.btn-check)+.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{--bs-nav-link-padding-x:1rem;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-link-color);--bs-nav-link-hover-color:var(--bs-link-hover-color);--bs-nav-link-disabled-color:rgba(255, 255, 255, 0.7);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y)var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;background:0 0;border:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media(prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(232,50,131,.25)}.nav-link.disabled,.nav-link:disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width:0;--bs-nav-tabs-border-color:var(--bs-border-color);--bs-nav-tabs-border-radius:0;--bs-nav-tabs-link-hover-border-color:var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color);--bs-nav-tabs-link-active-color:#343a40;--bs-nav-tabs-link-active-bg:#fff;--bs-nav-tabs-link-active-border-color:#dee2e6 #dee2e6 #fff;border-bottom:var(--bs-nav-tabs-border-width)solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width)solid transparent;border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius:50rem;--bs-nav-pills-link-active-color:#fff;--bs-nav-pills-link-active-bg:#e83283}.nav-pills .nav-link{border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg)}.nav-underline{--bs-nav-underline-gap:1rem;--bs-nav-underline-border-width:0.125rem;--bs-nav-underline-link-active-color:#fff;gap:var(--bs-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width)solid transparent}.nav-underline .nav-link:focus,.nav-underline .nav-link:hover{border-bottom-color:initial}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:initial}.nav-fill .nav-item,.nav-fill>.nav-link{flex:auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-grow:1;flex-basis:0;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{--bs-navbar-padding-x:0;--bs-navbar-padding-y:1rem;--bs-navbar-color:rgba(var(--bs-emphasis-color-rgb), 0.65);--bs-navbar-hover-color:rgba(var(--bs-emphasis-color-rgb), 0.8);--bs-navbar-disabled-color:rgba(var(--bs-emphasis-color-rgb), 0.3);--bs-navbar-active-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-padding-y:0.3125rem;--bs-navbar-brand-margin-end:1rem;--bs-navbar-brand-font-size:1.25rem;--bs-navbar-brand-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-hover-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-nav-link-padding-x:0.5rem;--bs-navbar-toggler-padding-y:0.25rem;--bs-navbar-toggler-padding-x:0.75rem;--bs-navbar-toggler-font-size:1.25rem;--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color:rgba(var(--bs-emphasis-color-rgb), 0.15);--bs-navbar-toggler-border-radius:var(--bs-border-radius);--bs-navbar-toggler-focus-width:0.25rem;--bs-navbar-toggler-transition:box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y)var(--bs-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x:0;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-navbar-color);--bs-nav-link-hover-color:var(--bs-navbar-hover-color);--bs-nav-link-disabled-color:var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:focus,.navbar-text a:hover{color:var(--bs-navbar-active-color)}.navbar-collapse{flex-grow:1;flex-basis:100%;align-items:center}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y)var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:initial;border:var(--bs-border-width)solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@media(prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:50%;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media(min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:initial!important;border:0!important;transform:none!important;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:initial!important;border:0!important;transform:none!important;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:initial!important;border:0!important;transform:none!important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:initial!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:initial!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:initial!important;border:0!important;transform:none!important;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark]{--bs-navbar-color:rgba(255, 255, 255, 0.55);--bs-navbar-hover-color:rgba(255, 255, 255, 0.75);--bs-navbar-disabled-color:rgba(255, 255, 255, 0.25);--bs-navbar-active-color:#fff;--bs-navbar-brand-color:#fff;--bs-navbar-brand-hover-color:#fff;--bs-navbar-toggler-border-color:rgba(255, 255, 255, 0.1);--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--bs-card-spacer-y:1.75rem;--bs-card-spacer-x:2rem;--bs-card-title-spacer-y:1rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width:var(--bs-border-width);--bs-card-border-color:rgba(255, 255, 255, 0.2);--bs-card-border-radius:var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y:0.875rem;--bs-card-cap-padding-x:2rem;--bs-card-cap-bg:transparent;--bs-card-cap-color:#fff;--bs-card-height: ;--bs-card-color:#fff;--bs-card-bg:transparent;--bs-card-img-overlay-padding:2rem;--bs-card-group-margin:0.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width)solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:auto;padding:var(--bs-card-spacer-y)var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color)}.card-subtitle{margin-top:calc(-.5 * var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y)var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width)solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius)var(--bs-card-inner-border-radius)0 0}.card-footer{padding:var(--bs-card-cap-padding-y)var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width)solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius)var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-bottom:calc(-1 * var(--bs-card-cap-padding-y));margin-left:calc(-.5 * var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-left:calc(-.5 * var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@media(min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child)>.card-header,.card-group>.card:not(:last-child)>.card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child)>.card-footer,.card-group>.card:not(:last-child)>.card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child)>.card-header,.card-group>.card:not(:first-child)>.card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child)>.card-footer,.card-group>.card:not(:first-child)>.card-img-bottom{border-bottom-left-radius:0}}.accordion{--bs-accordion-color:var(--bs-body-color);--bs-accordion-bg:var(--bs-body-bg);--bs-accordion-transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease;--bs-accordion-border-color:var(--bs-border-color);--bs-accordion-border-width:var(--bs-border-width);--bs-accordion-border-radius:var(--bs-border-radius);--bs-accordion-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-accordion-btn-padding-x:1.25rem;--bs-accordion-btn-padding-y:1rem;--bs-accordion-btn-color:var(--bs-body-color);--bs-accordion-btn-bg:rgba(255, 255, 255, 0.4);--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m2 5 6 6 6-6'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width:1.25rem;--bs-accordion-btn-icon-transform:rotate(-180deg);--bs-accordion-btn-icon-transition:transform 0.2s ease-in-out;--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%235d1434' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m2 5 6 6 6-6'/%3e%3c/svg%3e");--bs-accordion-btn-focus-box-shadow:0 0 0 0.25rem rgba(232, 50, 131, 0.25);--bs-accordion-body-padding-x:1.25rem;--bs-accordion-body-padding-y:1rem;--bs-accordion-active-color:#fff;--bs-accordion-active-bg:#e83283}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y)var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media(prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--bs-accordion-border-width))0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media(prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width)solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type>.accordion-header .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type>.accordion-header .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type>.accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y)var(--bs-accordion-body-padding-x)}.accordion-flush>.accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush>.accordion-item:first-child{border-top:0}.accordion-flush>.accordion-item:last-child{border-bottom:0}.accordion-flush>.accordion-item>.accordion-collapse,.accordion-flush>.accordion-item>.accordion-header .accordion-button,.accordion-flush>.accordion-item>.accordion-header .accordion-button.collapsed{border-radius:0}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23f184b5'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e");--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23f184b5'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e")}.breadcrumb{--bs-breadcrumb-padding-x:0;--bs-breadcrumb-padding-y:0;--bs-breadcrumb-margin-bottom:1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color:#fff;--bs-breadcrumb-item-padding-x:0.5rem;--bs-breadcrumb-item-active-color:#fff;display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y)var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider,"/")}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x:0.75rem;--bs-pagination-padding-y:0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color:var(--bs-link-color);--bs-pagination-bg:rgba(255, 255, 255, 0.3);--bs-pagination-border-width:0;--bs-pagination-border-color:var(--bs-border-color);--bs-pagination-border-radius:var(--bs-border-radius);--bs-pagination-hover-color:#fff;--bs-pagination-hover-bg:rgba(255, 255, 255, 0.4);--bs-pagination-hover-border-color:var(--bs-border-color);--bs-pagination-focus-color:#fff;--bs-pagination-focus-bg:rgba(255, 255, 255, 0.4);--bs-pagination-focus-box-shadow:none;--bs-pagination-active-color:#fff;--bs-pagination-active-bg:#e83283;--bs-pagination-active-border-color:#e83283;--bs-pagination-disabled-color:rgba(255, 255, 255, 0.7);--bs-pagination-disabled-bg:rgba(255, 255, 255, 0.3);--bs-pagination-disabled-border-color:var(--bs-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y)var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width)solid var(--bs-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.active>.page-link,.page-link.active{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.disabled>.page-link,.page-link.disabled{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(-1 * 0)}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x:1.5rem;--bs-pagination-padding-y:0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius:var(--bs-border-radius-lg)}.pagination-sm{--bs-pagination-padding-x:0.5rem;--bs-pagination-padding-y:0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius:var(--bs-border-radius-sm)}.badge{--bs-badge-padding-x:0.65em;--bs-badge-padding-y:0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight:700;--bs-badge-color:#fff;--bs-badge-border-radius:var(--bs-border-radius);display:inline-block;padding:var(--bs-badge-padding-y)var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg:transparent;--bs-alert-padding-x:2rem;--bs-alert-padding-y:2rem;--bs-alert-margin-bottom:1rem;--bs-alert-color:inherit;--bs-alert-border-color:transparent;--bs-alert-border:var(--bs-border-width) solid var(--bs-alert-border-color);--bs-alert-border-radius:var(--bs-border-radius);--bs-alert-link-color:inherit;position:relative;padding:var(--bs-alert-padding-y)var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:6rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:2.5rem 2rem}.alert-primary{--bs-alert-color:var(--bs-primary-text-emphasis);--bs-alert-bg:var(--bs-primary-bg-subtle);--bs-alert-border-color:var(--bs-primary-border-subtle);--bs-alert-link-color:var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color:var(--bs-secondary-text-emphasis);--bs-alert-bg:var(--bs-secondary-bg-subtle);--bs-alert-border-color:var(--bs-secondary-border-subtle);--bs-alert-link-color:var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color:var(--bs-success-text-emphasis);--bs-alert-bg:var(--bs-success-bg-subtle);--bs-alert-border-color:var(--bs-success-border-subtle);--bs-alert-link-color:var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color:var(--bs-info-text-emphasis);--bs-alert-bg:var(--bs-info-bg-subtle);--bs-alert-border-color:var(--bs-info-border-subtle);--bs-alert-link-color:var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color:var(--bs-warning-text-emphasis);--bs-alert-bg:var(--bs-warning-bg-subtle);--bs-alert-border-color:var(--bs-warning-border-subtle);--bs-alert-link-color:var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color:var(--bs-danger-text-emphasis);--bs-alert-bg:var(--bs-danger-bg-subtle);--bs-alert-border-color:var(--bs-danger-border-subtle);--bs-alert-link-color:var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color:var(--bs-light-text-emphasis);--bs-alert-bg:var(--bs-light-bg-subtle);--bs-alert-border-color:var(--bs-light-border-subtle);--bs-alert-link-color:var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color:var(--bs-dark-text-emphasis);--bs-alert-bg:var(--bs-dark-bg-subtle);--bs-alert-border-color:var(--bs-dark-border-subtle);--bs-alert-link-color:var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:var(--bs-progress-height)}}.progress,.progress-stacked{--bs-progress-height:1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg:rgba(0, 0, 0, 0.2);--bs-progress-border-radius:var(--bs-border-radius);--bs-progress-box-shadow:var(--bs-box-shadow-inset);--bs-progress-bar-color:#fff;--bs-progress-bar-bg:#e83283;--bs-progress-bar-transition:width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@media(prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--bs-progress-height)var(--bs-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media(prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color:#fff;--bs-list-group-bg:transparent;--bs-list-group-border-color:rgba(255, 255, 255, 0.2);--bs-list-group-border-width:0;--bs-list-group-border-radius:var(--bs-border-radius);--bs-list-group-item-padding-x:2rem;--bs-list-group-item-padding-y:1rem;--bs-list-group-action-color:#fff;--bs-list-group-action-hover-color:#fff;--bs-list-group-action-hover-bg:rgba(255, 255, 255, 0.4);--bs-list-group-action-active-color:var(--bs-body-color);--bs-list-group-action-active-bg:var(--bs-secondary-bg);--bs-list-group-disabled-color:rgba(255, 255, 255, 0.7);--bs-list-group-disabled-bg:transparent;--bs-list-group-active-color:#fff;--bs-list-group-active-bg:#e83283;--bs-list-group-active-border-color:#e83283;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section,".")". ";counter-increment:section}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y)var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width)solid var(--bs-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:not(.active):focus,.list-group-item-action:not(.active):hover{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:not(.active):active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media(min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{--bs-list-group-color:var(--bs-primary-text-emphasis);--bs-list-group-bg:var(--bs-primary-bg-subtle);--bs-list-group-border-color:var(--bs-primary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-primary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-primary-border-subtle);--bs-list-group-active-color:var(--bs-primary-bg-subtle);--bs-list-group-active-bg:var(--bs-primary-text-emphasis);--bs-list-group-active-border-color:var(--bs-primary-text-emphasis)}.list-group-item-secondary{--bs-list-group-color:var(--bs-secondary-text-emphasis);--bs-list-group-bg:var(--bs-secondary-bg-subtle);--bs-list-group-border-color:var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-secondary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-secondary-border-subtle);--bs-list-group-active-color:var(--bs-secondary-bg-subtle);--bs-list-group-active-bg:var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color:var(--bs-secondary-text-emphasis)}.list-group-item-success{--bs-list-group-color:var(--bs-success-text-emphasis);--bs-list-group-bg:var(--bs-success-bg-subtle);--bs-list-group-border-color:var(--bs-success-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-success-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-success-border-subtle);--bs-list-group-active-color:var(--bs-success-bg-subtle);--bs-list-group-active-bg:var(--bs-success-text-emphasis);--bs-list-group-active-border-color:var(--bs-success-text-emphasis)}.list-group-item-info{--bs-list-group-color:var(--bs-info-text-emphasis);--bs-list-group-bg:var(--bs-info-bg-subtle);--bs-list-group-border-color:var(--bs-info-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-info-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-info-border-subtle);--bs-list-group-active-color:var(--bs-info-bg-subtle);--bs-list-group-active-bg:var(--bs-info-text-emphasis);--bs-list-group-active-border-color:var(--bs-info-text-emphasis)}.list-group-item-warning{--bs-list-group-color:var(--bs-warning-text-emphasis);--bs-list-group-bg:var(--bs-warning-bg-subtle);--bs-list-group-border-color:var(--bs-warning-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-warning-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-warning-border-subtle);--bs-list-group-active-color:var(--bs-warning-bg-subtle);--bs-list-group-active-bg:var(--bs-warning-text-emphasis);--bs-list-group-active-border-color:var(--bs-warning-text-emphasis)}.list-group-item-danger{--bs-list-group-color:var(--bs-danger-text-emphasis);--bs-list-group-bg:var(--bs-danger-bg-subtle);--bs-list-group-border-color:var(--bs-danger-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-danger-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-danger-border-subtle);--bs-list-group-active-color:var(--bs-danger-bg-subtle);--bs-list-group-active-bg:var(--bs-danger-text-emphasis);--bs-list-group-active-border-color:var(--bs-danger-text-emphasis)}.list-group-item-light{--bs-list-group-color:var(--bs-light-text-emphasis);--bs-list-group-bg:var(--bs-light-bg-subtle);--bs-list-group-border-color:var(--bs-light-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-light-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-light-border-subtle);--bs-list-group-active-color:var(--bs-light-bg-subtle);--bs-list-group-active-bg:var(--bs-light-text-emphasis);--bs-list-group-active-border-color:var(--bs-light-text-emphasis)}.list-group-item-dark{--bs-list-group-color:var(--bs-dark-text-emphasis);--bs-list-group-bg:var(--bs-dark-bg-subtle);--bs-list-group-border-color:var(--bs-dark-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-dark-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-dark-border-subtle);--bs-list-group-active-color:var(--bs-dark-bg-subtle);--bs-list-group-active-bg:var(--bs-dark-text-emphasis);--bs-list-group-active-border-color:var(--bs-dark-text-emphasis)}.btn-close{--bs-btn-close-color:#fff;--bs-btn-close-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414'/%3e%3c/svg%3e");--bs-btn-close-opacity:0.5;--bs-btn-close-hover-opacity:0.75;--bs-btn-close-focus-shadow:0 0 0 0.25rem rgba(232, 50, 131, 0.25);--bs-btn-close-focus-opacity:1;--bs-btn-close-disabled-opacity:0.25;box-sizing:content-box;width:1em;height:1em;padding:.25em;color:var(--bs-btn-close-color);background:var(--bs-btn-close-bg)50%/1em no-repeat;filter:var(--bs-btn-close-filter);border:0;border-radius:.5rem;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:var(--bs-btn-close-disabled-opacity)}.btn-close-white{--bs-btn-close-filter:invert(1) grayscale(100%) brightness(200%)}:root,[data-bs-theme=light]{--bs-btn-close-filter: }[data-bs-theme=dark]{--bs-btn-close-filter:invert(1) grayscale(100%) brightness(200%)}.toast{--bs-toast-zindex:1090;--bs-toast-padding-x:0.75rem;--bs-toast-padding-y:0.5rem;--bs-toast-spacing:1.5rem;--bs-toast-max-width:350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-border-width:var(--bs-border-width);--bs-toast-border-color:var(--bs-border-color-translucent);--bs-toast-border-radius:var(--bs-border-radius);--bs-toast-box-shadow:var(--bs-box-shadow);--bs-toast-header-color:#fff;--bs-toast-header-bg:transparent;--bs-toast-header-border-color:rgba(255, 255, 255, 0.2);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width)solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex:1090;position:absolute;z-index:var(--bs-toast-zindex);width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y)var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width)solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex:1055;--bs-modal-width:500px;--bs-modal-padding:2rem;--bs-modal-margin:0.5rem;--bs-modal-color:var(--bs-body-color);--bs-modal-bg:var(--bs-body-bg);--bs-modal-border-color:var(--bs-border-color-translucent);--bs-modal-border-width:var(--bs-border-width);--bs-modal-border-radius:var(--bs-border-radius-lg);--bs-modal-box-shadow:var(--bs-box-shadow-sm);--bs-modal-inner-border-radius:calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));--bs-modal-header-padding-x:2rem;--bs-modal-header-padding-y:2rem;--bs-modal-header-padding:2rem 2rem;--bs-modal-header-border-color:var(--bs-border-color);--bs-modal-header-border-width:var(--bs-border-width);--bs-modal-title-line-height:1.5;--bs-modal-footer-gap:0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color:var(--bs-border-color);--bs-modal-footer-border-width:var(--bs-border-width);position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transform:translate(0,-50px);transition:transform .3s ease-out}@media(prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--bs-modal-margin) * 2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--bs-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width)solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);outline:0}.modal-backdrop{--bs-backdrop-zindex:1050;--bs-backdrop-bg:#000;--bs-backdrop-opacity:0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width)solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y) * .5)calc(var(--bs-modal-header-padding-x) * .5);margin-top:calc(-.5 * var(--bs-modal-header-padding-y));margin-right:calc(-.5 * var(--bs-modal-header-padding-x));margin-bottom:calc(-.5 * var(--bs-modal-header-padding-y));margin-left:auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * .5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width)solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap) * .5)}@media(min-width:576px){.modal{--bs-modal-margin:1.75rem;--bs-modal-box-shadow:var(--bs-box-shadow)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width:300px}}@media(min-width:992px){.modal-lg,.modal-xl{--bs-modal-width:800px}}@media(min-width:1200px){.modal-xl{--bs-modal-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-footer,.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media(max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-footer,.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media(max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-footer,.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media(max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-footer,.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media(max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-footer,.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media(max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-footer,.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex:1080;--bs-tooltip-max-width:200px;--bs-tooltip-padding-x:1rem;--bs-tooltip-padding-y:0.5rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color:var(--bs-body-bg);--bs-tooltip-bg:var(--bs-emphasis-color);--bs-tooltip-border-radius:var(--bs-border-radius);--bs-tooltip-opacity:0.7;--bs-tooltip-arrow-width:0.8rem;--bs-tooltip-arrow-height:0.4rem;z-index:var(--bs-tooltip-zindex);display:block;margin:var(--bs-tooltip-margin);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height)calc(var(--bs-tooltip-arrow-width) * .5)0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5)var(--bs-tooltip-arrow-height)calc(var(--bs-tooltip-arrow-width) * .5)0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width) * .5)var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5)0 calc(var(--bs-tooltip-arrow-width) * .5)var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y)var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex:1070;--bs-popover-max-width:276px;--bs-popover-font-size:0.875rem;--bs-popover-bg:var(--bs-body-bg);--bs-popover-border-width:var(--bs-border-width);--bs-popover-border-color:var(--bs-border-color-translucent);--bs-popover-border-radius:var(--bs-border-radius-lg);--bs-popover-inner-border-radius:calc(var(--bs-border-radius-lg) - var(--bs-border-width));--bs-popover-box-shadow:var(--bs-box-shadow);--bs-popover-header-padding-x:2rem;--bs-popover-header-padding-y:0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color:inherit;--bs-popover-header-bg:transparent;--bs-popover-body-padding-x:2rem;--bs-popover-body-padding-y:1rem;--bs-popover-body-color:#fff;--bs-popover-arrow-width:1rem;--bs-popover-arrow-height:0.5rem;--bs-popover-arrow-border:var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width)solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-top>.popover-arrow::before{border-width:var(--bs-popover-arrow-height)calc(var(--bs-popover-arrow-width) * .5)0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-end>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5)var(--bs-popover-arrow-height)calc(var(--bs-popover-arrow-width) * .5)0}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::before{border-width:0 calc(var(--bs-popover-arrow-width) * .5)var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:var(--bs-popover-arrow-width);margin-left:calc(-.5 * var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width)solid var(--bs-popover-header-bg)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-start>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5)0 calc(var(--bs-popover-arrow-width) * .5)var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y)var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width)solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y)var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media(prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media(prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;filter:var(--bs-carousel-control-icon-filter);border:0;opacity:.5;transition:opacity .15s ease}@media(prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:initial;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:var(--bs-carousel-indicator-active-bg);background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media(prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:var(--bs-carousel-caption-color);text-align:center}.carousel-dark{--bs-carousel-indicator-active-bg:#000;--bs-carousel-caption-color:#000;--bs-carousel-control-icon-filter:invert(1) grayscale(100)}:root,[data-bs-theme=light]{--bs-carousel-indicator-active-bg:#fff;--bs-carousel-caption-color:#fff;--bs-carousel-control-icon-filter: }[data-bs-theme=dark]{--bs-carousel-indicator-active-bg:#000;--bs-carousel-caption-color:#000;--bs-carousel-control-icon-filter:invert(1) grayscale(100)}.spinner-border,.spinner-grow{display:inline-block;flex-shrink:0;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed)linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-border-width:0.25em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-border;border:var(--bs-spinner-border-width)solid;border-right-color:transparent}.spinner-border-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;--bs-spinner-border-width:0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem}@media(prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed:1.5s}}.offcanvas,.offcanvas-lg,.offcanvas-md,.offcanvas-sm,.offcanvas-xl,.offcanvas-xxl{--bs-offcanvas-zindex:1045;--bs-offcanvas-width:400px;--bs-offcanvas-height:30vh;--bs-offcanvas-padding-x:2rem;--bs-offcanvas-padding-y:2rem;--bs-offcanvas-color:var(--bs-body-color);--bs-offcanvas-bg:var(--bs-body-bg);--bs-offcanvas-border-width:var(--bs-border-width);--bs-offcanvas-border-color:var(--bs-border-color-translucent);--bs-offcanvas-box-shadow:var(--bs-box-shadow-sm);--bs-offcanvas-transition:transform 0.3s ease-in-out;--bs-offcanvas-title-line-height:1.5}@media(max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width:575.98px) and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media(max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.show:not(.hiding),.offcanvas-sm.showing{transform:none}.offcanvas-sm.hiding,.offcanvas-sm.show,.offcanvas-sm.showing{visibility:visible}}@media(min-width:576px){.offcanvas-sm{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:initial!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:initial!important}}@media(max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width:767.98px) and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media(max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.show:not(.hiding),.offcanvas-md.showing{transform:none}.offcanvas-md.hiding,.offcanvas-md.show,.offcanvas-md.showing{visibility:visible}}@media(min-width:768px){.offcanvas-md{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:initial!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:initial!important}}@media(max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width:991.98px) and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media(max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.show:not(.hiding),.offcanvas-lg.showing{transform:none}.offcanvas-lg.hiding,.offcanvas-lg.show,.offcanvas-lg.showing{visibility:visible}}@media(min-width:992px){.offcanvas-lg{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:initial!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:initial!important}}@media(max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width:1199.98px) and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media(max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.show:not(.hiding),.offcanvas-xl.showing{transform:none}.offcanvas-xl.hiding,.offcanvas-xl.show,.offcanvas-xl.showing{visibility:visible}}@media(min-width:1200px){.offcanvas-xl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:initial!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:initial!important}}@media(max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width:1399.98px) and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media(max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.show:not(.hiding),.offcanvas-xxl.showing{transform:none}.offcanvas-xxl.hiding,.offcanvas-xxl.show,.offcanvas-xxl.showing{visibility:visible}}@media(min-width:1400px){.offcanvas-xxl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:initial!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:initial!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}@media(prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.show:not(.hiding),.offcanvas.showing{transform:none}.offcanvas.hiding,.offcanvas.show,.offcanvas.showing{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;padding:var(--bs-offcanvas-padding-y)var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y) * .5)calc(var(--bs-offcanvas-padding-x) * .5);margin-top:calc(-.5 * var(--bs-offcanvas-padding-y));margin-right:calc(-.5 * var(--bs-offcanvas-padding-x));margin-bottom:calc(-.5 * var(--bs-offcanvas-padding-y));margin-left:auto}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y)var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.text-bg-primary{color:#fff!important;background-color:RGBA(var(--bs-primary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-secondary{color:#000!important;background-color:RGBA(var(--bs-secondary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-success{color:#fff!important;background-color:RGBA(var(--bs-success-rgb),var(--bs-bg-opacity,1))!important}.text-bg-info{color:#fff!important;background-color:RGBA(var(--bs-info-rgb),var(--bs-bg-opacity,1))!important}.text-bg-warning{color:#fff!important;background-color:RGBA(var(--bs-warning-rgb),var(--bs-bg-opacity,1))!important}.text-bg-danger{color:#fff!important;background-color:RGBA(var(--bs-danger-rgb),var(--bs-bg-opacity,1))!important}.text-bg-light{color:#000!important;background-color:RGBA(var(--bs-light-rgb),var(--bs-bg-opacity,1))!important}.text-bg-dark{color:#fff!important;background-color:RGBA(var(--bs-dark-rgb),var(--bs-bg-opacity,1))!important}.link-primary{color:RGBA(var(--bs-primary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important}.link-primary:focus,.link-primary:hover{color:RGBA(186,40,105,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(186,40,105,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(186,40,105,var(--bs-link-underline-opacity,1))!important}.link-secondary{color:RGBA(var(--bs-secondary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important}.link-secondary:focus,.link-secondary:hover{color:RGBA(255,255,255,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(255,255,255,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(255,255,255,var(--bs-link-underline-opacity,1))!important}.link-success{color:RGBA(var(--bs-success-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important}.link-success:focus,.link-success:hover{color:RGBA(52,172,134,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(52,172,134,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(52,172,134,var(--bs-link-underline-opacity,1))!important}.link-info{color:RGBA(var(--bs-info-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important}.link-info:focus,.link-info:hover{color:RGBA(46,162,201,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(46,162,201,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(46,162,201,var(--bs-link-underline-opacity,1))!important}.link-warning{color:RGBA(var(--bs-warning-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important}.link-warning:focus,.link-warning:hover{color:RGBA(204,154,6,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(204,154,6,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(204,154,6,var(--bs-link-underline-opacity,1))!important}.link-danger{color:RGBA(var(--bs-danger-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important}.link-danger:focus,.link-danger:hover{color:RGBA(202,101,16,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(202,101,16,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(202,101,16,var(--bs-link-underline-opacity,1))!important}.link-light{color:RGBA(var(--bs-light-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important}.link-light:focus,.link-light:hover{color:RGBA(237,237,237,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(237,237,237,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(237,237,237,var(--bs-link-underline-opacity,1))!important}.link-dark{color:RGBA(var(--bs-dark-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important}.link-dark:focus,.link-dark:hover{color:RGBA(26,30,33,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-body-emphasis:focus,.link-body-emphasis:hover{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,.75))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,.75))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,.75))!important}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x,0)var(--bs-focus-ring-y,0)var(--bs-focus-ring-blur,0)var(--bs-focus-ring-width)var(--bs-focus-ring-color)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,.5));text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,.5));text-underline-offset:.25em;-webkit-backface-visibility:hidden;backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media(prefers-reduced-motion:reduce){.icon-link>.bi{transition:none}}.icon-link-hover:focus-visible>.bi,.icon-link-hover:hover>.bi{transform:var(--bs-icon-link-transform,translate3d(.25em,0,0))}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:75%}.ratio-16x9{--bs-aspect-ratio:56.25%}.ratio-21x9{--bs-aspect-ratio:42.8571428571%}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}@media(min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media(min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media(min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media(min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media(min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption),.visually-hidden:not(caption){position:absolute!important}.visually-hidden *,.visually-hidden-focusable:not(:focus):not(:focus-within) *{overflow:hidden!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:var(--bs-border-width);min-height:1em;background-color:currentcolor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.object-fit-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-none{-o-object-fit:none!important;object-fit:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.overflow-x-auto{overflow-x:auto!important}.overflow-x-hidden{overflow-x:hidden!important}.overflow-x-visible{overflow-x:visible!important}.overflow-x-scroll{overflow-x:scroll!important}.overflow-y-auto{overflow-y:auto!important}.overflow-y-hidden{overflow-y:hidden!important}.overflow-y-visible{overflow-y:visible!important}.overflow-y-scroll{overflow-y:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-inline-grid{display:inline-grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:var(--bs-box-shadow)!important}.shadow-sm{box-shadow:var(--bs-box-shadow-sm)!important}.shadow-lg{box-shadow:var(--bs-box-shadow-lg)!important}.shadow-none{box-shadow:none!important}.focus-ring-primary{--bs-focus-ring-color:rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-secondary{--bs-focus-ring-color:rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-success{--bs-focus-ring-color:rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity))}.focus-ring-info{--bs-focus-ring-color:rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity))}.focus-ring-warning{--bs-focus-ring-color:rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity))}.focus-ring-danger{--bs-focus-ring-color:rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity))}.focus-ring-light{--bs-focus-ring-color:rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity))}.focus-ring-dark{--bs-focus-ring-color:rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity))}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-0{border:0!important}.border-top{border-top:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-start-0{border-left:0!important}.border-primary{--bs-border-opacity:1;border-color:rgba(var(--bs-primary-rgb),var(--bs-border-opacity))!important}.border-secondary{--bs-border-opacity:1;border-color:rgba(var(--bs-secondary-rgb),var(--bs-border-opacity))!important}.border-success{--bs-border-opacity:1;border-color:rgba(var(--bs-success-rgb),var(--bs-border-opacity))!important}.border-info{--bs-border-opacity:1;border-color:rgba(var(--bs-info-rgb),var(--bs-border-opacity))!important}.border-warning{--bs-border-opacity:1;border-color:rgba(var(--bs-warning-rgb),var(--bs-border-opacity))!important}.border-danger{--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important}.border-light{--bs-border-opacity:1;border-color:rgba(var(--bs-light-rgb),var(--bs-border-opacity))!important}.border-dark{--bs-border-opacity:1;border-color:rgba(var(--bs-dark-rgb),var(--bs-border-opacity))!important}.border-black{--bs-border-opacity:1;border-color:rgba(var(--bs-black-rgb),var(--bs-border-opacity))!important}.border-white{--bs-border-opacity:1;border-color:rgba(var(--bs-white-rgb),var(--bs-border-opacity))!important}.border-primary-subtle{border-color:var(--bs-primary-border-subtle)!important}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle)!important}.border-success-subtle{border-color:var(--bs-success-border-subtle)!important}.border-info-subtle{border-color:var(--bs-info-border-subtle)!important}.border-warning-subtle{border-color:var(--bs-warning-border-subtle)!important}.border-danger-subtle{border-color:var(--bs-danger-border-subtle)!important}.border-light-subtle{border-color:var(--bs-light-border-subtle)!important}.border-dark-subtle{border-color:var(--bs-dark-border-subtle)!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.border-opacity-10{--bs-border-opacity:0.1}.border-opacity-25{--bs-border-opacity:0.25}.border-opacity-50{--bs-border-opacity:0.5}.border-opacity-75{--bs-border-opacity:0.75}.border-opacity-100{--bs-border-opacity:1}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.5rem!important}.m-2{margin:1rem!important}.m-3{margin:2rem!important}.m-4{margin:3rem!important}.m-5{margin:6rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.5rem!important;margin-left:.5rem!important}.mx-2{margin-right:1rem!important;margin-left:1rem!important}.mx-3{margin-right:2rem!important;margin-left:2rem!important}.mx-4{margin-right:3rem!important;margin-left:3rem!important}.mx-5{margin-right:6rem!important;margin-left:6rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-2{margin-top:1rem!important;margin-bottom:1rem!important}.my-3{margin-top:2rem!important;margin-bottom:2rem!important}.my-4{margin-top:3rem!important;margin-bottom:3rem!important}.my-5{margin-top:6rem!important;margin-bottom:6rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.5rem!important}.mt-2{margin-top:1rem!important}.mt-3{margin-top:2rem!important}.mt-4{margin-top:3rem!important}.mt-5{margin-top:6rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.5rem!important}.me-2{margin-right:1rem!important}.me-3{margin-right:2rem!important}.me-4{margin-right:3rem!important}.me-5{margin-right:6rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.5rem!important}.mb-2{margin-bottom:1rem!important}.mb-3{margin-bottom:2rem!important}.mb-4{margin-bottom:3rem!important}.mb-5{margin-bottom:6rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.5rem!important}.ms-2{margin-left:1rem!important}.ms-3{margin-left:2rem!important}.ms-4{margin-left:3rem!important}.ms-5{margin-left:6rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.5rem!important}.p-2{padding:1rem!important}.p-3{padding:2rem!important}.p-4{padding:3rem!important}.p-5{padding:6rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.5rem!important;padding-left:.5rem!important}.px-2{padding-right:1rem!important;padding-left:1rem!important}.px-3{padding-right:2rem!important;padding-left:2rem!important}.px-4{padding-right:3rem!important;padding-left:3rem!important}.px-5{padding-right:6rem!important;padding-left:6rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-2{padding-top:1rem!important;padding-bottom:1rem!important}.py-3{padding-top:2rem!important;padding-bottom:2rem!important}.py-4{padding-top:3rem!important;padding-bottom:3rem!important}.py-5{padding-top:6rem!important;padding-bottom:6rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.5rem!important}.pt-2{padding-top:1rem!important}.pt-3{padding-top:2rem!important}.pt-4{padding-top:3rem!important}.pt-5{padding-top:6rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.5rem!important}.pe-2{padding-right:1rem!important}.pe-3{padding-right:2rem!important}.pe-4{padding-right:3rem!important}.pe-5{padding-right:6rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.5rem!important}.pb-2{padding-bottom:1rem!important}.pb-3{padding-bottom:2rem!important}.pb-4{padding-bottom:3rem!important}.pb-5{padding-bottom:6rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.5rem!important}.ps-2{padding-left:1rem!important}.ps-3{padding-left:2rem!important}.ps-4{padding-left:3rem!important}.ps-5{padding-left:6rem!important}.gap-0{gap:0!important}.gap-1{gap:.5rem!important}.gap-2{gap:1rem!important}.gap-3{gap:2rem!important}.gap-4{gap:3rem!important}.gap-5{gap:6rem!important}.row-gap-0{row-gap:0!important}.row-gap-1{row-gap:.5rem!important}.row-gap-2{row-gap:1rem!important}.row-gap-3{row-gap:2rem!important}.row-gap-4{row-gap:3rem!important}.row-gap-5{row-gap:6rem!important}.column-gap-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-1{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-2{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-3{-moz-column-gap:2rem!important;column-gap:2rem!important}.column-gap-4{-moz-column-gap:3rem!important;column-gap:3rem!important}.column-gap-5{-moz-column-gap:6rem!important;column-gap:6rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-lighter{font-weight:lighter!important}.fw-light{font-weight:300!important}.fw-normal{font-weight:400!important}.fw-medium{font-weight:500!important}.fw-semibold{font-weight:600!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-body-secondary{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-body-tertiary{--bs-text-opacity:1;color:var(--bs-tertiary-color)!important}.text-body-emphasis{--bs-text-opacity:1;color:var(--bs-emphasis-color)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.text-primary-emphasis{color:var(--bs-primary-text-emphasis)!important}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis)!important}.text-success-emphasis{color:var(--bs-success-text-emphasis)!important}.text-info-emphasis{color:var(--bs-info-text-emphasis)!important}.text-warning-emphasis{color:var(--bs-warning-text-emphasis)!important}.text-danger-emphasis{color:var(--bs-danger-text-emphasis)!important}.text-light-emphasis{color:var(--bs-light-text-emphasis)!important}.text-dark-emphasis{color:var(--bs-dark-text-emphasis)!important}.link-opacity-10{--bs-link-opacity:0.1}.link-opacity-10-hover:hover{--bs-link-opacity:0.1}.link-opacity-25{--bs-link-opacity:0.25}.link-opacity-25-hover:hover{--bs-link-opacity:0.25}.link-opacity-50{--bs-link-opacity:0.5}.link-opacity-50-hover:hover{--bs-link-opacity:0.5}.link-opacity-75{--bs-link-opacity:0.75}.link-opacity-75-hover:hover{--bs-link-opacity:0.75}.link-opacity-100{--bs-link-opacity:1}.link-opacity-100-hover:hover{--bs-link-opacity:1}.link-offset-1{text-underline-offset:.125em!important}.link-offset-1-hover:hover{text-underline-offset:.125em!important}.link-offset-2{text-underline-offset:.25em!important}.link-offset-2-hover:hover{text-underline-offset:.25em!important}.link-offset-3{text-underline-offset:.375em!important}.link-offset-3-hover:hover{text-underline-offset:.375em!important}.link-underline-primary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-secondary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-success{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important}.link-underline-info{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important}.link-underline-warning{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important}.link-underline-danger{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important}.link-underline-light{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important}.link-underline-dark{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important}.link-underline{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-underline-opacity-0{--bs-link-underline-opacity:0}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity:0}.link-underline-opacity-10{--bs-link-underline-opacity:0.1}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity:0.1}.link-underline-opacity-25{--bs-link-underline-opacity:0.25}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity:0.25}.link-underline-opacity-50{--bs-link-underline-opacity:0.5}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity:0.5}.link-underline-opacity-75{--bs-link-underline-opacity:0.75}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity:0.75}.link-underline-opacity-100{--bs-link-underline-opacity:1}.link-underline-opacity-100-hover:hover{--bs-link-underline-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:initial!important}.bg-body-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-bg-rgb),var(--bs-bg-opacity))!important}.bg-body-tertiary{--bs-bg-opacity:1;background-color:rgba(var(--bs-tertiary-bg-rgb),var(--bs-bg-opacity))!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-primary-subtle{background-color:var(--bs-primary-bg-subtle)!important}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle)!important}.bg-success-subtle{background-color:var(--bs-success-bg-subtle)!important}.bg-info-subtle{background-color:var(--bs-info-bg-subtle)!important}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle)!important}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle)!important}.bg-light-subtle{background-color:var(--bs-light-bg-subtle)!important}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle)!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:var(--bs-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--bs-border-radius-sm)!important}.rounded-2{border-radius:var(--bs-border-radius)!important}.rounded-3{border-radius:var(--bs-border-radius-lg)!important}.rounded-4{border-radius:var(--bs-border-radius-xl)!important}.rounded-5{border-radius:var(--bs-border-radius-xxl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--bs-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-0{border-top-left-radius:0!important;border-top-right-radius:0!important}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm)!important;border-top-right-radius:var(--bs-border-radius-sm)!important}.rounded-top-2{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg)!important;border-top-right-radius:var(--bs-border-radius-lg)!important}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl)!important;border-top-right-radius:var(--bs-border-radius-xl)!important}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl)!important;border-top-right-radius:var(--bs-border-radius-xxl)!important}.rounded-top-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill)!important;border-top-right-radius:var(--bs-border-radius-pill)!important}.rounded-end{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm)!important;border-bottom-right-radius:var(--bs-border-radius-sm)!important}.rounded-end-2{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg)!important;border-bottom-right-radius:var(--bs-border-radius-lg)!important}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl)!important;border-bottom-right-radius:var(--bs-border-radius-xl)!important}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-right-radius:var(--bs-border-radius-xxl)!important}.rounded-end-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill)!important;border-bottom-right-radius:var(--bs-border-radius-pill)!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-0{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm)!important;border-bottom-left-radius:var(--bs-border-radius-sm)!important}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg)!important;border-bottom-left-radius:var(--bs-border-radius-lg)!important}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl)!important;border-bottom-left-radius:var(--bs-border-radius-xl)!important}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-left-radius:var(--bs-border-radius-xxl)!important}.rounded-bottom-circle{border-bottom-right-radius:50%!important;border-bottom-left-radius:50%!important}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill)!important;border-bottom-left-radius:var(--bs-border-radius-pill)!important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-0{border-bottom-left-radius:0!important;border-top-left-radius:0!important}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm)!important;border-top-left-radius:var(--bs-border-radius-sm)!important}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg)!important;border-top-left-radius:var(--bs-border-radius-lg)!important}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl)!important;border-top-left-radius:var(--bs-border-radius-xl)!important}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl)!important;border-top-left-radius:var(--bs-border-radius-xxl)!important}.rounded-start-circle{border-bottom-left-radius:50%!important;border-top-left-radius:50%!important}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill)!important;border-top-left-radius:var(--bs-border-radius-pill)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}.z-n1{z-index:-1!important}.z-0{z-index:0!important}.z-1{z-index:1!important}.z-2{z-index:2!important}.z-3{z-index:3!important}@media(min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.object-fit-sm-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-sm-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-sm-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-sm-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-sm-none{-o-object-fit:none!important;object-fit:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-inline-grid{display:inline-grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.5rem!important}.m-sm-2{margin:1rem!important}.m-sm-3{margin:2rem!important}.m-sm-4{margin:3rem!important}.m-sm-5{margin:6rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-2{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-3{margin-right:2rem!important;margin-left:2rem!important}.mx-sm-4{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-5{margin-right:6rem!important;margin-left:6rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-2{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-3{margin-top:2rem!important;margin-bottom:2rem!important}.my-sm-4{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-5{margin-top:6rem!important;margin-bottom:6rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.5rem!important}.mt-sm-2{margin-top:1rem!important}.mt-sm-3{margin-top:2rem!important}.mt-sm-4{margin-top:3rem!important}.mt-sm-5{margin-top:6rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.5rem!important}.me-sm-2{margin-right:1rem!important}.me-sm-3{margin-right:2rem!important}.me-sm-4{margin-right:3rem!important}.me-sm-5{margin-right:6rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.5rem!important}.mb-sm-2{margin-bottom:1rem!important}.mb-sm-3{margin-bottom:2rem!important}.mb-sm-4{margin-bottom:3rem!important}.mb-sm-5{margin-bottom:6rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.5rem!important}.ms-sm-2{margin-left:1rem!important}.ms-sm-3{margin-left:2rem!important}.ms-sm-4{margin-left:3rem!important}.ms-sm-5{margin-left:6rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.5rem!important}.p-sm-2{padding:1rem!important}.p-sm-3{padding:2rem!important}.p-sm-4{padding:3rem!important}.p-sm-5{padding:6rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-2{padding-right:1rem!important;padding-left:1rem!important}.px-sm-3{padding-right:2rem!important;padding-left:2rem!important}.px-sm-4{padding-right:3rem!important;padding-left:3rem!important}.px-sm-5{padding-right:6rem!important;padding-left:6rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-2{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-3{padding-top:2rem!important;padding-bottom:2rem!important}.py-sm-4{padding-top:3rem!important;padding-bottom:3rem!important}.py-sm-5{padding-top:6rem!important;padding-bottom:6rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.5rem!important}.pt-sm-2{padding-top:1rem!important}.pt-sm-3{padding-top:2rem!important}.pt-sm-4{padding-top:3rem!important}.pt-sm-5{padding-top:6rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.5rem!important}.pe-sm-2{padding-right:1rem!important}.pe-sm-3{padding-right:2rem!important}.pe-sm-4{padding-right:3rem!important}.pe-sm-5{padding-right:6rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.5rem!important}.pb-sm-2{padding-bottom:1rem!important}.pb-sm-3{padding-bottom:2rem!important}.pb-sm-4{padding-bottom:3rem!important}.pb-sm-5{padding-bottom:6rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.5rem!important}.ps-sm-2{padding-left:1rem!important}.ps-sm-3{padding-left:2rem!important}.ps-sm-4{padding-left:3rem!important}.ps-sm-5{padding-left:6rem!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.5rem!important}.gap-sm-2{gap:1rem!important}.gap-sm-3{gap:2rem!important}.gap-sm-4{gap:3rem!important}.gap-sm-5{gap:6rem!important}.row-gap-sm-0{row-gap:0!important}.row-gap-sm-1{row-gap:.5rem!important}.row-gap-sm-2{row-gap:1rem!important}.row-gap-sm-3{row-gap:2rem!important}.row-gap-sm-4{row-gap:3rem!important}.row-gap-sm-5{row-gap:6rem!important}.column-gap-sm-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-sm-1{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-sm-2{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-sm-3{-moz-column-gap:2rem!important;column-gap:2rem!important}.column-gap-sm-4{-moz-column-gap:3rem!important;column-gap:3rem!important}.column-gap-sm-5{-moz-column-gap:6rem!important;column-gap:6rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media(min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.object-fit-md-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-md-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-md-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-md-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-md-none{-o-object-fit:none!important;object-fit:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-inline-grid{display:inline-grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.5rem!important}.m-md-2{margin:1rem!important}.m-md-3{margin:2rem!important}.m-md-4{margin:3rem!important}.m-md-5{margin:6rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-2{margin-right:1rem!important;margin-left:1rem!important}.mx-md-3{margin-right:2rem!important;margin-left:2rem!important}.mx-md-4{margin-right:3rem!important;margin-left:3rem!important}.mx-md-5{margin-right:6rem!important;margin-left:6rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-2{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-3{margin-top:2rem!important;margin-bottom:2rem!important}.my-md-4{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-5{margin-top:6rem!important;margin-bottom:6rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.5rem!important}.mt-md-2{margin-top:1rem!important}.mt-md-3{margin-top:2rem!important}.mt-md-4{margin-top:3rem!important}.mt-md-5{margin-top:6rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.5rem!important}.me-md-2{margin-right:1rem!important}.me-md-3{margin-right:2rem!important}.me-md-4{margin-right:3rem!important}.me-md-5{margin-right:6rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.5rem!important}.mb-md-2{margin-bottom:1rem!important}.mb-md-3{margin-bottom:2rem!important}.mb-md-4{margin-bottom:3rem!important}.mb-md-5{margin-bottom:6rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.5rem!important}.ms-md-2{margin-left:1rem!important}.ms-md-3{margin-left:2rem!important}.ms-md-4{margin-left:3rem!important}.ms-md-5{margin-left:6rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.5rem!important}.p-md-2{padding:1rem!important}.p-md-3{padding:2rem!important}.p-md-4{padding:3rem!important}.p-md-5{padding:6rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-2{padding-right:1rem!important;padding-left:1rem!important}.px-md-3{padding-right:2rem!important;padding-left:2rem!important}.px-md-4{padding-right:3rem!important;padding-left:3rem!important}.px-md-5{padding-right:6rem!important;padding-left:6rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-2{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-3{padding-top:2rem!important;padding-bottom:2rem!important}.py-md-4{padding-top:3rem!important;padding-bottom:3rem!important}.py-md-5{padding-top:6rem!important;padding-bottom:6rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.5rem!important}.pt-md-2{padding-top:1rem!important}.pt-md-3{padding-top:2rem!important}.pt-md-4{padding-top:3rem!important}.pt-md-5{padding-top:6rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.5rem!important}.pe-md-2{padding-right:1rem!important}.pe-md-3{padding-right:2rem!important}.pe-md-4{padding-right:3rem!important}.pe-md-5{padding-right:6rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.5rem!important}.pb-md-2{padding-bottom:1rem!important}.pb-md-3{padding-bottom:2rem!important}.pb-md-4{padding-bottom:3rem!important}.pb-md-5{padding-bottom:6rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.5rem!important}.ps-md-2{padding-left:1rem!important}.ps-md-3{padding-left:2rem!important}.ps-md-4{padding-left:3rem!important}.ps-md-5{padding-left:6rem!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.5rem!important}.gap-md-2{gap:1rem!important}.gap-md-3{gap:2rem!important}.gap-md-4{gap:3rem!important}.gap-md-5{gap:6rem!important}.row-gap-md-0{row-gap:0!important}.row-gap-md-1{row-gap:.5rem!important}.row-gap-md-2{row-gap:1rem!important}.row-gap-md-3{row-gap:2rem!important}.row-gap-md-4{row-gap:3rem!important}.row-gap-md-5{row-gap:6rem!important}.column-gap-md-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-md-1{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-md-2{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-md-3{-moz-column-gap:2rem!important;column-gap:2rem!important}.column-gap-md-4{-moz-column-gap:3rem!important;column-gap:3rem!important}.column-gap-md-5{-moz-column-gap:6rem!important;column-gap:6rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media(min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.object-fit-lg-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-lg-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-lg-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-lg-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-lg-none{-o-object-fit:none!important;object-fit:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-inline-grid{display:inline-grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.5rem!important}.m-lg-2{margin:1rem!important}.m-lg-3{margin:2rem!important}.m-lg-4{margin:3rem!important}.m-lg-5{margin:6rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-2{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-3{margin-right:2rem!important;margin-left:2rem!important}.mx-lg-4{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-5{margin-right:6rem!important;margin-left:6rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-2{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-3{margin-top:2rem!important;margin-bottom:2rem!important}.my-lg-4{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-5{margin-top:6rem!important;margin-bottom:6rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.5rem!important}.mt-lg-2{margin-top:1rem!important}.mt-lg-3{margin-top:2rem!important}.mt-lg-4{margin-top:3rem!important}.mt-lg-5{margin-top:6rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.5rem!important}.me-lg-2{margin-right:1rem!important}.me-lg-3{margin-right:2rem!important}.me-lg-4{margin-right:3rem!important}.me-lg-5{margin-right:6rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.5rem!important}.mb-lg-2{margin-bottom:1rem!important}.mb-lg-3{margin-bottom:2rem!important}.mb-lg-4{margin-bottom:3rem!important}.mb-lg-5{margin-bottom:6rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.5rem!important}.ms-lg-2{margin-left:1rem!important}.ms-lg-3{margin-left:2rem!important}.ms-lg-4{margin-left:3rem!important}.ms-lg-5{margin-left:6rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.5rem!important}.p-lg-2{padding:1rem!important}.p-lg-3{padding:2rem!important}.p-lg-4{padding:3rem!important}.p-lg-5{padding:6rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-2{padding-right:1rem!important;padding-left:1rem!important}.px-lg-3{padding-right:2rem!important;padding-left:2rem!important}.px-lg-4{padding-right:3rem!important;padding-left:3rem!important}.px-lg-5{padding-right:6rem!important;padding-left:6rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-2{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-3{padding-top:2rem!important;padding-bottom:2rem!important}.py-lg-4{padding-top:3rem!important;padding-bottom:3rem!important}.py-lg-5{padding-top:6rem!important;padding-bottom:6rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.5rem!important}.pt-lg-2{padding-top:1rem!important}.pt-lg-3{padding-top:2rem!important}.pt-lg-4{padding-top:3rem!important}.pt-lg-5{padding-top:6rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.5rem!important}.pe-lg-2{padding-right:1rem!important}.pe-lg-3{padding-right:2rem!important}.pe-lg-4{padding-right:3rem!important}.pe-lg-5{padding-right:6rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.5rem!important}.pb-lg-2{padding-bottom:1rem!important}.pb-lg-3{padding-bottom:2rem!important}.pb-lg-4{padding-bottom:3rem!important}.pb-lg-5{padding-bottom:6rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.5rem!important}.ps-lg-2{padding-left:1rem!important}.ps-lg-3{padding-left:2rem!important}.ps-lg-4{padding-left:3rem!important}.ps-lg-5{padding-left:6rem!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.5rem!important}.gap-lg-2{gap:1rem!important}.gap-lg-3{gap:2rem!important}.gap-lg-4{gap:3rem!important}.gap-lg-5{gap:6rem!important}.row-gap-lg-0{row-gap:0!important}.row-gap-lg-1{row-gap:.5rem!important}.row-gap-lg-2{row-gap:1rem!important}.row-gap-lg-3{row-gap:2rem!important}.row-gap-lg-4{row-gap:3rem!important}.row-gap-lg-5{row-gap:6rem!important}.column-gap-lg-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-lg-1{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-lg-2{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-lg-3{-moz-column-gap:2rem!important;column-gap:2rem!important}.column-gap-lg-4{-moz-column-gap:3rem!important;column-gap:3rem!important}.column-gap-lg-5{-moz-column-gap:6rem!important;column-gap:6rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media(min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.object-fit-xl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xl-none{-o-object-fit:none!important;object-fit:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-inline-grid{display:inline-grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.5rem!important}.m-xl-2{margin:1rem!important}.m-xl-3{margin:2rem!important}.m-xl-4{margin:3rem!important}.m-xl-5{margin:6rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-2{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-3{margin-right:2rem!important;margin-left:2rem!important}.mx-xl-4{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-5{margin-right:6rem!important;margin-left:6rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-2{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-3{margin-top:2rem!important;margin-bottom:2rem!important}.my-xl-4{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-5{margin-top:6rem!important;margin-bottom:6rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.5rem!important}.mt-xl-2{margin-top:1rem!important}.mt-xl-3{margin-top:2rem!important}.mt-xl-4{margin-top:3rem!important}.mt-xl-5{margin-top:6rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.5rem!important}.me-xl-2{margin-right:1rem!important}.me-xl-3{margin-right:2rem!important}.me-xl-4{margin-right:3rem!important}.me-xl-5{margin-right:6rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.5rem!important}.mb-xl-2{margin-bottom:1rem!important}.mb-xl-3{margin-bottom:2rem!important}.mb-xl-4{margin-bottom:3rem!important}.mb-xl-5{margin-bottom:6rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.5rem!important}.ms-xl-2{margin-left:1rem!important}.ms-xl-3{margin-left:2rem!important}.ms-xl-4{margin-left:3rem!important}.ms-xl-5{margin-left:6rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.5rem!important}.p-xl-2{padding:1rem!important}.p-xl-3{padding:2rem!important}.p-xl-4{padding:3rem!important}.p-xl-5{padding:6rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-2{padding-right:1rem!important;padding-left:1rem!important}.px-xl-3{padding-right:2rem!important;padding-left:2rem!important}.px-xl-4{padding-right:3rem!important;padding-left:3rem!important}.px-xl-5{padding-right:6rem!important;padding-left:6rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-2{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-3{padding-top:2rem!important;padding-bottom:2rem!important}.py-xl-4{padding-top:3rem!important;padding-bottom:3rem!important}.py-xl-5{padding-top:6rem!important;padding-bottom:6rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.5rem!important}.pt-xl-2{padding-top:1rem!important}.pt-xl-3{padding-top:2rem!important}.pt-xl-4{padding-top:3rem!important}.pt-xl-5{padding-top:6rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.5rem!important}.pe-xl-2{padding-right:1rem!important}.pe-xl-3{padding-right:2rem!important}.pe-xl-4{padding-right:3rem!important}.pe-xl-5{padding-right:6rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.5rem!important}.pb-xl-2{padding-bottom:1rem!important}.pb-xl-3{padding-bottom:2rem!important}.pb-xl-4{padding-bottom:3rem!important}.pb-xl-5{padding-bottom:6rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.5rem!important}.ps-xl-2{padding-left:1rem!important}.ps-xl-3{padding-left:2rem!important}.ps-xl-4{padding-left:3rem!important}.ps-xl-5{padding-left:6rem!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.5rem!important}.gap-xl-2{gap:1rem!important}.gap-xl-3{gap:2rem!important}.gap-xl-4{gap:3rem!important}.gap-xl-5{gap:6rem!important}.row-gap-xl-0{row-gap:0!important}.row-gap-xl-1{row-gap:.5rem!important}.row-gap-xl-2{row-gap:1rem!important}.row-gap-xl-3{row-gap:2rem!important}.row-gap-xl-4{row-gap:3rem!important}.row-gap-xl-5{row-gap:6rem!important}.column-gap-xl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xl-1{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-xl-2{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xl-3{-moz-column-gap:2rem!important;column-gap:2rem!important}.column-gap-xl-4{-moz-column-gap:3rem!important;column-gap:3rem!important}.column-gap-xl-5{-moz-column-gap:6rem!important;column-gap:6rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media(min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.object-fit-xxl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xxl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xxl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xxl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xxl-none{-o-object-fit:none!important;object-fit:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-inline-grid{display:inline-grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.5rem!important}.m-xxl-2{margin:1rem!important}.m-xxl-3{margin:2rem!important}.m-xxl-4{margin:3rem!important}.m-xxl-5{margin:6rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-2{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-3{margin-right:2rem!important;margin-left:2rem!important}.mx-xxl-4{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-5{margin-right:6rem!important;margin-left:6rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-2{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-3{margin-top:2rem!important;margin-bottom:2rem!important}.my-xxl-4{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-5{margin-top:6rem!important;margin-bottom:6rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.5rem!important}.mt-xxl-2{margin-top:1rem!important}.mt-xxl-3{margin-top:2rem!important}.mt-xxl-4{margin-top:3rem!important}.mt-xxl-5{margin-top:6rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.5rem!important}.me-xxl-2{margin-right:1rem!important}.me-xxl-3{margin-right:2rem!important}.me-xxl-4{margin-right:3rem!important}.me-xxl-5{margin-right:6rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.5rem!important}.mb-xxl-2{margin-bottom:1rem!important}.mb-xxl-3{margin-bottom:2rem!important}.mb-xxl-4{margin-bottom:3rem!important}.mb-xxl-5{margin-bottom:6rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.5rem!important}.ms-xxl-2{margin-left:1rem!important}.ms-xxl-3{margin-left:2rem!important}.ms-xxl-4{margin-left:3rem!important}.ms-xxl-5{margin-left:6rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.5rem!important}.p-xxl-2{padding:1rem!important}.p-xxl-3{padding:2rem!important}.p-xxl-4{padding:3rem!important}.p-xxl-5{padding:6rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-2{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-3{padding-right:2rem!important;padding-left:2rem!important}.px-xxl-4{padding-right:3rem!important;padding-left:3rem!important}.px-xxl-5{padding-right:6rem!important;padding-left:6rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-2{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-3{padding-top:2rem!important;padding-bottom:2rem!important}.py-xxl-4{padding-top:3rem!important;padding-bottom:3rem!important}.py-xxl-5{padding-top:6rem!important;padding-bottom:6rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.5rem!important}.pt-xxl-2{padding-top:1rem!important}.pt-xxl-3{padding-top:2rem!important}.pt-xxl-4{padding-top:3rem!important}.pt-xxl-5{padding-top:6rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.5rem!important}.pe-xxl-2{padding-right:1rem!important}.pe-xxl-3{padding-right:2rem!important}.pe-xxl-4{padding-right:3rem!important}.pe-xxl-5{padding-right:6rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.5rem!important}.pb-xxl-2{padding-bottom:1rem!important}.pb-xxl-3{padding-bottom:2rem!important}.pb-xxl-4{padding-bottom:3rem!important}.pb-xxl-5{padding-bottom:6rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.5rem!important}.ps-xxl-2{padding-left:1rem!important}.ps-xxl-3{padding-left:2rem!important}.ps-xxl-4{padding-left:3rem!important}.ps-xxl-5{padding-left:6rem!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.5rem!important}.gap-xxl-2{gap:1rem!important}.gap-xxl-3{gap:2rem!important}.gap-xxl-4{gap:3rem!important}.gap-xxl-5{gap:6rem!important}.row-gap-xxl-0{row-gap:0!important}.row-gap-xxl-1{row-gap:.5rem!important}.row-gap-xxl-2{row-gap:1rem!important}.row-gap-xxl-3{row-gap:2rem!important}.row-gap-xxl-4{row-gap:3rem!important}.row-gap-xxl-5{row-gap:6rem!important}.column-gap-xxl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xxl-1{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-xxl-2{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xxl-3{-moz-column-gap:2rem!important;column-gap:2rem!important}.column-gap-xxl-4{-moz-column-gap:3rem!important;column-gap:3rem!important}.column-gap-xxl-5{-moz-column-gap:6rem!important;column-gap:6rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media(min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-inline-grid{display:inline-grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}}body{background-image:linear-gradient(90deg,#21373f,#5e62b0,#700936)}[data-bs-theme=dark] body{color:#fff}.table-secondary{--bs-table-hover-color:$white}.btn-secondary{color:#fff;border:none}.btn-secondary:focus,.btn-secondary:hover{color:#fff}.btn-secondary.disabled{color:#fff}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:0}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:0}.bg-light .btn{background-color:rgba(0,0,0,.2)}.form-control::-webkit-file-upload-button{background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);border:none}.form-control::file-selector-button,.input-group-text{background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);border:none}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:rgba(255,255,255,.1)}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:rgba(255,255,255,.1)}.input-group-text{box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1)}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:0;border-left:none}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu){border-right:none}.form-select:not([multiple]){transition:border-color .15s ease-in-out}.form-select:not([multiple]) option{color:#000}.bg-light .form-control{color:#343a40;border-color:rgba(0,0,0,.2)}.bg-light .form-control::-moz-placeholder{color:rgba(0,0,0,.2)}.bg-light .form-control::placeholder{color:rgba(0,0,0,.2)}.dropdown-menu{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.dropdown-menu a{color:#fff}.dropdown-menu .text-muted{color:rgba(255,255,255,.7)!important}.nav-tabs .nav-link{transition:none}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{color:#fff;isolation:auto}.nav-tabs .nav-link,.nav-tabs .nav-link.disabled{background-color:rgba(0,0,0,.2)}.nav-tabs .nav-item:first-child>.nav-link{border-radius:.5rem 0 0 .5rem}.nav-tabs .nav-item:last-child>.nav-link{border-radius:0 .5rem .5rem 0}.nav-tabs .nav-item .nav-link.active,.nav-tabs .nav-item.show .nav-link{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,#fff,rgba(255,255,255,.9) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);border-radius:.5rem;transform:scale(1.1)}.nav-tabs .nav-item .nav-link.active a,.nav-tabs .nav-item.show .nav-link a{color:#fff}.nav-tabs .nav-item .nav-link.active .text-muted,.nav-tabs .nav-item.show .nav-link .text-muted{color:rgba(255,255,255,.7)!important}.nav-tabs .nav-item .nav-link.active:focus,.nav-tabs .nav-item .nav-link.active:hover,.nav-tabs .nav-item.show .nav-link:focus,.nav-tabs .nav-item.show .nav-link:hover{color:#343a40}.nav-tabs .dropdown-menu{border-radius:.5rem}.nav-pills .nav-link{transition:none}.nav-pills .nav-link:focus,.nav-pills .nav-link:hover{color:#fff;isolation:auto}.alert{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);position:relative;overflow:hidden;color:#fff}.alert a{color:#fff}.alert .text-muted{color:rgba(255,255,255,.7)!important}.alert-primary::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:#e83283}.alert-secondary::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:rgba(255,255,255,.4)}.alert-success::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:#41d7a7}.alert-info::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:#39cbfb}.alert-warning::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:#ffc107}.alert-danger::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:#fd7e14}.alert-light::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:#e9e9e8}.alert-dark::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:#212529}.alert .alert-link{color:#fff}.badge.bg-secondary{--bs-bg-opacity:.4}.badge.bg-light{color:#343a40}.list-group{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.list-group a{color:#fff}.list-group .text-muted{color:rgba(255,255,255,.7)!important}.card{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);--bs-secondary-rgb:transparent}.card a{color:#fff}.card .text-muted{color:rgba(255,255,255,.7)!important}.card.bg-primary{background-image:none}.card.bg-success{background-image:none}.card.bg-info{background-image:none}.card.bg-warning{background-image:none}.card.bg-danger{background-image:none}.card.bg-light{background-image:none}.card.bg-dark{background-image:none}.card.bg-light .card-body,.card.bg-light .card-header{color:#343a40}.card.border-primary{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-primary a{color:#fff}.card.border-primary .text-muted{color:rgba(255,255,255,.7)!important}.card.border-primary .card-header{background-color:#e83283!important;border-bottom:none}.card.border-secondary{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-secondary a{color:#fff}.card.border-secondary .text-muted{color:rgba(255,255,255,.7)!important}.card.border-secondary .card-header{background-color:rgba(255,255,255,.4)!important;border-bottom:none}.card.border-success{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-success a{color:#fff}.card.border-success .text-muted{color:rgba(255,255,255,.7)!important}.card.border-success .card-header{background-color:#41d7a7!important;border-bottom:none}.card.border-info{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-info a{color:#fff}.card.border-info .text-muted{color:rgba(255,255,255,.7)!important}.card.border-info .card-header{background-color:#39cbfb!important;border-bottom:none}.card.border-warning{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-warning a{color:#fff}.card.border-warning .text-muted{color:rgba(255,255,255,.7)!important}.card.border-warning .card-header{background-color:#ffc107!important;border-bottom:none}.card.border-danger{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-danger a{color:#fff}.card.border-danger .text-muted{color:rgba(255,255,255,.7)!important}.card.border-danger .card-header{background-color:#fd7e14!important;border-bottom:none}.card.border-light{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-light a{color:#fff}.card.border-light .text-muted{color:rgba(255,255,255,.7)!important}.card.border-light .card-header{background-color:#e9e9e8!important;border-bottom:none;color:#343a40}.card.border-dark{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-dark a{color:#fff}.card.border-dark .text-muted{color:rgba(255,255,255,.7)!important}.card.border-dark .card-header{background-color:#212529!important;border-bottom:none}.card-header{font-weight:700}.accordion-item{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);box-shadow:none}.accordion-item a{color:#fff}.accordion-item .text-muted{color:rgba(255,255,255,.7)!important}.toast{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.toast a{color:#fff}.toast .text-muted{color:rgba(255,255,255,.7)!important}.popover{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.popover a{color:#fff}.popover .text-muted{color:rgba(255,255,255,.7)!important}.popover-header{border-bottom-color:rgba(255,255,255,.2)}.tooltip{-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.modal-content{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.modal-content a{color:#fff}.modal-content .text-muted{color:rgba(255,255,255,.7)!important}.offcanvas{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.offcanvas a{color:#fff}.offcanvas .text-muted{color:rgba(255,255,255,.7)!important}/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file +* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)*/:root,[data-bs-theme=light]{--bs-blue:#3a8fd9;--bs-indigo:#6610f2;--bs-purple:#686dc3;--bs-pink:#e83283;--bs-red:#fc346f;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#41d7a7;--bs-teal:#528fb3;--bs-cyan:#39cbfb;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9e9e8;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#e83283;--bs-secondary:rgba(255, 255, 255, 0.4);--bs-success:#41d7a7;--bs-info:#39cbfb;--bs-warning:#ffc107;--bs-danger:#fd7e14;--bs-light:#e9e9e8;--bs-dark:#212529;--bs-primary-rgb:232, 50, 131;--bs-secondary-rgb:255, 255, 255;--bs-success-rgb:65, 215, 167;--bs-info-rgb:57, 203, 251;--bs-warning-rgb:255, 193, 7;--bs-danger-rgb:253, 126, 20;--bs-light-rgb:233, 233, 232;--bs-dark-rgb:33, 37, 41;--bs-primary-text-emphasis:#5d1434;--bs-secondary-text-emphasis:rgba(36, 36, 36, 0.76);--bs-success-text-emphasis:#1a5643;--bs-info-text-emphasis:#175164;--bs-warning-text-emphasis:#664d03;--bs-danger-text-emphasis:#653208;--bs-light-text-emphasis:#495057;--bs-dark-text-emphasis:#495057;--bs-primary-bg-subtle:#fad6e6;--bs-secondary-bg-subtle:rgba(255, 255, 255, 0.88);--bs-success-bg-subtle:#d9f7ed;--bs-info-bg-subtle:#d7f5fe;--bs-warning-bg-subtle:#fff3cd;--bs-danger-bg-subtle:#ffe5d0;--bs-light-bg-subtle:#fcfcfd;--bs-dark-bg-subtle:#ced4da;--bs-primary-border-subtle:#f6adcd;--bs-secondary-border-subtle:rgba(255, 255, 255, 0.76);--bs-success-border-subtle:#b3efdc;--bs-info-border-subtle:#b0eafd;--bs-warning-border-subtle:#ffe69c;--bs-danger-border-subtle:#fecba1;--bs-light-border-subtle:#e9e9e8;--bs-dark-border-subtle:#adb5bd;--bs-white-rgb:255, 255, 255;--bs-black-rgb:0, 0, 0;--bs-font-sans-serif:system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--bs-font-monospace:SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#fff;--bs-body-color-rgb:255, 255, 255;--bs-body-bg:#686dc3;--bs-body-bg-rgb:104, 109, 195;--bs-emphasis-color:#000;--bs-emphasis-color-rgb:0, 0, 0;--bs-secondary-color:rgba(255, 255, 255, 0.75);--bs-secondary-color-rgb:255, 255, 255;--bs-secondary-bg:#e9e9e8;--bs-secondary-bg-rgb:233, 233, 232;--bs-tertiary-color:rgba(255, 255, 255, 0.5);--bs-tertiary-color-rgb:255, 255, 255;--bs-tertiary-bg:#f8f9fa;--bs-tertiary-bg-rgb:248, 249, 250;--bs-heading-color:inherit;--bs-link-color:#fff;--bs-link-color-rgb:255, 255, 255;--bs-link-decoration:underline;--bs-link-hover-color:#cccccc;--bs-link-hover-color-rgb:204, 204, 204;--bs-code-color:#e83283;--bs-highlight-color:#fff;--bs-highlight-bg:#fff3cd;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:rgba(255, 255, 255, 0.2);--bs-border-color-translucent:rgba(0, 0, 0, 0.175);--bs-border-radius:0.5rem;--bs-border-radius-sm:0.6rem;--bs-border-radius-lg:0.7rem;--bs-border-radius-xl:1rem;--bs-border-radius-xxl:2rem;--bs-border-radius-2xl:var(--bs-border-radius-xxl);--bs-border-radius-pill:50rem;--bs-box-shadow:1px 3px 24px -1px rgba(0, 0, 0, 0.15);--bs-box-shadow-sm:0 1px 1px rgba(0, 0, 0, 0.1);--bs-box-shadow-lg:0 1rem 3rem rgba(0, 0, 0, 0.175);--bs-box-shadow-inset:inset 0 1px 2px rgba(0, 0, 0, 0.075);--bs-focus-ring-width:0.25rem;--bs-focus-ring-opacity:0.25;--bs-focus-ring-color:rgba(232, 50, 131, 0.25);--bs-form-valid-color:#41d7a7;--bs-form-valid-border-color:#41d7a7;--bs-form-invalid-color:#fd7e14;--bs-form-invalid-border-color:#fd7e14}[data-bs-theme=dark]{color-scheme:dark;--bs-body-color:#dee2e6;--bs-body-color-rgb:222, 226, 230;--bs-body-bg:#212529;--bs-body-bg-rgb:33, 37, 41;--bs-emphasis-color:#fff;--bs-emphasis-color-rgb:255, 255, 255;--bs-secondary-color:rgba(222, 226, 230, 0.75);--bs-secondary-color-rgb:222, 226, 230;--bs-secondary-bg:#343a40;--bs-secondary-bg-rgb:52, 58, 64;--bs-tertiary-color:rgba(222, 226, 230, 0.5);--bs-tertiary-color-rgb:222, 226, 230;--bs-tertiary-bg:#2b3035;--bs-tertiary-bg-rgb:43, 48, 53;--bs-primary-text-emphasis:#f184b5;--bs-secondary-text-emphasis:rgba(255, 255, 255, 0.64);--bs-success-text-emphasis:#8de7ca;--bs-info-text-emphasis:#88e0fd;--bs-warning-text-emphasis:#ffda6a;--bs-danger-text-emphasis:#feb272;--bs-light-text-emphasis:#f8f9fa;--bs-dark-text-emphasis:#dee2e6;--bs-primary-bg-subtle:#2e0a1a;--bs-secondary-bg-subtle:rgba(15, 15, 15, 0.88);--bs-success-bg-subtle:#0d2b21;--bs-info-bg-subtle:#0b2932;--bs-warning-bg-subtle:#332701;--bs-danger-bg-subtle:#331904;--bs-light-bg-subtle:#343a40;--bs-dark-bg-subtle:#1a1d20;--bs-primary-border-subtle:#8b1e4f;--bs-secondary-border-subtle:rgba(70, 70, 70, 0.64);--bs-success-border-subtle:#278164;--bs-info-border-subtle:#227a97;--bs-warning-border-subtle:#997404;--bs-danger-border-subtle:#984c0c;--bs-light-border-subtle:#495057;--bs-dark-border-subtle:#343a40;--bs-heading-color:inherit;--bs-link-color:#f184b5;--bs-link-hover-color:#f49dc4;--bs-link-color-rgb:241, 132, 181;--bs-link-hover-color-rgb:244, 157, 196;--bs-code-color:#f184b5;--bs-highlight-color:#dee2e6;--bs-highlight-bg:#664d03;--bs-border-color:#495057;--bs-border-color-translucent:rgba(255, 255, 255, 0.15);--bs-form-valid-color:#8de7ca;--bs-form-valid-border-color:#8de7ca;--bs-form-invalid-color:#fd85a9;--bs-form-invalid-border-color:#fd85a9}*,::after,::before{box-sizing:border-box}@media(prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}hr{margin:2rem 0;color:inherit;border:0;border-top:var(--bs-border-width)solid;opacity:.25}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:1rem;font-weight:700;line-height:1.2;color:var(--bs-heading-color)}.h1,h1{font-size:calc(1.375rem + 1.5vw)}@media(min-width:1200px){.h1,h1{font-size:2.5rem}}.h2,h2{font-size:calc(1.325rem + .9vw)}@media(min-width:1200px){.h2,h2{font-size:2rem}}.h3,h3{font-size:calc(1.3rem + .6vw)}@media(min-width:1200px){.h3,h3{font-size:1.75rem}}.h4,h4{font-size:calc(1.275rem + .3vw)}@media(min-width:1200px){.h4,h4{font-size:1.5rem}}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}p{margin-top:0;margin-bottom:1rem}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;-webkit-text-decoration-skip-ink:none;text-decoration-skip-ink:none}address{margin-bottom:1rem;font-style:normal;line-height:inherit}ol,ul{padding-left:2rem}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}b,strong{font-weight:bolder}.small,small{font-size:.875em}.mark,mark{padding:.1875em;color:var(--bs-highlight-color);background-color:var(--bs-highlight-bg)}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,1));text-decoration:underline}a:hover{--bs-link-color-rgb:var(--bs-link-hover-color-rgb)}a:not([href]):not([class]),a:not([href]):not([class]):hover{color:inherit;text-decoration:none}code,kbd,pre,samp{font-family:var(--bs-font-monospace);font-size:1em}pre{display:block;margin-top:0;margin-bottom:1rem;overflow:auto;font-size:.875em}pre code{font-size:inherit;color:inherit;word-break:normal}code{font-size:.875em;color:var(--bs-code-color);word-wrap:break-word}a>code{color:inherit}kbd{padding:.1875rem .375rem;font-size:.875em;color:var(--bs-body-bg);background-color:var(--bs-body-color);border-radius:.6rem}kbd kbd{padding:0;font-size:1em}figure{margin:0 0 1rem}img,svg{vertical-align:middle}table{caption-side:bottom;border-collapse:collapse}caption{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-secondary-color);text-align:left}th{text-align:inherit;text-align:-webkit-match-parent}tbody,td,tfoot,th,thead,tr{border-color:inherit;border-style:solid;border-width:0}label{display:inline-block}button{border-radius:0}button:focus:not(:focus-visible){outline:0}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,select{text-transform:none}[role=button]{cursor:pointer}select{word-wrap:normal}select:disabled{opacity:1}[list]:not([type=date]):not([type=datetime-local]):not([type=month]):not([type=week]):not([type=time])::-webkit-calendar-picker-indicator{display:none!important}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]:not(:disabled),[type=reset]:not(:disabled),[type=submit]:not(:disabled),button:not(:disabled){cursor:pointer}::-moz-focus-inner{padding:0;border-style:none}textarea{resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{float:left;width:100%;padding:0;margin-bottom:.5rem;line-height:inherit;font-size:calc(1.275rem + .3vw)}@media(min-width:1200px){legend{font-size:1.5rem}}legend+*{clear:left}::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-fields-wrapper,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-text,::-webkit-datetime-edit-year-field{padding:0}::-webkit-inner-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button{cursor:pointer;filter:grayscale(1)}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-color-swatch-wrapper{padding:0}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}::file-selector-button{font:inherit;-webkit-appearance:button}output{display:inline-block}iframe{border:0}summary{display:list-item;cursor:pointer}progress{vertical-align:baseline}[hidden]{display:none!important}.lead{font-size:1.25rem;font-weight:300}.display-1{font-weight:300;line-height:1.2;font-size:calc(1.625rem + 4.5vw)}@media(min-width:1200px){.display-1{font-size:5rem}}.display-2{font-weight:300;line-height:1.2;font-size:calc(1.575rem + 3.9vw)}@media(min-width:1200px){.display-2{font-size:4.5rem}}.display-3{font-weight:300;line-height:1.2;font-size:calc(1.525rem + 3.3vw)}@media(min-width:1200px){.display-3{font-size:4rem}}.display-4{font-weight:300;line-height:1.2;font-size:calc(1.475rem + 2.7vw)}@media(min-width:1200px){.display-4{font-size:3.5rem}}.display-5{font-weight:300;line-height:1.2;font-size:calc(1.425rem + 2.1vw)}@media(min-width:1200px){.display-5{font-size:3rem}}.display-6{font-weight:300;line-height:1.2;font-size:calc(1.375rem + 1.5vw)}@media(min-width:1200px){.display-6{font-size:2.5rem}}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:.875em;text-transform:uppercase}.blockquote{margin-bottom:2rem;font-size:1.25rem}.blockquote>:last-child{margin-bottom:0}.blockquote-footer{margin-top:-2rem;margin-bottom:2rem;font-size:.875em;color:rgba(255,255,255,.7)}.blockquote-footer::before{content:"— "}.img-fluid{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:var(--bs-body-bg);border:var(--bs-border-width)solid var(--bs-border-color);border-radius:var(--bs-border-radius);max-width:100%;height:auto}.figure{display:inline-block}.figure-img{margin-bottom:1rem;line-height:1}.figure-caption{font-size:.875em;color:var(--bs-secondary-color)}.container,.container-fluid,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{--bs-gutter-x:1.5rem;--bs-gutter-y:0;width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-right:auto;margin-left:auto}@media(min-width:576px){.container,.container-sm{max-width:540px}}@media(min-width:768px){.container,.container-md,.container-sm{max-width:720px}}@media(min-width:992px){.container,.container-lg,.container-md,.container-sm{max-width:960px}}@media(min-width:1200px){.container,.container-lg,.container-md,.container-sm,.container-xl{max-width:1140px}}@media(min-width:1400px){.container,.container-lg,.container-md,.container-sm,.container-xl,.container-xxl{max-width:1320px}}:root{--bs-breakpoint-xs:0;--bs-breakpoint-sm:576px;--bs-breakpoint-md:768px;--bs-breakpoint-lg:992px;--bs-breakpoint-xl:1200px;--bs-breakpoint-xxl:1400px}.row{--bs-gutter-x:1.5rem;--bs-gutter-y:0;display:flex;flex-wrap:wrap;margin-top:calc(-1 * var(--bs-gutter-y));margin-right:calc(-.5 * var(--bs-gutter-x));margin-left:calc(-.5 * var(--bs-gutter-x))}.row>*{flex-shrink:0;width:100%;max-width:100%;padding-right:calc(var(--bs-gutter-x) * .5);padding-left:calc(var(--bs-gutter-x) * .5);margin-top:var(--bs-gutter-y)}.col{flex:1 0}.row-cols-auto>*{flex:none;width:auto}.row-cols-1>*{flex:none;width:100%}.row-cols-2>*{flex:none;width:50%}.row-cols-3>*{flex:none;width:33.33333333%}.row-cols-4>*{flex:none;width:25%}.row-cols-5>*{flex:none;width:20%}.row-cols-6>*{flex:none;width:16.66666667%}.col-auto{flex:none;width:auto}.col-1{flex:none;width:8.33333333%}.col-2{flex:none;width:16.66666667%}.col-3{flex:none;width:25%}.col-4{flex:none;width:33.33333333%}.col-5{flex:none;width:41.66666667%}.col-6{flex:none;width:50%}.col-7{flex:none;width:58.33333333%}.col-8{flex:none;width:66.66666667%}.col-9{flex:none;width:75%}.col-10{flex:none;width:83.33333333%}.col-11{flex:none;width:91.66666667%}.col-12{flex:none;width:100%}.offset-1{margin-left:8.33333333%}.offset-2{margin-left:16.66666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333333%}.offset-5{margin-left:41.66666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333333%}.offset-8{margin-left:66.66666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333333%}.offset-11{margin-left:91.66666667%}.g-0,.gx-0{--bs-gutter-x:0}.g-0,.gy-0{--bs-gutter-y:0}.g-1,.gx-1{--bs-gutter-x:0.5rem}.g-1,.gy-1{--bs-gutter-y:0.5rem}.g-2,.gx-2{--bs-gutter-x:1rem}.g-2,.gy-2{--bs-gutter-y:1rem}.g-3,.gx-3{--bs-gutter-x:2rem}.g-3,.gy-3{--bs-gutter-y:2rem}.g-4,.gx-4{--bs-gutter-x:3rem}.g-4,.gy-4{--bs-gutter-y:3rem}.g-5,.gx-5{--bs-gutter-x:6rem}.g-5,.gy-5{--bs-gutter-y:6rem}@media(min-width:576px){.col-sm{flex:1 0}.row-cols-sm-auto>*{flex:none;width:auto}.row-cols-sm-1>*{flex:none;width:100%}.row-cols-sm-2>*{flex:none;width:50%}.row-cols-sm-3>*{flex:none;width:33.33333333%}.row-cols-sm-4>*{flex:none;width:25%}.row-cols-sm-5>*{flex:none;width:20%}.row-cols-sm-6>*{flex:none;width:16.66666667%}.col-sm-auto{flex:none;width:auto}.col-sm-1{flex:none;width:8.33333333%}.col-sm-2{flex:none;width:16.66666667%}.col-sm-3{flex:none;width:25%}.col-sm-4{flex:none;width:33.33333333%}.col-sm-5{flex:none;width:41.66666667%}.col-sm-6{flex:none;width:50%}.col-sm-7{flex:none;width:58.33333333%}.col-sm-8{flex:none;width:66.66666667%}.col-sm-9{flex:none;width:75%}.col-sm-10{flex:none;width:83.33333333%}.col-sm-11{flex:none;width:91.66666667%}.col-sm-12{flex:none;width:100%}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333333%}.offset-sm-2{margin-left:16.66666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333333%}.offset-sm-5{margin-left:41.66666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333333%}.offset-sm-8{margin-left:66.66666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333333%}.offset-sm-11{margin-left:91.66666667%}.g-sm-0,.gx-sm-0{--bs-gutter-x:0}.g-sm-0,.gy-sm-0{--bs-gutter-y:0}.g-sm-1,.gx-sm-1{--bs-gutter-x:0.5rem}.g-sm-1,.gy-sm-1{--bs-gutter-y:0.5rem}.g-sm-2,.gx-sm-2{--bs-gutter-x:1rem}.g-sm-2,.gy-sm-2{--bs-gutter-y:1rem}.g-sm-3,.gx-sm-3{--bs-gutter-x:2rem}.g-sm-3,.gy-sm-3{--bs-gutter-y:2rem}.g-sm-4,.gx-sm-4{--bs-gutter-x:3rem}.g-sm-4,.gy-sm-4{--bs-gutter-y:3rem}.g-sm-5,.gx-sm-5{--bs-gutter-x:6rem}.g-sm-5,.gy-sm-5{--bs-gutter-y:6rem}}@media(min-width:768px){.col-md{flex:1 0}.row-cols-md-auto>*{flex:none;width:auto}.row-cols-md-1>*{flex:none;width:100%}.row-cols-md-2>*{flex:none;width:50%}.row-cols-md-3>*{flex:none;width:33.33333333%}.row-cols-md-4>*{flex:none;width:25%}.row-cols-md-5>*{flex:none;width:20%}.row-cols-md-6>*{flex:none;width:16.66666667%}.col-md-auto{flex:none;width:auto}.col-md-1{flex:none;width:8.33333333%}.col-md-2{flex:none;width:16.66666667%}.col-md-3{flex:none;width:25%}.col-md-4{flex:none;width:33.33333333%}.col-md-5{flex:none;width:41.66666667%}.col-md-6{flex:none;width:50%}.col-md-7{flex:none;width:58.33333333%}.col-md-8{flex:none;width:66.66666667%}.col-md-9{flex:none;width:75%}.col-md-10{flex:none;width:83.33333333%}.col-md-11{flex:none;width:91.66666667%}.col-md-12{flex:none;width:100%}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333333%}.offset-md-2{margin-left:16.66666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333333%}.offset-md-5{margin-left:41.66666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333333%}.offset-md-8{margin-left:66.66666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333333%}.offset-md-11{margin-left:91.66666667%}.g-md-0,.gx-md-0{--bs-gutter-x:0}.g-md-0,.gy-md-0{--bs-gutter-y:0}.g-md-1,.gx-md-1{--bs-gutter-x:0.5rem}.g-md-1,.gy-md-1{--bs-gutter-y:0.5rem}.g-md-2,.gx-md-2{--bs-gutter-x:1rem}.g-md-2,.gy-md-2{--bs-gutter-y:1rem}.g-md-3,.gx-md-3{--bs-gutter-x:2rem}.g-md-3,.gy-md-3{--bs-gutter-y:2rem}.g-md-4,.gx-md-4{--bs-gutter-x:3rem}.g-md-4,.gy-md-4{--bs-gutter-y:3rem}.g-md-5,.gx-md-5{--bs-gutter-x:6rem}.g-md-5,.gy-md-5{--bs-gutter-y:6rem}}@media(min-width:992px){.col-lg{flex:1 0}.row-cols-lg-auto>*{flex:none;width:auto}.row-cols-lg-1>*{flex:none;width:100%}.row-cols-lg-2>*{flex:none;width:50%}.row-cols-lg-3>*{flex:none;width:33.33333333%}.row-cols-lg-4>*{flex:none;width:25%}.row-cols-lg-5>*{flex:none;width:20%}.row-cols-lg-6>*{flex:none;width:16.66666667%}.col-lg-auto{flex:none;width:auto}.col-lg-1{flex:none;width:8.33333333%}.col-lg-2{flex:none;width:16.66666667%}.col-lg-3{flex:none;width:25%}.col-lg-4{flex:none;width:33.33333333%}.col-lg-5{flex:none;width:41.66666667%}.col-lg-6{flex:none;width:50%}.col-lg-7{flex:none;width:58.33333333%}.col-lg-8{flex:none;width:66.66666667%}.col-lg-9{flex:none;width:75%}.col-lg-10{flex:none;width:83.33333333%}.col-lg-11{flex:none;width:91.66666667%}.col-lg-12{flex:none;width:100%}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333333%}.offset-lg-2{margin-left:16.66666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333333%}.offset-lg-5{margin-left:41.66666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333333%}.offset-lg-8{margin-left:66.66666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333333%}.offset-lg-11{margin-left:91.66666667%}.g-lg-0,.gx-lg-0{--bs-gutter-x:0}.g-lg-0,.gy-lg-0{--bs-gutter-y:0}.g-lg-1,.gx-lg-1{--bs-gutter-x:0.5rem}.g-lg-1,.gy-lg-1{--bs-gutter-y:0.5rem}.g-lg-2,.gx-lg-2{--bs-gutter-x:1rem}.g-lg-2,.gy-lg-2{--bs-gutter-y:1rem}.g-lg-3,.gx-lg-3{--bs-gutter-x:2rem}.g-lg-3,.gy-lg-3{--bs-gutter-y:2rem}.g-lg-4,.gx-lg-4{--bs-gutter-x:3rem}.g-lg-4,.gy-lg-4{--bs-gutter-y:3rem}.g-lg-5,.gx-lg-5{--bs-gutter-x:6rem}.g-lg-5,.gy-lg-5{--bs-gutter-y:6rem}}@media(min-width:1200px){.col-xl{flex:1 0}.row-cols-xl-auto>*{flex:none;width:auto}.row-cols-xl-1>*{flex:none;width:100%}.row-cols-xl-2>*{flex:none;width:50%}.row-cols-xl-3>*{flex:none;width:33.33333333%}.row-cols-xl-4>*{flex:none;width:25%}.row-cols-xl-5>*{flex:none;width:20%}.row-cols-xl-6>*{flex:none;width:16.66666667%}.col-xl-auto{flex:none;width:auto}.col-xl-1{flex:none;width:8.33333333%}.col-xl-2{flex:none;width:16.66666667%}.col-xl-3{flex:none;width:25%}.col-xl-4{flex:none;width:33.33333333%}.col-xl-5{flex:none;width:41.66666667%}.col-xl-6{flex:none;width:50%}.col-xl-7{flex:none;width:58.33333333%}.col-xl-8{flex:none;width:66.66666667%}.col-xl-9{flex:none;width:75%}.col-xl-10{flex:none;width:83.33333333%}.col-xl-11{flex:none;width:91.66666667%}.col-xl-12{flex:none;width:100%}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333333%}.offset-xl-2{margin-left:16.66666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333333%}.offset-xl-5{margin-left:41.66666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333333%}.offset-xl-8{margin-left:66.66666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333333%}.offset-xl-11{margin-left:91.66666667%}.g-xl-0,.gx-xl-0{--bs-gutter-x:0}.g-xl-0,.gy-xl-0{--bs-gutter-y:0}.g-xl-1,.gx-xl-1{--bs-gutter-x:0.5rem}.g-xl-1,.gy-xl-1{--bs-gutter-y:0.5rem}.g-xl-2,.gx-xl-2{--bs-gutter-x:1rem}.g-xl-2,.gy-xl-2{--bs-gutter-y:1rem}.g-xl-3,.gx-xl-3{--bs-gutter-x:2rem}.g-xl-3,.gy-xl-3{--bs-gutter-y:2rem}.g-xl-4,.gx-xl-4{--bs-gutter-x:3rem}.g-xl-4,.gy-xl-4{--bs-gutter-y:3rem}.g-xl-5,.gx-xl-5{--bs-gutter-x:6rem}.g-xl-5,.gy-xl-5{--bs-gutter-y:6rem}}@media(min-width:1400px){.col-xxl{flex:1 0}.row-cols-xxl-auto>*{flex:none;width:auto}.row-cols-xxl-1>*{flex:none;width:100%}.row-cols-xxl-2>*{flex:none;width:50%}.row-cols-xxl-3>*{flex:none;width:33.33333333%}.row-cols-xxl-4>*{flex:none;width:25%}.row-cols-xxl-5>*{flex:none;width:20%}.row-cols-xxl-6>*{flex:none;width:16.66666667%}.col-xxl-auto{flex:none;width:auto}.col-xxl-1{flex:none;width:8.33333333%}.col-xxl-2{flex:none;width:16.66666667%}.col-xxl-3{flex:none;width:25%}.col-xxl-4{flex:none;width:33.33333333%}.col-xxl-5{flex:none;width:41.66666667%}.col-xxl-6{flex:none;width:50%}.col-xxl-7{flex:none;width:58.33333333%}.col-xxl-8{flex:none;width:66.66666667%}.col-xxl-9{flex:none;width:75%}.col-xxl-10{flex:none;width:83.33333333%}.col-xxl-11{flex:none;width:91.66666667%}.col-xxl-12{flex:none;width:100%}.offset-xxl-0{margin-left:0}.offset-xxl-1{margin-left:8.33333333%}.offset-xxl-2{margin-left:16.66666667%}.offset-xxl-3{margin-left:25%}.offset-xxl-4{margin-left:33.33333333%}.offset-xxl-5{margin-left:41.66666667%}.offset-xxl-6{margin-left:50%}.offset-xxl-7{margin-left:58.33333333%}.offset-xxl-8{margin-left:66.66666667%}.offset-xxl-9{margin-left:75%}.offset-xxl-10{margin-left:83.33333333%}.offset-xxl-11{margin-left:91.66666667%}.g-xxl-0,.gx-xxl-0{--bs-gutter-x:0}.g-xxl-0,.gy-xxl-0{--bs-gutter-y:0}.g-xxl-1,.gx-xxl-1{--bs-gutter-x:0.5rem}.g-xxl-1,.gy-xxl-1{--bs-gutter-y:0.5rem}.g-xxl-2,.gx-xxl-2{--bs-gutter-x:1rem}.g-xxl-2,.gy-xxl-2{--bs-gutter-y:1rem}.g-xxl-3,.gx-xxl-3{--bs-gutter-x:2rem}.g-xxl-3,.gy-xxl-3{--bs-gutter-y:2rem}.g-xxl-4,.gx-xxl-4{--bs-gutter-x:3rem}.g-xxl-4,.gy-xxl-4{--bs-gutter-y:3rem}.g-xxl-5,.gx-xxl-5{--bs-gutter-x:6rem}.g-xxl-5,.gy-xxl-5{--bs-gutter-y:6rem}}.table{--bs-table-color-type:initial;--bs-table-bg-type:initial;--bs-table-color-state:initial;--bs-table-bg-state:initial;--bs-table-color:initial;--bs-table-bg:var(--bs-body-bg);--bs-table-border-color:var(--bs-border-color);--bs-table-accent-bg:transparent;--bs-table-striped-color:initial;--bs-table-striped-bg:rgba(var(--bs-emphasis-color-rgb), 0.05);--bs-table-active-color:initial;--bs-table-active-bg:rgba(var(--bs-emphasis-color-rgb), 0.1);--bs-table-hover-color:initial;--bs-table-hover-bg:rgba(var(--bs-emphasis-color-rgb), 0.075);width:100%;margin-bottom:2rem;vertical-align:top;border-color:var(--bs-table-border-color)}.table>:not(caption)>*>*{padding:.5rem;color:var(--bs-table-color-state,var(--bs-table-color-type,var(--bs-table-color)));background-color:var(--bs-table-bg);border-bottom-width:var(--bs-border-width);box-shadow:inset 0 0 0 9999px var(--bs-table-bg-state,var(--bs-table-bg-type,var(--bs-table-accent-bg)))}.table>tbody{vertical-align:inherit}.table>thead{vertical-align:bottom}.table-group-divider{border-top:calc(var(--bs-border-width) * 2)solid}.caption-top{caption-side:top}.table-sm>:not(caption)>*>*{padding:.25rem}.table-bordered>:not(caption)>*{border-width:var(--bs-border-width)0}.table-bordered>:not(caption)>*>*{border-width:0 var(--bs-border-width)}.table-borderless>:not(caption)>*>*{border-bottom-width:0}.table-borderless>:not(:first-child){border-top-width:0}.table-striped>tbody>tr:nth-of-type(odd)>*{--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-striped-columns>:not(caption)>tr>:nth-child(2n){--bs-table-color-type:var(--bs-table-striped-color);--bs-table-bg-type:var(--bs-table-striped-bg)}.table-active{--bs-table-color-state:var(--bs-table-active-color);--bs-table-bg-state:var(--bs-table-active-bg)}.table-hover>tbody>tr:hover>*{--bs-table-color-state:var(--bs-table-hover-color);--bs-table-bg-state:var(--bs-table-hover-bg)}.table-primary{--bs-table-color:#fff;--bs-table-bg:#e83283;--bs-table-border-color:#ed5b9c;--bs-table-striped-bg:#e93c89;--bs-table-striped-color:#fff;--bs-table-active-bg:#ea478f;--bs-table-active-color:#fff;--bs-table-hover-bg:#ea418c;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-secondary{--bs-table-color:#fff;--bs-table-bg:rgba(255, 255, 255, 0.4);--bs-table-border-color:rgba(255, 255, 255, 0.52);--bs-table-striped-bg:rgba(255, 255, 255, 0.43);--bs-table-striped-color:#000;--bs-table-active-bg:rgba(255, 255, 255, 0.46);--bs-table-active-color:#000;--bs-table-hover-bg:rgba(255, 255, 255, 0.445);--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-success{--bs-table-color:#fff;--bs-table-bg:#41d7a7;--bs-table-border-color:#67dfb9;--bs-table-striped-bg:#4bd9ab;--bs-table-striped-color:#fff;--bs-table-active-bg:#54dbb0;--bs-table-active-color:#fff;--bs-table-hover-bg:#4fdaae;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-info{--bs-table-color:#fff;--bs-table-bg:#39cbfb;--bs-table-border-color:#61d5fc;--bs-table-striped-bg:#43cefb;--bs-table-striped-color:#fff;--bs-table-active-bg:#4dd0fb;--bs-table-active-color:#fff;--bs-table-hover-bg:#48cffb;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-warning{--bs-table-color:#fff;--bs-table-bg:#ffc107;--bs-table-border-color:#ffcd39;--bs-table-striped-bg:#ffc413;--bs-table-striped-color:#fff;--bs-table-active-bg:#ffc720;--bs-table-active-color:#fff;--bs-table-hover-bg:#ffc61a;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-danger{--bs-table-color:#fff;--bs-table-bg:#fd7e14;--bs-table-border-color:#fd9843;--bs-table-striped-bg:#fd8420;--bs-table-striped-color:#fff;--bs-table-active-bg:#fd8b2c;--bs-table-active-color:#fff;--bs-table-hover-bg:#fd8826;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-light{--bs-table-color:#000;--bs-table-bg:#e9e9e8;--bs-table-border-color:#bababa;--bs-table-striped-bg:#dddddc;--bs-table-striped-color:#000;--bs-table-active-bg:#d2d2d1;--bs-table-active-color:#fff;--bs-table-hover-bg:#d8d8d7;--bs-table-hover-color:#000;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-dark{--bs-table-color:#fff;--bs-table-bg:#212529;--bs-table-border-color:#4d5154;--bs-table-striped-bg:#2c3034;--bs-table-striped-color:#fff;--bs-table-active-bg:#373b3e;--bs-table-active-color:#fff;--bs-table-hover-bg:#323539;--bs-table-hover-color:#fff;color:var(--bs-table-color);border-color:var(--bs-table-border-color)}.table-responsive{overflow-x:auto;-webkit-overflow-scrolling:touch}@media(max-width:575.98px){.table-responsive-sm{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:767.98px){.table-responsive-md{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:991.98px){.table-responsive-lg{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:1199.98px){.table-responsive-xl{overflow-x:auto;-webkit-overflow-scrolling:touch}}@media(max-width:1399.98px){.table-responsive-xxl{overflow-x:auto;-webkit-overflow-scrolling:touch}}.form-label{margin-bottom:.5rem}.col-form-label{padding-top:calc(.75rem + 1px);padding-bottom:calc(.75rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem}.form-text{margin-top:.25rem;font-size:.875em;color:var(--bs-secondary-color)}.form-control{display:block;width:100%;padding:.75rem 1.5rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:initial;background-clip:padding-box;border:1px solid rgba(255,255,255,.4);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control[type=file]{overflow:hidden}.form-control[type=file]:not(:disabled):not([readonly]){cursor:pointer}.form-control:focus{color:var(--bs-body-color);background-color:initial;border-color:rgba(255,255,255,.4);outline:0;box-shadow:none}.form-control::-webkit-date-and-time-value{min-width:85px;height:1.5em;margin:0}.form-control::-webkit-datetime-edit{display:block;padding:0}.form-control::-moz-placeholder{color:rgba(255,255,255,.7);opacity:1}.form-control::placeholder{color:rgba(255,255,255,.7);opacity:1}.form-control:disabled{background-color:rgba(255,255,255,.1);opacity:1}.form-control::-webkit-file-upload-button{padding:.75rem 1.5rem;margin:-.75rem -1.5rem;-webkit-margin-end:1.5rem;margin-inline-end:1.5rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;-webkit-transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-control::file-selector-button{padding:.75rem 1.5rem;margin:-.75rem -1.5rem;-webkit-margin-end:1.5rem;margin-inline-end:1.5rem;color:var(--bs-body-color);background-color:var(--bs-tertiary-bg);pointer-events:none;border-color:inherit;border-style:solid;border-width:0;border-inline-end-width:1px;border-radius:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-control::-webkit-file-upload-button{-webkit-transition:none;transition:none}.form-control::file-selector-button{transition:none}}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:var(--bs-secondary-bg)}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:var(--bs-secondary-bg)}.form-control-plaintext{display:block;width:100%;padding:.75rem 0;margin-bottom:0;line-height:1.5;color:var(--bs-body-color);background-color:initial;border:solid transparent;border-width:1px 0}.form-control-plaintext:focus{outline:0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{min-height:calc(1.5em + .5rem + calc(1px * 2));padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-control-sm::-webkit-file-upload-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-sm::file-selector-button{padding:.25rem .5rem;margin:-.25rem -.5rem;-webkit-margin-end:.5rem;margin-inline-end:.5rem}.form-control-lg{min-height:calc(1.5em + 1rem + calc(1px * 2));padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.form-control-lg::-webkit-file-upload-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}.form-control-lg::file-selector-button{padding:.5rem 1rem;margin:-.5rem -1rem;-webkit-margin-end:1rem;margin-inline-end:1rem}textarea.form-control{min-height:calc(1.5em + 1.5rem + calc(1px * 2))}textarea.form-control-sm{min-height:calc(1.5em + .5rem + calc(1px * 2))}textarea.form-control-lg{min-height:calc(1.5em + 1rem + calc(1px * 2))}.form-control-color{width:3rem;height:calc(1.5em + 1.5rem + calc(1px * 2));padding:.75rem}.form-control-color:not(:disabled):not([readonly]){cursor:pointer}.form-control-color::-moz-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color::-webkit-color-swatch{border:0!important;border-radius:var(--bs-border-radius)}.form-control-color.form-control-sm{height:calc(1.5em + .5rem + calc(1px * 2))}.form-control-color.form-control-lg{height:calc(1.5em + 1rem + calc(1px * 2))}.form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e");display:block;width:100%;padding:.75rem 4.5rem .75rem 1.5rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:initial;background-image:var(--bs-form-select-bg-img),var(--bs-form-select-bg-icon,none);background-repeat:no-repeat;background-position:right 1.5rem center;background-size:16px 12px;border:1px solid rgba(255,255,255,.4);border-radius:var(--bs-border-radius);transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-select{transition:none}}.form-select:focus{border-color:rgba(255,255,255,.4);outline:0;box-shadow:none}.form-select[multiple],.form-select[size]:not([size="1"]){padding-right:1.5rem;background-image:none}.form-select:disabled{color:rgba(255,255,255,.7);background-color:rgba(255,255,255,.1)}.form-select:-moz-focusring{color:transparent;text-shadow:0 0 0 var(--bs-body-color)}.form-select-sm{padding-top:.25rem;padding-bottom:.25rem;padding-left:.5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.form-select-lg{padding-top:.5rem;padding-bottom:.5rem;padding-left:1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}[data-bs-theme=dark] .form-select{--bs-form-select-bg-img:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e")}.form-check{display:block;min-height:1.5rem;padding-left:1.5em;margin-bottom:.125rem}.form-check .form-check-input{float:left;margin-left:-1.5em}.form-check-reverse{padding-right:1.5em;padding-left:0;text-align:right}.form-check-reverse .form-check-input{float:right;margin-right:-1.5em;margin-left:0}.form-check-input{--bs-form-check-bg:rgba(255, 255, 255, 0.3);flex-shrink:0;width:1em;height:1em;margin-top:.25em;vertical-align:top;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:var(--bs-form-check-bg);background-image:var(--bs-form-check-bg-image);background-repeat:no-repeat;background-position:50%;background-size:contain;border:1px solid rgba(255,255,255,.2);-webkit-print-color-adjust:exact;color-adjust:exact;print-color-adjust:exact}.form-check-input[type=checkbox]{border-radius:.25em}.form-check-input[type=radio]{border-radius:50%}.form-check-input:active{filter:brightness(90%)}.form-check-input:focus{border-color:rgba(255,255,255,.4);outline:0;box-shadow:0 0 0 .25rem rgba(232,50,131,.25)}.form-check-input:checked{background-color:#e83283;border-color:#e83283}.form-check-input:checked[type=checkbox]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e")}.form-check-input:checked[type=radio]{--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e")}.form-check-input[type=checkbox]:indeterminate{background-color:#e83283;border-color:#e83283;--bs-form-check-bg-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e")}.form-check-input:disabled{pointer-events:none;filter:none;opacity:.5}.form-check-input:disabled~.form-check-label,.form-check-input[disabled]~.form-check-label{cursor:default;opacity:.5}.form-switch{padding-left:2.5em}.form-switch .form-check-input{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e");width:2em;margin-left:-2.5em;background-image:var(--bs-form-switch-bg);background-position:0;border-radius:2em;transition:background-position .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-switch .form-check-input{transition:none}}.form-switch .form-check-input:focus{--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-switch .form-check-input:checked{background-position:100%;--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e")}.form-switch.form-check-reverse{padding-right:2.5em;padding-left:0}.form-switch.form-check-reverse .form-check-input{margin-right:-2.5em;margin-left:0}.form-check-inline{display:inline-block;margin-right:1rem}.btn-check{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.btn-check:disabled+.btn,.btn-check[disabled]+.btn{pointer-events:none;filter:none;opacity:.65}[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus){--bs-form-switch-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e")}.form-range{width:100%;height:1.5rem;padding:0;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:initial}.form-range:focus{outline:0}.form-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #686dc3,none}.form-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #686dc3,none}.form-range::-moz-focus-outer{border:0}.form-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;-webkit-appearance:none;appearance:none;background-color:#e83283;border:0;border-radius:1rem;-webkit-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-range::-webkit-slider-thumb{-webkit-transition:none;transition:none}}.form-range::-webkit-slider-thumb:active{background-color:#f8c2da}.form-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:rgba(0,0,0,.2);border-color:transparent;border-radius:1rem}.form-range::-moz-range-thumb{width:1rem;height:1rem;-moz-appearance:none;appearance:none;background-color:#e83283;border:0;border-radius:1rem;-moz-transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.form-range::-moz-range-thumb{-moz-transition:none;transition:none}}.form-range::-moz-range-thumb:active{background-color:#f8c2da}.form-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:rgba(0,0,0,.2);border-color:transparent;border-radius:1rem}.form-range:disabled{pointer-events:none}.form-range:disabled::-webkit-slider-thumb{background-color:var(--bs-secondary-color)}.form-range:disabled::-moz-range-thumb{background-color:var(--bs-secondary-color)}.form-floating{position:relative}.form-floating>.form-control,.form-floating>.form-control-plaintext,.form-floating>.form-select{height:calc(3.5rem + calc(1px * 2));min-height:calc(3.5rem + calc(1px * 2));line-height:1.25}.form-floating>label{position:absolute;top:0;left:0;z-index:2;max-width:100%;height:100%;padding:1rem 1.5rem;overflow:hidden;color:rgba(var(--bs-body-color-rgb),.65);text-align:start;text-overflow:ellipsis;white-space:nowrap;pointer-events:none;border:1px solid transparent;transform-origin:0 0;transition:opacity .1s ease-in-out,transform .1s ease-in-out}@media(prefers-reduced-motion:reduce){.form-floating>label{transition:none}}.form-floating>.form-control,.form-floating>.form-control-plaintext{padding:1rem 1.5rem}.form-floating>.form-control-plaintext::-moz-placeholder,.form-floating>.form-control::-moz-placeholder{color:transparent}.form-floating>.form-control-plaintext::placeholder,.form-floating>.form-control::placeholder{color:transparent}.form-floating>.form-control-plaintext:not(:-moz-placeholder-shown),.form-floating>.form-control:not(:-moz-placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:focus,.form-floating>.form-control-plaintext:not(:placeholder-shown),.form-floating>.form-control:focus,.form-floating>.form-control:not(:placeholder-shown){padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-control-plaintext:-webkit-autofill,.form-floating>.form-control:-webkit-autofill{padding-top:1.625rem;padding-bottom:.625rem}.form-floating>.form-select{padding-top:1.625rem;padding-bottom:.625rem;padding-left:1.5rem}.form-floating>.form-control:not(:-moz-placeholder-shown)~label{transform:scale(.85)translateY(-.5rem)translateX(.15rem)}.form-floating>.form-control-plaintext~label,.form-floating>.form-control:focus~label,.form-floating>.form-control:not(:placeholder-shown)~label,.form-floating>.form-select~label{transform:scale(.85)translateY(-.5rem)translateX(.15rem)}.form-floating>.form-control:-webkit-autofill~label{transform:scale(.85)translateY(-.5rem)translateX(.15rem)}.form-floating>textarea:not(:-moz-placeholder-shown)~label::after{position:absolute;inset:1rem .75rem;z-index:-1;height:1.5em;content:"";background-color:initial;border-radius:var(--bs-border-radius)}.form-floating>textarea:focus~label::after,.form-floating>textarea:not(:placeholder-shown)~label::after{position:absolute;inset:1rem .75rem;z-index:-1;height:1.5em;content:"";background-color:initial;border-radius:var(--bs-border-radius)}.form-floating>textarea:disabled~label::after{background-color:rgba(255,255,255,.1)}.form-floating>.form-control-plaintext~label{border-width:1px 0}.form-floating>.form-control:disabled~label,.form-floating>:disabled~label{color:#6c757d}.input-group{position:relative;display:flex;flex-wrap:wrap;align-items:stretch;width:100%}.input-group>.form-control,.input-group>.form-floating,.input-group>.form-select{position:relative;flex:auto;width:1%;min-width:0}.input-group>.form-control:focus,.input-group>.form-floating:focus-within,.input-group>.form-select:focus{z-index:5}.input-group .btn{position:relative;z-index:2}.input-group .btn:focus{z-index:5}.input-group-text{display:flex;align-items:center;padding:.75rem 1.5rem;font-size:1rem;font-weight:400;line-height:1.5;color:var(--bs-body-color);text-align:center;white-space:nowrap;background-color:initial;border:1px solid rgba(255,255,255,.4);border-radius:var(--bs-border-radius)}.input-group-lg>.btn,.input-group-lg>.form-control,.input-group-lg>.form-select,.input-group-lg>.input-group-text{padding:.5rem 1rem;font-size:1.25rem;border-radius:var(--bs-border-radius-lg)}.input-group-sm>.btn,.input-group-sm>.form-control,.input-group-sm>.form-select,.input-group-sm>.input-group-text{padding:.25rem .5rem;font-size:.875rem;border-radius:var(--bs-border-radius-sm)}.input-group-lg>.form-select,.input-group-sm>.form-select{padding-right:6rem}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-control,.input-group:not(.has-validation)>.form-floating:not(:last-child)>.form-select,.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group.has-validation>.dropdown-toggle:nth-last-child(n+4),.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-control,.input-group.has-validation>.form-floating:nth-last-child(n+3)>.form-select,.input-group.has-validation>:nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu):not(.form-floating){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:calc(-1 * 1px);border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.form-floating:not(:first-child)>.form-control,.input-group>.form-floating:not(:first-child)>.form-select{border-top-left-radius:0;border-bottom-left-radius:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-valid-color)}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.5rem 1rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-success);border-radius:var(--bs-border-radius)}.is-valid~.valid-feedback,.is-valid~.valid-tooltip,.was-validated :valid~.valid-feedback,.was-validated :valid~.valid-tooltip{display:block}.form-control.is-valid,.was-validated .form-control:valid{border-color:var(--bs-form-valid-border-color);padding-right:calc(1.5em + 1.5rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2341d7a7' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .375rem)center;background-size:calc(.75em + .75rem)calc(.75em + .75rem)}.form-control.is-valid:focus,.was-validated .form-control:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.was-validated textarea.form-control:valid,textarea.form-control.is-valid{padding-right:calc(1.5em + 1.5rem);background-position:top calc(.375em + .375rem)right calc(.375em + .375rem)}.form-select.is-valid,.was-validated .form-select:valid{border-color:var(--bs-form-valid-border-color)}.form-select.is-valid:not([multiple]):not([size]),.form-select.is-valid:not([multiple])[size="1"],.was-validated .form-select:valid:not([multiple]):not([size]),.was-validated .form-select:valid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%2341d7a7' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1'/%3e%3c/svg%3e");padding-right:8.25rem;background-position:right 1.5rem center,center right 4.5rem;background-size:16px 12px,calc(.75em + .75rem)calc(.75em + .75rem)}.form-select.is-valid:focus,.was-validated .form-select:valid:focus{border-color:var(--bs-form-valid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-control-color.is-valid,.was-validated .form-control-color:valid{width:calc(3rem + calc(1.5em + 1.5rem))}.form-check-input.is-valid,.was-validated .form-check-input:valid{border-color:var(--bs-form-valid-border-color)}.form-check-input.is-valid:checked,.was-validated .form-check-input:valid:checked{background-color:var(--bs-form-valid-color)}.form-check-input.is-valid:focus,.was-validated .form-check-input:valid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-success-rgb),.25)}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:var(--bs-form-valid-color)}.form-check-inline .form-check-input~.valid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-valid,.input-group>.form-floating:not(:focus-within).is-valid,.input-group>.form-select:not(:focus).is-valid,.was-validated .input-group>.form-control:not(:focus):valid,.was-validated .input-group>.form-floating:not(:focus-within):valid,.was-validated .input-group>.form-select:not(:focus):valid{z-index:3}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:.875em;color:var(--bs-form-invalid-color)}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.5rem 1rem;margin-top:.1rem;font-size:.875rem;color:#fff;background-color:var(--bs-danger);border-radius:var(--bs-border-radius)}.is-invalid~.invalid-feedback,.is-invalid~.invalid-tooltip,.was-validated :invalid~.invalid-feedback,.was-validated :invalid~.invalid-tooltip{display:block}.form-control.is-invalid,.was-validated .form-control:invalid{border-color:var(--bs-form-invalid-border-color);padding-right:calc(1.5em + 1.5rem);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23fd7e14'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23fd7e14' stroke='none'/%3e%3c/svg%3e");background-repeat:no-repeat;background-position:right calc(.375em + .375rem)center;background-size:calc(.75em + .75rem)calc(.75em + .75rem)}.form-control.is-invalid:focus,.was-validated .form-control:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.was-validated textarea.form-control:invalid,textarea.form-control.is-invalid{padding-right:calc(1.5em + 1.5rem);background-position:top calc(.375em + .375rem)right calc(.375em + .375rem)}.form-select.is-invalid,.was-validated .form-select:invalid{border-color:var(--bs-form-invalid-border-color)}.form-select.is-invalid:not([multiple]):not([size]),.form-select.is-invalid:not([multiple])[size="1"],.was-validated .form-select:invalid:not([multiple]):not([size]),.was-validated .form-select:invalid:not([multiple])[size="1"]{--bs-form-select-bg-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23fd7e14'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23fd7e14' stroke='none'/%3e%3c/svg%3e");padding-right:8.25rem;background-position:right 1.5rem center,center right 4.5rem;background-size:16px 12px,calc(.75em + .75rem)calc(.75em + .75rem)}.form-select.is-invalid:focus,.was-validated .form-select:invalid:focus{border-color:var(--bs-form-invalid-border-color);box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-control-color.is-invalid,.was-validated .form-control-color:invalid{width:calc(3rem + calc(1.5em + 1.5rem))}.form-check-input.is-invalid,.was-validated .form-check-input:invalid{border-color:var(--bs-form-invalid-border-color)}.form-check-input.is-invalid:checked,.was-validated .form-check-input:invalid:checked{background-color:var(--bs-form-invalid-color)}.form-check-input.is-invalid:focus,.was-validated .form-check-input:invalid:focus{box-shadow:0 0 0 .25rem rgba(var(--bs-danger-rgb),.25)}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:var(--bs-form-invalid-color)}.form-check-inline .form-check-input~.invalid-feedback{margin-left:.5em}.input-group>.form-control:not(:focus).is-invalid,.input-group>.form-floating:not(:focus-within).is-invalid,.input-group>.form-select:not(:focus).is-invalid,.was-validated .input-group>.form-control:not(:focus):invalid,.was-validated .input-group>.form-floating:not(:focus-within):invalid,.was-validated .input-group>.form-select:not(:focus):invalid{z-index:4}.btn{--bs-btn-padding-x:1.5rem;--bs-btn-padding-y:0.75rem;--bs-btn-font-family: ;--bs-btn-font-size:1rem;--bs-btn-font-weight:400;--bs-btn-line-height:1.5;--bs-btn-color:var(--bs-body-color);--bs-btn-bg:transparent;--bs-btn-border-width:var(--bs-border-width);--bs-btn-border-color:transparent;--bs-btn-border-radius:var(--bs-border-radius);--bs-btn-hover-border-color:transparent;--bs-btn-box-shadow:1px 3px 24px -1px rgba(0, 0, 0, 0.15);--bs-btn-disabled-opacity:0.65;--bs-btn-focus-box-shadow:0 0 0 0.25rem rgba(var(--bs-btn-focus-shadow-rgb), .5);display:inline-block;padding:var(--bs-btn-padding-y)var(--bs-btn-padding-x);font-family:var(--bs-btn-font-family);font-size:var(--bs-btn-font-size);font-weight:var(--bs-btn-font-weight);line-height:var(--bs-btn-line-height);color:var(--bs-btn-color);text-align:center;text-decoration:none;vertical-align:middle;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none;border:var(--bs-btn-border-width)solid var(--bs-btn-border-color);border-radius:var(--bs-btn-border-radius);background-color:var(--bs-btn-bg);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.btn{transition:none}}.btn:hover{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color)}.btn-check+.btn:hover{color:var(--bs-btn-color);background-color:var(--bs-btn-bg);border-color:var(--bs-btn-border-color)}.btn:focus-visible{color:var(--bs-btn-hover-color);background-color:var(--bs-btn-hover-bg);border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:focus-visible+.btn{border-color:var(--bs-btn-hover-border-color);outline:0;box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked+.btn,.btn.active,.btn.show,.btn:first-child:active,:not(.btn-check)+.btn:active{color:var(--bs-btn-active-color);background-color:var(--bs-btn-active-bg);border-color:var(--bs-btn-active-border-color)}.btn-check:checked+.btn:focus-visible,.btn.active:focus-visible,.btn.show:focus-visible,.btn:first-child:active:focus-visible,:not(.btn-check)+.btn:active:focus-visible{box-shadow:var(--bs-btn-focus-box-shadow)}.btn-check:checked:focus-visible+.btn{box-shadow:var(--bs-btn-focus-box-shadow)}.btn.disabled,.btn:disabled,fieldset:disabled .btn{color:var(--bs-btn-disabled-color);pointer-events:none;background-color:var(--bs-btn-disabled-bg);border-color:var(--bs-btn-disabled-border-color);opacity:var(--bs-btn-disabled-opacity)}.btn-primary{--bs-btn-color:#fff;--bs-btn-bg:#e83283;--bs-btn-border-color:#e83283;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#c52b6f;--bs-btn-hover-border-color:#ba2869;--bs-btn-focus-shadow-rgb:235, 81, 150;--bs-btn-active-color:#fff;--bs-btn-active-bg:#ba2869;--bs-btn-active-border-color:#ae2662;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#e83283;--bs-btn-disabled-border-color:#e83283}.btn-secondary{--bs-btn-color:#000;--bs-btn-bg:rgba(255, 255, 255, 0.4);--bs-btn-border-color:rgba(255, 255, 255, 0.4);--bs-btn-hover-color:#000;--bs-btn-hover-bg:rgba(255, 255, 255, 0.49);--bs-btn-hover-border-color:rgba(255, 255, 255, 0.46);--bs-btn-focus-shadow-rgb:149, 149, 149;--bs-btn-active-color:#000;--bs-btn-active-bg:rgba(255, 255, 255, 0.52);--bs-btn-active-border-color:rgba(255, 255, 255, 0.46);--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:rgba(255, 255, 255, 0.4);--bs-btn-disabled-border-color:rgba(255, 255, 255, 0.4)}.btn-success{--bs-btn-color:#fff;--bs-btn-bg:#41d7a7;--bs-btn-border-color:#41d7a7;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#37b78e;--bs-btn-hover-border-color:#34ac86;--bs-btn-focus-shadow-rgb:94, 221, 180;--bs-btn-active-color:#fff;--bs-btn-active-bg:#34ac86;--bs-btn-active-border-color:#31a17d;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#41d7a7;--bs-btn-disabled-border-color:#41d7a7}.btn-info{--bs-btn-color:#fff;--bs-btn-bg:#39cbfb;--bs-btn-border-color:#39cbfb;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#30add5;--bs-btn-hover-border-color:#2ea2c9;--bs-btn-focus-shadow-rgb:87, 211, 252;--bs-btn-active-color:#fff;--bs-btn-active-bg:#2ea2c9;--bs-btn-active-border-color:#2b98bc;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#39cbfb;--bs-btn-disabled-border-color:#39cbfb}.btn-warning{--bs-btn-color:#fff;--bs-btn-bg:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#d9a406;--bs-btn-hover-border-color:#cc9a06;--bs-btn-focus-shadow-rgb:255, 202, 44;--bs-btn-active-color:#fff;--bs-btn-active-bg:#cc9a06;--bs-btn-active-border-color:#bf9105;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#ffc107;--bs-btn-disabled-border-color:#ffc107}.btn-danger{--bs-btn-color:#fff;--bs-btn-bg:#fd7e14;--bs-btn-border-color:#fd7e14;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#d76b11;--bs-btn-hover-border-color:#ca6510;--bs-btn-focus-shadow-rgb:253, 145, 55;--bs-btn-active-color:#fff;--bs-btn-active-bg:#ca6510;--bs-btn-active-border-color:#be5f0f;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#fd7e14;--bs-btn-disabled-border-color:#fd7e14}.btn-light{--bs-btn-color:#000;--bs-btn-bg:#e9e9e8;--bs-btn-border-color:#e9e9e8;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#c6c6c5;--bs-btn-hover-border-color:#bababa;--bs-btn-focus-shadow-rgb:198, 198, 197;--bs-btn-active-color:#fff;--bs-btn-active-bg:#bababa;--bs-btn-active-border-color:#afafae;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#000;--bs-btn-disabled-bg:#e9e9e8;--bs-btn-disabled-border-color:#e9e9e8}.btn-dark{--bs-btn-color:#fff;--bs-btn-bg:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#424649;--bs-btn-hover-border-color:#373b3e;--bs-btn-focus-shadow-rgb:66, 70, 73;--bs-btn-active-color:#fff;--bs-btn-active-bg:#4d5154;--bs-btn-active-border-color:#373b3e;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fff;--bs-btn-disabled-bg:#212529;--bs-btn-disabled-border-color:#212529}.btn-outline-primary{--bs-btn-color:#e83283;--bs-btn-border-color:#e83283;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#e83283;--bs-btn-hover-border-color:#e83283;--bs-btn-focus-shadow-rgb:232, 50, 131;--bs-btn-active-color:#fff;--bs-btn-active-bg:#e83283;--bs-btn-active-border-color:#e83283;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#e83283;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#e83283;--bs-gradient:none}.btn-outline-secondary{--bs-btn-color:rgba(255, 255, 255, 0.4);--bs-btn-border-color:rgba(255, 255, 255, 0.4);--bs-btn-hover-color:#000;--bs-btn-hover-bg:rgba(255, 255, 255, 0.4);--bs-btn-hover-border-color:rgba(255, 255, 255, 0.4);--bs-btn-focus-shadow-rgb:255, 255, 255;--bs-btn-active-color:#000;--bs-btn-active-bg:rgba(255, 255, 255, 0.4);--bs-btn-active-border-color:rgba(255, 255, 255, 0.4);--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:rgba(255, 255, 255, 0.4);--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:rgba(255, 255, 255, 0.4);--bs-gradient:none}.btn-outline-success{--bs-btn-color:#41d7a7;--bs-btn-border-color:#41d7a7;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#41d7a7;--bs-btn-hover-border-color:#41d7a7;--bs-btn-focus-shadow-rgb:65, 215, 167;--bs-btn-active-color:#fff;--bs-btn-active-bg:#41d7a7;--bs-btn-active-border-color:#41d7a7;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#41d7a7;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#41d7a7;--bs-gradient:none}.btn-outline-info{--bs-btn-color:#39cbfb;--bs-btn-border-color:#39cbfb;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#39cbfb;--bs-btn-hover-border-color:#39cbfb;--bs-btn-focus-shadow-rgb:57, 203, 251;--bs-btn-active-color:#fff;--bs-btn-active-bg:#39cbfb;--bs-btn-active-border-color:#39cbfb;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#39cbfb;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#39cbfb;--bs-gradient:none}.btn-outline-warning{--bs-btn-color:#ffc107;--bs-btn-border-color:#ffc107;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#ffc107;--bs-btn-hover-border-color:#ffc107;--bs-btn-focus-shadow-rgb:255, 193, 7;--bs-btn-active-color:#fff;--bs-btn-active-bg:#ffc107;--bs-btn-active-border-color:#ffc107;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#ffc107;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#ffc107;--bs-gradient:none}.btn-outline-danger{--bs-btn-color:#fd7e14;--bs-btn-border-color:#fd7e14;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#fd7e14;--bs-btn-hover-border-color:#fd7e14;--bs-btn-focus-shadow-rgb:253, 126, 20;--bs-btn-active-color:#fff;--bs-btn-active-bg:#fd7e14;--bs-btn-active-border-color:#fd7e14;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#fd7e14;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#fd7e14;--bs-gradient:none}.btn-outline-light{--bs-btn-color:#e9e9e8;--bs-btn-border-color:#e9e9e8;--bs-btn-hover-color:#000;--bs-btn-hover-bg:#e9e9e8;--bs-btn-hover-border-color:#e9e9e8;--bs-btn-focus-shadow-rgb:233, 233, 232;--bs-btn-active-color:#000;--bs-btn-active-bg:#e9e9e8;--bs-btn-active-border-color:#e9e9e8;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#e9e9e8;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#e9e9e8;--bs-gradient:none}.btn-outline-dark{--bs-btn-color:#212529;--bs-btn-border-color:#212529;--bs-btn-hover-color:#fff;--bs-btn-hover-bg:#212529;--bs-btn-hover-border-color:#212529;--bs-btn-focus-shadow-rgb:33, 37, 41;--bs-btn-active-color:#fff;--bs-btn-active-bg:#212529;--bs-btn-active-border-color:#212529;--bs-btn-active-shadow:inset 0 3px 5px rgba(0, 0, 0, 0.125);--bs-btn-disabled-color:#212529;--bs-btn-disabled-bg:transparent;--bs-btn-disabled-border-color:#212529;--bs-gradient:none}.btn-link{--bs-btn-font-weight:400;--bs-btn-color:var(--bs-link-color);--bs-btn-bg:transparent;--bs-btn-border-color:transparent;--bs-btn-hover-color:var(--bs-link-hover-color);--bs-btn-hover-border-color:transparent;--bs-btn-active-color:var(--bs-link-hover-color);--bs-btn-active-border-color:transparent;--bs-btn-disabled-color:#6c757d;--bs-btn-disabled-border-color:transparent;--bs-btn-box-shadow:0 0 0 #000;--bs-btn-focus-shadow-rgb:217, 217, 217;text-decoration:underline}.btn-link:focus-visible{color:var(--bs-btn-color)}.btn-link:hover{color:var(--bs-btn-hover-color)}.btn-group-lg>.btn,.btn-lg{--bs-btn-padding-y:0.5rem;--bs-btn-padding-x:1rem;--bs-btn-font-size:1.25rem;--bs-btn-border-radius:var(--bs-border-radius-lg)}.btn-group-sm>.btn,.btn-sm{--bs-btn-padding-y:0.25rem;--bs-btn-padding-x:0.5rem;--bs-btn-font-size:0.875rem;--bs-btn-border-radius:var(--bs-border-radius-sm)}.fade{transition:opacity .15s linear}@media(prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{height:0;overflow:hidden;transition:height .35s ease}@media(prefers-reduced-motion:reduce){.collapsing{transition:none}}.collapsing.collapse-horizontal{width:0;height:auto;transition:width .35s ease}@media(prefers-reduced-motion:reduce){.collapsing.collapse-horizontal{transition:none}}.dropdown,.dropdown-center,.dropend,.dropstart,.dropup,.dropup-center{position:relative}.dropdown-toggle{white-space:nowrap}.dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty::after{margin-left:0}.dropdown-menu{--bs-dropdown-zindex:1000;--bs-dropdown-min-width:10rem;--bs-dropdown-padding-x:0;--bs-dropdown-padding-y:0.5rem;--bs-dropdown-spacer:0.125rem;--bs-dropdown-font-size:1rem;--bs-dropdown-color:var(--bs-body-color);--bs-dropdown-bg:var(--bs-body-bg);--bs-dropdown-border-color:rgba(255, 255, 255, 0.2);--bs-dropdown-border-radius:var(--bs-border-radius);--bs-dropdown-border-width:var(--bs-border-width);--bs-dropdown-inner-border-radius:calc(var(--bs-border-radius) - var(--bs-border-width));--bs-dropdown-divider-bg:rgba(255, 255, 255, 0.2);--bs-dropdown-divider-margin-y:1rem;--bs-dropdown-box-shadow:var(--bs-box-shadow);--bs-dropdown-link-color:var(--bs-body-color);--bs-dropdown-link-hover-color:#fff;--bs-dropdown-link-hover-bg:rgba(255, 255, 255, 0.4);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#e83283;--bs-dropdown-link-disabled-color:var(--bs-tertiary-color);--bs-dropdown-item-padding-x:2rem;--bs-dropdown-item-padding-y:0.5rem;--bs-dropdown-header-color:#6c757d;--bs-dropdown-header-padding-x:2rem;--bs-dropdown-header-padding-y:0.5rem;position:absolute;z-index:var(--bs-dropdown-zindex);display:none;min-width:var(--bs-dropdown-min-width);padding:var(--bs-dropdown-padding-y)var(--bs-dropdown-padding-x);margin:0;font-size:var(--bs-dropdown-font-size);color:var(--bs-dropdown-color);text-align:left;list-style:none;background-color:var(--bs-dropdown-bg);background-clip:padding-box;border:var(--bs-dropdown-border-width)solid var(--bs-dropdown-border-color);border-radius:var(--bs-dropdown-border-radius)}.dropdown-menu[data-bs-popper]{top:100%;left:0;margin-top:var(--bs-dropdown-spacer)}.dropdown-menu-start{--bs-position:start}.dropdown-menu-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-end{--bs-position:end}.dropdown-menu-end[data-bs-popper]{right:0;left:auto}@media(min-width:576px){.dropdown-menu-sm-start{--bs-position:start}.dropdown-menu-sm-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-sm-end{--bs-position:end}.dropdown-menu-sm-end[data-bs-popper]{right:0;left:auto}}@media(min-width:768px){.dropdown-menu-md-start{--bs-position:start}.dropdown-menu-md-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-md-end{--bs-position:end}.dropdown-menu-md-end[data-bs-popper]{right:0;left:auto}}@media(min-width:992px){.dropdown-menu-lg-start{--bs-position:start}.dropdown-menu-lg-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-lg-end{--bs-position:end}.dropdown-menu-lg-end[data-bs-popper]{right:0;left:auto}}@media(min-width:1200px){.dropdown-menu-xl-start{--bs-position:start}.dropdown-menu-xl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xl-end{--bs-position:end}.dropdown-menu-xl-end[data-bs-popper]{right:0;left:auto}}@media(min-width:1400px){.dropdown-menu-xxl-start{--bs-position:start}.dropdown-menu-xxl-start[data-bs-popper]{right:auto;left:0}.dropdown-menu-xxl-end{--bs-position:end}.dropdown-menu-xxl-end[data-bs-popper]{right:0;left:auto}}.dropup .dropdown-menu[data-bs-popper]{top:auto;bottom:100%;margin-top:0;margin-bottom:var(--bs-dropdown-spacer)}.dropup .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-menu[data-bs-popper]{top:0;right:auto;left:100%;margin-top:0;margin-left:var(--bs-dropdown-spacer)}.dropend .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropend .dropdown-toggle:empty::after{margin-left:0}.dropend .dropdown-toggle::after{vertical-align:0}.dropstart .dropdown-menu[data-bs-popper]{top:0;right:100%;left:auto;margin-top:0;margin-right:var(--bs-dropdown-spacer)}.dropstart .dropdown-toggle::after{display:inline-block;margin-left:.255em;vertical-align:.255em;content:""}.dropstart .dropdown-toggle::after{display:none}.dropstart .dropdown-toggle::before{display:inline-block;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropstart .dropdown-toggle:empty::after{margin-left:0}.dropstart .dropdown-toggle::before{vertical-align:0}.dropdown-divider{height:0;margin:var(--bs-dropdown-divider-margin-y)0;overflow:hidden;border-top:1px solid var(--bs-dropdown-divider-bg);opacity:1}.dropdown-item{display:block;width:100%;padding:var(--bs-dropdown-item-padding-y)var(--bs-dropdown-item-padding-x);clear:both;font-weight:400;color:var(--bs-dropdown-link-color);text-align:inherit;text-decoration:none;white-space:nowrap;background-color:initial;border:0;border-radius:var(--bs-dropdown-item-border-radius,0)}.dropdown-item:focus,.dropdown-item:hover{color:var(--bs-dropdown-link-hover-color);background-color:var(--bs-dropdown-link-hover-bg)}.dropdown-item.active,.dropdown-item:active{color:var(--bs-dropdown-link-active-color);text-decoration:none;background-color:var(--bs-dropdown-link-active-bg)}.dropdown-item.disabled,.dropdown-item:disabled{color:var(--bs-dropdown-link-disabled-color);pointer-events:none;background-color:initial}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:var(--bs-dropdown-header-padding-y)var(--bs-dropdown-header-padding-x);margin-bottom:0;font-size:.875rem;color:var(--bs-dropdown-header-color);white-space:nowrap}.dropdown-item-text{display:block;padding:var(--bs-dropdown-item-padding-y)var(--bs-dropdown-item-padding-x);color:var(--bs-dropdown-link-color)}.dropdown-menu-dark{--bs-dropdown-color:#dee2e6;--bs-dropdown-bg:#343a40;--bs-dropdown-border-color:rgba(255, 255, 255, 0.2);--bs-dropdown-box-shadow: ;--bs-dropdown-link-color:#dee2e6;--bs-dropdown-link-hover-color:#fff;--bs-dropdown-divider-bg:rgba(255, 255, 255, 0.2);--bs-dropdown-link-hover-bg:rgba(255, 255, 255, 0.15);--bs-dropdown-link-active-color:#fff;--bs-dropdown-link-active-bg:#e83283;--bs-dropdown-link-disabled-color:#adb5bd;--bs-dropdown-header-color:#adb5bd}.btn-group,.btn-group-vertical{position:relative;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;flex:auto}.btn-group-vertical>.btn-check:checked+.btn,.btn-group-vertical>.btn-check:focus+.btn,.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn-check:checked+.btn,.btn-group>.btn-check:focus+.btn,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-toolbar{display:flex;flex-wrap:wrap;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group{border-radius:var(--bs-border-radius)}.btn-group>.btn-group:not(:first-child),.btn-group>:not(.btn-check:first-child)+.btn{margin-left:calc(-1 * var(--bs-border-width))}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn.dropdown-toggle-split:first-child,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:nth-child(n+3),.btn-group>:not(.btn-check)+.btn{border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:1.125rem;padding-left:1.125rem}.dropdown-toggle-split::after,.dropend .dropdown-toggle-split::after,.dropup .dropdown-toggle-split::after{margin-left:0}.dropstart .dropdown-toggle-split::before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{flex-direction:column;align-items:flex-start;justify-content:center}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group{width:100%}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:calc(-1 * var(--bs-border-width))}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:nth-child(n+3),.btn-group-vertical>:not(.btn-check)+.btn{border-top-left-radius:0;border-top-right-radius:0}.nav{--bs-nav-link-padding-x:1rem;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-link-color);--bs-nav-link-hover-color:var(--bs-link-hover-color);--bs-nav-link-disabled-color:rgba(255, 255, 255, 0.7);display:flex;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:var(--bs-nav-link-padding-y)var(--bs-nav-link-padding-x);font-size:var(--bs-nav-link-font-size);font-weight:var(--bs-nav-link-font-weight);color:var(--bs-nav-link-color);text-decoration:none;background:0 0;border:0;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out}@media(prefers-reduced-motion:reduce){.nav-link{transition:none}}.nav-link:focus,.nav-link:hover{color:var(--bs-nav-link-hover-color)}.nav-link:focus-visible{outline:0;box-shadow:0 0 0 .25rem rgba(232,50,131,.25)}.nav-link.disabled,.nav-link:disabled{color:var(--bs-nav-link-disabled-color);pointer-events:none;cursor:default}.nav-tabs{--bs-nav-tabs-border-width:0;--bs-nav-tabs-border-color:var(--bs-border-color);--bs-nav-tabs-border-radius:0;--bs-nav-tabs-link-hover-border-color:var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color);--bs-nav-tabs-link-active-color:#343a40;--bs-nav-tabs-link-active-bg:#fff;--bs-nav-tabs-link-active-border-color:#dee2e6 #dee2e6 #fff;border-bottom:var(--bs-nav-tabs-border-width)solid var(--bs-nav-tabs-border-color)}.nav-tabs .nav-link{margin-bottom:calc(-1 * var(--bs-nav-tabs-border-width));border:var(--bs-nav-tabs-border-width)solid transparent;border-top-left-radius:var(--bs-nav-tabs-border-radius);border-top-right-radius:var(--bs-nav-tabs-border-radius)}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{isolation:isolate;border-color:var(--bs-nav-tabs-link-hover-border-color)}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:var(--bs-nav-tabs-link-active-color);background-color:var(--bs-nav-tabs-link-active-bg);border-color:var(--bs-nav-tabs-link-active-border-color)}.nav-tabs .dropdown-menu{margin-top:calc(-1 * var(--bs-nav-tabs-border-width));border-top-left-radius:0;border-top-right-radius:0}.nav-pills{--bs-nav-pills-border-radius:50rem;--bs-nav-pills-link-active-color:#fff;--bs-nav-pills-link-active-bg:#e83283}.nav-pills .nav-link{border-radius:var(--bs-nav-pills-border-radius)}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:var(--bs-nav-pills-link-active-color);background-color:var(--bs-nav-pills-link-active-bg)}.nav-underline{--bs-nav-underline-gap:1rem;--bs-nav-underline-border-width:0.125rem;--bs-nav-underline-link-active-color:#fff;gap:var(--bs-nav-underline-gap)}.nav-underline .nav-link{padding-right:0;padding-left:0;border-bottom:var(--bs-nav-underline-border-width)solid transparent}.nav-underline .nav-link:focus,.nav-underline .nav-link:hover{border-bottom-color:initial}.nav-underline .nav-link.active,.nav-underline .show>.nav-link{font-weight:700;color:var(--bs-nav-underline-link-active-color);border-bottom-color:initial}.nav-fill .nav-item,.nav-fill>.nav-link{flex:auto;text-align:center}.nav-justified .nav-item,.nav-justified>.nav-link{flex-grow:1;flex-basis:0;text-align:center}.nav-fill .nav-item .nav-link,.nav-justified .nav-item .nav-link{width:100%}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{--bs-navbar-padding-x:0;--bs-navbar-padding-y:1rem;--bs-navbar-color:rgba(var(--bs-emphasis-color-rgb), 0.65);--bs-navbar-hover-color:rgba(var(--bs-emphasis-color-rgb), 0.8);--bs-navbar-disabled-color:rgba(var(--bs-emphasis-color-rgb), 0.3);--bs-navbar-active-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-padding-y:0.3125rem;--bs-navbar-brand-margin-end:1rem;--bs-navbar-brand-font-size:1.25rem;--bs-navbar-brand-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-brand-hover-color:rgba(var(--bs-emphasis-color-rgb), 1);--bs-navbar-nav-link-padding-x:0.5rem;--bs-navbar-toggler-padding-y:0.25rem;--bs-navbar-toggler-padding-x:0.75rem;--bs-navbar-toggler-font-size:1.25rem;--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e");--bs-navbar-toggler-border-color:rgba(var(--bs-emphasis-color-rgb), 0.15);--bs-navbar-toggler-border-radius:var(--bs-border-radius);--bs-navbar-toggler-focus-width:0.25rem;--bs-navbar-toggler-transition:box-shadow 0.15s ease-in-out;position:relative;display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:var(--bs-navbar-padding-y)var(--bs-navbar-padding-x)}.navbar>.container,.navbar>.container-fluid,.navbar>.container-lg,.navbar>.container-md,.navbar>.container-sm,.navbar>.container-xl,.navbar>.container-xxl{display:flex;flex-wrap:inherit;align-items:center;justify-content:space-between}.navbar-brand{padding-top:var(--bs-navbar-brand-padding-y);padding-bottom:var(--bs-navbar-brand-padding-y);margin-right:var(--bs-navbar-brand-margin-end);font-size:var(--bs-navbar-brand-font-size);color:var(--bs-navbar-brand-color);text-decoration:none;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{color:var(--bs-navbar-brand-hover-color)}.navbar-nav{--bs-nav-link-padding-x:0;--bs-nav-link-padding-y:0.5rem;--bs-nav-link-font-weight: ;--bs-nav-link-color:var(--bs-navbar-color);--bs-nav-link-hover-color:var(--bs-navbar-hover-color);--bs-nav-link-disabled-color:var(--bs-navbar-disabled-color);display:flex;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link.active,.navbar-nav .nav-link.show{color:var(--bs-navbar-active-color)}.navbar-nav .dropdown-menu{position:static}.navbar-text{padding-top:.5rem;padding-bottom:.5rem;color:var(--bs-navbar-color)}.navbar-text a,.navbar-text a:focus,.navbar-text a:hover{color:var(--bs-navbar-active-color)}.navbar-collapse{flex-grow:1;flex-basis:100%;align-items:center}.navbar-toggler{padding:var(--bs-navbar-toggler-padding-y)var(--bs-navbar-toggler-padding-x);font-size:var(--bs-navbar-toggler-font-size);line-height:1;color:var(--bs-navbar-color);background-color:initial;border:var(--bs-border-width)solid var(--bs-navbar-toggler-border-color);border-radius:var(--bs-navbar-toggler-border-radius);transition:var(--bs-navbar-toggler-transition)}@media(prefers-reduced-motion:reduce){.navbar-toggler{transition:none}}.navbar-toggler:hover{text-decoration:none}.navbar-toggler:focus{text-decoration:none;outline:0;box-shadow:0 0 0 var(--bs-navbar-toggler-focus-width)}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;background-image:var(--bs-navbar-toggler-icon-bg);background-repeat:no-repeat;background-position:50%;background-size:100%}.navbar-nav-scroll{max-height:var(--bs-scroll-height,75vh);overflow-y:auto}@media(min-width:576px){.navbar-expand-sm{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-sm .navbar-nav{flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-sm .navbar-nav-scroll{overflow:visible}.navbar-expand-sm .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}.navbar-expand-sm .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:initial!important;border:0!important;transform:none!important;transition:none}.navbar-expand-sm .offcanvas .offcanvas-header{display:none}.navbar-expand-sm .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:768px){.navbar-expand-md{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-md .navbar-nav{flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-md .navbar-nav-scroll{overflow:visible}.navbar-expand-md .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}.navbar-expand-md .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:initial!important;border:0!important;transform:none!important;transition:none}.navbar-expand-md .offcanvas .offcanvas-header{display:none}.navbar-expand-md .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:992px){.navbar-expand-lg{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-lg .navbar-nav{flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-lg .navbar-nav-scroll{overflow:visible}.navbar-expand-lg .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}.navbar-expand-lg .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:initial!important;border:0!important;transform:none!important;transition:none}.navbar-expand-lg .offcanvas .offcanvas-header{display:none}.navbar-expand-lg .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:1200px){.navbar-expand-xl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xl .navbar-nav{flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xl .navbar-nav-scroll{overflow:visible}.navbar-expand-xl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}.navbar-expand-xl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:initial!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xl .offcanvas .offcanvas-header{display:none}.navbar-expand-xl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}@media(min-width:1400px){.navbar-expand-xxl{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand-xxl .navbar-nav{flex-direction:row}.navbar-expand-xxl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xxl .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand-xxl .navbar-nav-scroll{overflow:visible}.navbar-expand-xxl .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand-xxl .navbar-toggler{display:none}.navbar-expand-xxl .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:initial!important;border:0!important;transform:none!important;transition:none}.navbar-expand-xxl .offcanvas .offcanvas-header{display:none}.navbar-expand-xxl .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}}.navbar-expand{flex-wrap:nowrap;justify-content:flex-start}.navbar-expand .navbar-nav{flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:var(--bs-navbar-nav-link-padding-x);padding-left:var(--bs-navbar-nav-link-padding-x)}.navbar-expand .navbar-nav-scroll{overflow:visible}.navbar-expand .navbar-collapse{display:flex!important;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-expand .offcanvas{position:static;z-index:auto;flex-grow:1;width:auto!important;height:auto!important;visibility:visible!important;background-color:initial!important;border:0!important;transform:none!important;transition:none}.navbar-expand .offcanvas .offcanvas-header{display:none}.navbar-expand .offcanvas .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible}.navbar-dark,.navbar[data-bs-theme=dark]{--bs-navbar-color:rgba(255, 255, 255, 0.55);--bs-navbar-hover-color:rgba(255, 255, 255, 0.75);--bs-navbar-disabled-color:rgba(255, 255, 255, 0.25);--bs-navbar-active-color:#fff;--bs-navbar-brand-color:#fff;--bs-navbar-brand-hover-color:#fff;--bs-navbar-toggler-border-color:rgba(255, 255, 255, 0.1);--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}[data-bs-theme=dark] .navbar-toggler-icon{--bs-navbar-toggler-icon-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e")}.card{--bs-card-spacer-y:1.75rem;--bs-card-spacer-x:2rem;--bs-card-title-spacer-y:1rem;--bs-card-title-color: ;--bs-card-subtitle-color: ;--bs-card-border-width:var(--bs-border-width);--bs-card-border-color:rgba(255, 255, 255, 0.2);--bs-card-border-radius:var(--bs-border-radius);--bs-card-box-shadow: ;--bs-card-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-card-cap-padding-y:0.875rem;--bs-card-cap-padding-x:2rem;--bs-card-cap-bg:transparent;--bs-card-cap-color:#fff;--bs-card-height: ;--bs-card-color:#fff;--bs-card-bg:transparent;--bs-card-img-overlay-padding:2rem;--bs-card-group-margin:0.75rem;position:relative;display:flex;flex-direction:column;min-width:0;height:var(--bs-card-height);color:var(--bs-body-color);word-wrap:break-word;background-color:var(--bs-card-bg);background-clip:border-box;border:var(--bs-card-border-width)solid var(--bs-card-border-color);border-radius:var(--bs-card-border-radius)}.card>hr{margin-right:0;margin-left:0}.card>.list-group{border-top:inherit;border-bottom:inherit}.card>.list-group:first-child{border-top-width:0;border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card>.list-group:last-child{border-bottom-width:0;border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card>.card-header+.list-group,.card>.list-group+.card-footer{border-top:0}.card-body{flex:auto;padding:var(--bs-card-spacer-y)var(--bs-card-spacer-x);color:var(--bs-card-color)}.card-title{margin-bottom:var(--bs-card-title-spacer-y);color:var(--bs-card-title-color)}.card-subtitle{margin-top:calc(-.5 * var(--bs-card-title-spacer-y));margin-bottom:0;color:var(--bs-card-subtitle-color)}.card-text:last-child{margin-bottom:0}.card-link+.card-link{margin-left:var(--bs-card-spacer-x)}.card-header{padding:var(--bs-card-cap-padding-y)var(--bs-card-cap-padding-x);margin-bottom:0;color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-bottom:var(--bs-card-border-width)solid var(--bs-card-border-color)}.card-header:first-child{border-radius:var(--bs-card-inner-border-radius)var(--bs-card-inner-border-radius)0 0}.card-footer{padding:var(--bs-card-cap-padding-y)var(--bs-card-cap-padding-x);color:var(--bs-card-cap-color);background-color:var(--bs-card-cap-bg);border-top:var(--bs-card-border-width)solid var(--bs-card-border-color)}.card-footer:last-child{border-radius:0 0 var(--bs-card-inner-border-radius)var(--bs-card-inner-border-radius)}.card-header-tabs{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-bottom:calc(-1 * var(--bs-card-cap-padding-y));margin-left:calc(-.5 * var(--bs-card-cap-padding-x));border-bottom:0}.card-header-tabs .nav-link.active{background-color:var(--bs-card-bg);border-bottom-color:var(--bs-card-bg)}.card-header-pills{margin-right:calc(-.5 * var(--bs-card-cap-padding-x));margin-left:calc(-.5 * var(--bs-card-cap-padding-x))}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:var(--bs-card-img-overlay-padding);border-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom,.card-img-top{width:100%}.card-img,.card-img-top{border-top-left-radius:var(--bs-card-inner-border-radius);border-top-right-radius:var(--bs-card-inner-border-radius)}.card-img,.card-img-bottom{border-bottom-right-radius:var(--bs-card-inner-border-radius);border-bottom-left-radius:var(--bs-card-inner-border-radius)}.card-group>.card{margin-bottom:var(--bs-card-group-margin)}@media(min-width:576px){.card-group{display:flex;flex-flow:row wrap}.card-group>.card{flex:1 0;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:not(:last-child)>.card-header,.card-group>.card:not(:last-child)>.card-img-top{border-top-right-radius:0}.card-group>.card:not(:last-child)>.card-footer,.card-group>.card:not(:last-child)>.card-img-bottom{border-bottom-right-radius:0}.card-group>.card:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:not(:first-child)>.card-header,.card-group>.card:not(:first-child)>.card-img-top{border-top-left-radius:0}.card-group>.card:not(:first-child)>.card-footer,.card-group>.card:not(:first-child)>.card-img-bottom{border-bottom-left-radius:0}}.accordion{--bs-accordion-color:var(--bs-body-color);--bs-accordion-bg:var(--bs-body-bg);--bs-accordion-transition:color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease;--bs-accordion-border-color:var(--bs-border-color);--bs-accordion-border-width:var(--bs-border-width);--bs-accordion-border-radius:var(--bs-border-radius);--bs-accordion-inner-border-radius:calc(var(--bs-border-radius) - (var(--bs-border-width)));--bs-accordion-btn-padding-x:1.25rem;--bs-accordion-btn-padding-y:1rem;--bs-accordion-btn-color:var(--bs-body-color);--bs-accordion-btn-bg:rgba(255, 255, 255, 0.4);--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m2 5 6 6 6-6'/%3e%3c/svg%3e");--bs-accordion-btn-icon-width:1.25rem;--bs-accordion-btn-icon-transform:rotate(-180deg);--bs-accordion-btn-icon-transition:transform 0.2s ease-in-out;--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%235d1434' stroke-linecap='round' stroke-linejoin='round'%3e%3cpath d='m2 5 6 6 6-6'/%3e%3c/svg%3e");--bs-accordion-btn-focus-box-shadow:0 0 0 0.25rem rgba(232, 50, 131, 0.25);--bs-accordion-body-padding-x:1.25rem;--bs-accordion-body-padding-y:1rem;--bs-accordion-active-color:#fff;--bs-accordion-active-bg:#e83283}.accordion-button{position:relative;display:flex;align-items:center;width:100%;padding:var(--bs-accordion-btn-padding-y)var(--bs-accordion-btn-padding-x);font-size:1rem;color:var(--bs-accordion-btn-color);text-align:left;background-color:var(--bs-accordion-btn-bg);border:0;border-radius:0;overflow-anchor:none;transition:var(--bs-accordion-transition)}@media(prefers-reduced-motion:reduce){.accordion-button{transition:none}}.accordion-button:not(.collapsed){color:var(--bs-accordion-active-color);background-color:var(--bs-accordion-active-bg);box-shadow:inset 0 calc(-1 * var(--bs-accordion-border-width))0 var(--bs-accordion-border-color)}.accordion-button:not(.collapsed)::after{background-image:var(--bs-accordion-btn-active-icon);transform:var(--bs-accordion-btn-icon-transform)}.accordion-button::after{flex-shrink:0;width:var(--bs-accordion-btn-icon-width);height:var(--bs-accordion-btn-icon-width);margin-left:auto;content:"";background-image:var(--bs-accordion-btn-icon);background-repeat:no-repeat;background-size:var(--bs-accordion-btn-icon-width);transition:var(--bs-accordion-btn-icon-transition)}@media(prefers-reduced-motion:reduce){.accordion-button::after{transition:none}}.accordion-button:hover{z-index:2}.accordion-button:focus{z-index:3;outline:0;box-shadow:var(--bs-accordion-btn-focus-box-shadow)}.accordion-header{margin-bottom:0}.accordion-item{color:var(--bs-accordion-color);background-color:var(--bs-accordion-bg);border:var(--bs-accordion-border-width)solid var(--bs-accordion-border-color)}.accordion-item:first-of-type{border-top-left-radius:var(--bs-accordion-border-radius);border-top-right-radius:var(--bs-accordion-border-radius)}.accordion-item:first-of-type>.accordion-header .accordion-button{border-top-left-radius:var(--bs-accordion-inner-border-radius);border-top-right-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:not(:first-of-type){border-top:0}.accordion-item:last-of-type{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-item:last-of-type>.accordion-header .accordion-button.collapsed{border-bottom-right-radius:var(--bs-accordion-inner-border-radius);border-bottom-left-radius:var(--bs-accordion-inner-border-radius)}.accordion-item:last-of-type>.accordion-collapse{border-bottom-right-radius:var(--bs-accordion-border-radius);border-bottom-left-radius:var(--bs-accordion-border-radius)}.accordion-body{padding:var(--bs-accordion-body-padding-y)var(--bs-accordion-body-padding-x)}.accordion-flush>.accordion-item{border-right:0;border-left:0;border-radius:0}.accordion-flush>.accordion-item:first-child{border-top:0}.accordion-flush>.accordion-item:last-child{border-bottom:0}.accordion-flush>.accordion-item>.accordion-collapse,.accordion-flush>.accordion-item>.accordion-header .accordion-button,.accordion-flush>.accordion-item>.accordion-header .accordion-button.collapsed{border-radius:0}[data-bs-theme=dark] .accordion-button::after{--bs-accordion-btn-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23f184b5'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e");--bs-accordion-btn-active-icon:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23f184b5'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e")}.breadcrumb{--bs-breadcrumb-padding-x:0;--bs-breadcrumb-padding-y:0;--bs-breadcrumb-margin-bottom:1rem;--bs-breadcrumb-bg: ;--bs-breadcrumb-border-radius: ;--bs-breadcrumb-divider-color:#fff;--bs-breadcrumb-item-padding-x:0.5rem;--bs-breadcrumb-item-active-color:#fff;display:flex;flex-wrap:wrap;padding:var(--bs-breadcrumb-padding-y)var(--bs-breadcrumb-padding-x);margin-bottom:var(--bs-breadcrumb-margin-bottom);font-size:var(--bs-breadcrumb-font-size);list-style:none;background-color:var(--bs-breadcrumb-bg);border-radius:var(--bs-breadcrumb-border-radius)}.breadcrumb-item+.breadcrumb-item{padding-left:var(--bs-breadcrumb-item-padding-x)}.breadcrumb-item+.breadcrumb-item::before{float:left;padding-right:var(--bs-breadcrumb-item-padding-x);color:var(--bs-breadcrumb-divider-color);content:var(--bs-breadcrumb-divider,"/")}.breadcrumb-item.active{color:var(--bs-breadcrumb-item-active-color)}.pagination{--bs-pagination-padding-x:0.75rem;--bs-pagination-padding-y:0.375rem;--bs-pagination-font-size:1rem;--bs-pagination-color:var(--bs-link-color);--bs-pagination-bg:rgba(255, 255, 255, 0.3);--bs-pagination-border-width:0;--bs-pagination-border-color:var(--bs-border-color);--bs-pagination-border-radius:var(--bs-border-radius);--bs-pagination-hover-color:#fff;--bs-pagination-hover-bg:rgba(255, 255, 255, 0.4);--bs-pagination-hover-border-color:var(--bs-border-color);--bs-pagination-focus-color:#fff;--bs-pagination-focus-bg:rgba(255, 255, 255, 0.4);--bs-pagination-focus-box-shadow:none;--bs-pagination-active-color:#fff;--bs-pagination-active-bg:#e83283;--bs-pagination-active-border-color:#e83283;--bs-pagination-disabled-color:rgba(255, 255, 255, 0.7);--bs-pagination-disabled-bg:rgba(255, 255, 255, 0.3);--bs-pagination-disabled-border-color:var(--bs-border-color);display:flex;padding-left:0;list-style:none}.page-link{position:relative;display:block;padding:var(--bs-pagination-padding-y)var(--bs-pagination-padding-x);font-size:var(--bs-pagination-font-size);color:var(--bs-pagination-color);text-decoration:none;background-color:var(--bs-pagination-bg);border:var(--bs-pagination-border-width)solid var(--bs-pagination-border-color);transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media(prefers-reduced-motion:reduce){.page-link{transition:none}}.page-link:hover{z-index:2;color:var(--bs-pagination-hover-color);background-color:var(--bs-pagination-hover-bg);border-color:var(--bs-pagination-hover-border-color)}.page-link:focus{z-index:3;color:var(--bs-pagination-focus-color);background-color:var(--bs-pagination-focus-bg);outline:0;box-shadow:var(--bs-pagination-focus-box-shadow)}.active>.page-link,.page-link.active{z-index:3;color:var(--bs-pagination-active-color);background-color:var(--bs-pagination-active-bg);border-color:var(--bs-pagination-active-border-color)}.disabled>.page-link,.page-link.disabled{color:var(--bs-pagination-disabled-color);pointer-events:none;background-color:var(--bs-pagination-disabled-bg);border-color:var(--bs-pagination-disabled-border-color)}.page-item:not(:first-child) .page-link{margin-left:calc(-1 * 0)}.page-item:first-child .page-link{border-top-left-radius:var(--bs-pagination-border-radius);border-bottom-left-radius:var(--bs-pagination-border-radius)}.page-item:last-child .page-link{border-top-right-radius:var(--bs-pagination-border-radius);border-bottom-right-radius:var(--bs-pagination-border-radius)}.pagination-lg{--bs-pagination-padding-x:1.5rem;--bs-pagination-padding-y:0.75rem;--bs-pagination-font-size:1.25rem;--bs-pagination-border-radius:var(--bs-border-radius-lg)}.pagination-sm{--bs-pagination-padding-x:0.5rem;--bs-pagination-padding-y:0.25rem;--bs-pagination-font-size:0.875rem;--bs-pagination-border-radius:var(--bs-border-radius-sm)}.badge{--bs-badge-padding-x:0.65em;--bs-badge-padding-y:0.35em;--bs-badge-font-size:0.75em;--bs-badge-font-weight:700;--bs-badge-color:#fff;--bs-badge-border-radius:var(--bs-border-radius);display:inline-block;padding:var(--bs-badge-padding-y)var(--bs-badge-padding-x);font-size:var(--bs-badge-font-size);font-weight:var(--bs-badge-font-weight);line-height:1;color:var(--bs-badge-color);text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:var(--bs-badge-border-radius)}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.alert{--bs-alert-bg:transparent;--bs-alert-padding-x:2rem;--bs-alert-padding-y:2rem;--bs-alert-margin-bottom:1rem;--bs-alert-color:inherit;--bs-alert-border-color:transparent;--bs-alert-border:var(--bs-border-width) solid var(--bs-alert-border-color);--bs-alert-border-radius:var(--bs-border-radius);--bs-alert-link-color:inherit;position:relative;padding:var(--bs-alert-padding-y)var(--bs-alert-padding-x);margin-bottom:var(--bs-alert-margin-bottom);color:var(--bs-alert-color);background-color:var(--bs-alert-bg);border:var(--bs-alert-border);border-radius:var(--bs-alert-border-radius)}.alert-heading{color:inherit}.alert-link{font-weight:700;color:var(--bs-alert-link-color)}.alert-dismissible{padding-right:6rem}.alert-dismissible .btn-close{position:absolute;top:0;right:0;z-index:2;padding:2.5rem 2rem}.alert-primary{--bs-alert-color:var(--bs-primary-text-emphasis);--bs-alert-bg:var(--bs-primary-bg-subtle);--bs-alert-border-color:var(--bs-primary-border-subtle);--bs-alert-link-color:var(--bs-primary-text-emphasis)}.alert-secondary{--bs-alert-color:var(--bs-secondary-text-emphasis);--bs-alert-bg:var(--bs-secondary-bg-subtle);--bs-alert-border-color:var(--bs-secondary-border-subtle);--bs-alert-link-color:var(--bs-secondary-text-emphasis)}.alert-success{--bs-alert-color:var(--bs-success-text-emphasis);--bs-alert-bg:var(--bs-success-bg-subtle);--bs-alert-border-color:var(--bs-success-border-subtle);--bs-alert-link-color:var(--bs-success-text-emphasis)}.alert-info{--bs-alert-color:var(--bs-info-text-emphasis);--bs-alert-bg:var(--bs-info-bg-subtle);--bs-alert-border-color:var(--bs-info-border-subtle);--bs-alert-link-color:var(--bs-info-text-emphasis)}.alert-warning{--bs-alert-color:var(--bs-warning-text-emphasis);--bs-alert-bg:var(--bs-warning-bg-subtle);--bs-alert-border-color:var(--bs-warning-border-subtle);--bs-alert-link-color:var(--bs-warning-text-emphasis)}.alert-danger{--bs-alert-color:var(--bs-danger-text-emphasis);--bs-alert-bg:var(--bs-danger-bg-subtle);--bs-alert-border-color:var(--bs-danger-border-subtle);--bs-alert-link-color:var(--bs-danger-text-emphasis)}.alert-light{--bs-alert-color:var(--bs-light-text-emphasis);--bs-alert-bg:var(--bs-light-bg-subtle);--bs-alert-border-color:var(--bs-light-border-subtle);--bs-alert-link-color:var(--bs-light-text-emphasis)}.alert-dark{--bs-alert-color:var(--bs-dark-text-emphasis);--bs-alert-bg:var(--bs-dark-bg-subtle);--bs-alert-border-color:var(--bs-dark-border-subtle);--bs-alert-link-color:var(--bs-dark-text-emphasis)}@keyframes progress-bar-stripes{0%{background-position-x:var(--bs-progress-height)}}.progress,.progress-stacked{--bs-progress-height:1rem;--bs-progress-font-size:0.75rem;--bs-progress-bg:rgba(0, 0, 0, 0.2);--bs-progress-border-radius:var(--bs-border-radius);--bs-progress-box-shadow:var(--bs-box-shadow-inset);--bs-progress-bar-color:#fff;--bs-progress-bar-bg:#e83283;--bs-progress-bar-transition:width 0.6s ease;display:flex;height:var(--bs-progress-height);overflow:hidden;font-size:var(--bs-progress-font-size);background-color:var(--bs-progress-bg);border-radius:var(--bs-progress-border-radius)}.progress-bar{display:flex;flex-direction:column;justify-content:center;overflow:hidden;color:var(--bs-progress-bar-color);text-align:center;white-space:nowrap;background-color:var(--bs-progress-bar-bg);transition:var(--bs-progress-bar-transition)}@media(prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-size:var(--bs-progress-height)var(--bs-progress-height)}.progress-stacked>.progress{overflow:visible}.progress-stacked>.progress>.progress-bar{width:100%}.progress-bar-animated{animation:1s linear infinite progress-bar-stripes}@media(prefers-reduced-motion:reduce){.progress-bar-animated{animation:none}}.list-group{--bs-list-group-color:#fff;--bs-list-group-bg:transparent;--bs-list-group-border-color:rgba(255, 255, 255, 0.2);--bs-list-group-border-width:0;--bs-list-group-border-radius:var(--bs-border-radius);--bs-list-group-item-padding-x:2rem;--bs-list-group-item-padding-y:1rem;--bs-list-group-action-color:#fff;--bs-list-group-action-hover-color:#fff;--bs-list-group-action-hover-bg:rgba(255, 255, 255, 0.4);--bs-list-group-action-active-color:var(--bs-body-color);--bs-list-group-action-active-bg:var(--bs-secondary-bg);--bs-list-group-disabled-color:rgba(255, 255, 255, 0.7);--bs-list-group-disabled-bg:transparent;--bs-list-group-active-color:#fff;--bs-list-group-active-bg:#e83283;--bs-list-group-active-border-color:#e83283;display:flex;flex-direction:column;padding-left:0;margin-bottom:0;border-radius:var(--bs-list-group-border-radius)}.list-group-numbered{list-style-type:none;counter-reset:section}.list-group-numbered>.list-group-item::before{content:counters(section,".")". ";counter-increment:section}.list-group-item{position:relative;display:block;padding:var(--bs-list-group-item-padding-y)var(--bs-list-group-item-padding-x);color:var(--bs-list-group-color);text-decoration:none;background-color:var(--bs-list-group-bg);border:var(--bs-list-group-border-width)solid var(--bs-list-group-border-color)}.list-group-item:first-child{border-top-left-radius:inherit;border-top-right-radius:inherit}.list-group-item:last-child{border-bottom-right-radius:inherit;border-bottom-left-radius:inherit}.list-group-item.disabled,.list-group-item:disabled{color:var(--bs-list-group-disabled-color);pointer-events:none;background-color:var(--bs-list-group-disabled-bg)}.list-group-item.active{z-index:2;color:var(--bs-list-group-active-color);background-color:var(--bs-list-group-active-bg);border-color:var(--bs-list-group-active-border-color)}.list-group-item+.list-group-item{border-top-width:0}.list-group-item+.list-group-item.active{margin-top:calc(-1 * var(--bs-list-group-border-width));border-top-width:var(--bs-list-group-border-width)}.list-group-item-action{width:100%;color:var(--bs-list-group-action-color);text-align:inherit}.list-group-item-action:not(.active):focus,.list-group-item-action:not(.active):hover{z-index:1;color:var(--bs-list-group-action-hover-color);text-decoration:none;background-color:var(--bs-list-group-action-hover-bg)}.list-group-item-action:not(.active):active{color:var(--bs-list-group-action-active-color);background-color:var(--bs-list-group-action-active-bg)}.list-group-horizontal{flex-direction:row}.list-group-horizontal>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal>.list-group-item.active{margin-top:0}.list-group-horizontal>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}@media(min-width:576px){.list-group-horizontal-sm{flex-direction:row}.list-group-horizontal-sm>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-sm>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-sm>.list-group-item.active{margin-top:0}.list-group-horizontal-sm>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-sm>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:768px){.list-group-horizontal-md{flex-direction:row}.list-group-horizontal-md>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-md>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-md>.list-group-item.active{margin-top:0}.list-group-horizontal-md>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-md>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:992px){.list-group-horizontal-lg{flex-direction:row}.list-group-horizontal-lg>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-lg>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-lg>.list-group-item.active{margin-top:0}.list-group-horizontal-lg>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-lg>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:1200px){.list-group-horizontal-xl{flex-direction:row}.list-group-horizontal-xl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xl>.list-group-item.active{margin-top:0}.list-group-horizontal-xl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}@media(min-width:1400px){.list-group-horizontal-xxl{flex-direction:row}.list-group-horizontal-xxl>.list-group-item:first-child:not(:last-child){border-bottom-left-radius:var(--bs-list-group-border-radius);border-top-right-radius:0}.list-group-horizontal-xxl>.list-group-item:last-child:not(:first-child){border-top-right-radius:var(--bs-list-group-border-radius);border-bottom-left-radius:0}.list-group-horizontal-xxl>.list-group-item.active{margin-top:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item{border-top-width:var(--bs-list-group-border-width);border-left-width:0}.list-group-horizontal-xxl>.list-group-item+.list-group-item.active{margin-left:calc(-1 * var(--bs-list-group-border-width));border-left-width:var(--bs-list-group-border-width)}}.list-group-flush{border-radius:0}.list-group-flush>.list-group-item{border-width:0 0 var(--bs-list-group-border-width)}.list-group-flush>.list-group-item:last-child{border-bottom-width:0}.list-group-item-primary{--bs-list-group-color:var(--bs-primary-text-emphasis);--bs-list-group-bg:var(--bs-primary-bg-subtle);--bs-list-group-border-color:var(--bs-primary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-primary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-primary-border-subtle);--bs-list-group-active-color:var(--bs-primary-bg-subtle);--bs-list-group-active-bg:var(--bs-primary-text-emphasis);--bs-list-group-active-border-color:var(--bs-primary-text-emphasis)}.list-group-item-secondary{--bs-list-group-color:var(--bs-secondary-text-emphasis);--bs-list-group-bg:var(--bs-secondary-bg-subtle);--bs-list-group-border-color:var(--bs-secondary-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-secondary-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-secondary-border-subtle);--bs-list-group-active-color:var(--bs-secondary-bg-subtle);--bs-list-group-active-bg:var(--bs-secondary-text-emphasis);--bs-list-group-active-border-color:var(--bs-secondary-text-emphasis)}.list-group-item-success{--bs-list-group-color:var(--bs-success-text-emphasis);--bs-list-group-bg:var(--bs-success-bg-subtle);--bs-list-group-border-color:var(--bs-success-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-success-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-success-border-subtle);--bs-list-group-active-color:var(--bs-success-bg-subtle);--bs-list-group-active-bg:var(--bs-success-text-emphasis);--bs-list-group-active-border-color:var(--bs-success-text-emphasis)}.list-group-item-info{--bs-list-group-color:var(--bs-info-text-emphasis);--bs-list-group-bg:var(--bs-info-bg-subtle);--bs-list-group-border-color:var(--bs-info-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-info-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-info-border-subtle);--bs-list-group-active-color:var(--bs-info-bg-subtle);--bs-list-group-active-bg:var(--bs-info-text-emphasis);--bs-list-group-active-border-color:var(--bs-info-text-emphasis)}.list-group-item-warning{--bs-list-group-color:var(--bs-warning-text-emphasis);--bs-list-group-bg:var(--bs-warning-bg-subtle);--bs-list-group-border-color:var(--bs-warning-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-warning-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-warning-border-subtle);--bs-list-group-active-color:var(--bs-warning-bg-subtle);--bs-list-group-active-bg:var(--bs-warning-text-emphasis);--bs-list-group-active-border-color:var(--bs-warning-text-emphasis)}.list-group-item-danger{--bs-list-group-color:var(--bs-danger-text-emphasis);--bs-list-group-bg:var(--bs-danger-bg-subtle);--bs-list-group-border-color:var(--bs-danger-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-danger-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-danger-border-subtle);--bs-list-group-active-color:var(--bs-danger-bg-subtle);--bs-list-group-active-bg:var(--bs-danger-text-emphasis);--bs-list-group-active-border-color:var(--bs-danger-text-emphasis)}.list-group-item-light{--bs-list-group-color:var(--bs-light-text-emphasis);--bs-list-group-bg:var(--bs-light-bg-subtle);--bs-list-group-border-color:var(--bs-light-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-light-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-light-border-subtle);--bs-list-group-active-color:var(--bs-light-bg-subtle);--bs-list-group-active-bg:var(--bs-light-text-emphasis);--bs-list-group-active-border-color:var(--bs-light-text-emphasis)}.list-group-item-dark{--bs-list-group-color:var(--bs-dark-text-emphasis);--bs-list-group-bg:var(--bs-dark-bg-subtle);--bs-list-group-border-color:var(--bs-dark-border-subtle);--bs-list-group-action-hover-color:var(--bs-emphasis-color);--bs-list-group-action-hover-bg:var(--bs-dark-border-subtle);--bs-list-group-action-active-color:var(--bs-emphasis-color);--bs-list-group-action-active-bg:var(--bs-dark-border-subtle);--bs-list-group-active-color:var(--bs-dark-bg-subtle);--bs-list-group-active-bg:var(--bs-dark-text-emphasis);--bs-list-group-active-border-color:var(--bs-dark-text-emphasis)}.btn-close{--bs-btn-close-color:#fff;--bs-btn-close-bg:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414'/%3e%3c/svg%3e");--bs-btn-close-opacity:0.5;--bs-btn-close-hover-opacity:0.75;--bs-btn-close-focus-shadow:0 0 0 0.25rem rgba(232, 50, 131, 0.25);--bs-btn-close-focus-opacity:1;--bs-btn-close-disabled-opacity:0.25;box-sizing:content-box;width:1em;height:1em;padding:.25em;color:var(--bs-btn-close-color);background:var(--bs-btn-close-bg)50%/1em no-repeat;filter:var(--bs-btn-close-filter);border:0;border-radius:.5rem;opacity:var(--bs-btn-close-opacity)}.btn-close:hover{color:var(--bs-btn-close-color);text-decoration:none;opacity:var(--bs-btn-close-hover-opacity)}.btn-close:focus{outline:0;box-shadow:var(--bs-btn-close-focus-shadow);opacity:var(--bs-btn-close-focus-opacity)}.btn-close.disabled,.btn-close:disabled{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;opacity:var(--bs-btn-close-disabled-opacity)}.btn-close-white{--bs-btn-close-filter:invert(1) grayscale(100%) brightness(200%)}:root,[data-bs-theme=light]{--bs-btn-close-filter: }[data-bs-theme=dark]{--bs-btn-close-filter:invert(1) grayscale(100%) brightness(200%)}.toast{--bs-toast-zindex:1090;--bs-toast-padding-x:0.75rem;--bs-toast-padding-y:0.5rem;--bs-toast-spacing:1.5rem;--bs-toast-max-width:350px;--bs-toast-font-size:0.875rem;--bs-toast-color: ;--bs-toast-bg:rgba(var(--bs-body-bg-rgb), 0.85);--bs-toast-border-width:var(--bs-border-width);--bs-toast-border-color:var(--bs-border-color-translucent);--bs-toast-border-radius:var(--bs-border-radius);--bs-toast-box-shadow:var(--bs-box-shadow);--bs-toast-header-color:#fff;--bs-toast-header-bg:transparent;--bs-toast-header-border-color:rgba(255, 255, 255, 0.2);width:var(--bs-toast-max-width);max-width:100%;font-size:var(--bs-toast-font-size);color:var(--bs-toast-color);pointer-events:auto;background-color:var(--bs-toast-bg);background-clip:padding-box;border:var(--bs-toast-border-width)solid var(--bs-toast-border-color);box-shadow:var(--bs-toast-box-shadow);border-radius:var(--bs-toast-border-radius)}.toast.showing{opacity:0}.toast:not(.show){display:none}.toast-container{--bs-toast-zindex:1090;position:absolute;z-index:var(--bs-toast-zindex);width:-webkit-max-content;width:-moz-max-content;width:max-content;max-width:100%;pointer-events:none}.toast-container>:not(:last-child){margin-bottom:var(--bs-toast-spacing)}.toast-header{display:flex;align-items:center;padding:var(--bs-toast-padding-y)var(--bs-toast-padding-x);color:var(--bs-toast-header-color);background-color:var(--bs-toast-header-bg);background-clip:padding-box;border-bottom:var(--bs-toast-border-width)solid var(--bs-toast-header-border-color);border-top-left-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width));border-top-right-radius:calc(var(--bs-toast-border-radius) - var(--bs-toast-border-width))}.toast-header .btn-close{margin-right:calc(-.5 * var(--bs-toast-padding-x));margin-left:var(--bs-toast-padding-x)}.toast-body{padding:var(--bs-toast-padding-x);word-wrap:break-word}.modal{--bs-modal-zindex:1055;--bs-modal-width:500px;--bs-modal-padding:2rem;--bs-modal-margin:0.5rem;--bs-modal-color:var(--bs-body-color);--bs-modal-bg:var(--bs-body-bg);--bs-modal-border-color:var(--bs-border-color-translucent);--bs-modal-border-width:var(--bs-border-width);--bs-modal-border-radius:var(--bs-border-radius-lg);--bs-modal-box-shadow:var(--bs-box-shadow-sm);--bs-modal-inner-border-radius:calc(var(--bs-border-radius-lg) - (var(--bs-border-width)));--bs-modal-header-padding-x:2rem;--bs-modal-header-padding-y:2rem;--bs-modal-header-padding:2rem 2rem;--bs-modal-header-border-color:var(--bs-border-color);--bs-modal-header-border-width:var(--bs-border-width);--bs-modal-title-line-height:1.5;--bs-modal-footer-gap:0.5rem;--bs-modal-footer-bg: ;--bs-modal-footer-border-color:var(--bs-border-color);--bs-modal-footer-border-width:var(--bs-border-width);position:fixed;top:0;left:0;z-index:var(--bs-modal-zindex);display:none;width:100%;height:100%;overflow-x:hidden;overflow-y:auto;outline:0}.modal-dialog{position:relative;width:auto;margin:var(--bs-modal-margin);pointer-events:none}.modal.fade .modal-dialog{transform:translate(0,-50px);transition:transform .3s ease-out}@media(prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{transform:none}.modal.modal-static .modal-dialog{transform:scale(1.02)}.modal-dialog-scrollable{height:calc(100% - var(--bs-modal-margin) * 2)}.modal-dialog-scrollable .modal-content{max-height:100%;overflow:hidden}.modal-dialog-scrollable .modal-body{overflow-y:auto}.modal-dialog-centered{display:flex;align-items:center;min-height:calc(100% - var(--bs-modal-margin) * 2)}.modal-content{position:relative;display:flex;flex-direction:column;width:100%;color:var(--bs-modal-color);pointer-events:auto;background-color:var(--bs-modal-bg);background-clip:padding-box;border:var(--bs-modal-border-width)solid var(--bs-modal-border-color);border-radius:var(--bs-modal-border-radius);outline:0}.modal-backdrop{--bs-backdrop-zindex:1050;--bs-backdrop-bg:#000;--bs-backdrop-opacity:0.5;position:fixed;top:0;left:0;z-index:var(--bs-backdrop-zindex);width:100vw;height:100vh;background-color:var(--bs-backdrop-bg)}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:var(--bs-backdrop-opacity)}.modal-header{display:flex;flex-shrink:0;align-items:center;padding:var(--bs-modal-header-padding);border-bottom:var(--bs-modal-header-border-width)solid var(--bs-modal-header-border-color);border-top-left-radius:var(--bs-modal-inner-border-radius);border-top-right-radius:var(--bs-modal-inner-border-radius)}.modal-header .btn-close{padding:calc(var(--bs-modal-header-padding-y) * .5)calc(var(--bs-modal-header-padding-x) * .5);margin-top:calc(-.5 * var(--bs-modal-header-padding-y));margin-right:calc(-.5 * var(--bs-modal-header-padding-x));margin-bottom:calc(-.5 * var(--bs-modal-header-padding-y));margin-left:auto}.modal-title{margin-bottom:0;line-height:var(--bs-modal-title-line-height)}.modal-body{position:relative;flex:auto;padding:var(--bs-modal-padding)}.modal-footer{display:flex;flex-shrink:0;flex-wrap:wrap;align-items:center;justify-content:flex-end;padding:calc(var(--bs-modal-padding) - var(--bs-modal-footer-gap) * .5);background-color:var(--bs-modal-footer-bg);border-top:var(--bs-modal-footer-border-width)solid var(--bs-modal-footer-border-color);border-bottom-right-radius:var(--bs-modal-inner-border-radius);border-bottom-left-radius:var(--bs-modal-inner-border-radius)}.modal-footer>*{margin:calc(var(--bs-modal-footer-gap) * .5)}@media(min-width:576px){.modal{--bs-modal-margin:1.75rem;--bs-modal-box-shadow:var(--bs-box-shadow)}.modal-dialog{max-width:var(--bs-modal-width);margin-right:auto;margin-left:auto}.modal-sm{--bs-modal-width:300px}}@media(min-width:992px){.modal-lg,.modal-xl{--bs-modal-width:800px}}@media(min-width:1200px){.modal-xl{--bs-modal-width:1140px}}.modal-fullscreen{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen .modal-footer,.modal-fullscreen .modal-header{border-radius:0}.modal-fullscreen .modal-body{overflow-y:auto}@media(max-width:575.98px){.modal-fullscreen-sm-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-sm-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-sm-down .modal-footer,.modal-fullscreen-sm-down .modal-header{border-radius:0}.modal-fullscreen-sm-down .modal-body{overflow-y:auto}}@media(max-width:767.98px){.modal-fullscreen-md-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-md-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-md-down .modal-footer,.modal-fullscreen-md-down .modal-header{border-radius:0}.modal-fullscreen-md-down .modal-body{overflow-y:auto}}@media(max-width:991.98px){.modal-fullscreen-lg-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-lg-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-lg-down .modal-footer,.modal-fullscreen-lg-down .modal-header{border-radius:0}.modal-fullscreen-lg-down .modal-body{overflow-y:auto}}@media(max-width:1199.98px){.modal-fullscreen-xl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xl-down .modal-footer,.modal-fullscreen-xl-down .modal-header{border-radius:0}.modal-fullscreen-xl-down .modal-body{overflow-y:auto}}@media(max-width:1399.98px){.modal-fullscreen-xxl-down{width:100vw;max-width:none;height:100%;margin:0}.modal-fullscreen-xxl-down .modal-content{height:100%;border:0;border-radius:0}.modal-fullscreen-xxl-down .modal-footer,.modal-fullscreen-xxl-down .modal-header{border-radius:0}.modal-fullscreen-xxl-down .modal-body{overflow-y:auto}}.tooltip{--bs-tooltip-zindex:1080;--bs-tooltip-max-width:200px;--bs-tooltip-padding-x:1rem;--bs-tooltip-padding-y:0.5rem;--bs-tooltip-margin: ;--bs-tooltip-font-size:0.875rem;--bs-tooltip-color:var(--bs-body-bg);--bs-tooltip-bg:var(--bs-emphasis-color);--bs-tooltip-border-radius:var(--bs-border-radius);--bs-tooltip-opacity:0.7;--bs-tooltip-arrow-width:0.8rem;--bs-tooltip-arrow-height:0.4rem;z-index:var(--bs-tooltip-zindex);display:block;margin:var(--bs-tooltip-margin);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-tooltip-font-size);word-wrap:break-word;opacity:0}.tooltip.show{opacity:var(--bs-tooltip-opacity)}.tooltip .tooltip-arrow{display:block;width:var(--bs-tooltip-arrow-width);height:var(--bs-tooltip-arrow-height)}.tooltip .tooltip-arrow::before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow,.bs-tooltip-top .tooltip-arrow{bottom:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before,.bs-tooltip-top .tooltip-arrow::before{top:-1px;border-width:var(--bs-tooltip-arrow-height)calc(var(--bs-tooltip-arrow-width) * .5)0;border-top-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow,.bs-tooltip-end .tooltip-arrow{left:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow::before,.bs-tooltip-end .tooltip-arrow::before{right:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5)var(--bs-tooltip-arrow-height)calc(var(--bs-tooltip-arrow-width) * .5)0;border-right-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow,.bs-tooltip-bottom .tooltip-arrow{top:calc(-1 * var(--bs-tooltip-arrow-height))}.bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before,.bs-tooltip-bottom .tooltip-arrow::before{bottom:-1px;border-width:0 calc(var(--bs-tooltip-arrow-width) * .5)var(--bs-tooltip-arrow-height);border-bottom-color:var(--bs-tooltip-bg)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow,.bs-tooltip-start .tooltip-arrow{right:calc(-1 * var(--bs-tooltip-arrow-height));width:var(--bs-tooltip-arrow-height);height:var(--bs-tooltip-arrow-width)}.bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow::before,.bs-tooltip-start .tooltip-arrow::before{left:-1px;border-width:calc(var(--bs-tooltip-arrow-width) * .5)0 calc(var(--bs-tooltip-arrow-width) * .5)var(--bs-tooltip-arrow-height);border-left-color:var(--bs-tooltip-bg)}.tooltip-inner{max-width:var(--bs-tooltip-max-width);padding:var(--bs-tooltip-padding-y)var(--bs-tooltip-padding-x);color:var(--bs-tooltip-color);text-align:center;background-color:var(--bs-tooltip-bg);border-radius:var(--bs-tooltip-border-radius)}.popover{--bs-popover-zindex:1070;--bs-popover-max-width:276px;--bs-popover-font-size:0.875rem;--bs-popover-bg:var(--bs-body-bg);--bs-popover-border-width:var(--bs-border-width);--bs-popover-border-color:var(--bs-border-color-translucent);--bs-popover-border-radius:var(--bs-border-radius-lg);--bs-popover-inner-border-radius:calc(var(--bs-border-radius-lg) - var(--bs-border-width));--bs-popover-box-shadow:var(--bs-box-shadow);--bs-popover-header-padding-x:2rem;--bs-popover-header-padding-y:0.5rem;--bs-popover-header-font-size:1rem;--bs-popover-header-color:inherit;--bs-popover-header-bg:transparent;--bs-popover-body-padding-x:2rem;--bs-popover-body-padding-y:1rem;--bs-popover-body-color:#fff;--bs-popover-arrow-width:1rem;--bs-popover-arrow-height:0.5rem;--bs-popover-arrow-border:var(--bs-popover-border-color);z-index:var(--bs-popover-zindex);display:block;max-width:var(--bs-popover-max-width);font-family:var(--bs-font-sans-serif);font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;white-space:normal;word-spacing:normal;line-break:auto;font-size:var(--bs-popover-font-size);word-wrap:break-word;background-color:var(--bs-popover-bg);background-clip:padding-box;border:var(--bs-popover-border-width)solid var(--bs-popover-border-color);border-radius:var(--bs-popover-border-radius)}.popover .popover-arrow{display:block;width:var(--bs-popover-arrow-width);height:var(--bs-popover-arrow-height)}.popover .popover-arrow::after,.popover .popover-arrow::before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid;border-width:0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow,.bs-popover-top>.popover-arrow{bottom:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::after,.bs-popover-top>.popover-arrow::before{border-width:var(--bs-popover-arrow-height)calc(var(--bs-popover-arrow-width) * .5)0}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::before,.bs-popover-top>.popover-arrow::before{bottom:0;border-top-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=top]>.popover-arrow::after,.bs-popover-top>.popover-arrow::after{bottom:var(--bs-popover-border-width);border-top-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow,.bs-popover-end>.popover-arrow{left:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::after,.bs-popover-end>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5)var(--bs-popover-arrow-height)calc(var(--bs-popover-arrow-width) * .5)0}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::before,.bs-popover-end>.popover-arrow::before{left:0;border-right-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=right]>.popover-arrow::after,.bs-popover-end>.popover-arrow::after{left:var(--bs-popover-border-width);border-right-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow,.bs-popover-bottom>.popover-arrow{top:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width))}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::before{border-width:0 calc(var(--bs-popover-arrow-width) * .5)var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::before,.bs-popover-bottom>.popover-arrow::before{top:0;border-bottom-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=bottom]>.popover-arrow::after,.bs-popover-bottom>.popover-arrow::after{top:var(--bs-popover-border-width);border-bottom-color:var(--bs-popover-bg)}.bs-popover-auto[data-popper-placement^=bottom] .popover-header::before,.bs-popover-bottom .popover-header::before{position:absolute;top:0;left:50%;display:block;width:var(--bs-popover-arrow-width);margin-left:calc(-.5 * var(--bs-popover-arrow-width));content:"";border-bottom:var(--bs-popover-border-width)solid var(--bs-popover-header-bg)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow,.bs-popover-start>.popover-arrow{right:calc(-1 * (var(--bs-popover-arrow-height)) - var(--bs-popover-border-width));width:var(--bs-popover-arrow-height);height:var(--bs-popover-arrow-width)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::after,.bs-popover-start>.popover-arrow::before{border-width:calc(var(--bs-popover-arrow-width) * .5)0 calc(var(--bs-popover-arrow-width) * .5)var(--bs-popover-arrow-height)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::before,.bs-popover-start>.popover-arrow::before{right:0;border-left-color:var(--bs-popover-arrow-border)}.bs-popover-auto[data-popper-placement^=left]>.popover-arrow::after,.bs-popover-start>.popover-arrow::after{right:var(--bs-popover-border-width);border-left-color:var(--bs-popover-bg)}.popover-header{padding:var(--bs-popover-header-padding-y)var(--bs-popover-header-padding-x);margin-bottom:0;font-size:var(--bs-popover-header-font-size);color:var(--bs-popover-header-color);background-color:var(--bs-popover-header-bg);border-bottom:var(--bs-popover-border-width)solid var(--bs-popover-border-color);border-top-left-radius:var(--bs-popover-inner-border-radius);border-top-right-radius:var(--bs-popover-inner-border-radius)}.popover-header:empty{display:none}.popover-body{padding:var(--bs-popover-body-padding-y)var(--bs-popover-body-padding-x);color:var(--bs-popover-body-color)}.carousel{position:relative}.carousel.pointer-event{touch-action:pan-y}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner::after{display:block;clear:both;content:""}.carousel-item{position:relative;display:none;float:left;width:100%;margin-right:-100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;transition:transform .6s ease-in-out}@media(prefers-reduced-motion:reduce){.carousel-item{transition:none}}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block}.active.carousel-item-end,.carousel-item-next:not(.carousel-item-start){transform:translateX(100%)}.active.carousel-item-start,.carousel-item-prev:not(.carousel-item-end){transform:translateX(-100%)}.carousel-fade .carousel-item{opacity:0;transition-property:opacity;transform:none}.carousel-fade .carousel-item-next.carousel-item-start,.carousel-fade .carousel-item-prev.carousel-item-end,.carousel-fade .carousel-item.active{z-index:1;opacity:1}.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{z-index:0;opacity:0;transition:opacity 0s .6s}@media(prefers-reduced-motion:reduce){.carousel-fade .active.carousel-item-end,.carousel-fade .active.carousel-item-start{transition:none}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;z-index:1;display:flex;align-items:center;justify-content:center;width:15%;padding:0;color:#fff;text-align:center;background:0 0;filter:var(--bs-carousel-control-icon-filter);border:0;opacity:.5;transition:opacity .15s ease}@media(prefers-reduced-motion:reduce){.carousel-control-next,.carousel-control-prev{transition:none}}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:2rem;height:2rem;background-repeat:no-repeat;background-position:50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M11.354 1.646a.5.5 0 0 1 0 .708L5.707 8l5.647 5.646a.5.5 0 0 1-.708.708l-6-6a.5.5 0 0 1 0-.708l6-6a.5.5 0 0 1 .708 0'/%3e%3c/svg%3e")}.carousel-control-next-icon{background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23fff'%3e%3cpath d='M4.646 1.646a.5.5 0 0 1 .708 0l6 6a.5.5 0 0 1 0 .708l-6 6a.5.5 0 0 1-.708-.708L10.293 8 4.646 2.354a.5.5 0 0 1 0-.708'/%3e%3c/svg%3e")}.carousel-indicators{position:absolute;right:0;bottom:0;left:0;z-index:2;display:flex;justify-content:center;padding:0;margin-right:15%;margin-bottom:1rem;margin-left:15%}.carousel-indicators [data-bs-target]{box-sizing:content-box;flex:initial;width:30px;height:3px;padding:0;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:var(--bs-carousel-indicator-active-bg);background-clip:padding-box;border:0;border-top:10px solid transparent;border-bottom:10px solid transparent;opacity:.5;transition:opacity .6s ease}@media(prefers-reduced-motion:reduce){.carousel-indicators [data-bs-target]{transition:none}}.carousel-indicators .active{opacity:1}.carousel-caption{position:absolute;right:15%;bottom:1.25rem;left:15%;padding-top:1.25rem;padding-bottom:1.25rem;color:var(--bs-carousel-caption-color);text-align:center}.carousel-dark{--bs-carousel-indicator-active-bg:#000;--bs-carousel-caption-color:#000;--bs-carousel-control-icon-filter:invert(1) grayscale(100)}:root,[data-bs-theme=light]{--bs-carousel-indicator-active-bg:#fff;--bs-carousel-caption-color:#fff;--bs-carousel-control-icon-filter: }[data-bs-theme=dark]{--bs-carousel-indicator-active-bg:#000;--bs-carousel-caption-color:#000;--bs-carousel-control-icon-filter:invert(1) grayscale(100)}.spinner-border,.spinner-grow{display:inline-block;flex-shrink:0;width:var(--bs-spinner-width);height:var(--bs-spinner-height);vertical-align:var(--bs-spinner-vertical-align);border-radius:50%;animation:var(--bs-spinner-animation-speed)linear infinite var(--bs-spinner-animation-name)}@keyframes spinner-border{to{transform:rotate(360deg)}}.spinner-border{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-border-width:0.25em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-border;border:var(--bs-spinner-border-width)solid;border-right-color:transparent}.spinner-border-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem;--bs-spinner-border-width:0.2em}@keyframes spinner-grow{0%{transform:scale(0)}50%{opacity:1;transform:none}}.spinner-grow{--bs-spinner-width:2rem;--bs-spinner-height:2rem;--bs-spinner-vertical-align:-0.125em;--bs-spinner-animation-speed:0.75s;--bs-spinner-animation-name:spinner-grow;background-color:currentcolor;opacity:0}.spinner-grow-sm{--bs-spinner-width:1rem;--bs-spinner-height:1rem}@media(prefers-reduced-motion:reduce){.spinner-border,.spinner-grow{--bs-spinner-animation-speed:1.5s}}.offcanvas,.offcanvas-lg,.offcanvas-md,.offcanvas-sm,.offcanvas-xl,.offcanvas-xxl{--bs-offcanvas-zindex:1045;--bs-offcanvas-width:400px;--bs-offcanvas-height:30vh;--bs-offcanvas-padding-x:2rem;--bs-offcanvas-padding-y:2rem;--bs-offcanvas-color:var(--bs-body-color);--bs-offcanvas-bg:var(--bs-body-bg);--bs-offcanvas-border-width:var(--bs-border-width);--bs-offcanvas-border-color:var(--bs-border-color-translucent);--bs-offcanvas-box-shadow:var(--bs-box-shadow-sm);--bs-offcanvas-transition:transform 0.3s ease-in-out;--bs-offcanvas-title-line-height:1.5}@media(max-width:575.98px){.offcanvas-sm{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width:575.98px) and (prefers-reduced-motion:reduce){.offcanvas-sm{transition:none}}@media(max-width:575.98px){.offcanvas-sm.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-sm.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-sm.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-sm.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-sm.show:not(.hiding),.offcanvas-sm.showing{transform:none}.offcanvas-sm.hiding,.offcanvas-sm.show,.offcanvas-sm.showing{visibility:visible}}@media(min-width:576px){.offcanvas-sm{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:initial!important}.offcanvas-sm .offcanvas-header{display:none}.offcanvas-sm .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:initial!important}}@media(max-width:767.98px){.offcanvas-md{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width:767.98px) and (prefers-reduced-motion:reduce){.offcanvas-md{transition:none}}@media(max-width:767.98px){.offcanvas-md.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-md.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-md.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-md.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-md.show:not(.hiding),.offcanvas-md.showing{transform:none}.offcanvas-md.hiding,.offcanvas-md.show,.offcanvas-md.showing{visibility:visible}}@media(min-width:768px){.offcanvas-md{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:initial!important}.offcanvas-md .offcanvas-header{display:none}.offcanvas-md .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:initial!important}}@media(max-width:991.98px){.offcanvas-lg{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width:991.98px) and (prefers-reduced-motion:reduce){.offcanvas-lg{transition:none}}@media(max-width:991.98px){.offcanvas-lg.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-lg.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-lg.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-lg.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-lg.show:not(.hiding),.offcanvas-lg.showing{transform:none}.offcanvas-lg.hiding,.offcanvas-lg.show,.offcanvas-lg.showing{visibility:visible}}@media(min-width:992px){.offcanvas-lg{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:initial!important}.offcanvas-lg .offcanvas-header{display:none}.offcanvas-lg .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:initial!important}}@media(max-width:1199.98px){.offcanvas-xl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width:1199.98px) and (prefers-reduced-motion:reduce){.offcanvas-xl{transition:none}}@media(max-width:1199.98px){.offcanvas-xl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xl.show:not(.hiding),.offcanvas-xl.showing{transform:none}.offcanvas-xl.hiding,.offcanvas-xl.show,.offcanvas-xl.showing{visibility:visible}}@media(min-width:1200px){.offcanvas-xl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:initial!important}.offcanvas-xl .offcanvas-header{display:none}.offcanvas-xl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:initial!important}}@media(max-width:1399.98px){.offcanvas-xxl{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}}@media(max-width:1399.98px) and (prefers-reduced-motion:reduce){.offcanvas-xxl{transition:none}}@media(max-width:1399.98px){.offcanvas-xxl.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas-xxl.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas-xxl.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas-xxl.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas-xxl.show:not(.hiding),.offcanvas-xxl.showing{transform:none}.offcanvas-xxl.hiding,.offcanvas-xxl.show,.offcanvas-xxl.showing{visibility:visible}}@media(min-width:1400px){.offcanvas-xxl{--bs-offcanvas-height:auto;--bs-offcanvas-border-width:0;background-color:initial!important}.offcanvas-xxl .offcanvas-header{display:none}.offcanvas-xxl .offcanvas-body{display:flex;flex-grow:0;padding:0;overflow-y:visible;background-color:initial!important}}.offcanvas{position:fixed;bottom:0;z-index:var(--bs-offcanvas-zindex);display:flex;flex-direction:column;max-width:100%;color:var(--bs-offcanvas-color);visibility:hidden;background-color:var(--bs-offcanvas-bg);background-clip:padding-box;outline:0;transition:var(--bs-offcanvas-transition)}@media(prefers-reduced-motion:reduce){.offcanvas{transition:none}}.offcanvas.offcanvas-start{top:0;left:0;width:var(--bs-offcanvas-width);border-right:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(-100%)}.offcanvas.offcanvas-end{top:0;right:0;width:var(--bs-offcanvas-width);border-left:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateX(100%)}.offcanvas.offcanvas-top{top:0;right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-bottom:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(-100%)}.offcanvas.offcanvas-bottom{right:0;left:0;height:var(--bs-offcanvas-height);max-height:100%;border-top:var(--bs-offcanvas-border-width)solid var(--bs-offcanvas-border-color);transform:translateY(100%)}.offcanvas.show:not(.hiding),.offcanvas.showing{transform:none}.offcanvas.hiding,.offcanvas.show,.offcanvas.showing{visibility:visible}.offcanvas-backdrop{position:fixed;top:0;left:0;z-index:1040;width:100vw;height:100vh;background-color:#000}.offcanvas-backdrop.fade{opacity:0}.offcanvas-backdrop.show{opacity:.5}.offcanvas-header{display:flex;align-items:center;padding:var(--bs-offcanvas-padding-y)var(--bs-offcanvas-padding-x)}.offcanvas-header .btn-close{padding:calc(var(--bs-offcanvas-padding-y) * .5)calc(var(--bs-offcanvas-padding-x) * .5);margin-top:calc(-.5 * var(--bs-offcanvas-padding-y));margin-right:calc(-.5 * var(--bs-offcanvas-padding-x));margin-bottom:calc(-.5 * var(--bs-offcanvas-padding-y));margin-left:auto}.offcanvas-title{margin-bottom:0;line-height:var(--bs-offcanvas-title-line-height)}.offcanvas-body{flex-grow:1;padding:var(--bs-offcanvas-padding-y)var(--bs-offcanvas-padding-x);overflow-y:auto}.placeholder{display:inline-block;min-height:1em;vertical-align:middle;cursor:wait;background-color:currentcolor;opacity:.5}.placeholder.btn::before{display:inline-block;content:""}.placeholder-xs{min-height:.6em}.placeholder-sm{min-height:.8em}.placeholder-lg{min-height:1.2em}.placeholder-glow .placeholder{animation:placeholder-glow 2s ease-in-out infinite}@keyframes placeholder-glow{50%{opacity:.2}}.placeholder-wave{-webkit-mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,.8) 75%,#000 95%);mask-image:linear-gradient(130deg,#000 55%,rgba(0,0,0,.8) 75%,#000 95%);-webkit-mask-size:200% 100%;mask-size:200% 100%;animation:placeholder-wave 2s linear infinite}@keyframes placeholder-wave{100%{-webkit-mask-position:-200% 0%;mask-position:-200% 0%}}.clearfix::after{display:block;clear:both;content:""}.text-bg-primary{color:#fff!important;background-color:RGBA(var(--bs-primary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-secondary{color:#000!important;background-color:RGBA(var(--bs-secondary-rgb),var(--bs-bg-opacity,1))!important}.text-bg-success{color:#fff!important;background-color:RGBA(var(--bs-success-rgb),var(--bs-bg-opacity,1))!important}.text-bg-info{color:#fff!important;background-color:RGBA(var(--bs-info-rgb),var(--bs-bg-opacity,1))!important}.text-bg-warning{color:#fff!important;background-color:RGBA(var(--bs-warning-rgb),var(--bs-bg-opacity,1))!important}.text-bg-danger{color:#fff!important;background-color:RGBA(var(--bs-danger-rgb),var(--bs-bg-opacity,1))!important}.text-bg-light{color:#000!important;background-color:RGBA(var(--bs-light-rgb),var(--bs-bg-opacity,1))!important}.text-bg-dark{color:#fff!important;background-color:RGBA(var(--bs-dark-rgb),var(--bs-bg-opacity,1))!important}.link-primary{color:RGBA(var(--bs-primary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-primary-rgb),var(--bs-link-underline-opacity,1))!important}.link-primary:focus,.link-primary:hover{color:RGBA(186,40,105,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(186,40,105,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(186,40,105,var(--bs-link-underline-opacity,1))!important}.link-secondary{color:RGBA(var(--bs-secondary-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-secondary-rgb),var(--bs-link-underline-opacity,1))!important}.link-secondary:focus,.link-secondary:hover{color:RGBA(255,255,255,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(255,255,255,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(255,255,255,var(--bs-link-underline-opacity,1))!important}.link-success{color:RGBA(var(--bs-success-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-success-rgb),var(--bs-link-underline-opacity,1))!important}.link-success:focus,.link-success:hover{color:RGBA(52,172,134,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(52,172,134,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(52,172,134,var(--bs-link-underline-opacity,1))!important}.link-info{color:RGBA(var(--bs-info-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-info-rgb),var(--bs-link-underline-opacity,1))!important}.link-info:focus,.link-info:hover{color:RGBA(46,162,201,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(46,162,201,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(46,162,201,var(--bs-link-underline-opacity,1))!important}.link-warning{color:RGBA(var(--bs-warning-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-warning-rgb),var(--bs-link-underline-opacity,1))!important}.link-warning:focus,.link-warning:hover{color:RGBA(204,154,6,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(204,154,6,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(204,154,6,var(--bs-link-underline-opacity,1))!important}.link-danger{color:RGBA(var(--bs-danger-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-danger-rgb),var(--bs-link-underline-opacity,1))!important}.link-danger:focus,.link-danger:hover{color:RGBA(202,101,16,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(202,101,16,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(202,101,16,var(--bs-link-underline-opacity,1))!important}.link-light{color:RGBA(var(--bs-light-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-light-rgb),var(--bs-link-underline-opacity,1))!important}.link-light:focus,.link-light:hover{color:RGBA(237,237,237,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(237,237,237,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(237,237,237,var(--bs-link-underline-opacity,1))!important}.link-dark{color:RGBA(var(--bs-dark-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-dark-rgb),var(--bs-link-underline-opacity,1))!important}.link-dark:focus,.link-dark:hover{color:RGBA(26,30,33,var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(26,30,33,var(--bs-link-underline-opacity,1))!important}.link-body-emphasis{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,1))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-body-emphasis:focus,.link-body-emphasis:hover{color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-opacity,.75))!important;-webkit-text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,.75))!important;text-decoration-color:RGBA(var(--bs-emphasis-color-rgb),var(--bs-link-underline-opacity,.75))!important}.focus-ring:focus{outline:0;box-shadow:var(--bs-focus-ring-x,0)var(--bs-focus-ring-y,0)var(--bs-focus-ring-blur,0)var(--bs-focus-ring-width)var(--bs-focus-ring-color)}.icon-link{display:inline-flex;gap:.375rem;align-items:center;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,.5));text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-opacity,.5));text-underline-offset:.25em;-webkit-backface-visibility:hidden;backface-visibility:hidden}.icon-link>.bi{flex-shrink:0;width:1em;height:1em;fill:currentcolor;transition:.2s ease-in-out transform}@media(prefers-reduced-motion:reduce){.icon-link>.bi{transition:none}}.icon-link-hover:focus-visible>.bi,.icon-link-hover:hover>.bi{transform:var(--bs-icon-link-transform,translate3d(.25em,0,0))}.ratio{position:relative;width:100%}.ratio::before{display:block;padding-top:var(--bs-aspect-ratio);content:""}.ratio>*{position:absolute;top:0;left:0;width:100%;height:100%}.ratio-1x1{--bs-aspect-ratio:100%}.ratio-4x3{--bs-aspect-ratio:75%}.ratio-16x9{--bs-aspect-ratio:56.25%}.ratio-21x9{--bs-aspect-ratio:42.8571428571%}.fixed-top{position:fixed;top:0;right:0;left:0;z-index:1030}.fixed-bottom{position:fixed;right:0;bottom:0;left:0;z-index:1030}.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}@media(min-width:576px){.sticky-sm-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-sm-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media(min-width:768px){.sticky-md-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-md-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media(min-width:992px){.sticky-lg-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-lg-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media(min-width:1200px){.sticky-xl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}@media(min-width:1400px){.sticky-xxl-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}.sticky-xxl-bottom{position:-webkit-sticky;position:sticky;bottom:0;z-index:1020}}.hstack{display:flex;flex-direction:row;align-items:center;align-self:stretch}.vstack{display:flex;flex:auto;flex-direction:column;align-self:stretch}.visually-hidden,.visually-hidden-focusable:not(:focus):not(:focus-within){width:1px!important;height:1px!important;padding:0!important;margin:-1px!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption),.visually-hidden:not(caption){position:absolute!important}.visually-hidden *,.visually-hidden-focusable:not(:focus):not(:focus-within) *{overflow:hidden!important}.stretched-link::after{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1;content:""}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.vr{display:inline-block;align-self:stretch;width:var(--bs-border-width);min-height:1em;background-color:currentcolor;opacity:.25}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.float-start{float:left!important}.float-end{float:right!important}.float-none{float:none!important}.object-fit-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-none{-o-object-fit:none!important;object-fit:none!important}.opacity-0{opacity:0!important}.opacity-25{opacity:.25!important}.opacity-50{opacity:.5!important}.opacity-75{opacity:.75!important}.opacity-100{opacity:1!important}.overflow-auto{overflow:auto!important}.overflow-hidden{overflow:hidden!important}.overflow-visible{overflow:visible!important}.overflow-scroll{overflow:scroll!important}.overflow-x-auto{overflow-x:auto!important}.overflow-x-hidden{overflow-x:hidden!important}.overflow-x-visible{overflow-x:visible!important}.overflow-x-scroll{overflow-x:scroll!important}.overflow-y-auto{overflow-y:auto!important}.overflow-y-hidden{overflow-y:hidden!important}.overflow-y-visible{overflow-y:visible!important}.overflow-y-scroll{overflow-y:scroll!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-grid{display:grid!important}.d-inline-grid{display:inline-grid!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:flex!important}.d-inline-flex{display:inline-flex!important}.d-none{display:none!important}.shadow{box-shadow:var(--bs-box-shadow)!important}.shadow-sm{box-shadow:var(--bs-box-shadow-sm)!important}.shadow-lg{box-shadow:var(--bs-box-shadow-lg)!important}.shadow-none{box-shadow:none!important}.focus-ring-primary{--bs-focus-ring-color:rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-secondary{--bs-focus-ring-color:rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity))}.focus-ring-success{--bs-focus-ring-color:rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity))}.focus-ring-info{--bs-focus-ring-color:rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity))}.focus-ring-warning{--bs-focus-ring-color:rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity))}.focus-ring-danger{--bs-focus-ring-color:rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity))}.focus-ring-light{--bs-focus-ring-color:rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity))}.focus-ring-dark{--bs-focus-ring-color:rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity))}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.top-0{top:0!important}.top-50{top:50%!important}.top-100{top:100%!important}.bottom-0{bottom:0!important}.bottom-50{bottom:50%!important}.bottom-100{bottom:100%!important}.start-0{left:0!important}.start-50{left:50%!important}.start-100{left:100%!important}.end-0{right:0!important}.end-50{right:50%!important}.end-100{right:100%!important}.translate-middle{transform:translate(-50%,-50%)!important}.translate-middle-x{transform:translateX(-50%)!important}.translate-middle-y{transform:translateY(-50%)!important}.border{border:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-0{border:0!important}.border-top{border-top:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-top-0{border-top:0!important}.border-end{border-right:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-end-0{border-right:0!important}.border-bottom{border-bottom:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-bottom-0{border-bottom:0!important}.border-start{border-left:var(--bs-border-width)var(--bs-border-style)var(--bs-border-color)!important}.border-start-0{border-left:0!important}.border-primary{--bs-border-opacity:1;border-color:rgba(var(--bs-primary-rgb),var(--bs-border-opacity))!important}.border-secondary{--bs-border-opacity:1;border-color:rgba(var(--bs-secondary-rgb),var(--bs-border-opacity))!important}.border-success{--bs-border-opacity:1;border-color:rgba(var(--bs-success-rgb),var(--bs-border-opacity))!important}.border-info{--bs-border-opacity:1;border-color:rgba(var(--bs-info-rgb),var(--bs-border-opacity))!important}.border-warning{--bs-border-opacity:1;border-color:rgba(var(--bs-warning-rgb),var(--bs-border-opacity))!important}.border-danger{--bs-border-opacity:1;border-color:rgba(var(--bs-danger-rgb),var(--bs-border-opacity))!important}.border-light{--bs-border-opacity:1;border-color:rgba(var(--bs-light-rgb),var(--bs-border-opacity))!important}.border-dark{--bs-border-opacity:1;border-color:rgba(var(--bs-dark-rgb),var(--bs-border-opacity))!important}.border-black{--bs-border-opacity:1;border-color:rgba(var(--bs-black-rgb),var(--bs-border-opacity))!important}.border-white{--bs-border-opacity:1;border-color:rgba(var(--bs-white-rgb),var(--bs-border-opacity))!important}.border-primary-subtle{border-color:var(--bs-primary-border-subtle)!important}.border-secondary-subtle{border-color:var(--bs-secondary-border-subtle)!important}.border-success-subtle{border-color:var(--bs-success-border-subtle)!important}.border-info-subtle{border-color:var(--bs-info-border-subtle)!important}.border-warning-subtle{border-color:var(--bs-warning-border-subtle)!important}.border-danger-subtle{border-color:var(--bs-danger-border-subtle)!important}.border-light-subtle{border-color:var(--bs-light-border-subtle)!important}.border-dark-subtle{border-color:var(--bs-dark-border-subtle)!important}.border-1{border-width:1px!important}.border-2{border-width:2px!important}.border-3{border-width:3px!important}.border-4{border-width:4px!important}.border-5{border-width:5px!important}.border-opacity-10{--bs-border-opacity:0.1}.border-opacity-25{--bs-border-opacity:0.25}.border-opacity-50{--bs-border-opacity:0.5}.border-opacity-75{--bs-border-opacity:0.75}.border-opacity-100{--bs-border-opacity:1}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.mw-100{max-width:100%!important}.vw-100{width:100vw!important}.min-vw-100{min-width:100vw!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mh-100{max-height:100%!important}.vh-100{height:100vh!important}.min-vh-100{min-height:100vh!important}.flex-fill{flex:auto!important}.flex-row{flex-direction:row!important}.flex-column{flex-direction:column!important}.flex-row-reverse{flex-direction:row-reverse!important}.flex-column-reverse{flex-direction:column-reverse!important}.flex-grow-0{flex-grow:0!important}.flex-grow-1{flex-grow:1!important}.flex-shrink-0{flex-shrink:0!important}.flex-shrink-1{flex-shrink:1!important}.flex-wrap{flex-wrap:wrap!important}.flex-nowrap{flex-wrap:nowrap!important}.flex-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-start{justify-content:flex-start!important}.justify-content-end{justify-content:flex-end!important}.justify-content-center{justify-content:center!important}.justify-content-between{justify-content:space-between!important}.justify-content-around{justify-content:space-around!important}.justify-content-evenly{justify-content:space-evenly!important}.align-items-start{align-items:flex-start!important}.align-items-end{align-items:flex-end!important}.align-items-center{align-items:center!important}.align-items-baseline{align-items:baseline!important}.align-items-stretch{align-items:stretch!important}.align-content-start{align-content:flex-start!important}.align-content-end{align-content:flex-end!important}.align-content-center{align-content:center!important}.align-content-between{align-content:space-between!important}.align-content-around{align-content:space-around!important}.align-content-stretch{align-content:stretch!important}.align-self-auto{align-self:auto!important}.align-self-start{align-self:flex-start!important}.align-self-end{align-self:flex-end!important}.align-self-center{align-self:center!important}.align-self-baseline{align-self:baseline!important}.align-self-stretch{align-self:stretch!important}.order-first{order:-1!important}.order-0{order:0!important}.order-1{order:1!important}.order-2{order:2!important}.order-3{order:3!important}.order-4{order:4!important}.order-5{order:5!important}.order-last{order:6!important}.m-0{margin:0!important}.m-1{margin:.5rem!important}.m-2{margin:1rem!important}.m-3{margin:2rem!important}.m-4{margin:3rem!important}.m-5{margin:6rem!important}.m-auto{margin:auto!important}.mx-0{margin-right:0!important;margin-left:0!important}.mx-1{margin-right:.5rem!important;margin-left:.5rem!important}.mx-2{margin-right:1rem!important;margin-left:1rem!important}.mx-3{margin-right:2rem!important;margin-left:2rem!important}.mx-4{margin-right:3rem!important;margin-left:3rem!important}.mx-5{margin-right:6rem!important;margin-left:6rem!important}.mx-auto{margin-right:auto!important;margin-left:auto!important}.my-0{margin-top:0!important;margin-bottom:0!important}.my-1{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-2{margin-top:1rem!important;margin-bottom:1rem!important}.my-3{margin-top:2rem!important;margin-bottom:2rem!important}.my-4{margin-top:3rem!important;margin-bottom:3rem!important}.my-5{margin-top:6rem!important;margin-bottom:6rem!important}.my-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-0{margin-top:0!important}.mt-1{margin-top:.5rem!important}.mt-2{margin-top:1rem!important}.mt-3{margin-top:2rem!important}.mt-4{margin-top:3rem!important}.mt-5{margin-top:6rem!important}.mt-auto{margin-top:auto!important}.me-0{margin-right:0!important}.me-1{margin-right:.5rem!important}.me-2{margin-right:1rem!important}.me-3{margin-right:2rem!important}.me-4{margin-right:3rem!important}.me-5{margin-right:6rem!important}.me-auto{margin-right:auto!important}.mb-0{margin-bottom:0!important}.mb-1{margin-bottom:.5rem!important}.mb-2{margin-bottom:1rem!important}.mb-3{margin-bottom:2rem!important}.mb-4{margin-bottom:3rem!important}.mb-5{margin-bottom:6rem!important}.mb-auto{margin-bottom:auto!important}.ms-0{margin-left:0!important}.ms-1{margin-left:.5rem!important}.ms-2{margin-left:1rem!important}.ms-3{margin-left:2rem!important}.ms-4{margin-left:3rem!important}.ms-5{margin-left:6rem!important}.ms-auto{margin-left:auto!important}.p-0{padding:0!important}.p-1{padding:.5rem!important}.p-2{padding:1rem!important}.p-3{padding:2rem!important}.p-4{padding:3rem!important}.p-5{padding:6rem!important}.px-0{padding-right:0!important;padding-left:0!important}.px-1{padding-right:.5rem!important;padding-left:.5rem!important}.px-2{padding-right:1rem!important;padding-left:1rem!important}.px-3{padding-right:2rem!important;padding-left:2rem!important}.px-4{padding-right:3rem!important;padding-left:3rem!important}.px-5{padding-right:6rem!important;padding-left:6rem!important}.py-0{padding-top:0!important;padding-bottom:0!important}.py-1{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-2{padding-top:1rem!important;padding-bottom:1rem!important}.py-3{padding-top:2rem!important;padding-bottom:2rem!important}.py-4{padding-top:3rem!important;padding-bottom:3rem!important}.py-5{padding-top:6rem!important;padding-bottom:6rem!important}.pt-0{padding-top:0!important}.pt-1{padding-top:.5rem!important}.pt-2{padding-top:1rem!important}.pt-3{padding-top:2rem!important}.pt-4{padding-top:3rem!important}.pt-5{padding-top:6rem!important}.pe-0{padding-right:0!important}.pe-1{padding-right:.5rem!important}.pe-2{padding-right:1rem!important}.pe-3{padding-right:2rem!important}.pe-4{padding-right:3rem!important}.pe-5{padding-right:6rem!important}.pb-0{padding-bottom:0!important}.pb-1{padding-bottom:.5rem!important}.pb-2{padding-bottom:1rem!important}.pb-3{padding-bottom:2rem!important}.pb-4{padding-bottom:3rem!important}.pb-5{padding-bottom:6rem!important}.ps-0{padding-left:0!important}.ps-1{padding-left:.5rem!important}.ps-2{padding-left:1rem!important}.ps-3{padding-left:2rem!important}.ps-4{padding-left:3rem!important}.ps-5{padding-left:6rem!important}.gap-0{gap:0!important}.gap-1{gap:.5rem!important}.gap-2{gap:1rem!important}.gap-3{gap:2rem!important}.gap-4{gap:3rem!important}.gap-5{gap:6rem!important}.row-gap-0{row-gap:0!important}.row-gap-1{row-gap:.5rem!important}.row-gap-2{row-gap:1rem!important}.row-gap-3{row-gap:2rem!important}.row-gap-4{row-gap:3rem!important}.row-gap-5{row-gap:6rem!important}.column-gap-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-1{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-2{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-3{-moz-column-gap:2rem!important;column-gap:2rem!important}.column-gap-4{-moz-column-gap:3rem!important;column-gap:3rem!important}.column-gap-5{-moz-column-gap:6rem!important;column-gap:6rem!important}.font-monospace{font-family:var(--bs-font-monospace)!important}.fs-1{font-size:calc(1.375rem + 1.5vw)!important}.fs-2{font-size:calc(1.325rem + .9vw)!important}.fs-3{font-size:calc(1.3rem + .6vw)!important}.fs-4{font-size:calc(1.275rem + .3vw)!important}.fs-5{font-size:1.25rem!important}.fs-6{font-size:1rem!important}.fst-italic{font-style:italic!important}.fst-normal{font-style:normal!important}.fw-lighter{font-weight:lighter!important}.fw-light{font-weight:300!important}.fw-normal{font-weight:400!important}.fw-medium{font-weight:500!important}.fw-semibold{font-weight:600!important}.fw-bold{font-weight:700!important}.fw-bolder{font-weight:bolder!important}.lh-1{line-height:1!important}.lh-sm{line-height:1.25!important}.lh-base{line-height:1.5!important}.lh-lg{line-height:2!important}.text-start{text-align:left!important}.text-end{text-align:right!important}.text-center{text-align:center!important}.text-decoration-none{text-decoration:none!important}.text-decoration-underline{text-decoration:underline!important}.text-decoration-line-through{text-decoration:line-through!important}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.text-wrap{white-space:normal!important}.text-nowrap{white-space:nowrap!important}.text-break{word-wrap:break-word!important;word-break:break-word!important}.text-primary{--bs-text-opacity:1;color:rgba(var(--bs-primary-rgb),var(--bs-text-opacity))!important}.text-secondary{--bs-text-opacity:1;color:rgba(var(--bs-secondary-rgb),var(--bs-text-opacity))!important}.text-success{--bs-text-opacity:1;color:rgba(var(--bs-success-rgb),var(--bs-text-opacity))!important}.text-info{--bs-text-opacity:1;color:rgba(var(--bs-info-rgb),var(--bs-text-opacity))!important}.text-warning{--bs-text-opacity:1;color:rgba(var(--bs-warning-rgb),var(--bs-text-opacity))!important}.text-danger{--bs-text-opacity:1;color:rgba(var(--bs-danger-rgb),var(--bs-text-opacity))!important}.text-light{--bs-text-opacity:1;color:rgba(var(--bs-light-rgb),var(--bs-text-opacity))!important}.text-dark{--bs-text-opacity:1;color:rgba(var(--bs-dark-rgb),var(--bs-text-opacity))!important}.text-black{--bs-text-opacity:1;color:rgba(var(--bs-black-rgb),var(--bs-text-opacity))!important}.text-white{--bs-text-opacity:1;color:rgba(var(--bs-white-rgb),var(--bs-text-opacity))!important}.text-body{--bs-text-opacity:1;color:rgba(var(--bs-body-color-rgb),var(--bs-text-opacity))!important}.text-muted{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-black-50{--bs-text-opacity:1;color:rgba(0,0,0,.5)!important}.text-white-50{--bs-text-opacity:1;color:rgba(255,255,255,.5)!important}.text-body-secondary{--bs-text-opacity:1;color:var(--bs-secondary-color)!important}.text-body-tertiary{--bs-text-opacity:1;color:var(--bs-tertiary-color)!important}.text-body-emphasis{--bs-text-opacity:1;color:var(--bs-emphasis-color)!important}.text-reset{--bs-text-opacity:1;color:inherit!important}.text-opacity-25{--bs-text-opacity:0.25}.text-opacity-50{--bs-text-opacity:0.5}.text-opacity-75{--bs-text-opacity:0.75}.text-opacity-100{--bs-text-opacity:1}.text-primary-emphasis{color:var(--bs-primary-text-emphasis)!important}.text-secondary-emphasis{color:var(--bs-secondary-text-emphasis)!important}.text-success-emphasis{color:var(--bs-success-text-emphasis)!important}.text-info-emphasis{color:var(--bs-info-text-emphasis)!important}.text-warning-emphasis{color:var(--bs-warning-text-emphasis)!important}.text-danger-emphasis{color:var(--bs-danger-text-emphasis)!important}.text-light-emphasis{color:var(--bs-light-text-emphasis)!important}.text-dark-emphasis{color:var(--bs-dark-text-emphasis)!important}.link-opacity-10{--bs-link-opacity:0.1}.link-opacity-10-hover:hover{--bs-link-opacity:0.1}.link-opacity-25{--bs-link-opacity:0.25}.link-opacity-25-hover:hover{--bs-link-opacity:0.25}.link-opacity-50{--bs-link-opacity:0.5}.link-opacity-50-hover:hover{--bs-link-opacity:0.5}.link-opacity-75{--bs-link-opacity:0.75}.link-opacity-75-hover:hover{--bs-link-opacity:0.75}.link-opacity-100{--bs-link-opacity:1}.link-opacity-100-hover:hover{--bs-link-opacity:1}.link-offset-1{text-underline-offset:.125em!important}.link-offset-1-hover:hover{text-underline-offset:.125em!important}.link-offset-2{text-underline-offset:.25em!important}.link-offset-2-hover:hover{text-underline-offset:.25em!important}.link-offset-3{text-underline-offset:.375em!important}.link-offset-3-hover:hover{text-underline-offset:.375em!important}.link-underline-primary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-primary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-secondary{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-secondary-rgb),var(--bs-link-underline-opacity))!important}.link-underline-success{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-success-rgb),var(--bs-link-underline-opacity))!important}.link-underline-info{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-info-rgb),var(--bs-link-underline-opacity))!important}.link-underline-warning{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-warning-rgb),var(--bs-link-underline-opacity))!important}.link-underline-danger{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-danger-rgb),var(--bs-link-underline-opacity))!important}.link-underline-light{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-light-rgb),var(--bs-link-underline-opacity))!important}.link-underline-dark{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important;text-decoration-color:rgba(var(--bs-dark-rgb),var(--bs-link-underline-opacity))!important}.link-underline{--bs-link-underline-opacity:1;-webkit-text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important;text-decoration-color:rgba(var(--bs-link-color-rgb),var(--bs-link-underline-opacity,1))!important}.link-underline-opacity-0{--bs-link-underline-opacity:0}.link-underline-opacity-0-hover:hover{--bs-link-underline-opacity:0}.link-underline-opacity-10{--bs-link-underline-opacity:0.1}.link-underline-opacity-10-hover:hover{--bs-link-underline-opacity:0.1}.link-underline-opacity-25{--bs-link-underline-opacity:0.25}.link-underline-opacity-25-hover:hover{--bs-link-underline-opacity:0.25}.link-underline-opacity-50{--bs-link-underline-opacity:0.5}.link-underline-opacity-50-hover:hover{--bs-link-underline-opacity:0.5}.link-underline-opacity-75{--bs-link-underline-opacity:0.75}.link-underline-opacity-75-hover:hover{--bs-link-underline-opacity:0.75}.link-underline-opacity-100{--bs-link-underline-opacity:1}.link-underline-opacity-100-hover:hover{--bs-link-underline-opacity:1}.bg-primary{--bs-bg-opacity:1;background-color:rgba(var(--bs-primary-rgb),var(--bs-bg-opacity))!important}.bg-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-rgb),var(--bs-bg-opacity))!important}.bg-success{--bs-bg-opacity:1;background-color:rgba(var(--bs-success-rgb),var(--bs-bg-opacity))!important}.bg-info{--bs-bg-opacity:1;background-color:rgba(var(--bs-info-rgb),var(--bs-bg-opacity))!important}.bg-warning{--bs-bg-opacity:1;background-color:rgba(var(--bs-warning-rgb),var(--bs-bg-opacity))!important}.bg-danger{--bs-bg-opacity:1;background-color:rgba(var(--bs-danger-rgb),var(--bs-bg-opacity))!important}.bg-light{--bs-bg-opacity:1;background-color:rgba(var(--bs-light-rgb),var(--bs-bg-opacity))!important}.bg-dark{--bs-bg-opacity:1;background-color:rgba(var(--bs-dark-rgb),var(--bs-bg-opacity))!important}.bg-black{--bs-bg-opacity:1;background-color:rgba(var(--bs-black-rgb),var(--bs-bg-opacity))!important}.bg-white{--bs-bg-opacity:1;background-color:rgba(var(--bs-white-rgb),var(--bs-bg-opacity))!important}.bg-body{--bs-bg-opacity:1;background-color:rgba(var(--bs-body-bg-rgb),var(--bs-bg-opacity))!important}.bg-transparent{--bs-bg-opacity:1;background-color:initial!important}.bg-body-secondary{--bs-bg-opacity:1;background-color:rgba(var(--bs-secondary-bg-rgb),var(--bs-bg-opacity))!important}.bg-body-tertiary{--bs-bg-opacity:1;background-color:rgba(var(--bs-tertiary-bg-rgb),var(--bs-bg-opacity))!important}.bg-opacity-10{--bs-bg-opacity:0.1}.bg-opacity-25{--bs-bg-opacity:0.25}.bg-opacity-50{--bs-bg-opacity:0.5}.bg-opacity-75{--bs-bg-opacity:0.75}.bg-opacity-100{--bs-bg-opacity:1}.bg-primary-subtle{background-color:var(--bs-primary-bg-subtle)!important}.bg-secondary-subtle{background-color:var(--bs-secondary-bg-subtle)!important}.bg-success-subtle{background-color:var(--bs-success-bg-subtle)!important}.bg-info-subtle{background-color:var(--bs-info-bg-subtle)!important}.bg-warning-subtle{background-color:var(--bs-warning-bg-subtle)!important}.bg-danger-subtle{background-color:var(--bs-danger-bg-subtle)!important}.bg-light-subtle{background-color:var(--bs-light-bg-subtle)!important}.bg-dark-subtle{background-color:var(--bs-dark-bg-subtle)!important}.bg-gradient{background-image:var(--bs-gradient)!important}.user-select-all{-webkit-user-select:all!important;-moz-user-select:all!important;user-select:all!important}.user-select-auto{-webkit-user-select:auto!important;-moz-user-select:auto!important;user-select:auto!important}.user-select-none{-webkit-user-select:none!important;-moz-user-select:none!important;user-select:none!important}.pe-none{pointer-events:none!important}.pe-auto{pointer-events:auto!important}.rounded{border-radius:var(--bs-border-radius)!important}.rounded-0{border-radius:0!important}.rounded-1{border-radius:var(--bs-border-radius-sm)!important}.rounded-2{border-radius:var(--bs-border-radius)!important}.rounded-3{border-radius:var(--bs-border-radius-lg)!important}.rounded-4{border-radius:var(--bs-border-radius-xl)!important}.rounded-5{border-radius:var(--bs-border-radius-xxl)!important}.rounded-circle{border-radius:50%!important}.rounded-pill{border-radius:var(--bs-border-radius-pill)!important}.rounded-top{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-0{border-top-left-radius:0!important;border-top-right-radius:0!important}.rounded-top-1{border-top-left-radius:var(--bs-border-radius-sm)!important;border-top-right-radius:var(--bs-border-radius-sm)!important}.rounded-top-2{border-top-left-radius:var(--bs-border-radius)!important;border-top-right-radius:var(--bs-border-radius)!important}.rounded-top-3{border-top-left-radius:var(--bs-border-radius-lg)!important;border-top-right-radius:var(--bs-border-radius-lg)!important}.rounded-top-4{border-top-left-radius:var(--bs-border-radius-xl)!important;border-top-right-radius:var(--bs-border-radius-xl)!important}.rounded-top-5{border-top-left-radius:var(--bs-border-radius-xxl)!important;border-top-right-radius:var(--bs-border-radius-xxl)!important}.rounded-top-circle{border-top-left-radius:50%!important;border-top-right-radius:50%!important}.rounded-top-pill{border-top-left-radius:var(--bs-border-radius-pill)!important;border-top-right-radius:var(--bs-border-radius-pill)!important}.rounded-end{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-0{border-top-right-radius:0!important;border-bottom-right-radius:0!important}.rounded-end-1{border-top-right-radius:var(--bs-border-radius-sm)!important;border-bottom-right-radius:var(--bs-border-radius-sm)!important}.rounded-end-2{border-top-right-radius:var(--bs-border-radius)!important;border-bottom-right-radius:var(--bs-border-radius)!important}.rounded-end-3{border-top-right-radius:var(--bs-border-radius-lg)!important;border-bottom-right-radius:var(--bs-border-radius-lg)!important}.rounded-end-4{border-top-right-radius:var(--bs-border-radius-xl)!important;border-bottom-right-radius:var(--bs-border-radius-xl)!important}.rounded-end-5{border-top-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-right-radius:var(--bs-border-radius-xxl)!important}.rounded-end-circle{border-top-right-radius:50%!important;border-bottom-right-radius:50%!important}.rounded-end-pill{border-top-right-radius:var(--bs-border-radius-pill)!important;border-bottom-right-radius:var(--bs-border-radius-pill)!important}.rounded-bottom{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-0{border-bottom-right-radius:0!important;border-bottom-left-radius:0!important}.rounded-bottom-1{border-bottom-right-radius:var(--bs-border-radius-sm)!important;border-bottom-left-radius:var(--bs-border-radius-sm)!important}.rounded-bottom-2{border-bottom-right-radius:var(--bs-border-radius)!important;border-bottom-left-radius:var(--bs-border-radius)!important}.rounded-bottom-3{border-bottom-right-radius:var(--bs-border-radius-lg)!important;border-bottom-left-radius:var(--bs-border-radius-lg)!important}.rounded-bottom-4{border-bottom-right-radius:var(--bs-border-radius-xl)!important;border-bottom-left-radius:var(--bs-border-radius-xl)!important}.rounded-bottom-5{border-bottom-right-radius:var(--bs-border-radius-xxl)!important;border-bottom-left-radius:var(--bs-border-radius-xxl)!important}.rounded-bottom-circle{border-bottom-right-radius:50%!important;border-bottom-left-radius:50%!important}.rounded-bottom-pill{border-bottom-right-radius:var(--bs-border-radius-pill)!important;border-bottom-left-radius:var(--bs-border-radius-pill)!important}.rounded-start{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-0{border-bottom-left-radius:0!important;border-top-left-radius:0!important}.rounded-start-1{border-bottom-left-radius:var(--bs-border-radius-sm)!important;border-top-left-radius:var(--bs-border-radius-sm)!important}.rounded-start-2{border-bottom-left-radius:var(--bs-border-radius)!important;border-top-left-radius:var(--bs-border-radius)!important}.rounded-start-3{border-bottom-left-radius:var(--bs-border-radius-lg)!important;border-top-left-radius:var(--bs-border-radius-lg)!important}.rounded-start-4{border-bottom-left-radius:var(--bs-border-radius-xl)!important;border-top-left-radius:var(--bs-border-radius-xl)!important}.rounded-start-5{border-bottom-left-radius:var(--bs-border-radius-xxl)!important;border-top-left-radius:var(--bs-border-radius-xxl)!important}.rounded-start-circle{border-bottom-left-radius:50%!important;border-top-left-radius:50%!important}.rounded-start-pill{border-bottom-left-radius:var(--bs-border-radius-pill)!important;border-top-left-radius:var(--bs-border-radius-pill)!important}.visible{visibility:visible!important}.invisible{visibility:hidden!important}.z-n1{z-index:-1!important}.z-0{z-index:0!important}.z-1{z-index:1!important}.z-2{z-index:2!important}.z-3{z-index:3!important}@media(min-width:576px){.float-sm-start{float:left!important}.float-sm-end{float:right!important}.float-sm-none{float:none!important}.object-fit-sm-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-sm-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-sm-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-sm-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-sm-none{-o-object-fit:none!important;object-fit:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-grid{display:grid!important}.d-sm-inline-grid{display:inline-grid!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:flex!important}.d-sm-inline-flex{display:inline-flex!important}.d-sm-none{display:none!important}.flex-sm-fill{flex:auto!important}.flex-sm-row{flex-direction:row!important}.flex-sm-column{flex-direction:column!important}.flex-sm-row-reverse{flex-direction:row-reverse!important}.flex-sm-column-reverse{flex-direction:column-reverse!important}.flex-sm-grow-0{flex-grow:0!important}.flex-sm-grow-1{flex-grow:1!important}.flex-sm-shrink-0{flex-shrink:0!important}.flex-sm-shrink-1{flex-shrink:1!important}.flex-sm-wrap{flex-wrap:wrap!important}.flex-sm-nowrap{flex-wrap:nowrap!important}.flex-sm-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-sm-start{justify-content:flex-start!important}.justify-content-sm-end{justify-content:flex-end!important}.justify-content-sm-center{justify-content:center!important}.justify-content-sm-between{justify-content:space-between!important}.justify-content-sm-around{justify-content:space-around!important}.justify-content-sm-evenly{justify-content:space-evenly!important}.align-items-sm-start{align-items:flex-start!important}.align-items-sm-end{align-items:flex-end!important}.align-items-sm-center{align-items:center!important}.align-items-sm-baseline{align-items:baseline!important}.align-items-sm-stretch{align-items:stretch!important}.align-content-sm-start{align-content:flex-start!important}.align-content-sm-end{align-content:flex-end!important}.align-content-sm-center{align-content:center!important}.align-content-sm-between{align-content:space-between!important}.align-content-sm-around{align-content:space-around!important}.align-content-sm-stretch{align-content:stretch!important}.align-self-sm-auto{align-self:auto!important}.align-self-sm-start{align-self:flex-start!important}.align-self-sm-end{align-self:flex-end!important}.align-self-sm-center{align-self:center!important}.align-self-sm-baseline{align-self:baseline!important}.align-self-sm-stretch{align-self:stretch!important}.order-sm-first{order:-1!important}.order-sm-0{order:0!important}.order-sm-1{order:1!important}.order-sm-2{order:2!important}.order-sm-3{order:3!important}.order-sm-4{order:4!important}.order-sm-5{order:5!important}.order-sm-last{order:6!important}.m-sm-0{margin:0!important}.m-sm-1{margin:.5rem!important}.m-sm-2{margin:1rem!important}.m-sm-3{margin:2rem!important}.m-sm-4{margin:3rem!important}.m-sm-5{margin:6rem!important}.m-sm-auto{margin:auto!important}.mx-sm-0{margin-right:0!important;margin-left:0!important}.mx-sm-1{margin-right:.5rem!important;margin-left:.5rem!important}.mx-sm-2{margin-right:1rem!important;margin-left:1rem!important}.mx-sm-3{margin-right:2rem!important;margin-left:2rem!important}.mx-sm-4{margin-right:3rem!important;margin-left:3rem!important}.mx-sm-5{margin-right:6rem!important;margin-left:6rem!important}.mx-sm-auto{margin-right:auto!important;margin-left:auto!important}.my-sm-0{margin-top:0!important;margin-bottom:0!important}.my-sm-1{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-sm-2{margin-top:1rem!important;margin-bottom:1rem!important}.my-sm-3{margin-top:2rem!important;margin-bottom:2rem!important}.my-sm-4{margin-top:3rem!important;margin-bottom:3rem!important}.my-sm-5{margin-top:6rem!important;margin-bottom:6rem!important}.my-sm-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-sm-0{margin-top:0!important}.mt-sm-1{margin-top:.5rem!important}.mt-sm-2{margin-top:1rem!important}.mt-sm-3{margin-top:2rem!important}.mt-sm-4{margin-top:3rem!important}.mt-sm-5{margin-top:6rem!important}.mt-sm-auto{margin-top:auto!important}.me-sm-0{margin-right:0!important}.me-sm-1{margin-right:.5rem!important}.me-sm-2{margin-right:1rem!important}.me-sm-3{margin-right:2rem!important}.me-sm-4{margin-right:3rem!important}.me-sm-5{margin-right:6rem!important}.me-sm-auto{margin-right:auto!important}.mb-sm-0{margin-bottom:0!important}.mb-sm-1{margin-bottom:.5rem!important}.mb-sm-2{margin-bottom:1rem!important}.mb-sm-3{margin-bottom:2rem!important}.mb-sm-4{margin-bottom:3rem!important}.mb-sm-5{margin-bottom:6rem!important}.mb-sm-auto{margin-bottom:auto!important}.ms-sm-0{margin-left:0!important}.ms-sm-1{margin-left:.5rem!important}.ms-sm-2{margin-left:1rem!important}.ms-sm-3{margin-left:2rem!important}.ms-sm-4{margin-left:3rem!important}.ms-sm-5{margin-left:6rem!important}.ms-sm-auto{margin-left:auto!important}.p-sm-0{padding:0!important}.p-sm-1{padding:.5rem!important}.p-sm-2{padding:1rem!important}.p-sm-3{padding:2rem!important}.p-sm-4{padding:3rem!important}.p-sm-5{padding:6rem!important}.px-sm-0{padding-right:0!important;padding-left:0!important}.px-sm-1{padding-right:.5rem!important;padding-left:.5rem!important}.px-sm-2{padding-right:1rem!important;padding-left:1rem!important}.px-sm-3{padding-right:2rem!important;padding-left:2rem!important}.px-sm-4{padding-right:3rem!important;padding-left:3rem!important}.px-sm-5{padding-right:6rem!important;padding-left:6rem!important}.py-sm-0{padding-top:0!important;padding-bottom:0!important}.py-sm-1{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-sm-2{padding-top:1rem!important;padding-bottom:1rem!important}.py-sm-3{padding-top:2rem!important;padding-bottom:2rem!important}.py-sm-4{padding-top:3rem!important;padding-bottom:3rem!important}.py-sm-5{padding-top:6rem!important;padding-bottom:6rem!important}.pt-sm-0{padding-top:0!important}.pt-sm-1{padding-top:.5rem!important}.pt-sm-2{padding-top:1rem!important}.pt-sm-3{padding-top:2rem!important}.pt-sm-4{padding-top:3rem!important}.pt-sm-5{padding-top:6rem!important}.pe-sm-0{padding-right:0!important}.pe-sm-1{padding-right:.5rem!important}.pe-sm-2{padding-right:1rem!important}.pe-sm-3{padding-right:2rem!important}.pe-sm-4{padding-right:3rem!important}.pe-sm-5{padding-right:6rem!important}.pb-sm-0{padding-bottom:0!important}.pb-sm-1{padding-bottom:.5rem!important}.pb-sm-2{padding-bottom:1rem!important}.pb-sm-3{padding-bottom:2rem!important}.pb-sm-4{padding-bottom:3rem!important}.pb-sm-5{padding-bottom:6rem!important}.ps-sm-0{padding-left:0!important}.ps-sm-1{padding-left:.5rem!important}.ps-sm-2{padding-left:1rem!important}.ps-sm-3{padding-left:2rem!important}.ps-sm-4{padding-left:3rem!important}.ps-sm-5{padding-left:6rem!important}.gap-sm-0{gap:0!important}.gap-sm-1{gap:.5rem!important}.gap-sm-2{gap:1rem!important}.gap-sm-3{gap:2rem!important}.gap-sm-4{gap:3rem!important}.gap-sm-5{gap:6rem!important}.row-gap-sm-0{row-gap:0!important}.row-gap-sm-1{row-gap:.5rem!important}.row-gap-sm-2{row-gap:1rem!important}.row-gap-sm-3{row-gap:2rem!important}.row-gap-sm-4{row-gap:3rem!important}.row-gap-sm-5{row-gap:6rem!important}.column-gap-sm-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-sm-1{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-sm-2{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-sm-3{-moz-column-gap:2rem!important;column-gap:2rem!important}.column-gap-sm-4{-moz-column-gap:3rem!important;column-gap:3rem!important}.column-gap-sm-5{-moz-column-gap:6rem!important;column-gap:6rem!important}.text-sm-start{text-align:left!important}.text-sm-end{text-align:right!important}.text-sm-center{text-align:center!important}}@media(min-width:768px){.float-md-start{float:left!important}.float-md-end{float:right!important}.float-md-none{float:none!important}.object-fit-md-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-md-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-md-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-md-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-md-none{-o-object-fit:none!important;object-fit:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-grid{display:grid!important}.d-md-inline-grid{display:inline-grid!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:flex!important}.d-md-inline-flex{display:inline-flex!important}.d-md-none{display:none!important}.flex-md-fill{flex:auto!important}.flex-md-row{flex-direction:row!important}.flex-md-column{flex-direction:column!important}.flex-md-row-reverse{flex-direction:row-reverse!important}.flex-md-column-reverse{flex-direction:column-reverse!important}.flex-md-grow-0{flex-grow:0!important}.flex-md-grow-1{flex-grow:1!important}.flex-md-shrink-0{flex-shrink:0!important}.flex-md-shrink-1{flex-shrink:1!important}.flex-md-wrap{flex-wrap:wrap!important}.flex-md-nowrap{flex-wrap:nowrap!important}.flex-md-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-md-start{justify-content:flex-start!important}.justify-content-md-end{justify-content:flex-end!important}.justify-content-md-center{justify-content:center!important}.justify-content-md-between{justify-content:space-between!important}.justify-content-md-around{justify-content:space-around!important}.justify-content-md-evenly{justify-content:space-evenly!important}.align-items-md-start{align-items:flex-start!important}.align-items-md-end{align-items:flex-end!important}.align-items-md-center{align-items:center!important}.align-items-md-baseline{align-items:baseline!important}.align-items-md-stretch{align-items:stretch!important}.align-content-md-start{align-content:flex-start!important}.align-content-md-end{align-content:flex-end!important}.align-content-md-center{align-content:center!important}.align-content-md-between{align-content:space-between!important}.align-content-md-around{align-content:space-around!important}.align-content-md-stretch{align-content:stretch!important}.align-self-md-auto{align-self:auto!important}.align-self-md-start{align-self:flex-start!important}.align-self-md-end{align-self:flex-end!important}.align-self-md-center{align-self:center!important}.align-self-md-baseline{align-self:baseline!important}.align-self-md-stretch{align-self:stretch!important}.order-md-first{order:-1!important}.order-md-0{order:0!important}.order-md-1{order:1!important}.order-md-2{order:2!important}.order-md-3{order:3!important}.order-md-4{order:4!important}.order-md-5{order:5!important}.order-md-last{order:6!important}.m-md-0{margin:0!important}.m-md-1{margin:.5rem!important}.m-md-2{margin:1rem!important}.m-md-3{margin:2rem!important}.m-md-4{margin:3rem!important}.m-md-5{margin:6rem!important}.m-md-auto{margin:auto!important}.mx-md-0{margin-right:0!important;margin-left:0!important}.mx-md-1{margin-right:.5rem!important;margin-left:.5rem!important}.mx-md-2{margin-right:1rem!important;margin-left:1rem!important}.mx-md-3{margin-right:2rem!important;margin-left:2rem!important}.mx-md-4{margin-right:3rem!important;margin-left:3rem!important}.mx-md-5{margin-right:6rem!important;margin-left:6rem!important}.mx-md-auto{margin-right:auto!important;margin-left:auto!important}.my-md-0{margin-top:0!important;margin-bottom:0!important}.my-md-1{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-md-2{margin-top:1rem!important;margin-bottom:1rem!important}.my-md-3{margin-top:2rem!important;margin-bottom:2rem!important}.my-md-4{margin-top:3rem!important;margin-bottom:3rem!important}.my-md-5{margin-top:6rem!important;margin-bottom:6rem!important}.my-md-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-md-0{margin-top:0!important}.mt-md-1{margin-top:.5rem!important}.mt-md-2{margin-top:1rem!important}.mt-md-3{margin-top:2rem!important}.mt-md-4{margin-top:3rem!important}.mt-md-5{margin-top:6rem!important}.mt-md-auto{margin-top:auto!important}.me-md-0{margin-right:0!important}.me-md-1{margin-right:.5rem!important}.me-md-2{margin-right:1rem!important}.me-md-3{margin-right:2rem!important}.me-md-4{margin-right:3rem!important}.me-md-5{margin-right:6rem!important}.me-md-auto{margin-right:auto!important}.mb-md-0{margin-bottom:0!important}.mb-md-1{margin-bottom:.5rem!important}.mb-md-2{margin-bottom:1rem!important}.mb-md-3{margin-bottom:2rem!important}.mb-md-4{margin-bottom:3rem!important}.mb-md-5{margin-bottom:6rem!important}.mb-md-auto{margin-bottom:auto!important}.ms-md-0{margin-left:0!important}.ms-md-1{margin-left:.5rem!important}.ms-md-2{margin-left:1rem!important}.ms-md-3{margin-left:2rem!important}.ms-md-4{margin-left:3rem!important}.ms-md-5{margin-left:6rem!important}.ms-md-auto{margin-left:auto!important}.p-md-0{padding:0!important}.p-md-1{padding:.5rem!important}.p-md-2{padding:1rem!important}.p-md-3{padding:2rem!important}.p-md-4{padding:3rem!important}.p-md-5{padding:6rem!important}.px-md-0{padding-right:0!important;padding-left:0!important}.px-md-1{padding-right:.5rem!important;padding-left:.5rem!important}.px-md-2{padding-right:1rem!important;padding-left:1rem!important}.px-md-3{padding-right:2rem!important;padding-left:2rem!important}.px-md-4{padding-right:3rem!important;padding-left:3rem!important}.px-md-5{padding-right:6rem!important;padding-left:6rem!important}.py-md-0{padding-top:0!important;padding-bottom:0!important}.py-md-1{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-md-2{padding-top:1rem!important;padding-bottom:1rem!important}.py-md-3{padding-top:2rem!important;padding-bottom:2rem!important}.py-md-4{padding-top:3rem!important;padding-bottom:3rem!important}.py-md-5{padding-top:6rem!important;padding-bottom:6rem!important}.pt-md-0{padding-top:0!important}.pt-md-1{padding-top:.5rem!important}.pt-md-2{padding-top:1rem!important}.pt-md-3{padding-top:2rem!important}.pt-md-4{padding-top:3rem!important}.pt-md-5{padding-top:6rem!important}.pe-md-0{padding-right:0!important}.pe-md-1{padding-right:.5rem!important}.pe-md-2{padding-right:1rem!important}.pe-md-3{padding-right:2rem!important}.pe-md-4{padding-right:3rem!important}.pe-md-5{padding-right:6rem!important}.pb-md-0{padding-bottom:0!important}.pb-md-1{padding-bottom:.5rem!important}.pb-md-2{padding-bottom:1rem!important}.pb-md-3{padding-bottom:2rem!important}.pb-md-4{padding-bottom:3rem!important}.pb-md-5{padding-bottom:6rem!important}.ps-md-0{padding-left:0!important}.ps-md-1{padding-left:.5rem!important}.ps-md-2{padding-left:1rem!important}.ps-md-3{padding-left:2rem!important}.ps-md-4{padding-left:3rem!important}.ps-md-5{padding-left:6rem!important}.gap-md-0{gap:0!important}.gap-md-1{gap:.5rem!important}.gap-md-2{gap:1rem!important}.gap-md-3{gap:2rem!important}.gap-md-4{gap:3rem!important}.gap-md-5{gap:6rem!important}.row-gap-md-0{row-gap:0!important}.row-gap-md-1{row-gap:.5rem!important}.row-gap-md-2{row-gap:1rem!important}.row-gap-md-3{row-gap:2rem!important}.row-gap-md-4{row-gap:3rem!important}.row-gap-md-5{row-gap:6rem!important}.column-gap-md-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-md-1{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-md-2{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-md-3{-moz-column-gap:2rem!important;column-gap:2rem!important}.column-gap-md-4{-moz-column-gap:3rem!important;column-gap:3rem!important}.column-gap-md-5{-moz-column-gap:6rem!important;column-gap:6rem!important}.text-md-start{text-align:left!important}.text-md-end{text-align:right!important}.text-md-center{text-align:center!important}}@media(min-width:992px){.float-lg-start{float:left!important}.float-lg-end{float:right!important}.float-lg-none{float:none!important}.object-fit-lg-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-lg-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-lg-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-lg-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-lg-none{-o-object-fit:none!important;object-fit:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-grid{display:grid!important}.d-lg-inline-grid{display:inline-grid!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:flex!important}.d-lg-inline-flex{display:inline-flex!important}.d-lg-none{display:none!important}.flex-lg-fill{flex:auto!important}.flex-lg-row{flex-direction:row!important}.flex-lg-column{flex-direction:column!important}.flex-lg-row-reverse{flex-direction:row-reverse!important}.flex-lg-column-reverse{flex-direction:column-reverse!important}.flex-lg-grow-0{flex-grow:0!important}.flex-lg-grow-1{flex-grow:1!important}.flex-lg-shrink-0{flex-shrink:0!important}.flex-lg-shrink-1{flex-shrink:1!important}.flex-lg-wrap{flex-wrap:wrap!important}.flex-lg-nowrap{flex-wrap:nowrap!important}.flex-lg-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-lg-start{justify-content:flex-start!important}.justify-content-lg-end{justify-content:flex-end!important}.justify-content-lg-center{justify-content:center!important}.justify-content-lg-between{justify-content:space-between!important}.justify-content-lg-around{justify-content:space-around!important}.justify-content-lg-evenly{justify-content:space-evenly!important}.align-items-lg-start{align-items:flex-start!important}.align-items-lg-end{align-items:flex-end!important}.align-items-lg-center{align-items:center!important}.align-items-lg-baseline{align-items:baseline!important}.align-items-lg-stretch{align-items:stretch!important}.align-content-lg-start{align-content:flex-start!important}.align-content-lg-end{align-content:flex-end!important}.align-content-lg-center{align-content:center!important}.align-content-lg-between{align-content:space-between!important}.align-content-lg-around{align-content:space-around!important}.align-content-lg-stretch{align-content:stretch!important}.align-self-lg-auto{align-self:auto!important}.align-self-lg-start{align-self:flex-start!important}.align-self-lg-end{align-self:flex-end!important}.align-self-lg-center{align-self:center!important}.align-self-lg-baseline{align-self:baseline!important}.align-self-lg-stretch{align-self:stretch!important}.order-lg-first{order:-1!important}.order-lg-0{order:0!important}.order-lg-1{order:1!important}.order-lg-2{order:2!important}.order-lg-3{order:3!important}.order-lg-4{order:4!important}.order-lg-5{order:5!important}.order-lg-last{order:6!important}.m-lg-0{margin:0!important}.m-lg-1{margin:.5rem!important}.m-lg-2{margin:1rem!important}.m-lg-3{margin:2rem!important}.m-lg-4{margin:3rem!important}.m-lg-5{margin:6rem!important}.m-lg-auto{margin:auto!important}.mx-lg-0{margin-right:0!important;margin-left:0!important}.mx-lg-1{margin-right:.5rem!important;margin-left:.5rem!important}.mx-lg-2{margin-right:1rem!important;margin-left:1rem!important}.mx-lg-3{margin-right:2rem!important;margin-left:2rem!important}.mx-lg-4{margin-right:3rem!important;margin-left:3rem!important}.mx-lg-5{margin-right:6rem!important;margin-left:6rem!important}.mx-lg-auto{margin-right:auto!important;margin-left:auto!important}.my-lg-0{margin-top:0!important;margin-bottom:0!important}.my-lg-1{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-lg-2{margin-top:1rem!important;margin-bottom:1rem!important}.my-lg-3{margin-top:2rem!important;margin-bottom:2rem!important}.my-lg-4{margin-top:3rem!important;margin-bottom:3rem!important}.my-lg-5{margin-top:6rem!important;margin-bottom:6rem!important}.my-lg-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-lg-0{margin-top:0!important}.mt-lg-1{margin-top:.5rem!important}.mt-lg-2{margin-top:1rem!important}.mt-lg-3{margin-top:2rem!important}.mt-lg-4{margin-top:3rem!important}.mt-lg-5{margin-top:6rem!important}.mt-lg-auto{margin-top:auto!important}.me-lg-0{margin-right:0!important}.me-lg-1{margin-right:.5rem!important}.me-lg-2{margin-right:1rem!important}.me-lg-3{margin-right:2rem!important}.me-lg-4{margin-right:3rem!important}.me-lg-5{margin-right:6rem!important}.me-lg-auto{margin-right:auto!important}.mb-lg-0{margin-bottom:0!important}.mb-lg-1{margin-bottom:.5rem!important}.mb-lg-2{margin-bottom:1rem!important}.mb-lg-3{margin-bottom:2rem!important}.mb-lg-4{margin-bottom:3rem!important}.mb-lg-5{margin-bottom:6rem!important}.mb-lg-auto{margin-bottom:auto!important}.ms-lg-0{margin-left:0!important}.ms-lg-1{margin-left:.5rem!important}.ms-lg-2{margin-left:1rem!important}.ms-lg-3{margin-left:2rem!important}.ms-lg-4{margin-left:3rem!important}.ms-lg-5{margin-left:6rem!important}.ms-lg-auto{margin-left:auto!important}.p-lg-0{padding:0!important}.p-lg-1{padding:.5rem!important}.p-lg-2{padding:1rem!important}.p-lg-3{padding:2rem!important}.p-lg-4{padding:3rem!important}.p-lg-5{padding:6rem!important}.px-lg-0{padding-right:0!important;padding-left:0!important}.px-lg-1{padding-right:.5rem!important;padding-left:.5rem!important}.px-lg-2{padding-right:1rem!important;padding-left:1rem!important}.px-lg-3{padding-right:2rem!important;padding-left:2rem!important}.px-lg-4{padding-right:3rem!important;padding-left:3rem!important}.px-lg-5{padding-right:6rem!important;padding-left:6rem!important}.py-lg-0{padding-top:0!important;padding-bottom:0!important}.py-lg-1{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-lg-2{padding-top:1rem!important;padding-bottom:1rem!important}.py-lg-3{padding-top:2rem!important;padding-bottom:2rem!important}.py-lg-4{padding-top:3rem!important;padding-bottom:3rem!important}.py-lg-5{padding-top:6rem!important;padding-bottom:6rem!important}.pt-lg-0{padding-top:0!important}.pt-lg-1{padding-top:.5rem!important}.pt-lg-2{padding-top:1rem!important}.pt-lg-3{padding-top:2rem!important}.pt-lg-4{padding-top:3rem!important}.pt-lg-5{padding-top:6rem!important}.pe-lg-0{padding-right:0!important}.pe-lg-1{padding-right:.5rem!important}.pe-lg-2{padding-right:1rem!important}.pe-lg-3{padding-right:2rem!important}.pe-lg-4{padding-right:3rem!important}.pe-lg-5{padding-right:6rem!important}.pb-lg-0{padding-bottom:0!important}.pb-lg-1{padding-bottom:.5rem!important}.pb-lg-2{padding-bottom:1rem!important}.pb-lg-3{padding-bottom:2rem!important}.pb-lg-4{padding-bottom:3rem!important}.pb-lg-5{padding-bottom:6rem!important}.ps-lg-0{padding-left:0!important}.ps-lg-1{padding-left:.5rem!important}.ps-lg-2{padding-left:1rem!important}.ps-lg-3{padding-left:2rem!important}.ps-lg-4{padding-left:3rem!important}.ps-lg-5{padding-left:6rem!important}.gap-lg-0{gap:0!important}.gap-lg-1{gap:.5rem!important}.gap-lg-2{gap:1rem!important}.gap-lg-3{gap:2rem!important}.gap-lg-4{gap:3rem!important}.gap-lg-5{gap:6rem!important}.row-gap-lg-0{row-gap:0!important}.row-gap-lg-1{row-gap:.5rem!important}.row-gap-lg-2{row-gap:1rem!important}.row-gap-lg-3{row-gap:2rem!important}.row-gap-lg-4{row-gap:3rem!important}.row-gap-lg-5{row-gap:6rem!important}.column-gap-lg-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-lg-1{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-lg-2{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-lg-3{-moz-column-gap:2rem!important;column-gap:2rem!important}.column-gap-lg-4{-moz-column-gap:3rem!important;column-gap:3rem!important}.column-gap-lg-5{-moz-column-gap:6rem!important;column-gap:6rem!important}.text-lg-start{text-align:left!important}.text-lg-end{text-align:right!important}.text-lg-center{text-align:center!important}}@media(min-width:1200px){.float-xl-start{float:left!important}.float-xl-end{float:right!important}.float-xl-none{float:none!important}.object-fit-xl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xl-none{-o-object-fit:none!important;object-fit:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-grid{display:grid!important}.d-xl-inline-grid{display:inline-grid!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:flex!important}.d-xl-inline-flex{display:inline-flex!important}.d-xl-none{display:none!important}.flex-xl-fill{flex:auto!important}.flex-xl-row{flex-direction:row!important}.flex-xl-column{flex-direction:column!important}.flex-xl-row-reverse{flex-direction:row-reverse!important}.flex-xl-column-reverse{flex-direction:column-reverse!important}.flex-xl-grow-0{flex-grow:0!important}.flex-xl-grow-1{flex-grow:1!important}.flex-xl-shrink-0{flex-shrink:0!important}.flex-xl-shrink-1{flex-shrink:1!important}.flex-xl-wrap{flex-wrap:wrap!important}.flex-xl-nowrap{flex-wrap:nowrap!important}.flex-xl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xl-start{justify-content:flex-start!important}.justify-content-xl-end{justify-content:flex-end!important}.justify-content-xl-center{justify-content:center!important}.justify-content-xl-between{justify-content:space-between!important}.justify-content-xl-around{justify-content:space-around!important}.justify-content-xl-evenly{justify-content:space-evenly!important}.align-items-xl-start{align-items:flex-start!important}.align-items-xl-end{align-items:flex-end!important}.align-items-xl-center{align-items:center!important}.align-items-xl-baseline{align-items:baseline!important}.align-items-xl-stretch{align-items:stretch!important}.align-content-xl-start{align-content:flex-start!important}.align-content-xl-end{align-content:flex-end!important}.align-content-xl-center{align-content:center!important}.align-content-xl-between{align-content:space-between!important}.align-content-xl-around{align-content:space-around!important}.align-content-xl-stretch{align-content:stretch!important}.align-self-xl-auto{align-self:auto!important}.align-self-xl-start{align-self:flex-start!important}.align-self-xl-end{align-self:flex-end!important}.align-self-xl-center{align-self:center!important}.align-self-xl-baseline{align-self:baseline!important}.align-self-xl-stretch{align-self:stretch!important}.order-xl-first{order:-1!important}.order-xl-0{order:0!important}.order-xl-1{order:1!important}.order-xl-2{order:2!important}.order-xl-3{order:3!important}.order-xl-4{order:4!important}.order-xl-5{order:5!important}.order-xl-last{order:6!important}.m-xl-0{margin:0!important}.m-xl-1{margin:.5rem!important}.m-xl-2{margin:1rem!important}.m-xl-3{margin:2rem!important}.m-xl-4{margin:3rem!important}.m-xl-5{margin:6rem!important}.m-xl-auto{margin:auto!important}.mx-xl-0{margin-right:0!important;margin-left:0!important}.mx-xl-1{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xl-2{margin-right:1rem!important;margin-left:1rem!important}.mx-xl-3{margin-right:2rem!important;margin-left:2rem!important}.mx-xl-4{margin-right:3rem!important;margin-left:3rem!important}.mx-xl-5{margin-right:6rem!important;margin-left:6rem!important}.mx-xl-auto{margin-right:auto!important;margin-left:auto!important}.my-xl-0{margin-top:0!important;margin-bottom:0!important}.my-xl-1{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xl-2{margin-top:1rem!important;margin-bottom:1rem!important}.my-xl-3{margin-top:2rem!important;margin-bottom:2rem!important}.my-xl-4{margin-top:3rem!important;margin-bottom:3rem!important}.my-xl-5{margin-top:6rem!important;margin-bottom:6rem!important}.my-xl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xl-0{margin-top:0!important}.mt-xl-1{margin-top:.5rem!important}.mt-xl-2{margin-top:1rem!important}.mt-xl-3{margin-top:2rem!important}.mt-xl-4{margin-top:3rem!important}.mt-xl-5{margin-top:6rem!important}.mt-xl-auto{margin-top:auto!important}.me-xl-0{margin-right:0!important}.me-xl-1{margin-right:.5rem!important}.me-xl-2{margin-right:1rem!important}.me-xl-3{margin-right:2rem!important}.me-xl-4{margin-right:3rem!important}.me-xl-5{margin-right:6rem!important}.me-xl-auto{margin-right:auto!important}.mb-xl-0{margin-bottom:0!important}.mb-xl-1{margin-bottom:.5rem!important}.mb-xl-2{margin-bottom:1rem!important}.mb-xl-3{margin-bottom:2rem!important}.mb-xl-4{margin-bottom:3rem!important}.mb-xl-5{margin-bottom:6rem!important}.mb-xl-auto{margin-bottom:auto!important}.ms-xl-0{margin-left:0!important}.ms-xl-1{margin-left:.5rem!important}.ms-xl-2{margin-left:1rem!important}.ms-xl-3{margin-left:2rem!important}.ms-xl-4{margin-left:3rem!important}.ms-xl-5{margin-left:6rem!important}.ms-xl-auto{margin-left:auto!important}.p-xl-0{padding:0!important}.p-xl-1{padding:.5rem!important}.p-xl-2{padding:1rem!important}.p-xl-3{padding:2rem!important}.p-xl-4{padding:3rem!important}.p-xl-5{padding:6rem!important}.px-xl-0{padding-right:0!important;padding-left:0!important}.px-xl-1{padding-right:.5rem!important;padding-left:.5rem!important}.px-xl-2{padding-right:1rem!important;padding-left:1rem!important}.px-xl-3{padding-right:2rem!important;padding-left:2rem!important}.px-xl-4{padding-right:3rem!important;padding-left:3rem!important}.px-xl-5{padding-right:6rem!important;padding-left:6rem!important}.py-xl-0{padding-top:0!important;padding-bottom:0!important}.py-xl-1{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xl-2{padding-top:1rem!important;padding-bottom:1rem!important}.py-xl-3{padding-top:2rem!important;padding-bottom:2rem!important}.py-xl-4{padding-top:3rem!important;padding-bottom:3rem!important}.py-xl-5{padding-top:6rem!important;padding-bottom:6rem!important}.pt-xl-0{padding-top:0!important}.pt-xl-1{padding-top:.5rem!important}.pt-xl-2{padding-top:1rem!important}.pt-xl-3{padding-top:2rem!important}.pt-xl-4{padding-top:3rem!important}.pt-xl-5{padding-top:6rem!important}.pe-xl-0{padding-right:0!important}.pe-xl-1{padding-right:.5rem!important}.pe-xl-2{padding-right:1rem!important}.pe-xl-3{padding-right:2rem!important}.pe-xl-4{padding-right:3rem!important}.pe-xl-5{padding-right:6rem!important}.pb-xl-0{padding-bottom:0!important}.pb-xl-1{padding-bottom:.5rem!important}.pb-xl-2{padding-bottom:1rem!important}.pb-xl-3{padding-bottom:2rem!important}.pb-xl-4{padding-bottom:3rem!important}.pb-xl-5{padding-bottom:6rem!important}.ps-xl-0{padding-left:0!important}.ps-xl-1{padding-left:.5rem!important}.ps-xl-2{padding-left:1rem!important}.ps-xl-3{padding-left:2rem!important}.ps-xl-4{padding-left:3rem!important}.ps-xl-5{padding-left:6rem!important}.gap-xl-0{gap:0!important}.gap-xl-1{gap:.5rem!important}.gap-xl-2{gap:1rem!important}.gap-xl-3{gap:2rem!important}.gap-xl-4{gap:3rem!important}.gap-xl-5{gap:6rem!important}.row-gap-xl-0{row-gap:0!important}.row-gap-xl-1{row-gap:.5rem!important}.row-gap-xl-2{row-gap:1rem!important}.row-gap-xl-3{row-gap:2rem!important}.row-gap-xl-4{row-gap:3rem!important}.row-gap-xl-5{row-gap:6rem!important}.column-gap-xl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xl-1{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-xl-2{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xl-3{-moz-column-gap:2rem!important;column-gap:2rem!important}.column-gap-xl-4{-moz-column-gap:3rem!important;column-gap:3rem!important}.column-gap-xl-5{-moz-column-gap:6rem!important;column-gap:6rem!important}.text-xl-start{text-align:left!important}.text-xl-end{text-align:right!important}.text-xl-center{text-align:center!important}}@media(min-width:1400px){.float-xxl-start{float:left!important}.float-xxl-end{float:right!important}.float-xxl-none{float:none!important}.object-fit-xxl-contain{-o-object-fit:contain!important;object-fit:contain!important}.object-fit-xxl-cover{-o-object-fit:cover!important;object-fit:cover!important}.object-fit-xxl-fill{-o-object-fit:fill!important;object-fit:fill!important}.object-fit-xxl-scale{-o-object-fit:scale-down!important;object-fit:scale-down!important}.object-fit-xxl-none{-o-object-fit:none!important;object-fit:none!important}.d-xxl-inline{display:inline!important}.d-xxl-inline-block{display:inline-block!important}.d-xxl-block{display:block!important}.d-xxl-grid{display:grid!important}.d-xxl-inline-grid{display:inline-grid!important}.d-xxl-table{display:table!important}.d-xxl-table-row{display:table-row!important}.d-xxl-table-cell{display:table-cell!important}.d-xxl-flex{display:flex!important}.d-xxl-inline-flex{display:inline-flex!important}.d-xxl-none{display:none!important}.flex-xxl-fill{flex:auto!important}.flex-xxl-row{flex-direction:row!important}.flex-xxl-column{flex-direction:column!important}.flex-xxl-row-reverse{flex-direction:row-reverse!important}.flex-xxl-column-reverse{flex-direction:column-reverse!important}.flex-xxl-grow-0{flex-grow:0!important}.flex-xxl-grow-1{flex-grow:1!important}.flex-xxl-shrink-0{flex-shrink:0!important}.flex-xxl-shrink-1{flex-shrink:1!important}.flex-xxl-wrap{flex-wrap:wrap!important}.flex-xxl-nowrap{flex-wrap:nowrap!important}.flex-xxl-wrap-reverse{flex-wrap:wrap-reverse!important}.justify-content-xxl-start{justify-content:flex-start!important}.justify-content-xxl-end{justify-content:flex-end!important}.justify-content-xxl-center{justify-content:center!important}.justify-content-xxl-between{justify-content:space-between!important}.justify-content-xxl-around{justify-content:space-around!important}.justify-content-xxl-evenly{justify-content:space-evenly!important}.align-items-xxl-start{align-items:flex-start!important}.align-items-xxl-end{align-items:flex-end!important}.align-items-xxl-center{align-items:center!important}.align-items-xxl-baseline{align-items:baseline!important}.align-items-xxl-stretch{align-items:stretch!important}.align-content-xxl-start{align-content:flex-start!important}.align-content-xxl-end{align-content:flex-end!important}.align-content-xxl-center{align-content:center!important}.align-content-xxl-between{align-content:space-between!important}.align-content-xxl-around{align-content:space-around!important}.align-content-xxl-stretch{align-content:stretch!important}.align-self-xxl-auto{align-self:auto!important}.align-self-xxl-start{align-self:flex-start!important}.align-self-xxl-end{align-self:flex-end!important}.align-self-xxl-center{align-self:center!important}.align-self-xxl-baseline{align-self:baseline!important}.align-self-xxl-stretch{align-self:stretch!important}.order-xxl-first{order:-1!important}.order-xxl-0{order:0!important}.order-xxl-1{order:1!important}.order-xxl-2{order:2!important}.order-xxl-3{order:3!important}.order-xxl-4{order:4!important}.order-xxl-5{order:5!important}.order-xxl-last{order:6!important}.m-xxl-0{margin:0!important}.m-xxl-1{margin:.5rem!important}.m-xxl-2{margin:1rem!important}.m-xxl-3{margin:2rem!important}.m-xxl-4{margin:3rem!important}.m-xxl-5{margin:6rem!important}.m-xxl-auto{margin:auto!important}.mx-xxl-0{margin-right:0!important;margin-left:0!important}.mx-xxl-1{margin-right:.5rem!important;margin-left:.5rem!important}.mx-xxl-2{margin-right:1rem!important;margin-left:1rem!important}.mx-xxl-3{margin-right:2rem!important;margin-left:2rem!important}.mx-xxl-4{margin-right:3rem!important;margin-left:3rem!important}.mx-xxl-5{margin-right:6rem!important;margin-left:6rem!important}.mx-xxl-auto{margin-right:auto!important;margin-left:auto!important}.my-xxl-0{margin-top:0!important;margin-bottom:0!important}.my-xxl-1{margin-top:.5rem!important;margin-bottom:.5rem!important}.my-xxl-2{margin-top:1rem!important;margin-bottom:1rem!important}.my-xxl-3{margin-top:2rem!important;margin-bottom:2rem!important}.my-xxl-4{margin-top:3rem!important;margin-bottom:3rem!important}.my-xxl-5{margin-top:6rem!important;margin-bottom:6rem!important}.my-xxl-auto{margin-top:auto!important;margin-bottom:auto!important}.mt-xxl-0{margin-top:0!important}.mt-xxl-1{margin-top:.5rem!important}.mt-xxl-2{margin-top:1rem!important}.mt-xxl-3{margin-top:2rem!important}.mt-xxl-4{margin-top:3rem!important}.mt-xxl-5{margin-top:6rem!important}.mt-xxl-auto{margin-top:auto!important}.me-xxl-0{margin-right:0!important}.me-xxl-1{margin-right:.5rem!important}.me-xxl-2{margin-right:1rem!important}.me-xxl-3{margin-right:2rem!important}.me-xxl-4{margin-right:3rem!important}.me-xxl-5{margin-right:6rem!important}.me-xxl-auto{margin-right:auto!important}.mb-xxl-0{margin-bottom:0!important}.mb-xxl-1{margin-bottom:.5rem!important}.mb-xxl-2{margin-bottom:1rem!important}.mb-xxl-3{margin-bottom:2rem!important}.mb-xxl-4{margin-bottom:3rem!important}.mb-xxl-5{margin-bottom:6rem!important}.mb-xxl-auto{margin-bottom:auto!important}.ms-xxl-0{margin-left:0!important}.ms-xxl-1{margin-left:.5rem!important}.ms-xxl-2{margin-left:1rem!important}.ms-xxl-3{margin-left:2rem!important}.ms-xxl-4{margin-left:3rem!important}.ms-xxl-5{margin-left:6rem!important}.ms-xxl-auto{margin-left:auto!important}.p-xxl-0{padding:0!important}.p-xxl-1{padding:.5rem!important}.p-xxl-2{padding:1rem!important}.p-xxl-3{padding:2rem!important}.p-xxl-4{padding:3rem!important}.p-xxl-5{padding:6rem!important}.px-xxl-0{padding-right:0!important;padding-left:0!important}.px-xxl-1{padding-right:.5rem!important;padding-left:.5rem!important}.px-xxl-2{padding-right:1rem!important;padding-left:1rem!important}.px-xxl-3{padding-right:2rem!important;padding-left:2rem!important}.px-xxl-4{padding-right:3rem!important;padding-left:3rem!important}.px-xxl-5{padding-right:6rem!important;padding-left:6rem!important}.py-xxl-0{padding-top:0!important;padding-bottom:0!important}.py-xxl-1{padding-top:.5rem!important;padding-bottom:.5rem!important}.py-xxl-2{padding-top:1rem!important;padding-bottom:1rem!important}.py-xxl-3{padding-top:2rem!important;padding-bottom:2rem!important}.py-xxl-4{padding-top:3rem!important;padding-bottom:3rem!important}.py-xxl-5{padding-top:6rem!important;padding-bottom:6rem!important}.pt-xxl-0{padding-top:0!important}.pt-xxl-1{padding-top:.5rem!important}.pt-xxl-2{padding-top:1rem!important}.pt-xxl-3{padding-top:2rem!important}.pt-xxl-4{padding-top:3rem!important}.pt-xxl-5{padding-top:6rem!important}.pe-xxl-0{padding-right:0!important}.pe-xxl-1{padding-right:.5rem!important}.pe-xxl-2{padding-right:1rem!important}.pe-xxl-3{padding-right:2rem!important}.pe-xxl-4{padding-right:3rem!important}.pe-xxl-5{padding-right:6rem!important}.pb-xxl-0{padding-bottom:0!important}.pb-xxl-1{padding-bottom:.5rem!important}.pb-xxl-2{padding-bottom:1rem!important}.pb-xxl-3{padding-bottom:2rem!important}.pb-xxl-4{padding-bottom:3rem!important}.pb-xxl-5{padding-bottom:6rem!important}.ps-xxl-0{padding-left:0!important}.ps-xxl-1{padding-left:.5rem!important}.ps-xxl-2{padding-left:1rem!important}.ps-xxl-3{padding-left:2rem!important}.ps-xxl-4{padding-left:3rem!important}.ps-xxl-5{padding-left:6rem!important}.gap-xxl-0{gap:0!important}.gap-xxl-1{gap:.5rem!important}.gap-xxl-2{gap:1rem!important}.gap-xxl-3{gap:2rem!important}.gap-xxl-4{gap:3rem!important}.gap-xxl-5{gap:6rem!important}.row-gap-xxl-0{row-gap:0!important}.row-gap-xxl-1{row-gap:.5rem!important}.row-gap-xxl-2{row-gap:1rem!important}.row-gap-xxl-3{row-gap:2rem!important}.row-gap-xxl-4{row-gap:3rem!important}.row-gap-xxl-5{row-gap:6rem!important}.column-gap-xxl-0{-moz-column-gap:0!important;column-gap:0!important}.column-gap-xxl-1{-moz-column-gap:.5rem!important;column-gap:.5rem!important}.column-gap-xxl-2{-moz-column-gap:1rem!important;column-gap:1rem!important}.column-gap-xxl-3{-moz-column-gap:2rem!important;column-gap:2rem!important}.column-gap-xxl-4{-moz-column-gap:3rem!important;column-gap:3rem!important}.column-gap-xxl-5{-moz-column-gap:6rem!important;column-gap:6rem!important}.text-xxl-start{text-align:left!important}.text-xxl-end{text-align:right!important}.text-xxl-center{text-align:center!important}}@media(min-width:1200px){.fs-1{font-size:2.5rem!important}.fs-2{font-size:2rem!important}.fs-3{font-size:1.75rem!important}.fs-4{font-size:1.5rem!important}}@media print{.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-grid{display:grid!important}.d-print-inline-grid{display:inline-grid!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:flex!important}.d-print-inline-flex{display:inline-flex!important}.d-print-none{display:none!important}}body{background-image:linear-gradient(90deg,#21373f,#5e62b0,#700936)}[data-bs-theme=dark] body{color:#fff}.table-secondary{--bs-table-hover-color:$white}.btn-secondary{color:#fff;border:none}.btn-secondary:focus,.btn-secondary:hover{color:#fff}.btn-secondary.disabled{color:#fff}.btn-group>.btn-group:not(:first-child),.btn-group>.btn:not(:first-child){margin-left:0}.btn-group-vertical>.btn-group:not(:first-child),.btn-group-vertical>.btn:not(:first-child){margin-top:0}.bg-light .btn{background-color:rgba(0,0,0,.2)}.form-control::-webkit-file-upload-button{background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);border:none}.form-control::file-selector-button,.input-group-text{background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);border:none}.form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button{background-color:rgba(255,255,255,.1)}.form-control:hover:not(:disabled):not([readonly])::file-selector-button{background-color:rgba(255,255,255,.1)}.input-group-text{box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1)}.input-group>:not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback){margin-left:0;border-left:none}.input-group:not(.has-validation)>.dropdown-toggle:nth-last-child(n+3),.input-group:not(.has-validation)>:not(:last-child):not(.dropdown-toggle):not(.dropdown-menu){border-right:none}.form-select:not([multiple]){transition:border-color .15s ease-in-out}.form-select:not([multiple]) option{color:#000}.bg-light .form-control{color:#343a40;border-color:rgba(0,0,0,.2)}.bg-light .form-control::-moz-placeholder{color:rgba(0,0,0,.2)}.bg-light .form-control::placeholder{color:rgba(0,0,0,.2)}.dropdown-menu{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.dropdown-menu a{color:#fff}.dropdown-menu .text-muted{color:rgba(255,255,255,.7)!important}.nav-tabs .nav-link{transition:none}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{color:#fff;isolation:auto}.nav-tabs .nav-link,.nav-tabs .nav-link.disabled{background-color:rgba(0,0,0,.2)}.nav-tabs .nav-item:first-child>.nav-link{border-radius:.5rem 0 0 .5rem}.nav-tabs .nav-item:last-child>.nav-link{border-radius:0 .5rem .5rem 0}.nav-tabs .nav-item .nav-link.active,.nav-tabs .nav-item.show .nav-link{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,#fff,rgba(255,255,255,.9) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);border-radius:.5rem;transform:scale(1.1)}.nav-tabs .nav-item .nav-link.active a,.nav-tabs .nav-item.show .nav-link a{color:#fff}.nav-tabs .nav-item .nav-link.active .text-muted,.nav-tabs .nav-item.show .nav-link .text-muted{color:rgba(255,255,255,.7)!important}.nav-tabs .nav-item .nav-link.active:focus,.nav-tabs .nav-item .nav-link.active:hover,.nav-tabs .nav-item.show .nav-link:focus,.nav-tabs .nav-item.show .nav-link:hover{color:#343a40}.nav-tabs .dropdown-menu{border-radius:.5rem}.nav-pills .nav-link{transition:none}.nav-pills .nav-link:focus,.nav-pills .nav-link:hover{color:#fff;isolation:auto}.alert{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);position:relative;overflow:hidden;color:#fff}.alert a{color:#fff}.alert .text-muted{color:rgba(255,255,255,.7)!important}.alert-primary::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:#e83283}.alert-secondary::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:rgba(255,255,255,.4)}.alert-success::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:#41d7a7}.alert-info::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:#39cbfb}.alert-warning::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:#ffc107}.alert-danger::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:#fd7e14}.alert-light::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:#e9e9e8}.alert-dark::after{position:absolute;top:0;left:0;width:.5rem;height:100%;content:"";background-color:#212529}.alert .alert-link{color:#fff}.badge.bg-secondary{--bs-bg-opacity:.4}.badge.bg-light{color:#343a40}.list-group{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.list-group a{color:#fff}.list-group .text-muted{color:rgba(255,255,255,.7)!important}.card{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);--bs-secondary-rgb:transparent}.card a{color:#fff}.card .text-muted{color:rgba(255,255,255,.7)!important}.card.bg-primary{background-image:none}.card.bg-success{background-image:none}.card.bg-info{background-image:none}.card.bg-warning{background-image:none}.card.bg-danger{background-image:none}.card.bg-light{background-image:none}.card.bg-dark{background-image:none}.card.bg-light .card-body,.card.bg-light .card-header{color:#343a40}.card.border-primary{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-primary a{color:#fff}.card.border-primary .text-muted{color:rgba(255,255,255,.7)!important}.card.border-primary .card-header{background-color:#e83283!important;border-bottom:none}.card.border-secondary{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-secondary a{color:#fff}.card.border-secondary .text-muted{color:rgba(255,255,255,.7)!important}.card.border-secondary .card-header{background-color:rgba(255,255,255,.4)!important;border-bottom:none}.card.border-success{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-success a{color:#fff}.card.border-success .text-muted{color:rgba(255,255,255,.7)!important}.card.border-success .card-header{background-color:#41d7a7!important;border-bottom:none}.card.border-info{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-info a{color:#fff}.card.border-info .text-muted{color:rgba(255,255,255,.7)!important}.card.border-info .card-header{background-color:#39cbfb!important;border-bottom:none}.card.border-warning{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-warning a{color:#fff}.card.border-warning .text-muted{color:rgba(255,255,255,.7)!important}.card.border-warning .card-header{background-color:#ffc107!important;border-bottom:none}.card.border-danger{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-danger a{color:#fff}.card.border-danger .text-muted{color:rgba(255,255,255,.7)!important}.card.border-danger .card-header{background-color:#fd7e14!important;border-bottom:none}.card.border-light{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-light a{color:#fff}.card.border-light .text-muted{color:rgba(255,255,255,.7)!important}.card.border-light .card-header{background-color:#e9e9e8!important;border-bottom:none;color:#343a40}.card.border-dark{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.card.border-dark a{color:#fff}.card.border-dark .text-muted{color:rgba(255,255,255,.7)!important}.card.border-dark .card-header{background-color:#212529!important;border-bottom:none}.card-header{font-weight:700}.accordion-item{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);box-shadow:none}.accordion-item a{color:#fff}.accordion-item .text-muted{color:rgba(255,255,255,.7)!important}.toast{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.toast a{color:#fff}.toast .text-muted{color:rgba(255,255,255,.7)!important}.popover{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.popover a{color:#fff}.popover .text-muted{color:rgba(255,255,255,.7)!important}.popover-header{border-bottom-color:rgba(255,255,255,.2)}.tooltip{-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.modal-content{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.modal-content a{color:#fff}.modal-content .text-muted{color:rgba(255,255,255,.7)!important}.offcanvas{border:none;box-shadow:inset 1px 1px rgba(255,255,255,.2),inset -1px -1px rgba(255,255,255,.1),1px 3px 24px -1px rgba(0,0,0,.15);background-color:initial;background-image:linear-gradient(125deg,rgba(255,255,255,.3),rgba(255,255,255,.2) 70%);-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}.offcanvas a{color:#fff}.offcanvas .text-muted{color:rgba(255,255,255,.7)!important}/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/src/Yavsc.Server/Directory.Packages.props b/src/Yavsc.Server/Directory.Packages.props new file mode 100644 index 000000000..661f437f0 --- /dev/null +++ b/src/Yavsc.Server/Directory.Packages.props @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/Yavsc.Server/Helpers/EstimateSignatureFileHelper.cs b/src/Yavsc.Server/Helpers/EstimateSignatureFileHelper.cs new file mode 100644 index 000000000..29062bc3c --- /dev/null +++ b/src/Yavsc.Server/Helpers/EstimateSignatureFileHelper.cs @@ -0,0 +1,158 @@ +#nullable enable annotations + +using System.Security.Claims; +using System.Text; +using System.Text.Json; +using Microsoft.AspNetCore.Http; +using Yavsc.Models.Billing; +using Yavsc.Server.Models.FileSystem; +namespace Yavsc.Server.Helpers; + +/// +/// Filesystem counterpart of : writes +/// the wire-format JSON payload next to the user's other files, +/// under signatures/, and updates the user's disk quota. +/// +/// This is the JSON counterpart of the legacy +/// ReceiveProSignatureAsync method, which stored +/// sign-{billingCode}-{signType}-{estimateId}.png blobs. +/// We don't reuse that helper because (a) the wire format is no +/// longer a binary image, (b) there's no billingCode on +/// a freshly signed estimate in our model, and (c) the legacy +/// helper takes an whereas our pipeline +/// decodes a JSON body upstream of the controller and passes +/// in directly. +/// +public static class EstimateSignatureFileHelper +{ + /// + /// Sub-directory under the user's root where signature + /// payloads live. Kept short to leave room in PATH_MAX on + /// legacy filesystems; the rest of the filename is + /// sign-{type}-{estimateId}-{utcTicks}.json. + /// + public const string SignaturesSubdir = "signatures"; + + /// + /// Format string for signature file names. Public so the + /// migration and the admin tools can list by pattern. + /// + public static string FileNameFormat(SignatureType type, long estimateId, long utcTicks) + => $"sign-{type.ToString().ToLowerInvariant()}-{estimateId}-{utcTicks}.json"; + + /// + /// Persist a signature wire payload to disk. Returns the + /// file info (relative path under the user's root) suitable + /// for storing in ; the + /// caller is responsible for the database write. + /// + /// Signed-in user. Their + /// Identity.Name locates the disk root via + /// . + /// + /// Estimate this signature + /// attaches to. Used in the file name for human inspection + /// and to support multiple versions over time. + /// Provider or client signature. + /// Decoded wire payload (strokes + + /// coordinateMax + capturedAtUtc). Already validated + /// upstream. + /// Cancellation token forwarded to + /// the file write. + public static async Task ReceiveEstimateSignatureAsync( + this ClaimsPrincipal user, + long estimateId, + SignatureType type, + SignaturePadPayload payload, + SiteSettings siteSettings, + CancellationToken token = default) + { + if (user is null) throw new ArgumentNullException(nameof(user)); + if (payload is null) throw new ArgumentNullException(nameof(payload)); + if (estimateId <= 0) throw new ArgumentOutOfRangeException(nameof(estimateId)); + + // Ensure the user has a /signatures/ sub-directory we can + // write to. EnsureDestinationDirectory throws on invalid + // paths and creates the directory on the way; the + // SignaturesSubdir constant is a server-controlled value + // (not user-derived), so we skip the IsValidYavscPath + // check that ReceiveUserFile performs on user-supplied + // subpaths. + var root = user.EnsureDestinationDirectory(SignaturesSubdir, siteSettings); + + var fileName = FileNameFormat(type, estimateId, DateTime.UtcNow.Ticks); + var fullPath = Path.Combine(root, fileName); + + var envelope = new + { + format = "yavsc.signature/v1", + coordinateMax = payload.CoordinateMax, + capturedAtUtc = payload.CapturedAtUtc, + estimateId, + type = type.ToString(), + // Identity.Name is the username; we keep the wire + // payload keyed on the username rather than the + // numeric/guid Id so disk-side human inspection + // (e.g. cat sign-pro-1234-...json) is self-evident. + signerName = user.Identity?.Name, + strokes = payload.Strokes, + strokeCount = CountStrokes(payload.Strokes), + }; + + var json = JsonSerializer.Serialize(envelope, new JsonSerializerOptions { WriteIndented = true }); + await File.WriteAllTextAsync(fullPath, json, Encoding.UTF8, token).ConfigureAwait(false); + + // Quota update is the controller's responsibility: the + // helper has no DbContext access, and a ClaimsPrincipal + // is not an ApplicationUser. The controller looks up + // the user by Identity.Name and bumps DiskUsage after + // a successful database write. + + return new FileReceivedInfo(root, fileName); + } + + private static int CountStrokes(int[] strokes) + { + int n = 0; + for (int i = 0; i < strokes.Length;) + { + int k = strokes[i]; + if (k <= 0) break; + n++; + i += 1 + 2 * k; + } + return n; + } +} + +/// +/// Wire payload accepted by the signature endpoint and +/// persisted by . +/// Mirrors PostIt.Models.SignaturePadData's JSON shape +/// (without the disk-only envelope fields) so the two sides +/// stay trivially compatible. +/// +public class SignaturePadPayload +{ + /// + /// Normalised coordinate upper bound. Must be + /// PostIt.Models.SignaturePadData.CoordinateMax + /// (10_000) today; declared as a property so a future + /// resolution change can be replayed against the same + /// wire format. + /// + public int CoordinateMax { get; set; } = 10_000; + + /// + /// Client-reported capture time. The server may ignore + /// this for ordering (UTC now is the truth) but keeps it + /// for round-trip display. + /// + public DateTime CapturedAtUtc { get; set; } + + /// + /// Wire strokes. See + /// PostIt.Models.SignaturePadData for the format. + /// + public int[] Strokes { get; set; } = Array.Empty(); +} diff --git a/src/Yavsc.Server/Helpers/EstimateSignaturePersister.cs b/src/Yavsc.Server/Helpers/EstimateSignaturePersister.cs deleted file mode 100644 index be8848e11..000000000 --- a/src/Yavsc.Server/Helpers/EstimateSignaturePersister.cs +++ /dev/null @@ -1,111 +0,0 @@ -#nullable enable annotations - -using System.Security.Claims; -using Microsoft.EntityFrameworkCore; -using Yavsc.Models; -using Yavsc.Models.Billing; - -namespace Yavsc.Server.Helpers; - -/// -/// Shared signature-persistence logic for estimates, extracted from -/// BillingController.Sign. Stages the -/// row (find-or-add by (EstimateId, Type)) for the PostIt -/// wire-format payload — but does not call -/// SaveChanges. The caller owns the transaction so it can -/// combine the signature write with its own changes (e.g. setting a -/// validation date or a query status) in a single -/// SaveChanges. -/// -/// -/// The column is the source of -/// truth; there is no on-disk copy, so no disk-quota accounting is -/// involved. -/// -/// -public static class EstimateSignaturePersister -{ - /// - /// Stage a signature for signed by - /// as . Upserts - /// the row for the - /// (EstimateId, Type) pair, writing the wire payload - /// (Strokes, CoordinateMax, CapturedAtUtc) - /// and . Returns the staged - /// ; the caller persists with - /// SaveChanges. - /// - public static async Task StageAsync( - ApplicationDbContext db, - long estimateId, - SignatureType type, - string signerId, - SignaturePadPayload payload, - CancellationToken token = default) - { - if (db is null) throw new ArgumentNullException(nameof(db)); - if (payload is null) throw new ArgumentNullException(nameof(payload)); - if (estimateId <= 0) throw new ArgumentOutOfRangeException(nameof(estimateId)); - - // Find-or-add: the (EstimateId, Type) pair is unique, so a - // second submission for the same side of the estimate - // replaces the previous signature. EF translates this into a - // single UPDATE when the row exists and an INSERT otherwise; - // the unique index in ApplicationDbContext is the - // database-level guarantee that the contract holds if two - // requests race. - var signature = await db.Signatures - .FirstOrDefaultAsync(s => s.EstimateId == estimateId && s.Type == type, token) - .ConfigureAwait(false); - - if (signature is null) - { - signature = new Signature - { - EstimateId = estimateId, - SignerId = signerId, - Type = type, - }; - db.Signatures.Add(signature); - } - - signature.SignerId = signerId; - signature.CoordinateMax = payload.CoordinateMax; - signature.Strokes = payload.Strokes; - signature.CapturedAtUtc = payload.CapturedAtUtc; - - return signature; - } -} - -/// -/// Wire payload accepted by the signature path and staged by -/// . Mirrors -/// PostIt.Models.SignaturePadData's JSON shape (without the -/// disk-only envelope fields) so the two sides stay trivially -/// compatible. -/// -public class SignaturePadPayload -{ - /// - /// Normalised coordinate upper bound. Must be - /// PostIt.Models.SignaturePadData.CoordinateMax - /// (10_000) today; declared as a property so a future - /// resolution change can be replayed against the same - /// wire format. - /// - public int CoordinateMax { get; set; } = 10_000; - - /// - /// Client-reported capture time. The server may ignore - /// this for ordering (UTC now is the truth) but keeps it - /// for round-trip display. - /// - public DateTime CapturedAtUtc { get; set; } - - /// - /// Wire strokes. See - /// PostIt.Models.SignaturePadData for the format. - /// - public int[] Strokes { get; set; } = Array.Empty(); -} \ No newline at end of file diff --git a/src/Yavsc.Server/Helpers/FileSystemHelpers.cs b/src/Yavsc.Server/Helpers/FileSystemHelpers.cs index 77e691ea5..bc7b9c81d 100644 --- a/src/Yavsc.Server/Helpers/FileSystemHelpers.cs +++ b/src/Yavsc.Server/Helpers/FileSystemHelpers.cs @@ -34,7 +34,7 @@ namespace Yavsc.Server.Helpers { var item = new FileReceivedInfo( settings.Bills, - AbstractFileSystemHelpers.SignFileNameFormat(signType, billingCode, estimateId)); + AbstractFileSystemHelpers.SignFileNameFormat("pro", billingCode, estimateId)); var fi = new FileInfo(item.FullName); if (fi.Exists) item.Overridden = true; diff --git a/src/Yavsc.Server/Helpers/ServiceExtensions.cs b/src/Yavsc.Server/Helpers/ServiceExtensions.cs index 297084c28..f20d50476 100644 --- a/src/Yavsc.Server/Helpers/ServiceExtensions.cs +++ b/src/Yavsc.Server/Helpers/ServiceExtensions.cs @@ -113,7 +113,6 @@ public static class ServiceExtensions options.ClaimsIssuer = authority; options.Audience = audiences[0]; - // Dev: every Yavsc resource service (Yavsc.Api, Yavsc.Blogs, // Yavsc.Org itself) validates JWTs against the OP that runs // on https://localhost:5001 with a self-signed dev cert. diff --git a/src/Yavsc.Server/Helpers/UserHelpers.cs b/src/Yavsc.Server/Helpers/UserHelpers.cs index 4511308be..f0a30995f 100644 --- a/src/Yavsc.Server/Helpers/UserHelpers.cs +++ b/src/Yavsc.Server/Helpers/UserHelpers.cs @@ -14,9 +14,9 @@ namespace Yavsc.Server.Helpers if (readerId == null) { var userPosts = dbContext.blogSpotPublications.Include( - b => b.Post - ).Where(x => x.Post.AuthorId == posterId) - .Select(x => x.Post).ToArray(); + b => b.BlogPost + ).Where(x => x.BlogPost.AuthorId == posterId) + .Select(x => x.BlogPost).ToArray(); return userPosts; } else diff --git a/src/Yavsc.Server/Models/Billing/Estimate.cs b/src/Yavsc.Server/Models/Billing/Estimate.cs index 8a26faab9..74680e7b7 100644 --- a/src/Yavsc.Server/Models/Billing/Estimate.cs +++ b/src/Yavsc.Server/Models/Billing/Estimate.cs @@ -8,7 +8,6 @@ namespace Yavsc.Models.Billing { using Models.Workflow; using Newtonsoft.Json; - using System.Linq; public partial class Estimate : IEstimate { @@ -79,39 +78,14 @@ namespace Yavsc.Models.Billing public DateTime ClientValidationDate { get; set; } /// - /// All signatures captured against this estimate, keyed by - /// . The persister upserts by - /// (EstimateId, Type), so there is at most one row per type. - /// This collection is the persisted shape and stays - /// [JsonIgnore]: the wire payload exposes signatures - /// only through the dedicated and - /// projections, since an - /// estimate has exactly these two signature slots and never - /// more. See . + /// All signatures captured against this estimate, in + /// capture order. Multiple versions per (Type, SignerId) + /// are allowed; the controller reads the most recent + /// when asked. See . /// [InverseProperty(nameof(Signature.Estimate))] - [JsonIgnore, System.Text.Json.Serialization.JsonIgnore] public virtual ICollection Signatures { get; set; } = new List(); - /// - /// The provider's signature on this estimate, or null when the - /// provider has not signed. Projected from - /// ; not mapped (the table is - /// ). - /// - [NotMapped] - public Signature? SignaturePro - => Signatures?.FirstOrDefault(s => s.Type == SignatureType.Pro); - - /// - /// The client's signature on this estimate, or null when the - /// client has not signed. Projected from - /// ; not mapped. - /// - [NotMapped] - public Signature? SignatureClient - => Signatures?.FirstOrDefault(s => s.Type == SignatureType.Client); - } } diff --git a/src/Yavsc.Server/Models/Billing/Signature.cs b/src/Yavsc.Server/Models/Billing/Signature.cs index 7e568dbd1..10da97f1b 100644 --- a/src/Yavsc.Server/Models/Billing/Signature.cs +++ b/src/Yavsc.Server/Models/Billing/Signature.cs @@ -7,8 +7,8 @@ namespace Yavsc.Models.Billing; /// /// One captured signature, attached to a single /// . Multiple versions are allowed per -/// (EstimateId, Type, SignerId) tuple — the controller reads the -/// most recent when asked. The wire-format payload is the +/// (EstimateId, Type, SignerId) tuple — the controller reads +/// the most recent when asked. The wire-format payload is the /// same int[] shape PostIt produces (see /// PostIt.Models.SignaturePadData): a length-prefixed /// sequence of strokes, each stroke being @@ -16,12 +16,13 @@ namespace Yavsc.Models.Billing; /// CoordinateMax]. /// /// -/// The column is the source of truth: the -/// signature is captured and stored entirely in-app (PostIt) and -/// round-tripped as JSON, with no on-disk copy. A dedicated table -/// (instead of a JSON column on ) keeps the -/// row narrow for list views and preserves -/// superseded versions for audit. +/// Why a separate table (instead of a JSON column on +/// ): the jalon 1 spec calls for at least +/// two distinct signatures per estimate cycle (provider +/// validation + client agreement) and the audit value of +/// preserving superseded versions. A dedicated table also keeps +/// the row narrow, which matters for +/// list views. /// /// public class Signature @@ -31,7 +32,7 @@ public class Signature public long EstimateId { get; set; } - [ForeignKey(nameof(EstimateId)), JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ForeignKey(nameof(EstimateId)), JsonIgnore] public virtual Estimate Estimate { get; set; } /// @@ -48,7 +49,7 @@ public class Signature [Required] public string SignerId { get; set; } - [ForeignKey(nameof(SignerId)), JsonIgnore, System.Text.Json.Serialization.JsonIgnore] + [ForeignKey(nameof(SignerId)), JsonIgnore] public virtual ApplicationUser Signer { get; set; } public SignatureType Type { get; set; } @@ -74,4 +75,16 @@ public class Signature public int[] Strokes { get; set; } = Array.Empty(); public DateTime CapturedAtUtc { get; set; } -} \ No newline at end of file + + /// + /// Path to the JSON-serialised wire payload on disk, + /// relative to UserFilesDirName. The disk copy is the + /// source of truth for the wire bytes; the + /// column is a denormalised index for queries. They are + /// written together in the same transaction by the + /// controller; the migration should keep them in sync + /// through ApplicationDbContext.SaveChanges. + /// + [Required] + public string FilePath { get; set; } +} diff --git a/src/Yavsc.Abstract/Billing/SignatureType.cs b/src/Yavsc.Server/Models/Billing/SignatureType.cs similarity index 99% rename from src/Yavsc.Abstract/Billing/SignatureType.cs rename to src/Yavsc.Server/Models/Billing/SignatureType.cs index 5a27f5cdd..7c27924fd 100644 --- a/src/Yavsc.Abstract/Billing/SignatureType.cs +++ b/src/Yavsc.Server/Models/Billing/SignatureType.cs @@ -12,4 +12,4 @@ public enum SignatureType { Pro = 0, Client = 1, -} \ No newline at end of file +} diff --git a/src/Yavsc.Server/Models/Blog/BlogPost.cs b/src/Yavsc.Server/Models/Blog/BlogPost.cs index e05bf5600..6dc8ff750 100644 --- a/src/Yavsc.Server/Models/Blog/BlogPost.cs +++ b/src/Yavsc.Server/Models/Blog/BlogPost.cs @@ -57,7 +57,7 @@ namespace Yavsc.Models.Blog get; set; } - [Display(Name = "Utilisateur ayant modifié le dernier")] + [Display(Name = "Utilisateur ayant modifé le dernier")] public string? UserModified { get; set; @@ -96,23 +96,14 @@ namespace Yavsc.Models.Blog [InverseProperty("Post")] public virtual List Comments { get; set; } - [InverseProperty("Post")] - public virtual BlogSpotPublication Publication { get; set; } - /// /// Whether this post is published. Not a column: the /// existence of a row in BlogSpotPublication /// is the source of truth. EF skips this property via - /// [NotMapped] so no migration is needed. - /// - /// This is a value BlogSpotService owns and - /// sets explicitly on every post it returns — a single - /// bulk lookup against blogSpotPublications for a - /// list, an AnyAsync for a single post — so the - /// flag is correct whether or not the EF query Included - /// the navigation. Clients - /// (PostIt) read it as-is off the wire and must not - /// recompute it. + /// [NotMapped] so no migration is needed. The + /// service hydrates it after each fetch (single bulk + /// lookup, not N+1) and it surfaces through the wire + /// as part of the JSON-serialised BlogPost. /// [NotMapped] public bool IsPublished { get; set; } @@ -156,7 +147,5 @@ namespace Yavsc.Models.Blog }).ToList() ?? new List(); } } - - } } diff --git a/src/Yavsc.Server/Models/Blog/BlogPostEditViewModel.cs b/src/Yavsc.Server/Models/Blog/BlogPostEditViewModel.cs index 5c3c1e225..152e2e9a6 100644 --- a/src/Yavsc.Server/Models/Blog/BlogPostEditViewModel.cs +++ b/src/Yavsc.Server/Models/Blog/BlogPostEditViewModel.cs @@ -6,7 +6,7 @@ namespace Yavsc.Models.Blog public BlogPostEditViewModel() { } - + public BlogPostEditViewModel(BlogPost post, bool publish) { Id = post.Id; diff --git a/src/Yavsc.Server/Models/Blog/BlogspotPublication.cs b/src/Yavsc.Server/Models/Blog/BlogspotPublication.cs index c724f4821..bcae52218 100644 --- a/src/Yavsc.Server/Models/Blog/BlogspotPublication.cs +++ b/src/Yavsc.Server/Models/Blog/BlogspotPublication.cs @@ -7,9 +7,9 @@ namespace Yavsc.Models public class BlogSpotPublication { [Key] - public long PostId { get; set; } - - [ForeignKey("PostId")] - public virtual BlogPost Post{ get; set; } + public long BlogpostId { get; set; } + + [ForeignKey("BlogpostId")] + public virtual BlogPost BlogPost{ get; set; } } } diff --git a/src/Yavsc.Server/Services/BlogSpotService.cs b/src/Yavsc.Server/Services/BlogSpotService.cs index 5d2ad5eaa..b4bf4c7ad 100644 --- a/src/Yavsc.Server/Services/BlogSpotService.cs +++ b/src/Yavsc.Server/Services/BlogSpotService.cs @@ -61,7 +61,7 @@ public class BlogSpotService blogFilesSubdir); var di = new DirectoryInfo(destDir); - if (!di.Exists) + if (!di.Exists) { logger.LogInformation("Creating directory for blog attachments: {destDir}", destDir); di.Create(); @@ -108,17 +108,10 @@ public class BlogSpotService public BlogPost Create(string userId, BlogPost post ) { // Sauvegarder le post d'abord pour obtenir son ID - // Le créateur vient de l'authentification, donc on ne le prend pas du post + // Le createur vient de l'authentification, donc on ne le prend pas du post post.AuthorId = userId; _context.BlogSpot.Add(post); _context.SaveChanges(userId); - // A freshly created post has no BlogSpotPublication row, - // so the server-side truth is "not published". The client - // may have sent isPublished:true in the POST body, but - // publication is toggled separately via SetPublishAsync; - // overwrite whatever was deserialized so the create - // response reflects the actual state. - post.IsPublished = false; return post; } @@ -133,7 +126,7 @@ public class BlogSpotService if (!auth.Succeeded) throw new AuthorizationFailureException(auth); - var pub = await _context.blogSpotPublications.AnyAsync(x => x.PostId == blog.Id); + var pub = await _context.blogSpotPublications.AnyAsync(x => x.BlogpostId == blog.Id); ScrubAclForViewer(blog, user); return new BlogPostEditViewModel(blog, pub); @@ -146,24 +139,21 @@ public class BlogSpotService .Include(p => p.Tags) .Include(p => p.Comments) .Include(p => p.ACL) - .Include(p => p.Publication) .SingleAsync(m => m.Id == blogPostId); if (blog == null) return null; + // Hydrate le flag [NotMapped] depuis la table de publication. + blog.IsPublished = await _context.blogSpotPublications + .AnyAsync(pub => pub.BlogpostId == blogPostId); + var auth = await _authorizationService.AuthorizeAsync(user, blog, new ReadPermission()); if (!auth.Succeeded) throw new AuthorizationFailureException(auth); ScrubAclForViewer(blog, user); - // IsPublished is a value the service serves to clients; - // set it from the loaded Publication navigation (Details - // Includes it above) rather than leaving it at the - // default false a deserialized entity would have. - blog.IsPublished = blog.Publication != null; - foreach (var c in blog.Comments) c.Author = _context.Users.First(u => u.Id == c.AuthorId); @@ -186,7 +176,7 @@ public class BlogSpotService _context.Update(blog); var publication = await _context.blogSpotPublications - .SingleOrDefaultAsync(p => p.PostId == blogEdit.Id); + .SingleOrDefaultAsync(p => p.BlogpostId == blogEdit.Id); if (publication != null) { @@ -195,7 +185,7 @@ public class BlogSpotService } else if (blogEdit.Publish) { - _context.blogSpotPublications.Add(new BlogSpotPublication { PostId = blogEdit.Id }); + _context.blogSpotPublications.Add(new BlogSpotPublication { BlogpostId = blogEdit.Id }); } _context.SaveChanges(user.GetUserId()); @@ -241,38 +231,43 @@ public class BlogSpotService .Include(p => p.ACL) .Include(p => p.Tags) .Include(p => p.Comments) - .Include(p => p.Publication) - .Where(p => p.Publication != null + .Where(p => p.ACL == null + || p.ACL.Count == 0 || p.AuthorId == viewerId || (userCircles != null && p.ACL.Any(a => userCircles.Contains(a.CircleId)))); } else { posts = _context.blogSpotPublications - .Include(p => p.Post) - .Include(b => b.Post.Author) - .Include(p => p.Post.ACL) - .Include(p => p.Post.Tags) - .Include(p => p.Post.Comments) - .Select(p => p.Post) + .Include(p => p.BlogPost) + .Include(b => b.BlogPost.Author) + .Include(p => p.BlogPost.ACL) + .Include(p => p.BlogPost.Tags) + .Include(p => p.BlogPost.Comments) + .Where(p => p.BlogPost.ACL == null || p.BlogPost.ACL.Count == 0) + .Select(p => p.BlogPost) .ToArray(); } - var materialized = posts.ToList(); + var materialised = posts.ToList(); - // IsPublished is a value the service owns: hydrate it - // from blogSpotPublications in one bulk lookup so it is - // correct whether or not the branch above Included the - // Publication navigation (the anonymous branch does - // not). Without this, a freshly deserialized list would - // report IsPublished == false for every post. - var blogPosts = materialized.OfType().ToList(); - await HydrateIsPublishedAsync(blogPosts); + var postIds = materialised.Select(p => p.Id).ToList(); + if (postIds.Count > 0) + { + var publishedIds = await _context.blogSpotPublications + .Where(pub => postIds.Contains(pub.BlogpostId)) + .Select(pub => pub.BlogpostId) + .ToListAsync(); - foreach (var post in blogPosts) + var publishedSet = publishedIds.ToHashSet(); + foreach (var post in materialised.OfType()) + post.IsPublished = publishedSet.Contains(post.Id); + } + + foreach (var post in materialised.OfType()) ScrubAclForViewer(post, user); - return materialized + return materialised .OrderByDescending(p => p.DateModified) .Skip(skip) .Take(take); @@ -313,17 +308,10 @@ public class BlogSpotService public async Task GetBlogPostAsync(long value) { - var blog = await _context.BlogSpot + return await _context.BlogSpot .Include(b => b.Author) .Include(b => b.ACL) .SingleOrDefaultAsync(x => x.Id == value); - if (blog != null) - // Serve the publication state from its source of truth - // so the delete response's isPublished is correct — the - // query above does not Include the Publication navigation. - blog.IsPublished = await _context.blogSpotPublications - .AnyAsync(p => p.PostId == blog.Id); - return blog; } public async Task SetPublishAsync(ClaimsPrincipal user, long postId, bool publish) @@ -335,11 +323,11 @@ public class BlogSpotService if (!auth.Succeeded) throw new AuthorizationFailureException(auth); - var existing = await _context.blogSpotPublications.SingleOrDefaultAsync(p => p.PostId == postId); + var existing = await _context.blogSpotPublications.SingleOrDefaultAsync(p => p.BlogpostId == postId); if (publish) { if (existing == null) - _context.blogSpotPublications.Add(new BlogSpotPublication { PostId = postId }); + _context.blogSpotPublications.Add(new BlogSpotPublication { BlogpostId = postId }); } else { @@ -357,29 +345,6 @@ public class BlogSpotService post.ACL = new List(); } - /// - /// Populate for each post in - /// from the blogSpotPublications - /// table — a single bulk lookup, not one query per post. The - /// IsPublished flag is a wire-only value the service owns: - /// clients (PostIt) read it as-is off the JSON response rather - /// than computing it themselves. Call this on every collection - /// the service returns to a caller, so the flag is correct - /// regardless of whether the EF query Included the - /// navigation. - /// - private async Task HydrateIsPublishedAsync(IReadOnlyList posts) - { - if (posts.Count == 0) return; - var ids = posts.Select(p => p.Id).ToArray(); - var publishedIds = await _context.blogSpotPublications - .Where(p => ids.Contains(p.PostId)) - .Select(p => p.PostId) - .ToHashSetAsync(); - foreach (var post in posts) - post.IsPublished = publishedIds.Contains(post.Id); - } - private static bool IsOwner(BlogPost post, ClaimsPrincipal? user) { if (user?.Identity?.IsAuthenticated != true) return false; diff --git a/src/Yavsc.Server/Services/MailSender.cs b/src/Yavsc.Server/Services/MailSender.cs index 9a6c60ed9..81518eddb 100644 --- a/src/Yavsc.Server/Services/MailSender.cs +++ b/src/Yavsc.Server/Services/MailSender.cs @@ -140,10 +140,6 @@ namespace Yavsc.Services catch (FormatException ex) { logger.LogError(ex, "Refusing to send email because the recipient or sender address is malformed. To={To}, From={From}", email, siteSettings.Owner.EMail); - logger.LogError(ex.Message); - if (ex.InnerException!=null) - logger.LogError(ex.InnerException.Message); - return string.Empty; } catch (SmtpCommandException ex) diff --git a/src/Yavsc.Server/Services/PermissionHandler.cs b/src/Yavsc.Server/Services/PermissionHandler.cs index 3958f7ff3..77ee9fd57 100644 --- a/src/Yavsc.Server/Services/PermissionHandler.cs +++ b/src/Yavsc.Server/Services/PermissionHandler.cs @@ -59,7 +59,7 @@ public class PermissionHandler : IAuthorizationHandler { return applicationDbContext.blogSpotPublications - .Any(p=>p.PostId == blogPost.Id); + .Any(p=>p.BlogpostId == blogPost.Id); } return false; } diff --git a/src/Yavsc.Server/Yavsc.Server.csproj b/src/Yavsc.Server/Yavsc.Server.csproj index b291847c0..bb2022fc6 100644 --- a/src/Yavsc.Server/Yavsc.Server.csproj +++ b/src/Yavsc.Server/Yavsc.Server.csproj @@ -9,8 +9,8 @@ true 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1 diff --git a/src/Yavsc.Tests.Shared/Directory.Packages.props b/src/Yavsc.Tests.Shared/Directory.Packages.props new file mode 100644 index 000000000..d2342f795 --- /dev/null +++ b/src/Yavsc.Tests.Shared/Directory.Packages.props @@ -0,0 +1,8 @@ + + + + diff --git a/src/Yavsc.Tests.Shared/Yavsc.Tests.Shared.csproj b/src/Yavsc.Tests.Shared/Yavsc.Tests.Shared.csproj index 830377ca2..dd93a8b34 100644 --- a/src/Yavsc.Tests.Shared/Yavsc.Tests.Shared.csproj +++ b/src/Yavsc.Tests.Shared/Yavsc.Tests.Shared.csproj @@ -14,8 +14,8 @@ --> 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1 diff --git a/src/cli/Directory.Packages.props b/src/cli/Directory.Packages.props new file mode 100644 index 000000000..c1a40467d --- /dev/null +++ b/src/cli/Directory.Packages.props @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/src/cli/cli.csproj b/src/cli/cli.csproj index 39ef7660f..382dff670 100644 --- a/src/cli/cli.csproj +++ b/src/cli/cli.csproj @@ -7,8 +7,8 @@ true 1.1.0.0 1.1.0.0 - 1.1.0-estimate.1+343.Branch.feature-estimate.Sha.ba40698cc9562f0f4ad2dcad4dd20e638c05b07d - 1.1.0-estimate.1 + 1.1.0-feat-sign-estimate.1+306.Branch.feat-sign-estimate.Sha.e9a33695b38154859729b61d1280e596f1597acf + 1.1.0-feat-sign-estimate.1