From 771b949618bb4e07c09c2fb94a7f92e13f471b9e Mon Sep 17 00:00:00 2001 From: Tim Date: Thu, 25 Dec 2025 19:59:26 +0100 Subject: Initial commit --- .gitignore | 486 ++ LICENSE | 21 + README.md | 8 + ShareGuard.Web.Client/Pages/Redeem.razor | 53 + ShareGuard.Web.Client/Program.cs | 5 + .../Properties/launchSettings.json | 25 + ShareGuard.Web.Client/ShareGuard.Web.Client.csproj | 69 + ShareGuard.Web.Client/_Imports.razor | 9 + ShareGuard.Web/Api.cs | 40 + ShareGuard.Web/Components/App.razor | 22 + ShareGuard.Web/Components/MainLayout.razor | 5 + ShareGuard.Web/Components/Pages/Admin.razor | 119 + ShareGuard.Web/Components/Routes.razor | 6 + ShareGuard.Web/Components/_Imports.razor | 10 + ShareGuard.Web/Database.cs | 14 + ShareGuard.Web/DbModels/Link.cs | 24 + .../Migrations/20251222192008_Init.Designer.cs | 50 + ShareGuard.Web/Migrations/20251222192008_Init.cs | 37 + ShareGuard.Web/Migrations/DatabaseModelSnapshot.cs | 47 + ShareGuard.Web/Program.cs | 23 + ShareGuard.Web/Properties/launchSettings.json | 23 + ShareGuard.Web/ShareGuard.Web.csproj | 72 + ShareGuard.Web/appsettings.Development.json | 8 + ShareGuard.Web/appsettings.json | 9 + ShareGuard.Web/wwwroot/beer.css | 5724 ++++++++++++++++++++ ShareGuard.Web/wwwroot/beer.js | 1 + ShareGuard.Web/wwwroot/wireguard.js | 184 + ShareGuard.sln | 22 + 28 files changed, 7116 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 ShareGuard.Web.Client/Pages/Redeem.razor create mode 100644 ShareGuard.Web.Client/Program.cs create mode 100644 ShareGuard.Web.Client/Properties/launchSettings.json create mode 100644 ShareGuard.Web.Client/ShareGuard.Web.Client.csproj create mode 100644 ShareGuard.Web.Client/_Imports.razor create mode 100644 ShareGuard.Web/Api.cs create mode 100644 ShareGuard.Web/Components/App.razor create mode 100644 ShareGuard.Web/Components/MainLayout.razor create mode 100644 ShareGuard.Web/Components/Pages/Admin.razor create mode 100644 ShareGuard.Web/Components/Routes.razor create mode 100644 ShareGuard.Web/Components/_Imports.razor create mode 100644 ShareGuard.Web/Database.cs create mode 100644 ShareGuard.Web/DbModels/Link.cs create mode 100644 ShareGuard.Web/Migrations/20251222192008_Init.Designer.cs create mode 100644 ShareGuard.Web/Migrations/20251222192008_Init.cs create mode 100644 ShareGuard.Web/Migrations/DatabaseModelSnapshot.cs create mode 100644 ShareGuard.Web/Program.cs create mode 100644 ShareGuard.Web/Properties/launchSettings.json create mode 100644 ShareGuard.Web/ShareGuard.Web.csproj create mode 100644 ShareGuard.Web/appsettings.Development.json create mode 100644 ShareGuard.Web/appsettings.json create mode 100644 ShareGuard.Web/wwwroot/beer.css create mode 100644 ShareGuard.Web/wwwroot/beer.js create mode 100644 ShareGuard.Web/wwwroot/wireguard.js create mode 100644 ShareGuard.sln diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c5f3a41 --- /dev/null +++ b/.gitignore @@ -0,0 +1,486 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from `dotnet new gitignore` + +# dotenv files +.env + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET +project.lock.json +project.fragment.lock.json +artifacts/ + +# Tye +.tye/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +# but not Directory.Build.rsp, as it configures directory-level build defaults +!Directory.Build.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml +.idea/ + +## +## Visual studio for Mac +## + + +# globs +Makefile.in +*.userprefs +*.usertasks +config.make +config.status +aclocal.m4 +install-sh +autom4te.cache/ +*.tar.gz +tarballs/ +test-results/ + +# content below from: https://github.com/github/gitignore/blob/main/Global/macOS.gitignore +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# content below from: https://github.com/github/gitignore/blob/main/Global/Windows.gitignore +# Windows thumbnail cache files +Thumbs.db +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +# Vim temporary swap files +*.swp + +# Project specific +ShareGuard.Web/shareguard.db +ShareGuard.Web/wg-quick-template.txt diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4e2870f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Tim R. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6a5f4bb --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# ShareGuard + +*Better UX when sharing your (plain) WireGuard VPN. (WIP)* + +## TODOs +- [ ] Admin interface: Protect with OAuth +- [ ] Admin interface: validation of IP Address(es) +- [ ] Admin interface: Show link and qr code after gernerating diff --git a/ShareGuard.Web.Client/Pages/Redeem.razor b/ShareGuard.Web.Client/Pages/Redeem.razor new file mode 100644 index 0000000..d6abc95 --- /dev/null +++ b/ShareGuard.Web.Client/Pages/Redeem.razor @@ -0,0 +1,53 @@ +@page "/redeem/{Token}/" +@using System.Net +@rendermode @(new InteractiveWebAssemblyRenderMode(false)) +@inject IJSRuntime Js +@inject NavigationManager navigation + +@if (_responseCode == null) +{ +

Loading...

+} +else if (_responseCode == HttpStatusCode.Forbidden) +{ + Invalid invitation! +} +else if (_responseCode == HttpStatusCode.NoContent) +{ + No template providen by admin. +} +else if (_responseCode == HttpStatusCode.BadRequest) +{ + An internal error occured. +} +else +{ + @foreach (var configLine in _config!.Split("\n")) + { +

@configLine

+ } +} + +@code { + private readonly HttpClient _httpClient = new(); + [Parameter] public string Token { get; set; } + private HttpStatusCode? _responseCode; + private string? _config; + + protected override async Task OnInitializedAsync() + { + var keypair = await Js.InvokeAsync>("wireguard.generateKeypair"); + var requestBody = new Dictionary + { + { "PublicKey", keypair["publicKey"] } + }; + var response = await _httpClient.PostAsync($"{navigation.BaseUri}api/redeem/{Token}/", new FormUrlEncodedContent(requestBody)); + _responseCode = response.StatusCode; + var template = await response.Content.ReadAsStringAsync(); + + if (_responseCode == HttpStatusCode.OK) + _config = template.Replace("{publicKey}", keypair["publicKey"]) + .Replace("{privateKey}", keypair["privateKey"]); + } + +} diff --git a/ShareGuard.Web.Client/Program.cs b/ShareGuard.Web.Client/Program.cs new file mode 100644 index 0000000..91db88a --- /dev/null +++ b/ShareGuard.Web.Client/Program.cs @@ -0,0 +1,5 @@ +using Microsoft.AspNetCore.Components.WebAssembly.Hosting; + +var builder = WebAssemblyHostBuilder.CreateDefault(args); + +await builder.Build().RunAsync(); \ No newline at end of file diff --git a/ShareGuard.Web.Client/Properties/launchSettings.json b/ShareGuard.Web.Client/Properties/launchSettings.json new file mode 100644 index 0000000..421d5de --- /dev/null +++ b/ShareGuard.Web.Client/Properties/launchSettings.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "http://localhost:5225", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}", + "applicationUrl": "https://localhost:7291;http://localhost:5225", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/ShareGuard.Web.Client/ShareGuard.Web.Client.csproj b/ShareGuard.Web.Client/ShareGuard.Web.Client.csproj new file mode 100644 index 0000000..b0dfa37 --- /dev/null +++ b/ShareGuard.Web.Client/ShareGuard.Web.Client.csproj @@ -0,0 +1,69 @@ + + + + net10.0 + enable + enable + true + + + + + + + + + <_ContentIncludedByDefault Remove="Layout\MainLayout.razor"/> + <_ContentIncludedByDefault Remove="Layout\NavMenu.razor"/> + <_ContentIncludedByDefault Remove="wwwroot\css\app.css" /> + <_ContentIncludedByDefault Remove="wwwroot\favicon.png" /> + <_ContentIncludedByDefault Remove="wwwroot\icon-192.png" /> + <_ContentIncludedByDefault Remove="wwwroot\index.html" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.min.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.min.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.rtl.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.rtl.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.rtl.min.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.rtl.min.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.min.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.min.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.rtl.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.rtl.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.rtl.min.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.rtl.min.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.min.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.min.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.rtl.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.rtl.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.rtl.min.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.rtl.min.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.min.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.min.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.rtl.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.rtl.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.rtl.min.css" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.rtl.min.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.js" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.js.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.min.js" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.min.js.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.esm.js" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.esm.js.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.esm.min.js" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.esm.min.js.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.js" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.js.map" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.min.js" /> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.min.js.map" /> + <_ContentIncludedByDefault Remove="wwwroot\sample-data\weather.json" /> + + + diff --git a/ShareGuard.Web.Client/_Imports.razor b/ShareGuard.Web.Client/_Imports.razor new file mode 100644 index 0000000..8233d90 --- /dev/null +++ b/ShareGuard.Web.Client/_Imports.razor @@ -0,0 +1,9 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.AspNetCore.Components.WebAssembly.Http +@using Microsoft.JSInterop +@using ShareGuard.Web.Client \ No newline at end of file diff --git a/ShareGuard.Web/Api.cs b/ShareGuard.Web/Api.cs new file mode 100644 index 0000000..990abc9 --- /dev/null +++ b/ShareGuard.Web/Api.cs @@ -0,0 +1,40 @@ +namespace ShareGuard.Web; + +public static class Api +{ + private const string TemplateFilePath = "./wg-quick-template.txt"; + + public static async Task Redeem(HttpContext context) + { + await using var db = new Database(); + var link = db.Links.FirstOrDefault(link => link.Token == context.Request.RouteValues["token"]!.ToString() + && link.PeerPublicKey == null); + if (link == null) + { + context.Response.StatusCode = StatusCodes.Status403Forbidden; + await context.Response.WriteAsync("Invalid token."); + return; + } + + var form = await context.Request.ReadFormAsync(); + var peerPublicKey = form["PublicKey"]; + if (peerPublicKey.Count < 1) + { + context.Response.StatusCode = StatusCodes.Status400BadRequest; + await context.Response.WriteAsync("Bad request."); + return; + } + + link.PeerPublicKey = peerPublicKey[0]; + + if (!File.Exists(TemplateFilePath)) + { + context.Response.StatusCode = StatusCodes.Status204NoContent; + await context.Response.WriteAsync("Template is not set yet."); + return; + } + + db.SaveChanges(); + await context.Response.WriteAsync(File.ReadAllText(TemplateFilePath).Replace("{ip}", link.IpAddress)); + } +} \ No newline at end of file diff --git a/ShareGuard.Web/Components/App.razor b/ShareGuard.Web/Components/App.razor new file mode 100644 index 0000000..0a3e255 --- /dev/null +++ b/ShareGuard.Web/Components/App.razor @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ShareGuard.Web/Components/MainLayout.razor b/ShareGuard.Web/Components/MainLayout.razor new file mode 100644 index 0000000..f904d53 --- /dev/null +++ b/ShareGuard.Web/Components/MainLayout.razor @@ -0,0 +1,5 @@ +@inherits LayoutComponentBase + +
+ @Body +
diff --git a/ShareGuard.Web/Components/Pages/Admin.razor b/ShareGuard.Web/Components/Pages/Admin.razor new file mode 100644 index 0000000..3884630 --- /dev/null +++ b/ShareGuard.Web/Components/Pages/Admin.razor @@ -0,0 +1,119 @@ +@page "/admin/" +@using ShareGuard.Web.DbModels +@rendermode InteractiveServer + +ShareGuard Admin + +

ShareGuard Admin

+ +
Generate Share-Link
+
+ + +
+
+ + +
+ + +
WG-Quick Template
+

You can use the following placeholders: +
{ip} for the peer's ip address(es) +
{publicKey} for the peer's public key +
{privateKey} for the peer's private key

+
+ + This template will be generated for your users. +
+ + +@if (_links != null) +{ +
History
+ + + + + + + + + + + + @foreach (var link in _links) + { + + + + @if (link.PeerPublicKey == null) + { + + } + else + { + + } + + + + } + +
NameTokenClient Public KeyIP Address(es)Actions
@link.Name@link.TokenPending@link.PeerPublicKey@link.IpAddress + +
+} + +@code +{ + private const string TemplateFilePath = "./wg-quick-template.txt"; + private string? _template; + + private void SaveTemplate() + { + if (_template != null) + { + File.WriteAllText(TemplateFilePath, _template); + } + } + + private List? _links; + + protected override void OnInitialized() + { + if (File.Exists(TemplateFilePath)) _template = File.ReadAllText(TemplateFilePath); + using var db = new Database(); + _links = db.Links.ToList(); + } + + private string _newName = ""; + private string _newIpAddress = ""; + + private void GenerateNewLink() + { + using var db = new Database(); + db.Links.Add(new Link + { + Name = _newName, + IpAddress = _newIpAddress, + Token = Link.GenerateToken(db) + }); + db.SaveChanges(); + OnInitialized(); + } + + private void DeleteLink(Link link) + { + using var db = new Database(); + db.Remove(link); + db.SaveChanges(); + OnInitialized(); + } +} diff --git a/ShareGuard.Web/Components/Routes.razor b/ShareGuard.Web/Components/Routes.razor new file mode 100644 index 0000000..1f3983c --- /dev/null +++ b/ShareGuard.Web/Components/Routes.razor @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/ShareGuard.Web/Components/_Imports.razor b/ShareGuard.Web/Components/_Imports.razor new file mode 100644 index 0000000..33e59de --- /dev/null +++ b/ShareGuard.Web/Components/_Imports.razor @@ -0,0 +1,10 @@ +@using System.Net.Http +@using System.Net.Http.Json +@using Microsoft.AspNetCore.Components.Forms +@using Microsoft.AspNetCore.Components.Routing +@using Microsoft.AspNetCore.Components.Web +@using static Microsoft.AspNetCore.Components.Web.RenderMode +@using Microsoft.AspNetCore.Components.Web.Virtualization +@using Microsoft.JSInterop +@using ShareGuard.Web +@using ShareGuard.Web.Components \ No newline at end of file diff --git a/ShareGuard.Web/Database.cs b/ShareGuard.Web/Database.cs new file mode 100644 index 0000000..ceeed60 --- /dev/null +++ b/ShareGuard.Web/Database.cs @@ -0,0 +1,14 @@ +using Microsoft.EntityFrameworkCore; +using ShareGuard.Web.DbModels; + +namespace ShareGuard.Web; + +public class Database : DbContext +{ + public DbSet Links { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + optionsBuilder.UseSqlite("Data Source=shareguard.db"); + } +} \ No newline at end of file diff --git a/ShareGuard.Web/DbModels/Link.cs b/ShareGuard.Web/DbModels/Link.cs new file mode 100644 index 0000000..0cd66ac --- /dev/null +++ b/ShareGuard.Web/DbModels/Link.cs @@ -0,0 +1,24 @@ +namespace ShareGuard.Web.DbModels; + +public class Link +{ + private static readonly string _tokenChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; + private static readonly Random _random = new(); + public int Id { get; set; } + public string Token { get; set; } + public string Name { get; set; } + public string IpAddress { get; set; } + public string? PeerPublicKey { get; set; } + + public static string GenerateToken(Database database) + { + var token = ""; + while (token == "" || database.Links.Any(link => link.Token == token)) + { + token = ""; + for (var i = 0; i < 10; i++) token += _tokenChars[_random.Next(_tokenChars.Length)]; + } + + return token; + } +} \ No newline at end of file diff --git a/ShareGuard.Web/Migrations/20251222192008_Init.Designer.cs b/ShareGuard.Web/Migrations/20251222192008_Init.Designer.cs new file mode 100644 index 0000000..a0151ac --- /dev/null +++ b/ShareGuard.Web/Migrations/20251222192008_Init.Designer.cs @@ -0,0 +1,50 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using ShareGuard.Web; + +#nullable disable + +namespace ShareGuard.Web.Migrations +{ + [DbContext(typeof(Database))] + [Migration("20251222192008_Init")] + partial class Init + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.1"); + + modelBuilder.Entity("ShareGuard.Web.DbModels.Link", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IpAddress") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PeerPublicKey") + .HasColumnType("TEXT"); + + b.Property("Token") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Links"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ShareGuard.Web/Migrations/20251222192008_Init.cs b/ShareGuard.Web/Migrations/20251222192008_Init.cs new file mode 100644 index 0000000..d301a33 --- /dev/null +++ b/ShareGuard.Web/Migrations/20251222192008_Init.cs @@ -0,0 +1,37 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace ShareGuard.Web.Migrations +{ + /// + public partial class Init : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Links", + columns: table => new + { + Id = table.Column(type: "INTEGER", nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Token = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + IpAddress = table.Column(type: "TEXT", nullable: false), + PeerPublicKey = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Links", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Links"); + } + } +} diff --git a/ShareGuard.Web/Migrations/DatabaseModelSnapshot.cs b/ShareGuard.Web/Migrations/DatabaseModelSnapshot.cs new file mode 100644 index 0000000..6f3c5df --- /dev/null +++ b/ShareGuard.Web/Migrations/DatabaseModelSnapshot.cs @@ -0,0 +1,47 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using ShareGuard.Web; + +#nullable disable + +namespace ShareGuard.Web.Migrations +{ + [DbContext(typeof(Database))] + partial class DatabaseModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "10.0.1"); + + modelBuilder.Entity("ShareGuard.Web.DbModels.Link", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("IpAddress") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("PeerPublicKey") + .HasColumnType("TEXT"); + + b.Property("Token") + .IsRequired() + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("Links"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/ShareGuard.Web/Program.cs b/ShareGuard.Web/Program.cs new file mode 100644 index 0000000..250e4d4 --- /dev/null +++ b/ShareGuard.Web/Program.cs @@ -0,0 +1,23 @@ +using ShareGuard.Web; +using ShareGuard.Web.Components; +using _Imports = ShareGuard.Web.Client._Imports; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddRazorComponents() + .AddInteractiveServerComponents() + .AddInteractiveWebAssemblyComponents(); + +var app = builder.Build(); + +app.UseAntiforgery(); + +app.MapStaticAssets(); +app.MapRazorComponents() + .AddInteractiveServerRenderMode() + .AddInteractiveWebAssemblyRenderMode() + .AddAdditionalAssemblies(typeof(_Imports).Assembly); + +app.MapPost("/api/redeem/{token}/", Api.Redeem); + +app.Run(); \ No newline at end of file diff --git a/ShareGuard.Web/Properties/launchSettings.json b/ShareGuard.Web/Properties/launchSettings.json new file mode 100644 index 0000000..f7695ff --- /dev/null +++ b/ShareGuard.Web/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5187", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7256;http://localhost:5187", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/ShareGuard.Web/ShareGuard.Web.csproj b/ShareGuard.Web/ShareGuard.Web.csproj new file mode 100644 index 0000000..e53d716 --- /dev/null +++ b/ShareGuard.Web/ShareGuard.Web.csproj @@ -0,0 +1,72 @@ + + + + net10.0 + enable + enable + true + + + + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.min.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.min.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.rtl.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.rtl.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.rtl.min.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-grid.rtl.min.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.min.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.min.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.rtl.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.rtl.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.rtl.min.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-reboot.rtl.min.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.min.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.min.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.rtl.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.rtl.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.rtl.min.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap-utilities.rtl.min.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.min.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.min.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.rtl.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.rtl.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.rtl.min.css"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\css\bootstrap.rtl.min.css.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.js.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.min.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.bundle.min.js.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.esm.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.esm.js.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.esm.min.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.esm.min.js.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.js.map"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.min.js"/> + <_ContentIncludedByDefault Remove="wwwroot\lib\bootstrap\dist\js\bootstrap.min.js.map"/> + <_ContentIncludedByDefault Remove="Components\Layout\NavMenu.razor"/> + <_ContentIncludedByDefault Remove="Components\Layout\ReconnectModal.razor"/> + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + diff --git a/ShareGuard.Web/appsettings.Development.json b/ShareGuard.Web/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/ShareGuard.Web/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/ShareGuard.Web/appsettings.json b/ShareGuard.Web/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/ShareGuard.Web/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/ShareGuard.Web/wwwroot/beer.css b/ShareGuard.Web/wwwroot/beer.css new file mode 100644 index 0000000..05826f7 --- /dev/null +++ b/ShareGuard.Web/wwwroot/beer.css @@ -0,0 +1,5724 @@ +:root { + --size: 1rem; + --font: Inter, Roboto, "Helvetica Neue", "Arial Nova", "Nimbus Sans", Noto Sans, Arial, sans-serif; + --font-icon: "Material Symbols Outlined"; + --speed1: .1s; + --speed2: .2s; + --speed3: .3s; + --speed4: .4s; + --active: rgb(128 128 128 / .192); + --overlay: rgb(0 0 0 / .5); + --elevate1: 0 .125rem .125rem 0 rgb(0 0 0 / .32); + --elevate2: 0 .25rem .5rem 0 rgb(0 0 0 / .4); + --elevate3: 0 .375rem .75rem 0 rgb(0 0 0 / .48); + --top: env(safe-area-inset-top); + --bottom: env(safe-area-inset-bottom); + --left: env(safe-area-inset-left); + --right: env(safe-area-inset-right) +} + +:root, body.light { + --primary: #6750a4; + --on-primary: #ffffff; + --primary-container: #e9ddff; + --on-primary-container: #22005d; + --secondary: #625b71; + --on-secondary: #ffffff; + --secondary-container: #e8def8; + --on-secondary-container: #1e192b; + --tertiary: #7e5260; + --on-tertiary: #ffffff; + --tertiary-container: #ffd9e3; + --on-tertiary-container: #31101d; + --error: #ba1a1a; + --on-error: #ffffff; + --error-container: #ffdad6; + --on-error-container: #410002; + --background: #fffbff; + --on-background: #1c1b1e; + --surface: #fdf8fd; + --on-surface: #1c1b1e; + --surface-variant: #e7e0eb; + --on-surface-variant: #49454e; + --outline: #7a757f; + --outline-variant: #cac4cf; + --shadow: #000000; + --scrim: #000000; + --inverse-surface: #313033; + --inverse-on-surface: #f4eff4; + --inverse-primary: #cfbcff; + --surface-dim: #ddd8dd; + --surface-bright: #fdf8fd; + --surface-container-lowest: #ffffff; + --surface-container-low: #f7f2f7; + --surface-container: #f2ecf1; + --surface-container-high: #ece7eb; + --surface-container-highest: #e6e1e6 +} + +body.dark { + --primary: #cfbcff; + --on-primary: #381e72; + --primary-container: #4f378a; + --on-primary-container: #e9ddff; + --secondary: #cbc2db; + --on-secondary: #332d41; + --secondary-container: #4a4458; + --on-secondary-container: #e8def8; + --tertiary: #efb8c8; + --on-tertiary: #4a2532; + --tertiary-container: #633b48; + --on-tertiary-container: #ffd9e3; + --error: #ffb4ab; + --on-error: #690005; + --error-container: #93000a; + --on-error-container: #ffb4ab; + --background: #1c1b1e; + --on-background: #e6e1e6; + --surface: #141316; + --on-surface: #e6e1e6; + --surface-variant: #49454e; + --on-surface-variant: #cac4cf; + --outline: #948f99; + --outline-variant: #49454e; + --shadow: #000000; + --scrim: #000000; + --inverse-surface: #e6e1e6; + --inverse-on-surface: #313033; + --inverse-primary: #6750a4; + --surface-dim: #141316; + --surface-bright: #3a383c; + --surface-container-lowest: #0f0e11; + --surface-container-low: #1c1b1e; + --surface-container: #201f22; + --surface-container-high: #2b292d; + --surface-container-highest: #363438 +} + +@font-face { + font-family: Material Symbols Outlined; + font-style: normal; + font-weight: 400; + font-display: block; + src: url(material-symbols-outlined.woff2) format("woff2"), url(https://cdn.jsdelivr.net/npm/beercss@3.13.1/dist/cdn/material-symbols-outlined.woff2) format("woff2") +} + +@font-face { + font-family: Material Symbols Rounded; + font-style: normal; + font-weight: 400; + font-display: block; + src: url(material-symbols-rounded.woff2) format("woff2"), url(https://cdn.jsdelivr.net/npm/beercss@3.13.1/dist/cdn/material-symbols-rounded.woff2) format("woff2") +} + +@font-face { + font-family: Material Symbols Sharp; + font-style: normal; + font-weight: 400; + font-display: block; + src: url(material-symbols-sharp.woff2) format("woff2"), url(https://cdn.jsdelivr.net/npm/beercss@3.13.1/dist/cdn/material-symbols-sharp.woff2) format("woff2") +} + +@font-face { + font-family: Material Symbols Subset; + font-style: normal; + font-weight: 400; + font-display: block; + src: url(material-symbols-subset.woff2) format("woff2"), url(https://cdn.jsdelivr.net/npm/beercss@3.13.1/dist/cdn/material-symbols-subset.woff2) format("woff2") +} + +* { + -webkit-tap-highlight-color: transparent; + position: relative; + vertical-align: middle; + color: inherit; + margin: 0; + padding: 0; + border-radius: inherit; + box-sizing: border-box +} + +*:after, *:before { + all: unset +} + +body { + color: var(--on-surface); + background-color: var(--surface); + overflow-x: hidden +} + +label { + font-size: .75rem; + vertical-align: baseline +} + +a, b, i, span, strong, em, code { + vertical-align: baseline +} + +a, button, .button { + cursor: pointer; + text-decoration: none; + display: inline-flex; + align-items: center; + border: none; + font-family: inherit; + outline: inherit; + justify-content: center +} + +a, button, .button, i, label { + -webkit-user-select: none; + user-select: none +} + +@media (pointer: fine) { + body ::-webkit-scrollbar, body ::-webkit-scrollbar-thumb, body ::-webkit-scrollbar-button { + background: none; + inline-size: .4rem; + block-size: .4rem + } + + body :is(:focus,:hover)::-webkit-scrollbar-thumb, body :has(:focus,:hover)::-webkit-scrollbar-thumb { + background: var(--outline); + border-radius: 1rem + } +} + +:not(.grid,nav,.row) > :not(progress.max) + :is(address,article,blockquote,code,.field,fieldset,form,.grid,h1,h2,h3,h4,h5,h6,nav,ol,p,pre,.row,section,aside,table,.tabs,ul):not([class*=margin],.right,.left,.top,.bottom) { + margin-block-start: 1rem +} + +:has(>[class*=margin]) { + padding: .1px +} + +:is(a,button,.button,.chip):focus-visible { + outline: .125rem solid var(--primary); + outline-offset: .25rem +} + +:is(nav,.row,li).group > :focus-visible { + z-index: 1 +} + +:is(button,.button,.chip) > :is(span,i,img,svg) { + pointer-events: none +} + +.transparent { + background-color: transparent !important; + box-shadow: none !important; + color: inherit !important +} + +.primary { + background-color: var(--primary) !important; + color: var(--on-primary) !important +} + +.primary-text { + color: var(--primary) !important +} + +.primary-border { + border-color: var(--primary) !important +} + +.primary-container { + background-color: var(--primary-container) !important; + color: var(--on-primary-container) !important +} + +.secondary { + background-color: var(--secondary) !important; + color: var(--on-secondary) !important +} + +.secondary-text { + color: var(--secondary) !important +} + +.secondary-border { + border-color: var(--secondary) !important +} + +.secondary-container { + background-color: var(--secondary-container) !important; + color: var(--on-secondary-container) !important +} + +.tertiary { + background-color: var(--tertiary) !important; + color: var(--on-tertiary) !important +} + +.tertiary-text { + color: var(--tertiary) !important +} + +.tertiary-border { + border-color: var(--tertiary) !important +} + +.tertiary-container { + background-color: var(--tertiary-container) !important; + color: var(--on-tertiary-container) !important +} + +.error { + background-color: var(--error) !important; + color: var(--on-error) !important +} + +.error-text { + color: var(--error) !important +} + +.error-border { + border-color: var(--error) !important +} + +.error-container { + background-color: var(--error-container) !important; + color: var(--on-error-container) !important +} + +.background { + background-color: var(--background) !important; + color: var(--on-background) !important +} + +.surface, .surface-dim, .surface-bright, .surface-container-lowest, .surface-container-low, .surface-container, .surface-container-high, .surface-container-highest { + background-color: var(--surface) !important; + color: var(--on-surface) !important +} + +.surface-variant { + background-color: var(--surface-variant) !important; + color: var(--on-surface-variant) !important +} + +.inverse-surface { + background-color: var(--inverse-surface); + color: var(--inverse-on-surface) +} + +.inverse-primary { + background-color: var(--inverse-primary); + color: var(--primary) +} + +.inverse-primary-text { + color: var(--inverse-primary) !important +} + +.inverse-primary-border { + border-color: var(--inverse-primary) !important +} + +.surface-dim { + background-color: var(--surface-dim) !important +} + +.surface-bright { + background-color: var(--surface-bright) !important +} + +.surface-container-lowest { + background-color: var(--surface-container-lowest) !important +} + +.surface-container { + background-color: var(--surface-container) !important +} + +.surface-container-high { + background-color: var(--surface-container-high) !important +} + +.surface-container-highest { + background-color: var(--surface-container-highest) !important +} + +.surface-container-low { + background-color: var(--surface-container-low) !important +} + +.black { + background-color: #000 !important +} + +.black-border { + border-color: #000 !important +} + +.black-text { + color: #000 !important +} + +.white { + background-color: #fff !important +} + +.white-border { + border-color: #fff !important +} + +.white-text { + color: #fff !important +} + +.transparent-border { + border-color: transparent !important +} + +.transparent-text { + color: transparent !important +} + +.fill:not(i) { + background-color: var(--surface-variant) !important; + color: var(--on-surface-variant) !important +} + +.middle-align { + display: flex; + align-items: center !important +} + +.bottom-align { + display: flex; + align-items: flex-end !important +} + +.top-align { + display: flex; + align-items: flex-start !important +} + +.left-align { + text-align: start; + justify-content: flex-start !important +} + +.right-align { + text-align: end; + justify-content: flex-end !important +} + +.center-align { + text-align: center; + justify-content: center !important +} + +[class*=blur], [class*=blur].light { + --_blur: 1rem; + -webkit-backdrop-filter: blur(var(--_blur)); + backdrop-filter: blur(var(--_blur)); + color: var(--on-surface); + background-color: #ffffff80 !important +} + +.dark [class*=blur], [class*=blur].dark { + background-color: #00000080 !important +} + +.small-blur { + --_blur: .5rem +} + +.large-blur { + --_blur: 1.5rem +} + +.red, .red6 { + background-color: #f44336 !important +} + +.red-border { + border-color: #f44336 !important +} + +.red-text { + color: #f44336 !important +} + +.red1 { + background-color: #ffebee !important +} + +.red2 { + background-color: #ffcdd2 !important +} + +.red3 { + background-color: #ef9a9a !important +} + +.red4 { + background-color: #e57373 !important +} + +.red5 { + background-color: #ef5350 !important +} + +.red7 { + background-color: #e53935 !important +} + +.red8 { + background-color: #d32f2f !important +} + +.red9 { + background-color: #c62828 !important +} + +.red10 { + background-color: #b71c1c !important +} + +.pink, .pink6 { + background-color: #e91e63 !important +} + +.pink-border { + border-color: #e91e63 !important +} + +.pink-text { + color: #e91e63 !important +} + +.pink1 { + background-color: #fce4ec !important +} + +.pink2 { + background-color: #f8bbd0 !important +} + +.pink3 { + background-color: #f48fb1 !important +} + +.pink4 { + background-color: #f06292 !important +} + +.pink5 { + background-color: #ec407a !important +} + +.pink7 { + background-color: #d81b60 !important +} + +.pink8 { + background-color: #c2185b !important +} + +.pink9 { + background-color: #ad1457 !important +} + +.pink10 { + background-color: #880e4f !important +} + +.purple, .purple6 { + background-color: #9c27b0 !important +} + +.purple-border { + border-color: #9c27b0 !important +} + +.purple-text { + color: #9c27b0 !important +} + +.purple1 { + background-color: #f3e5f5 !important +} + +.purple2 { + background-color: #e1bee7 !important +} + +.purple3 { + background-color: #ce93d8 !important +} + +.purple4 { + background-color: #ba68c8 !important +} + +.purple5 { + background-color: #ab47bc !important +} + +.purple7 { + background-color: #8e24aa !important +} + +.purple8 { + background-color: #7b1fa2 !important +} + +.purple9 { + background-color: #6a1b9a !important +} + +.purple10 { + background-color: #4a148c !important +} + +.deep-purple, .deep-purple6 { + background-color: #673ab7 !important +} + +.deep-purple-border { + border-color: #673ab7 !important +} + +.deep-purple-text { + color: #673ab7 !important +} + +.deep-purple1 { + background-color: #ede7f6 !important +} + +.deep-purple2 { + background-color: #d1c4e9 !important +} + +.deep-purple3 { + background-color: #b39ddb !important +} + +.deep-purple4 { + background-color: #9575cd !important +} + +.deep-purple5 { + background-color: #7e57c2 !important +} + +.deep-purple7 { + background-color: #5e35b1 !important +} + +.deep-purple8 { + background-color: #512da8 !important +} + +.deep-purple9 { + background-color: #4527a0 !important +} + +.deep-purple10 { + background-color: #311b92 !important +} + +.indigo, .indigo6 { + background-color: #3f51b5 !important +} + +.indigo-border { + border-color: #3f51b5 !important +} + +.indigo-text { + color: #3f51b5 !important +} + +.indigo1 { + background-color: #e8eaf6 !important +} + +.indigo2 { + background-color: #c5cae9 !important +} + +.indigo3 { + background-color: #9fa8da !important +} + +.indigo4 { + background-color: #7986cb !important +} + +.indigo5 { + background-color: #5c6bc0 !important +} + +.indigo7 { + background-color: #3949ab !important +} + +.indigo8 { + background-color: #303f9f !important +} + +.indigo9 { + background-color: #283593 !important +} + +.indigo10 { + background-color: #1a237e !important +} + +.blue, .blue6 { + background-color: #2196f3 !important +} + +.blue-border { + border-color: #2196f3 !important +} + +.blue-text { + color: #2196f3 !important +} + +.blue1 { + background-color: #e3f2fd !important +} + +.blue2 { + background-color: #bbdefb !important +} + +.blue3 { + background-color: #90caf9 !important +} + +.blue4 { + background-color: #64b5f6 !important +} + +.blue5 { + background-color: #42a5f5 !important +} + +.blue7 { + background-color: #1e88e5 !important +} + +.blue8 { + background-color: #1976d2 !important +} + +.blue9 { + background-color: #1565c0 !important +} + +.blue10 { + background-color: #0d47a1 !important +} + +.light-blue, .light-blue6 { + background-color: #03a9f4 !important +} + +.light-blue-border { + border-color: #03a9f4 !important +} + +.light-blue-text { + color: #03a9f4 !important +} + +.light-blue1 { + background-color: #e1f5fe !important +} + +.light-blue2 { + background-color: #b3e5fc !important +} + +.light-blue3 { + background-color: #81d4fa !important +} + +.light-blue4 { + background-color: #4fc3f7 !important +} + +.light-blue5 { + background-color: #29b6f6 !important +} + +.light-blue7 { + background-color: #039be5 !important +} + +.light-blue8 { + background-color: #0288d1 !important +} + +.light-blue9 { + background-color: #0277bd !important +} + +.light-blue10 { + background-color: #01579b !important +} + +.cyan, .cyan6 { + background-color: #00bcd4 !important +} + +.cyan-border { + border-color: #00bcd4 !important +} + +.cyan-text { + color: #00bcd4 !important +} + +.cyan1 { + background-color: #e0f7fa !important +} + +.cyan2 { + background-color: #b2ebf2 !important +} + +.cyan3 { + background-color: #80deea !important +} + +.cyan4 { + background-color: #4dd0e1 !important +} + +.cyan5 { + background-color: #26c6da !important +} + +.cyan7 { + background-color: #00acc1 !important +} + +.cyan8 { + background-color: #0097a7 !important +} + +.cyan9 { + background-color: #00838f !important +} + +.cyan10 { + background-color: #006064 !important +} + +.teal, .teal6 { + background-color: #009688 !important +} + +.teal-border { + border-color: #009688 !important +} + +.teal-text { + color: #009688 !important +} + +.teal1 { + background-color: #e0f2f1 !important +} + +.teal2 { + background-color: #b2dfdb !important +} + +.teal3 { + background-color: #80cbc4 !important +} + +.teal4 { + background-color: #4db6ac !important +} + +.teal5 { + background-color: #26a69a !important +} + +.teal7 { + background-color: #00897b !important +} + +.teal8 { + background-color: #00796b !important +} + +.teal9 { + background-color: #00695c !important +} + +.teal10 { + background-color: #004d40 !important +} + +.green, .green6 { + background-color: #4caf50 !important +} + +.green-border { + border-color: #4caf50 !important +} + +.green-text { + color: #4caf50 !important +} + +.green1 { + background-color: #e8f5e9 !important +} + +.green2 { + background-color: #c8e6c9 !important +} + +.green3 { + background-color: #a5d6a7 !important +} + +.green4 { + background-color: #81c784 !important +} + +.green5 { + background-color: #66bb6a !important +} + +.green7 { + background-color: #43a047 !important +} + +.green8 { + background-color: #388e3c !important +} + +.green9 { + background-color: #2e7d32 !important +} + +.green10 { + background-color: #1b5e20 !important +} + +.light-green, .light-green6 { + background-color: #8bc34a !important +} + +.light-green-border { + border-color: #8bc34a !important +} + +.light-green-text { + color: #8bc34a !important +} + +.light-green1 { + background-color: #f1f8e9 !important +} + +.light-green2 { + background-color: #dcedc8 !important +} + +.light-green3 { + background-color: #c5e1a5 !important +} + +.light-green4 { + background-color: #aed581 !important +} + +.light-green5 { + background-color: #9ccc65 !important +} + +.light-green7 { + background-color: #7cb342 !important +} + +.light-green8 { + background-color: #689f38 !important +} + +.light-green9 { + background-color: #558b2f !important +} + +.light-green10 { + background-color: #33691e !important +} + +.lime, .lime6 { + background-color: #cddc39 !important +} + +.lime-border { + border-color: #cddc39 !important +} + +.lime-text { + color: #cddc39 !important +} + +.lime1 { + background-color: #f9fbe7 !important +} + +.lime2 { + background-color: #f0f4c3 !important +} + +.lime3 { + background-color: #e6ee9c !important +} + +.lime4 { + background-color: #dce775 !important +} + +.lime5 { + background-color: #d4e157 !important +} + +.lime7 { + background-color: #c0ca33 !important +} + +.lime8 { + background-color: #afb42b !important +} + +.lime9 { + background-color: #9e9d24 !important +} + +.lime10 { + background-color: #827717 !important +} + +.yellow, .yellow6 { + background-color: #ffeb3b !important +} + +.yellow-border { + border-color: #ffeb3b !important +} + +.yellow-text { + color: #ffeb3b !important +} + +.yellow1 { + background-color: #fffde7 !important +} + +.yellow2 { + background-color: #fff9c4 !important +} + +.yellow3 { + background-color: #fff59d !important +} + +.yellow4 { + background-color: #fff176 !important +} + +.yellow5 { + background-color: #ffee58 !important +} + +.yellow7 { + background-color: #fdd835 !important +} + +.yellow8 { + background-color: #fbc02d !important +} + +.yellow9 { + background-color: #f9a825 !important +} + +.yellow10 { + background-color: #f57f17 !important +} + +.amber, .amber6 { + background-color: #ffc107 !important +} + +.amber-border { + border-color: #ffc107 !important +} + +.amber-text { + color: #ffc107 !important +} + +.amber1 { + background-color: #fff8e1 !important +} + +.amber2 { + background-color: #ffecb3 !important +} + +.amber3 { + background-color: #ffe082 !important +} + +.amber4 { + background-color: #ffd54f !important +} + +.amber5 { + background-color: #ffca28 !important +} + +.amber7 { + background-color: #ffb300 !important +} + +.amber8 { + background-color: #ffa000 !important +} + +.amber9 { + background-color: #ff8f00 !important +} + +.amber10 { + background-color: #ff6f00 !important +} + +.orange, .orange6 { + background-color: #ff9800 !important +} + +.orange-border { + border-color: #ff9800 !important +} + +.orange-text { + color: #ff9800 !important +} + +.orange1 { + background-color: #fff3e0 !important +} + +.orange2 { + background-color: #ffe0b2 !important +} + +.orange3 { + background-color: #ffcc80 !important +} + +.orange4 { + background-color: #ffb74d !important +} + +.orange5 { + background-color: #ffa726 !important +} + +.orange7 { + background-color: #fb8c00 !important +} + +.orange8 { + background-color: #f57c00 !important +} + +.orange9 { + background-color: #ef6c00 !important +} + +.orange10 { + background-color: #e65100 !important +} + +.deep-orange, .deep-orange6 { + background-color: #ff5722 !important +} + +.deep-orange-border { + border-color: #ff5722 !important +} + +.deep-orange-text { + color: #ff5722 !important +} + +.deep-orange1 { + background-color: #fbe9e7 !important +} + +.deep-orange2 { + background-color: #ffccbc !important +} + +.deep-orange3 { + background-color: #ffab91 !important +} + +.deep-orange4 { + background-color: #ff8a65 !important +} + +.deep-orange5 { + background-color: #ff7043 !important +} + +.deep-orange7 { + background-color: #f4511e !important +} + +.deep-orange8 { + background-color: #e64a19 !important +} + +.deep-orange9 { + background-color: #d84315 !important +} + +.deep-orange10 { + background-color: #bf360c !important +} + +.brown, .brown6 { + background-color: #795548 !important +} + +.brown-border { + border-color: #795548 !important +} + +.brown-text { + color: #795548 !important +} + +.brown1 { + background-color: #efebe9 !important +} + +.brown2 { + background-color: #d7ccc8 !important +} + +.brown3 { + background-color: #bcaaa4 !important +} + +.brown4 { + background-color: #a1887f !important +} + +.brown5 { + background-color: #8d6e63 !important +} + +.brown7 { + background-color: #6d4c41 !important +} + +.brown8 { + background-color: #5d4037 !important +} + +.brown9 { + background-color: #4e342e !important +} + +.brown10 { + background-color: #3e2723 !important +} + +.blue-grey, .blue-grey6 { + background-color: #607d8b !important +} + +.blue-grey-border { + border-color: #607d8b !important +} + +.blue-grey-text { + color: #607d8b !important +} + +.blue-grey1 { + background-color: #eceff1 !important +} + +.blue-grey2 { + background-color: #cfd8dc !important +} + +.blue-grey3 { + background-color: #b0bec5 !important +} + +.blue-grey4 { + background-color: #90a4ae !important +} + +.blue-grey5 { + background-color: #78909c !important +} + +.blue-grey7 { + background-color: #546e7a !important +} + +.blue-grey8 { + background-color: #455a64 !important +} + +.blue-grey9 { + background-color: #37474f !important +} + +.blue-grey10 { + background-color: #263238 !important +} + +.grey, .grey6 { + background-color: #9e9e9e !important +} + +.grey-border { + border-color: #9e9e9e !important +} + +.grey-text { + color: #9e9e9e !important +} + +.grey1 { + background-color: #fafafa !important +} + +.grey2 { + background-color: #f5f5f5 !important +} + +.grey3 { + background-color: #eee !important +} + +.grey4 { + background-color: #e0e0e0 !important +} + +.grey5 { + background-color: #bdbdbd !important +} + +.grey7 { + background-color: #757575 !important +} + +.grey8 { + background-color: #616161 !important +} + +.grey9 { + background-color: #424242 !important +} + +.grey10 { + background-color: #212121 !important +} + +.horizontal { + display: inline-flex; + flex-direction: row !important; + gap: 1rem; + inline-size: auto !important; + max-inline-size: none !important +} + +.horizontal > * { + margin-block: 0 !important +} + +.vertical { + display: flex; + flex-direction: column !important +} + +:is(a,button,.button,.chip).vertical { + display: inline-flex; + gap: .25rem; + block-size: auto !important; + max-block-size: none !important; + padding-block: .5rem +} + +.vertical > * { + margin-inline: 0 !important +} + +.no-elevate { + box-shadow: none !important +} + +.small-elevate, .elevate { + box-shadow: var(--elevate1) !important +} + +.medium-elevate { + box-shadow: var(--elevate2) !important +} + +.large-elevate { + box-shadow: var(--elevate3) !important +} + +.round, [class*=-round] { + --_round: 2rem; + border-radius: var(--_round) !important +} + +.small-round { + --_round: .5rem +} + +.large-round { + --_round: 3.5rem +} + +.no-round, .square, .top-round, .bottom-round, .left-round, .right-round { + border-radius: .5rem !important +} + +.top-round { + border-start-start-radius: var(--_round) !important; + border-start-end-radius: var(--_round) !important +} + +.bottom-round { + border-end-end-radius: var(--_round) !important; + border-end-start-radius: var(--_round) !important +} + +.left-round { + border-start-start-radius: var(--_round) !important; + border-end-start-radius: var(--_round) !important +} + +.right-round { + border-start-end-radius: var(--_round) !important; + border-end-end-radius: var(--_round) !important +} + +.circle:not(.extend) { + border-radius: 50% +} + +:is(.circle,.square):is(button,.button,.chip) { + padding: 0; + block-size: var(--_size); + inline-size: var(--_size) +} + +:is(.circle,.square) > span { + display: none +} + +:is(.circle,.square).round { + border-radius: 1rem !important +} + +.border:not(table,.field,.list,menu,article) { + box-sizing: border-box; + border: .0625rem solid var(--outline); + background-color: transparent; + box-shadow: none +} + +.no-border { + border-color: transparent !important +} + +.border:not(.extend,.circle,.square,.badge) { + box-sizing: content-box +} + +.margin, [class*=-margin]:not(.left-margin,.right-margin,.top-margin,.bottom-margin,.horizontal-margin,.vertical-margin) { + margin: var(--_margin) !important +} + +.margin, [class*=-margin] { + --_margin: 1rem +} + +.no-margin { + --_margin: 0 +} + +.auto-margin { + --_margin: auto +} + +.tiny-margin { + --_margin: .25rem +} + +.small-margin { + --_margin: .5rem +} + +.large-margin { + --_margin: 1.5rem +} + +.left-margin, .horizontal-margin { + margin-inline-start: var(--_margin) !important +} + +.right-margin, .horizontal-margin { + margin-inline-end: var(--_margin) !important +} + +.top-margin, .vertical-margin { + margin-block-start: var(--_margin) !important +} + +.bottom-margin, .vertical-margin { + margin-block-end: var(--_margin) !important +} + +.no-opacity { + opacity: 1 !important +} + +.opacity { + opacity: 0 !important +} + +.small-opacity { + opacity: .75 !important +} + +.medium-opacity { + opacity: .5 !important +} + +.large-opacity { + opacity: .25 !important +} + +.padding, [class*=-padding]:not(.left-padding,.right-padding,.top-padding,.bottom-padding,.horizontal-padding,.vertical-padding) { + padding: var(--_padding) !important +} + +.padding, [class*=-padding] { + --_padding: 1rem +} + +.no-padding { + --_padding: 0 !important +} + +.tiny-padding { + --_padding: .25rem !important +} + +.small-padding { + --_padding: .5rem !important +} + +.large-padding { + --_padding: 1.5rem !important +} + +.left-padding, .horizontal-padding { + padding-inline-start: var(--_padding) !important +} + +.right-padding, .horizontal-padding { + padding-inline-end: var(--_padding) !important +} + +.top-padding, .vertical-padding { + padding-block-start: var(--_padding) !important +} + +.bottom-padding, .vertical-padding { + padding-block-end: var(--_padding) !important +} + +.front { + z-index: 10 !important +} + +.back { + z-index: -10 !important +} + +.left { + inset-inline-start: 0 +} + +.right { + inset-inline-end: 0 +} + +.top { + inset-block-start: 0 +} + +.bottom { + inset-block-end: 0 +} + +.center { + inset-inline-start: 50%; + transform: translate(-50%) +} + +[dir=rtl] .center { + transform: translate(50%) +} + +.middle { + inset-block-start: 50%; + transform: translateY(-50%) +} + +.middle.center { + transform: translate(-50%, -50%) +} + +[dir=rtl] .middle.center { + transform: translate(50%, -50%) +} + +.ripple { + --_duration: .6s +} + +.fast-ripple { + --_duration: .2s +} + +.slow-ripple { + --_duration: 1.8s +} + +.ripple-js { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + pointer-events: none; + overflow: hidden +} + +.ripple-js > div { + position: absolute; + border-radius: 50%; + background: currentColor; + opacity: .3; + transform: scale(0); + animation: to-ripple var(--_duration) linear +} + +@keyframes to-ripple { + to { + transform: scale(4); + opacity: 0 + } +} + +.scroll { + overflow: auto; + min-inline-size: 0 +} + +.no-scroll { + overflow: hidden +} + +.shadow { + background-color: #00000050 +} + +:is(.left-shadow,.right-shadow,.top-shadow,.bottom-shadow) { + background-color: transparent !important +} + +.left-shadow { + background-image: linear-gradient(to right, black, transparent) !important +} + +.right-shadow { + background-image: linear-gradient(to left, black, transparent) !important +} + +.bottom-shadow { + background-image: linear-gradient(to top, black, transparent) !important +} + +.top-shadow { + background-image: linear-gradient(to bottom, black, transparent) !important +} + +[class*=-width] { + max-inline-size: 100% +} + +.auto-width { + inline-size: auto +} + +.small-width { + inline-size: 12rem !important +} + +.medium-width { + inline-size: 24rem !important +} + +.large-width { + inline-size: 36rem !important +} + +.auto-height { + block-size: auto +} + +.small-height { + block-size: 12rem !important +} + +.medium-height { + block-size: 24rem !important +} + +.large-height { + block-size: 36rem !important +} + +.wrap { + display: block; + white-space: normal +} + +.no-wrap:not(menu) { + display: flex; + white-space: nowrap +} + +.tiny-space:not(nav,.row,.grid,table,.tooltip,.list,menu,.shape) { + block-size: .5rem +} + +:is(.space,.small-space):not(nav,.row,.grid,table,.tooltip,.list,menu,.shape) { + block-size: 1rem +} + +.medium-space:not(nav,.row,.grid,table,.tooltip,.list,menu,.shape) { + block-size: 2rem +} + +.large-space:not(nav,.row,.grid,table,.tooltip,.list,menu,.shape) { + block-size: 3rem +} + +.extra-space:not(nav,.row,.grid,table,.tooltip,.list,menu,.shape) { + block-size: 4rem +} + +.responsive { + inline-size: -webkit-fill-available; + inline-size: -moz-available +} + +@media only screen and (max-width: 600px) { + :is(.m,.l):not(.s) { + display: none !important + } +} + +@media only screen and (min-width: 601px)and (max-width: 992px) { + :is(.s,.l):not(.m) { + display: none !important + } +} + +@media only screen and (min-width: 993px) { + :is(.m,.s):not(.l) { + display: none !important + } +} + +html { + font-size: var(--size) +} + +body { + font-family: var(--font); + font-size: .875rem; + line-height: 1.5rem; + letter-spacing: .0313rem +} + +h1, h2, h3, h4, h5, h6 { + font-weight: 400; + display: block; + align-items: center; + line-height: normal +} + +h1 { + font-size: 3.5625rem +} + +h2 { + font-size: 2.8125rem +} + +h3 { + font-size: 2.25rem +} + +h4 { + font-size: 2rem +} + +h5 { + font-size: 1.75rem +} + +h6 { + font-size: 1.5rem +} + +h1.small { + font-size: 3.0625rem +} + +h2.small { + font-size: 2.3125rem +} + +h3.small { + font-size: 1.75rem +} + +h4.small { + font-size: 1.5rem +} + +h5.small { + font-size: 1.25rem +} + +h6.small { + font-size: 1rem +} + +h1.large { + font-size: 4.0625rem +} + +h2.large { + font-size: 3.3125rem +} + +h3.large { + font-size: 2.75rem +} + +h4.large { + font-size: 2.5rem +} + +h5.large { + font-size: 2.25rem +} + +h6.large { + font-size: 2rem +} + +.link { + color: var(--primary) !important +} + +.inverse-link { + color: var(--inverse-primary) !important +} + +.truncate { + overflow: hidden; + white-space: nowrap !important; + text-overflow: ellipsis; + flex: inherit +} + +.truncate > * { + white-space: nowrap !important +} + +.small-text { + font-size: .75rem +} + +.medium-text { + font-size: .875rem +} + +.large-text { + font-size: 1rem +} + +.upper { + text-transform: uppercase +} + +.lower { + text-transform: lowercase +} + +.capitalize { + text-transform: capitalize +} + +.bold { + font-weight: 700 +} + +.overline { + text-decoration: line-through +} + +.underline { + text-decoration: underline +} + +.italic { + font-style: italic +} + +p { + margin: .5rem 0 +} + +.no-line { + line-height: normal +} + +.tiny-line { + line-height: 1.25rem +} + +.small-line { + line-height: 1.5rem +} + +.medium-line { + line-height: 1.75rem +} + +.large-line { + line-height: 2rem +} + +.extra-line { + line-height: 2.25rem +} + +pre { + border-radius: 0; + background-color: var(--surface-container); + white-space: pre-wrap; + padding: 1rem; + border-inline-start: .25rem solid var(--primary); + font-family: inherit +} + +blockquote { + border-radius: 0; + padding: 1rem; + border-inline-start: .25rem solid var(--primary); + font-family: inherit +} + +code { + border-radius: 0; + background-color: var(--surface-container); + white-space: pre-wrap; + padding: .25rem +} + +pre > code, blockquote > code { + padding: 0 +} + +.scroll > code { + white-space: pre +} + +pre:has(>code) { + direction: ltr; + text-align: start +} + +sub { + vertical-align: sub +} + +sup { + vertical-align: super +} + +:is(.wave,.chip,.button,button,nav.tabbed>a,.tabs>a,nav.toolbar>a):not(.slow-ripple,.ripple,.fast-ripple):after, nav:is(.left,.right,.bottom,.top).max > a:after, nav:is(.left,.right,.bottom,.top).max > :is(ol,ul) > li > a:after, nav:is(.left,.right,.bottom,.top):not(.max) > a > i:after, nav:is(.left,.right,.bottom,.top):not(.max) > :is(ol,ul) > li > a > i:after { + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 1; + border-radius: inherit; + inline-size: 100%; + block-size: 100%; + background-position: center; + background-image: radial-gradient(circle, currentColor 1%, transparent 1%); + opacity: 0; + transition: none; + pointer-events: none +} + +:is(.wave,.chip,.button,button,nav.tabbed>a,.tabs>a,nav.toolbar>a):not(.slow-ripple,.ripple,.fast-ripple):is(:focus-visible,:hover):after, nav:is(.left,.right,.bottom,.top).max > a:not(.button,.chip):is(:focus-visible,:hover):after, nav:is(.left,.right,.bottom,.top).max > :is(ol,ul) > li > a:not(.button,.chip):is(:focus-visible,:hover):after, nav:is(.left,.right,.bottom,.top):not(.max) > a:not(.button,.chip):is(:focus-visible,:hover) > i:after, nav:is(.left,.right,.bottom,.top):not(.max) > :is(ol,ul) > li > a:not(.button,.chip):is(:focus-visible,:hover) > i:after { + background-size: 22500%; + opacity: .1; + transition: background-size var(--speed2) linear +} + +:is(.wave,.chip,.button,button,nav.tabbed>a,.tabs>a,nav.toolbar>a,nav.max>a):not(.slow-ripple,.ripple,.fast-ripple):active:after, nav:is(.left,.right,.bottom,.top).max > a:active:after, nav:is(.left,.right,.bottom,.top).max > :is(ol,ul) > li > a:active:after, nav:is(.left,.right,.bottom,.top):not(.max) > a:active > i:after, nav:is(.left,.right,.bottom,.top):not(.max) > :is(ol,ul) > li > a:active > i:after { + background-size: 0%; + opacity: 0; + transition: none +} + +.no-wave:after, .no-wave:is(:hover,:active):after { + display: none +} + +.zoom, .tiny-zoom { + zoom: 2 +} + +.small-zoom { + zoom: 3 +} + +.medium-zoom { + zoom: 4 +} + +.large-zoom { + zoom: 5 +} + +.extra-zoom { + zoom: 6 +} + +.badge { + --_x: 0; + --_y: -100%; + display: inline-flex; + align-items: center; + justify-content: center; + position: absolute; + font-size: .6875rem; + text-transform: none; + z-index: 2; + padding: 0 .25rem; + min-block-size: 1rem; + min-inline-size: 1rem; + background-color: var(--error); + color: var(--on-error); + line-height: normal; + border-radius: 1rem; + inset: 50% auto auto 50%; + transform: translate(var(--_x, 50%), var(--_y, -50%)); + font-family: var(--font) +} + +.badge.top { + --_y: -100% +} + +.badge.bottom { + --_y: 0 +} + +.badge.left { + --_x: -100% +} + +.badge.right { + --_x: 0 +} + +.badge.border { + border-color: var(--error); + color: var(--error); + background-color: var(--surface) +} + +.badge:is(.circle,.square) { + text-align: center; + inline-size: auto; + block-size: auto; + padding: 0 .25rem; + border-radius: 1rem +} + +.badge.square { + border-radius: 0 +} + +.badge.min > * { + display: none +} + +.badge.min { + clip-path: circle(18.75% at 50% 50%) +} + +nav:is(.left,.right,.top,.bottom) > a > .badge, nav:is(.left,.right,.top,.bottom) > :is(ol,ul) > li > a > .badge { + inset: 1rem auto auto 50% +} + +.badge.none { + inset: auto !important; + transform: none; + position: relative; + margin: 0 .125rem +} + +header, footer { + display: grid; + align-content: center; + border-radius: 0; + padding: 0 1rem +} + +:is(dialog,article) > :is(header,footer) { + padding-inline: 0; + top: 0; + right: 0; + bottom: 0; + left: 0 +} + +header { + min-block-size: 4rem +} + +footer { + min-block-size: 5rem +} + +:is(header,footer,menu>*).fixed { + position: sticky; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 11; + background-color: inherit +} + +header.fixed { + inset: calc(-1 * var(--_padding)) 0 0 0; + margin-block-start: calc(-1 * var(--_padding)) +} + +footer.fixed { + inset: 0 0 calc(-1 * var(--_padding)) 0; + margin-block-end: calc(-1 * var(--_padding)) +} + +:is(header,footer).fixed.min { + margin-inline: auto +} + +dialog > :is(header,footer) { + background: none +} + +dialog > :is(header,footer).fixed { + background-color: inherit +} + +:is(main,header,footer,section).responsive { + max-inline-size: min(100vw, 75rem); + margin: 0 auto +} + +:is(main,header,footer,section).responsive.max { + max-inline-size: none +} + +:has(>main) > :is(header,footer).fixed { + transform: none; + box-sizing: content-box; + position: sticky; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: 12 +} + +:has(>main) > header { + padding-block-start: var(--top) +} + +:has(>main) > footer { + padding-block-end: var(--bottom) +} + +nav.top ~ header, nav.bottom ~ footer { + padding-block: 0 +} + +nav.top ~ header.fixed { + inset-block: calc(var(--top) + 4.5rem) 0 +} + +nav.bottom ~ footer.fixed { + inset-block: 0 calc(var(--bottom) + 4.5rem) +} + +:is(nav,.row) > header { + background-color: inherit +} + +.button, button { + --_padding: 1rem; + --_size: 2.5rem; + box-sizing: content-box; + display: inline-flex; + align-items: center; + justify-content: center; + block-size: var(--_size); + font-size: .875rem; + font-weight: 500; + color: var(--on-primary); + padding: 0 var(--_padding); + background-color: var(--primary); + margin: 0 .5rem; + border-radius: var(--_size); + transition: transform var(--speed3), border-radius var(--speed2), padding var(--speed3); + -webkit-user-select: none; + user-select: none; + gap: .5rem; + line-height: normal +} + +:is(button,.button).small { + --_size: 2rem; + --_padding: .75rem +} + +:is(button,.button).large { + --_size: 3rem; + --_padding: 1.25rem +} + +:is(.button,button):is(.extra,.extend) { + --_size: 3.5rem; + font-size: 1rem; + --_padding: 1.5rem +} + +:is(button,.button):is(.square,.circle) { + --_padding: 0 +} + +:is(button,.button).border { + border-color: var(--outline-variant); + color: var(--primary) +} + +.extend > span { + display: none +} + +.extend:is(:hover,.active) { + inline-size: auto; + --_padding: 1.5rem; + padding: 0 var(--_padding) +} + +.extend:is(:hover,.active) > i + span { + display: inherit; + margin-inline-start: var(--_padding) +} + +.extend:is(:hover,.active) > :is(img,svg) + span { + display: inherit; + margin-inline-start: calc(1rem + var(--_padding)) +} + +:is(.button,button)[disabled] { + opacity: .5; + cursor: not-allowed +} + +.button[disabled] { + pointer-events: none +} + +:is(.button,button)[disabled]:before, :is(.button,button)[disabled]:after { + display: none +} + +:is(.button,button):not(.chip,.extend).fill { + background-color: var(--secondary-container) !important; + color: var(--on-secondary-container) !important +} + +:is(.button,button):not(.chip,.extend).active { + background-color: var(--primary-container); + color: var(--on-primary-container) +} + +:is(.button,button):not(.chip,.extend).fill.active { + background-color: var(--secondary) !important; + color: var(--on-secondary) !important +} + +:is(.button,button):not(.chip,.extend).border.active { + background-color: var(--inverse-surface) !important; + color: var(--inverse-on-surface) !important; + border-color: var(--inverse-surface) !important +} + +:is(.button,button):not(.chip):active, :is(.button,button):not(.chip).active { + border-radius: .5rem !important +} + +article { + --_padding: 1rem; + box-shadow: var(--elevate1); + background-color: var(--surface-container-low); + color: var(--on-surface); + padding: var(--_padding); + border-radius: .75rem; + display: block; + transition: transform var(--speed3), border-radius var(--speed3), padding var(--speed3) +} + +article.small { + block-size: 12rem +} + +article.medium { + block-size: 20rem +} + +article.large { + block-size: 32rem +} + +article.border { + box-shadow: none; + border: .0625rem solid var(--outline-variant) +} + +.chip { + --_padding: .75rem; + --_size: 2rem; + box-sizing: border-box; + display: inline-flex; + align-items: center; + justify-content: center; + block-size: var(--_size); + min-inline-size: var(--_size); + font-size: .875rem; + font-weight: 500; + background-color: transparent; + border: .0625rem solid var(--outline-variant); + color: var(--on-surface-variant); + padding: 0 var(--_padding); + margin: 0 .5rem; + text-transform: none; + border-radius: .5rem; + transition: transform var(--speed3), border-radius var(--speed3), padding var(--speed3); + -webkit-user-select: none; + user-select: none; + gap: .5rem; + line-height: normal; + letter-spacing: normal +} + +.chip.medium { + --_size: 2.5rem; + --_padding: 1rem +} + +.chip.large { + --_padding: 1.25rem; + --_size: 3rem +} + +.chip.fill { + border: none +} + +dialog { + --_padding: 1.5rem; + --_top: calc(var(--_padding) + var(--top)); + --_bottom: calc(var(--_padding) + var(--bottom)); + display: block; + visibility: hidden; + border: none; + opacity: 0; + position: fixed; + box-shadow: var(--elevate2); + color: var(--on-surface); + background-color: var(--surface-container-high); + padding: var(--_padding); + z-index: 100; + inset: 10% auto auto 50%; + min-inline-size: 20rem; + max-inline-size: 100%; + max-block-size: 80%; + overflow-x: hidden; + overflow-y: auto; + transition: all var(--speed3), 0s background-color; + border-radius: 1.75rem; + transform: translate(-50%, -4rem); + outline: none +} + +dialog.small { + inline-size: 25%; + block-size: 25% +} + +dialog.medium { + inline-size: 50%; + block-size: 50% +} + +dialog.large { + inline-size: 75%; + block-size: 75% +} + +dialog:is(.active,[open]) { + visibility: visible; + opacity: 1; + transform: translate(-50%) +} + +dialog:popover-open { + visibility: visible; + opacity: 1; + transform: translate(-50%) +} + +dialog:is(.top,.right,.bottom,.left,.max) { + --_padding: 1rem +} + +dialog:is(.top,.bottom) { + opacity: 1; + block-size: auto; + inline-size: 100%; + min-inline-size: auto; + max-block-size: 100% +} + +dialog.top { + inset: 0 auto auto 0; + transform: translateY(-100%); + border-radius: 0 0 1rem 1rem; + padding-block-start: var(--_top) +} + +dialog.bottom { + inset: auto auto 0 0; + transform: translateY(100%); + border-radius: 1rem 1rem 0 0; + padding-block-end: var(--_bottom) +} + +dialog:is(.left,.right) { + opacity: 1; + inset: 0 auto auto 0; + inline-size: auto; + block-size: 100%; + max-block-size: 100%; + background-color: var(--surface); + padding-block: var(--_top) var(--_bottom) +} + +[dir=rtl] dialog.right, dialog.left { + inset: 0 auto auto 0; + border-radius: 0 1rem 1rem 0; + transform: translate(-100%) +} + +[dir=rtl] dialog.left, dialog.right { + inset: 0 0 auto auto; + border-radius: 1rem 0 0 1rem; + transform: translate(100%) +} + +dialog.max { + inset: 0 auto auto 0; + inline-size: 100%; + block-size: 100%; + max-inline-size: 100%; + max-block-size: 100%; + transform: translateY(4rem); + background-color: var(--surface); + border-radius: 0; + padding-block: var(--_top) var(--_bottom) +} + +dialog:is(.active,[open]):is(.left,.right,.top,.bottom,.max) { + transform: translate(0) +} + +dialog:popover-open:is(.left,.right,.top,.bottom,.max) { + transform: translate(0) +} + +dialog.small:is(.left,.right) { + inline-size: 20rem +} + +dialog.medium:is(.left,.right) { + inline-size: 32rem +} + +dialog.large:is(.left,.right) { + inline-size: 44rem +} + +dialog.small:is(.top,.bottom) { + block-size: 16rem +} + +dialog.medium:is(.top,.bottom) { + block-size: 24rem +} + +dialog.large:is(.top,.bottom) { + block-size: 32rem +} + +@media (pointer: coarse) { + body:has(dialog[open],dialog.active) { + overflow: hidden + } +} + +hr, [class*=divider] { + all: unset; + inline-size: -webkit-fill-available; + min-block-size: auto; + block-size: .0625rem; + background-color: var(--outline-variant); + display: block +} + +hr + *, [class*=divider] + * { + margin: 0 !important +} + +hr.medium, .medium-divider { + margin: 1rem 0 !important +} + +hr.large, .large-divider { + margin: 1.5rem 0 !important +} + +hr.small, .small-divider { + margin: .5rem 0 !important +} + +li:has(>hr), li:has(>.divider) { + padding: 0 !important; + align-self: normal !important; + min-inline-size: auto !important; + min-block-size: auto !important; + inline-size: -webkit-fill-available +} + +hr.vertical, .divider.vertical, li:has(>hr.vertical), li:has(>.divider.vertical) { + padding: 0 !important; + align-self: center !important; + min-inline-size: auto; + min-block-size: 1.5rem; + inline-size: .0625rem +} + +summary, summary:focus { + list-style-type: none; + cursor: pointer; + outline: none +} + +summary::-webkit-details-marker { + display: none +} + +.field { + --_input: 3rem; + --_start: 1.2rem; + --_middle: calc(var(--_input) / 2); + border-radius: .25rem .25rem 0 0; + min-block-size: var(--_input); + display: flex; + flex-direction: column +} + +.field.fill { + --_background: var(--surface-variant); + background-color: unset !important; + color: unset !important +} + +.field.fill > :is(input,select,textarea) { + background-color: var(--_background); + z-index: 0 +} + +.field.small { + --_input: 2.5rem; + --_start: 1rem +} + +.field.large { + --_input: 3.5rem; + --_start: 1.4rem +} + +.field.extra { + --_input: 4rem; + --_start: 1.6rem +} + +.field.border { + border-radius: .25rem +} + +.field.round.small { + border-radius: 1.25rem +} + +.field.round { + border-radius: 1.5rem +} + +.field.round.large { + border-radius: 1.75rem +} + +.field.round.extra { + border-radius: 2rem +} + +.field > :is(i,img,svg,progress.circle,a) { + position: absolute; + inset: calc((var(--_input) / 2) - .75rem) auto auto auto; + cursor: pointer; + z-index: 10; + inline-size: 1.5rem; + block-size: 1.5rem; + margin: auto 0; + pointer-events: none +} + +.field > :is(a,.front) { + pointer-events: all !important +} + +.field > a > :is(i,img,svg,progress.circle,a) { + inline-size: 1.5rem; + block-size: 1.5rem +} + +.field > :is(i,img,svg,progress.circle,a), [dir=rtl] .field > :is(i,img,svg,progress.circle,a):first-child { + inset: calc(var(--_middle) - .75rem) 1rem auto auto +} + +.field > :is(i,img,svg,progress.circle,a):first-child, [dir=rtl] .field > :is(i,img,svg,progress.circle,a) { + inset: calc(var(--_middle) - .75rem) auto auto 1rem +} + +.field.invalid > i { + color: var(--error) +} + +.field > progress.circle { + border-width: .1875rem +} + +.field > :is(input,textarea,select) { + all: unset; + position: relative; + display: flex; + align-items: center; + box-sizing: border-box; + border-radius: inherit; + border: .0625rem solid transparent; + padding: 0 .9375rem; + font-family: inherit; + font-size: 1rem; + inline-size: 100%; + min-block-size: var(--_input); + outline: none; + z-index: 1; + background: none; + resize: none; + text-align: start; + cursor: text +} + +input::-webkit-date-and-time-value { + text-align: start +} + +:is(input,select,textarea):is(:-webkit-autofill,:autofill) { + -webkit-background-clip: text; + -webkit-text-fill-color: var(--on-surface) +} + +.field > :is(input,textarea,select):focus { + border: .125rem solid transparent; + padding: 0 .875rem +} + +.field > textarea:not([rows]) { + field-sizing: content; + max-block-size: 12rem +} + +input[type=file], input[type=color], :not(.field) > input[type^=date], :not(.field) > input[type^=time], input::-webkit-calendar-picker-indicator { + opacity: 0; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + inline-size: 100%; + block-size: 100%; + margin: 0; + padding: 0; + border: 0; + outline: 0; + z-index: 2 !important +} + +input::-webkit-search-decoration, input::-webkit-search-cancel-button, input::-webkit-search-results-button, input::-webkit-search-results-decoration, input::-webkit-inner-spin-button, input::-webkit-outer-spin-button { + display: none +} + +input[type=number] { + -webkit-appearance: textfield; + -moz-appearance: textfield; + appearance: textfield +} + +.field.border > :is(input,textarea,select) { + border-color: var(--outline) +} + +.field.border > :is(input,textarea,select):focus { + border-color: var(--primary) +} + +.field.round > :is(input,textarea,select) { + padding-inline: 1.4376rem +} + +.field.round > :is(input,textarea,select):focus { + padding-inline: 1.375rem +} + +.field.prefix > :is(input,textarea,select) { + padding-inline-start: 2.9375rem +} + +.field.prefix > .slider { + margin-inline-start: 3.5rem +} + +.field.prefix > :is(input,textarea,select):focus { + padding-inline-start: 2.875rem +} + +.field.suffix > :is(input,textarea,select) { + padding-inline-end: 2.9375rem +} + +.field.suffix > .slider { + margin-inline-end: 3.5rem +} + +.field.suffix > :is(input,textarea,select):focus { + padding-inline-end: 2.875rem +} + +.field:not(.border,.round) > :is(input,textarea,select) { + border-block-end-color: var(--outline) +} + +.field:not(.border,.round) > :is(input,textarea,select):focus { + border-block-end-color: var(--primary) +} + +.field.round:not(.border,.fill) > :is(input,textarea,select), .field.round:not(.border) > :is(input,textarea,select):focus { + box-shadow: var(--elevate1) +} + +.field.round:not(.border,.fill) > :is(input,textarea,select):focus { + box-shadow: var(--elevate2) +} + +.field.invalid:not(.border,.round) > :is(input,textarea,select), .field.invalid:not(.border,.round) > :is(input,textarea,select):focus { + border-block-end-color: var(--error) +} + +.field.invalid.border > :is(input,textarea,select), .field.invalid.border > :is(input,textarea,select):focus { + border-color: var(--error) +} + +.field:has(>:disabled) { + opacity: .5; + cursor: not-allowed +} + +.field > :disabled { + cursor: not-allowed +} + +.field > select { + -webkit-user-select: none; + user-select: none +} + +@-moz-document url-prefix() { + .field > select:focus { + background-color: var(--surface) + } + .field.fill > select:focus { + background-color: var(--surface-variant) + } + .field > select:focus + label { + z-index: 1 + } +} + +.field > select > option { + background-color: var(--surface) +} + +.field > :is(input,select) { + padding-block-start: 1rem +} + +.field:not(.label) > :is(input,select), .field.border:not(.fill) > :is(input,select) { + padding-block-start: 0 +} + +.field > textarea { + padding-block-start: calc(var(--_start)) !important +} + +.field > textarea:focus { + padding-block-start: calc(var(--_start) - .01rem) !important +} + +.field:not(.label) > textarea, .field.border:not(.fill) > textarea { + padding-block-start: calc(var(--_start) - .5rem) !important +} + +.field:not(.label) > textarea:focus, .field.border:not(.fill) > textarea:focus { + padding-block-start: calc(var(--_start) - .51rem) !important +} + +.field.label > label { + --_start: 1rem; + position: absolute; + inset: -.5rem 1rem 0 var(--_start); + display: flex; + block-size: calc(var(--_input) + 1rem); + line-height: calc(var(--_input) + 1rem); + font-size: 1rem; + transition: all .2s; + gap: .25rem; + white-space: nowrap; + pointer-events: none +} + +[dir=rtl] .field.label > label { + inset: -.5rem var(--_start) 0 1rem +} + +.field.label.round > label { + inset: -.5rem 1.75rem 0 var(--_start) +} + +[dir=rtl] .field.label.round > label { + inset: -.5rem 1.75rem 0 var(--_start) +} + +.field.label.border.prefix:not(.fill) > :is(label.active,:focus+label,[placeholder]:not(:placeholder-shown)+label,select+label) { + --_start: 1rem +} + +.field.label.round > label, .field.label.border.prefix.round:not(.fill) > :is(label.active,:focus+label,[placeholder]:not(:placeholder-shown)+label,select+label) { + --_start: 1.5rem +} + +.field.label.prefix > label { + --_start: 3rem +} + +.field.label > :is(label.active,:focus+label,[placeholder]:not(:placeholder-shown)+label,select+label) { + block-size: 2.5rem; + line-height: 2.5rem; + font-size: .75rem +} + +.field.label.border:not(.fill) > :is(label.active,:focus+label,[placeholder]:not(:placeholder-shown)+label,select+label) { + block-size: 1rem; + line-height: 1rem +} + +.field.label.border:not(.fill) > label:after { + content: ""; + display: block; + margin: .5rem 0 0; + border-block-start: .0625rem solid var(--outline); + block-size: 1rem; + transition: none; + flex: auto +} + +.field.label.border:not(.fill) > :focus + label:after { + border-block-start: .125rem solid var(--primary) +} + +.field.label.border:not(.fill) > :is(input,textarea):is(:focus,[placeholder]:not(:placeholder-shown),.active), .field.label.border:not(.fill) > select { + clip-path: polygon(-2% -2%, .75rem -2%, .75rem .5rem, calc(100% - 1rem) .5rem, calc(100% - 1rem) -2%, 102% -2%, 102% 102%, -2% 102%) +} + +[dir=rtl] .field.label.border:not(.fill) > :is(input,textarea):is(:focus,[placeholder]:not(:placeholder-shown),.active), [dir=rtl] .field.label.border:not(.fill) > select { + clip-path: polygon(-2% -2%, 1rem -2%, 1rem .5rem, calc(100% - .75rem) .5rem, calc(100% - .75rem) -2%, 102% -2%, 102% 102%, -2% 102%) +} + +.field.label.border.round:not(.fill) > :is(input,textarea):is(:focus,[placeholder]:not(:placeholder-shown),.active), .field.label.border.round:not(.fill) > select { + clip-path: polygon(-2% -2%, 1.25rem -2%, 1.25rem .5rem, calc(100% - 1.75rem) .5rem, calc(100% - 1.75rem) -2%, 102% -2%, 102% 102%, -2% 102%) +} + +[dir=rtl] .field.label.border.round:not(.fill) > :is(input,textarea):is(:focus,[placeholder]:not(:placeholder-shown),.active), [dir=rtl] .field.label.border.round:not(.fill) > select { + clip-path: polygon(-2% -2%, 1.75rem -2%, 1.75rem .5rem, calc(100% - 1.25rem) .5rem, calc(100% - 1.25rem) -2%, 102% -2%, 102% 102%, -2% 102%) +} + +.field.label > :focus + label { + color: var(--primary) +} + +.field.label.invalid > label, .field.label.invalid > label:after { + color: var(--error) !important; + border-color: var(--error) !important +} + +.field.label > label > a { + block-size: inherit; + line-height: inherit; + inline-size: 1rem +} + +.field.label > label > a > :is(i,img,svg) { + block-size: 1rem; + line-height: 1rem; + inline-size: 1rem; + font-size: 1rem +} + +.field > output { + display: inline-block; + font-size: .75rem; + background: none !important; + padding: .25rem 1rem; + line-height: 1.25rem; + align-self: start +} + +.field > output.invalid { + color: var(--error) !important +} + +.field.round > output { + padding: .25rem 1.5rem +} + +.field.invalid > output:not(.invalid) { + display: none +} + +table td > .field { + margin: 0 +} + +fieldset { + border-radius: .25rem; + padding: 1rem; + border: .0625rem solid var(--outline-variant) +} + +fieldset > legend { + margin: 0 -.25rem; + padding: 0 .25rem +} + +fieldset > legend + * { + margin-block-start: 0 !important +} + +.grid { + --_gap: 1rem; + display: grid; + grid-template-columns:repeat(12, 1fr); + gap: var(--_gap); + block-size: auto +} + +.grid.no-space { + --_gap: 0rem +} + +.grid.medium-space { + --_gap: 1.5rem +} + +.grid.large-space { + --_gap: 2rem +} + +.grid > * { + margin: 0 +} + +.s1 { + grid-area: auto/span 1 +} + +.s2 { + grid-area: auto/span 2 +} + +.s3 { + grid-area: auto/span 3 +} + +.s4 { + grid-area: auto/span 4 +} + +.s5 { + grid-area: auto/span 5 +} + +.s6 { + grid-area: auto/span 6 +} + +.s7 { + grid-area: auto/span 7 +} + +.s8 { + grid-area: auto/span 8 +} + +.s9 { + grid-area: auto/span 9 +} + +.s10 { + grid-area: auto/span 10 +} + +.s11 { + grid-area: auto/span 11 +} + +.s12 { + grid-area: auto/span 12 +} + +@media only screen and (min-width: 601px) { + .m1 { + grid-area: auto/span 1 + } + + .m2 { + grid-area: auto/span 2 + } + + .m3 { + grid-area: auto/span 3 + } + + .m4 { + grid-area: auto/span 4 + } + + .m5 { + grid-area: auto/span 5 + } + + .m6 { + grid-area: auto/span 6 + } + + .m7 { + grid-area: auto/span 7 + } + + .m8 { + grid-area: auto/span 8 + } + + .m9 { + grid-area: auto/span 9 + } + + .m10 { + grid-area: auto/span 10 + } + + .m11 { + grid-area: auto/span 11 + } + + .m12 { + grid-area: auto/span 12 + } +} + +@media only screen and (min-width: 993px) { + .l1 { + grid-area: auto/span 1 + } + + .l2 { + grid-area: auto/span 2 + } + + .l3 { + grid-area: auto/span 3 + } + + .l4 { + grid-area: auto/span 4 + } + + .l5 { + grid-area: auto/span 5 + } + + .l6 { + grid-area: auto/span 6 + } + + .l7 { + grid-area: auto/span 7 + } + + .l8 { + grid-area: auto/span 8 + } + + .l9 { + grid-area: auto/span 9 + } + + .l10 { + grid-area: auto/span 10 + } + + .l11 { + grid-area: auto/span 11 + } + + .l12 { + grid-area: auto/span 12 + } +} + +i, :is(.checkbox,.radio,.switch) > span:before, :is(.checkbox,.radio,.switch) > span > i { + --_size: 1.5rem; + font-family: var(--font-icon); + font-weight: 400; + font-style: normal; + font-size: var(--_size); + letter-spacing: normal; + text-transform: none; + display: inline-flex; + align-items: center; + justify-content: center; + white-space: nowrap; + word-wrap: normal; + direction: ltr; + font-feature-settings: "liga"; + -webkit-font-smoothing: antialiased; + vertical-align: middle; + text-align: center; + overflow: hidden; + inline-size: var(--_size); + min-inline-size: var(--_size); + block-size: var(--_size); + min-block-size: var(--_size); + box-sizing: content-box; + line-height: normal; + border-radius: 0 +} + +i:has(.badge) { + overflow: unset +} + +i.tiny { + --_size: 1rem +} + +.chip > i, i.small { + --_size: 1.25rem +} + +i.medium { + --_size: 1.5rem +} + +i.large { + --_size: 1.75rem +} + +i.extra { + --_size: 2rem +} + +i.fill, a.active > i, button.active > i { + font-variation-settings: "FILL" 1 +} + +i > :is(img,svg) { + inline-size: 100%; + block-size: 100%; + background-size: 100%; + border-radius: inherit; + position: absolute; + inset: 0 auto auto 0; + padding: inherit +} + +i[class*=fa-] { + font-size: calc(var(--_size) * .85); + line-height: normal; + block-size: auto; + min-block-size: auto +} + +.absolute { + position: absolute +} + +.fixed { + position: fixed +} + +:is(.absolute,.fixed).left.right { + inline-size: auto +} + +:is(.absolute,.fixed).left.right.small { + block-size: 20rem +} + +:is(.absolute,.fixed).left.right.medium { + block-size: 28rem +} + +:is(.absolute,.fixed).left.right.large { + block-size: 44rem +} + +:is(.absolute,.fixed).top.bottom.small { + inline-size: 20rem +} + +:is(.absolute,.fixed).top.bottom.medium { + inline-size: 28rem +} + +:is(.absolute,.fixed).top.bottom.large { + inline-size: 44rem +} + +.list { + display: flex; + flex-direction: column; + padding: 0; + margin: 0; + flex: 1 +} + +.list > li, .list > li > details > summary, .list > li > a:only-child { + all: unset; + box-sizing: border-box; + position: relative; + display: flex; + align-items: center; + align-self: normal; + text-align: start; + justify-content: flex-start; + white-space: nowrap; + gap: 1rem; + min-block-size: 3.5rem; + padding: .5rem 1rem; + cursor: pointer; + flex: 1 +} + +.list > li:has(ul,ol,details[open],a:only-child) { + padding: 0 +} + +.list > li > .list { + padding: 0 0 0 1rem +} + +.list > li > *, .list > li > a:only-child > *, .list > li > details > summary > * { + margin: 0 +} + +.list > li > :is(details,.max), .list > li > a:only-child > .max, .list > li > details > summary > .max { + flex: 1 +} + +.list.border > li:not(:last-child):before, .list.border > li > details[open] > summary:before { + content: ""; + position: absolute; + background-color: var(--outline-variant); + inset: auto 0 0 0; + block-size: .0625rem; + inline-size: auto +} + +.list.no-space > li, .list.no-space > li > details > summary { + min-block-size: 2.5rem +} + +.list.medium-space > li, .list.medium-space > li > details > summary { + min-block-size: 4.5rem +} + +.list.large-space > li, .list.large-space > li > details > summary { + min-block-size: 5.5rem +} + +:has(>main) { + display: grid; + grid-template-columns:auto 1fr auto; + grid-template-rows:auto auto 1fr auto auto; + grid-template-areas:"left top right" "left header right" "left main right" "left footer right" "left bottom right"; + min-block-size: 100dvh; + box-sizing: border-box; + background-color: var(--surface) +} + +nav.left { + grid-area: left +} + +nav.right { + grid-area: right +} + +nav.top { + grid-area: top +} + +nav.bottom { + grid-area: bottom +} + +header { + grid-area: header +} + +footer { + grid-area: footer +} + +main { + --_padding: .5rem; + grid-area: main; + padding: var(--_padding); + overflow: hidden +} + +aside { + z-index: 1 +} + +aside:not(.fixed,.absolute).right { + float: right +} + +aside:not(.fixed,.absolute).left { + float: left +} + +svg { + fill: currentcolor +} + +:is(img,svg,video):is(.small,.medium,.large,.tiny,.extra,.round,.circle,.square,.responsive) { + --_size: 3rem; + object-fit: cover; + object-position: center; + transition: transform var(--speed3), border-radius var(--speed3), padding var(--speed3); + block-size: var(--_size); + inline-size: var(--_size) +} + +:is(img,svg,video).round { + --_round: .5rem +} + +:is(img,svg,video).tiny { + --_size: 2rem +} + +:is(img,svg,video).small { + --_size: 2.5rem +} + +:is(img,svg,video).large { + --_size: 3.5rem +} + +:is(img,svg,video).extra { + --_size: 4rem +} + +:is(img,svg,video).responsive { + --_size: 100%; + margin: 0 auto +} + +:is(img,svg,video).responsive.tiny { + inline-size: 100%; + block-size: 4rem +} + +:is(img,svg,video).responsive.small { + inline-size: 100%; + block-size: 8rem +} + +:is(img,svg,video).responsive.medium { + inline-size: 100%; + block-size: 12rem +} + +:is(img,svg,video).responsive.large { + inline-size: 100%; + block-size: 16rem +} + +:is(img,svg,video).responsive.extra { + inline-size: 100%; + block-size: 20rem +} + +:is(img,svg,video).responsive.round { + --_round: 2rem +} + +:is(img,svg,video).empty-state { + max-inline-size: 100%; + inline-size: 24rem +} + +:is(button,.button,.chip):not(.transparent) > .responsive { + border: .25rem solid transparent +} + +:is(button,.button,.chip,.field) > :is(img,svg):not(.responsive), .tabs :is(img,svg):not(.responsive) { + min-inline-size: 1.5rem; + max-inline-size: 1.5rem; + min-block-size: 1.5rem; + max-block-size: 1.5rem +} + +:is(button,.button,.chip):not(.extend) > .responsive:first-child { + margin-inline-start: calc(-1 * var(--_padding)) +} + +:is(button,.button,.chip):not(.extend) > .responsive:not(:first-child) { + margin-inline-end: calc(-1 * var(--_padding)) +} + +:is(button,.button,.chip,.circle,.square,.extend) > .responsive { + --_size: inherit; + margin: 0 auto +} + +.extend > :is(.responsive,i) { + margin: 0; + position: absolute; + inset-inline: 1rem; + z-index: 1 +} + +.extend > .responsive { + inset-inline: 0; + inline-size: 3.5rem +} + +.extend.border > .responsive { + inline-size: 3.375rem +} + +menu { + opacity: 0; + visibility: hidden; + position: absolute; + box-shadow: var(--elevate2); + background-color: var(--surface-container); + z-index: 11; + inset: auto auto 0 0; + inline-size: 100%; + max-block-size: 50vh; + max-inline-size: none !important; + overflow-x: hidden; + overflow-y: auto; + font-size: .875rem; + font-weight: 400; + text-transform: none; + color: var(--on-surface); + line-height: normal; + text-align: start; + border-radius: .25rem; + transform: scale(.8) translateY(120%); + transition: all var(--speed2), 0s background-color; + justify-content: flex-start; + padding: .5rem 0 +} + +[dir=rtl] menu { + inset: auto 0 0 auto +} + +menu.no-wrap { + inline-size: max-content; + white-space: nowrap !important +} + +menu.active, :not(menu,[data-ui]):focus-within > menu, menu > li:hover > menu, menu > li > menu:hover { + opacity: 1; + visibility: visible; + transform: scale(1) translateY(100%) +} + +menu.active.top, :not(menu,[data-ui]):focus-within > menu.top, menu > li:hover > menu.top, menu > li > menu.top:hover { + transform: scale(1) translateY(-100%) +} + +menu * { + white-space: inherit !important +} + +menu > li, menu > li > a:only-child { + all: unset; + box-sizing: border-box; + position: relative; + display: flex; + align-items: center; + align-self: normal; + text-align: start; + justify-content: inherit; + white-space: nowrap; + gap: 1rem; + padding: .5rem 1rem; + min-block-size: 3rem; + flex: 1; + margin: 0 !important; + cursor: pointer +} + +menu > li:is(:hover,:focus,.active) { + background-color: var(--active) +} + +menu > li > :is(.max,.field), menu > li > a:only-child > .max, menu > li:has(.field,a:only-child) { + flex: 1; + padding: 0; + margin: 0 +} + +menu.min { + inset: 0 0 auto 0; + transform: none !important; + background-color: var(--surface-variant) !important; + color: var(--on-surface-variant) !important; + padding: 0 +} + +[dir=rtl] menu.min.right, menu.min.left, menu.top.left { + inset: 0 0 auto auto +} + +[dir=rtl] menu.min.left, menu.min.right, menu.top, menu.top.right { + inset: 0 auto auto 0 +} + +menu.max { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + block-size: 100%; + max-block-size: none; + min-block-size: auto; + z-index: 100; + transform: none !important; + background-color: var(--surface-variant) !important; + color: var(--on-surface-variant) !important; + border-radius: 0 +} + +menu.no-wrap:is(.min,.max) { + min-inline-size: 16rem +} + +[dir=rtl] menu.right, [dir=rtl] menu.top.min.right, menu.left, menu.top.min.left { + inset: auto 0 0 auto +} + +[dir=rtl] menu.left, [dir=rtl] menu.top.min.left, menu.right, menu.top.min { + inset: auto auto 0 0 +} + +menu.top { + transform: scale(.8) translateY(-120%) +} + +menu:has(menu) { + --_child: 1; + --_type: 0; + overflow: unset; + white-space: nowrap; + inline-size: auto; + min-inline-size: 12rem; + max-block-size: none +} + +menu > li > :is(menu,menu.right), [dir=rtl] menu > li > menu.left { + inset: auto auto calc(3rem * (var(--_child) - var(--_type))) 100% +} + +[dir=rtl] menu > li > :is(menu,menu.right), menu > li > menu.left { + inset: auto 100% calc(3rem * (var(--_child) - var(--_type))) auto +} + +menu > li > :is(menu.top,menu.top.right), [dir=rtl] menu > li > menu.top.left { + inset: calc(3rem * (var(--_child) - var(--_type))) auto auto 100% +} + +[dir=rtl] menu > li > :is(menu.top,menu.top.right), menu > li > menu.top.left { + inset: calc(3rem * (var(--_child) - var(--_type))) 100% auto auto +} + +menu.no-space > li { + min-block-size: 2.5rem +} + +menu.medium-space > li { + min-block-size: 3.5rem +} + +menu.large-space > li { + min-block-size: 4rem +} + +menu.border > li:not(:last-child):before { + content: ""; + position: absolute; + background-color: var(--outline-variant); + inset: auto 0 0 0; + block-size: .0625rem; + inline-size: auto +} + +menu.transparent { + margin: 0 -1rem !important; + padding: .5rem +} + +menu.transparent > li { + background-color: inherit; + box-shadow: none; + padding: 0 +} + +menu > li:nth-last-child(2) { + --_child: 2 +} + +menu > li:nth-last-child(3) { + --_child: 3 +} + +menu > li:nth-last-child(4) { + --_child: 4 +} + +menu > li:nth-last-child(5) { + --_child: 5 +} + +menu > li:nth-last-child(6) { + --_child: 6 +} + +menu > li:nth-last-child(7) { + --_child: 7 +} + +menu > li:nth-last-child(8) { + --_child: 8 +} + +menu > li:nth-last-child(9) { + --_child: 9 +} + +menu > li:nth-last-child(10) { + --_child: 10 +} + +menu > li:nth-last-child(11) { + --_child: 11 +} + +menu > li:nth-last-of-type(2) { + --_type: 1 +} + +menu > li:nth-last-of-type(3) { + --_type: 2 +} + +menu > li:nth-last-of-type(4) { + --_type: 3 +} + +menu > li:nth-last-of-type(5) { + --_type: 4 +} + +menu > li:nth-last-of-type(6) { + --_type: 5 +} + +menu > li:nth-last-of-type(7) { + --_type: 6 +} + +menu > li:nth-last-of-type(8) { + --_type: 7 +} + +menu > li:nth-last-of-type(9) { + --_type: 8 +} + +menu > li:nth-last-of-type(10) { + --_type: 9 +} + +menu > li:nth-last-of-type(11) { + --_type: 10 +} + +@media (pointer: coarse) { + :not(menu,[data-ui]):hover > menu { + opacity: 1; + visibility: visible; + transform: scale(1) translateY(100%) + } + + :not(menu,[data-ui]):hover > menu.top { + transform: scale(1) translateY(-100%) + } +} + +nav > :is(ol,ul), nav > :is(ol,ul) > li { + all: unset +} + +nav, .row, a.row { + display: flex; + align-items: center; + align-self: normal; + text-align: start; + justify-content: flex-start; + white-space: nowrap; + gap: 1rem; + border-radius: 0; + min-inline-size: 0 +} + +a.row, nav.row { + min-block-size: 3rem; + margin: 0 +} + +:is(nav,.row,.max) > :only-child, nav > :is(ol,ul) > li > :only-child { + margin: 0 +} + +:is(nav,.row) > :not(ul,ol,header,footer) { + margin: 0; + white-space: normal; + flex: none +} + +:is(nav,.row).min { + display: inline-flex +} + +:is(nav,.row,li).no-space { + gap: 0 +} + +:is(nav,.row,li).tiny-space { + gap: .5rem +} + +:is(nav,.row,li).medium-space { + gap: 1.5rem +} + +:is(nav,.row,li).large-space { + gap: 2rem +} + +:is(nav,.row) > .max, :is(nav,.row) > :is(ol,ul) > .max { + flex: 1 +} + +:is(nav,.row).wrap { + display: flex; + flex-wrap: wrap +} + +:is(header,footer) > :is(nav,.row) { + min-block-size: inherit +} + +nav:is(.left,.right,.top,.bottom) { + --_padding: .5rem; + --_top: calc(var(--_padding) + var(--top)); + --_bottom: calc(var(--_padding) + var(--bottom)); + position: sticky; + top: 0; + right: 0; + bottom: 0; + left: 0; + border: 0; + color: var(--on-surface); + transform: none; + z-index: 100; + text-align: center; + padding: var(--_padding); + margin: 0 +} + +nav:is(.left,.right) { + justify-content: flex-start; + flex-direction: column; + background-color: var(--surface); + block-size: 100dvh; + min-inline-size: 6rem; + padding-block: var(--_top) var(--_bottom) +} + +nav:is(.top,.bottom) { + position: sticky; + top: 0; + right: 0; + bottom: 0; + left: 0; + padding: .5rem; + justify-content: center; + flex-direction: row; + background-color: var(--surface-container); + block-size: auto; + min-block-size: 4.5rem +} + +nav.top { + block-size: calc(var(--top) + 4.5rem); + padding-block-start: var(--_top) +} + +nav.bottom { + block-size: calc(var(--bottom) + 4.5rem); + padding-block-end: var(--_bottom) +} + +nav > header { + min-block-size: auto; + padding: 0; + margin: 0 0 1rem; + align-items: flex-start; + gap: 1rem; + background: none !important +} + +nav:is(.top,.bottom) > header { + flex-direction: row; + align-items: center; + margin: 0 1rem 0 0 +} + +nav > header > * { + margin: 0 +} + +nav > header > .extend:hover { + --_padding: 0; + inline-size: var(--_size) +} + +nav > header > .extend:hover > span { + display: none +} + +nav > :is(ol,ul) { + all: inherit; + min-inline-size: auto; + margin: 0; + padding: 0; + flex: auto +} + +nav.max:is(.left,.right,.top,.bottom) { + gap: 0; + inline-size: auto; + align-items: flex-start; + min-inline-size: 12.75rem; + padding: var(--_top) 1.25rem var(--_bottom) 1.25rem +} + +nav.max > :is(ol,ul) { + padding: 0 +} + +nav.max > header { + margin: 0 0 1.25rem +} + +nav.max:is(.top,.bottom) > header { + margin: 0 1.25rem 0 0 +} + +nav.max > header > .extend { + --_padding: 1.5rem; + inline-size: auto; + padding: 0 var(--_padding) +} + +nav.max > header > .extend > span { + display: block; + margin-inline-start: var(--_padding) +} + +nav.max > header > .extend > :is(img,svg) + span { + margin-inline-start: calc(1rem + var(--_padding)) +} + +nav.max:is(.top,.bottom) { + padding: 0 .5rem; + align-items: center; + min-inline-size: auto; + max-inline-size: none +} + +nav:is(.left,.right,.top,.bottom) > a:not(.button,.chip), nav:is(.left,.right,.top,.bottom) > :is(ol,ul) > li > a:not(.button,.chip) { + display: flex; + flex-direction: column; + gap: .25rem; + line-height: normal; + inline-size: 3.5rem; + font-size: .8rem +} + +nav:not(.max):is(.left,.right,.top,.bottom) > a:not(.button,.chip) > i, nav:not(.max):is(.left,.right,.top,.bottom) > :is(ol,ul) > li > a:not(.button,.chip) > i { + padding: .25rem 1rem; + border-radius: 2rem; + transition: padding var(--speed1) linear; + margin: 0 auto +} + +nav.max:is(.left,.right,.top,.bottom) > a:not(.button,.chip), nav.max:is(.left,.right,.top,.bottom) > :is(ol,ul) > li > a:not(.button,.chip) { + flex-direction: row; + gap: .5rem; + inline-size: auto; + block-size: 3.5rem; + padding: 0 1rem; + border-radius: 2rem; + font-size: inherit +} + +nav.max:is(.top,.bottom) > a:not(.button,.chip), nav.max:is(.top,.bottom) > :is(ol,ul) > li > a:not(.button,.chip) { + gap: .25rem; + block-size: 2.5rem; + font-size: .8rem +} + +nav.max:is(.left,.right,.top,.bottom) > a.active:not(.button,.chip), nav.max:is(.left,.right,.top,.bottom) > :is(ol,ul) > li > a.active:not(.button,.chip), nav:is(.left,.right,.top,.bottom):not(.max) > a.active:not(.button,.chip) > i, nav:is(.left,.right,.top,.bottom):not(.max) > :is(ol,ul) > li > a.active:not(.button,.chip) > i { + background-color: var(--secondary-container); + color: var(--on-secondary-container) +} + +:is(nav,.row):is(.left-align,.top-align,.vertical) { + justify-content: flex-start +} + +:is(nav,.row):is(.right-align,.bottom-align) { + justify-content: flex-end +} + +:is(nav,.row):is(.center-align,.middle-align) { + justify-content: center +} + +:is(nav,.row):is(.left-align,.top-align,.vertical).vertical { + align-items: flex-start +} + +:is(nav,.row):is(.right-align,.bottom-align).vertical { + align-items: flex-end +} + +:is(nav,.row):is(.center-align,.middle-align).vertical { + align-items: center +} + +nav:not(.left,.right) > .space { + inline-size: .5rem +} + +nav:not(.left,.right) > .medium-space { + inline-size: 1rem +} + +nav:not(.left,.right) > .large-space { + inline-size: 1.5rem +} + +nav.tabbed { + background-color: var(--surface-container); + border-radius: 4rem !important; + gap: 0rem; + block-size: 4rem +} + +nav.tabbed.small { + block-size: 3rem +} + +nav.tabbed.large { + block-size: 5rem +} + +nav.tabbed > a { + border-radius: inherit; + block-size: inherit; + display: inline-flex; + align-items: center; + padding-inline: 1rem; + gap: .5rem; + font-size: 1rem; + flex: 1 +} + +nav.tabbed > a.active { + background-color: var(--primary-container) +} + +nav.toolbar { + display: inline-flex; + justify-content: space-around; + border-radius: 2rem; + background-color: var(--surface-container); + color: var(--on-surface); + padding: 0 1rem; + gap: .5rem; + min-block-size: 4rem; + min-inline-size: 4rem +} + +nav.toolbar > a { + display: inline-flex; + gap: .5rem; + min-inline-size: 2.5rem; + min-block-size: 2.5rem; + border-radius: 1.75rem +} + +nav.toolbar > a:has(>:not(i)) { + padding: 0 1rem +} + +nav.toolbar > a.active { + background-color: var(--secondary-container); + color: var(--on-secondary-container) +} + +nav.toolbar.fill { + background-color: var(--primary-container) !important; + color: var(--on-primary-container) !important +} + +nav.toolbar.fill > a.active { + background-color: var(--surface-container) !important; + color: var(--on-surface) !important +} + +nav.toolbar.vertical { + flex-direction: column !important; + min-inline-size: 4rem; + padding: 1rem 0; + align-self: center; + align-items: center !important +} + +nav.toolbar.vertical > a { + inline-size: 2.5rem; + block-size: 2.5rem +} + +nav.toolbar.vertical > a > :is(div,span):not(.badge,.tooltip) { + display: none +} + +nav.toolbar.max { + border-radius: 0; + display: flex +} + +nav.group { + background: none !important +} + +nav.group:is(.connected,.split) { + gap: .125rem +} + +nav.group:not(.split) > :is(.button,button):not(.border) { + background-color: var(--surface-container); + color: var(--on-surface-container) +} + +nav.group:not(.split) > :is(.button,button).active { + background-color: var(--primary); + color: var(--on-primary) +} + +nav.group.connected > :is(.button,button):not(.border) { + background-color: var(--surface-container); + color: var(--on-surface-container) +} + +nav.group.connected > :is(.button,button).active { + background-color: var(--secondary-container); + color: var(--on-secondary-container) +} + +nav.group:is(.connected,.split) > :is(.button,button).active, nav.split > :is(.button,button):active { + border-radius: 2rem !important +} + +:not(nav) > :is(ul,ol) { + all: revert +} + +:is(.scroll,.no-scroll,.no-space,.tabs,.tabbed) > :focus-visible { + outline: .125rem solid var(--primary); + outline-offset: -.125rem +} + +nav.split > :is(.button,button):not(.chip,.fill,.border) { + background-color: var(--primary); + color: var(--on-primary) +} + +nav.group.primary-container > button, nav:is(.max,.toolbar,.tabbed,.group).primary > :is(button,a).active, nav:not(.max,.toolbar,.tabbed,.group).primary > a.active:not(.button,.chip) > i { + background-color: var(--primary-container) !important; + color: var(--on-primary-container) !important +} + +nav.group.primary > button, nav:is(.max,.toolbar,.tabbed,.group).primary-container > :is(button,a).active, nav:not(.max,.toolbar,.tabbed,.group).primary-container > a.active:not(.button,.chip) > i { + background-color: var(--primary) !important; + color: var(--on-primary) !important +} + +nav.group.secondary-container > button, nav:is(.max,.toolbar,.tabbed,.group).secondary > :is(button,a).active, nav:not(.max,.toolbar,.tabbed,.group).secondary > a.active:not(.button,.chip) > i { + background-color: var(--secondary-container) !important; + color: var(--on-secondary-container) !important +} + +nav.group.secondary > button, nav:is(.max,.toolbar,.tabbed,.group).secondary-container > :is(button,a).active, nav:not(.max,.toolbar,.tabbed,.group).secondary-container > a.active:not(.button,.chip) > i { + background-color: var(--secondary) !important; + color: var(--on-secondary) !important +} + +nav.group.tertiary-container > button, nav:is(.max,.toolbar,.tabbed,.group).tertiary > :is(button,a).active, nav:not(.max,.toolbar,.tabbed,.group).tertiary > a.active:not(.button,.chip) > i { + background-color: var(--tertiary-container) !important; + color: var(--on-tertiary-container) !important +} + +nav.group.tertiary > button, nav:is(.max,.toolbar,.tabbed,.group).tertiary-container > :is(button,a).active, nav:not(.max,.toolbar,.tabbed,.group).tertiary-container > a.active:not(.button,.chip) > i { + background-color: var(--tertiary) !important; + color: var(--on-tertiary) !important +} + +@media only screen and (max-width: 600px) { + nav.top, nav.bottom { + justify-content: space-around + } +} + +.overlay, dialog::backdrop { + display: block !important; + opacity: 0; + visibility: hidden; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + color: var(--on-surface); + background-color: var(--overlay); + z-index: 100; + transition: all var(--speed3), 0s background-color; + border-radius: 0 +} + +.overlay.active { + opacity: 1; + visibility: visible +} + +dialog:popover-open::backdrop { + opacity: 1; + visibility: visible +} + +.overlay + dialog::backdrop, .snackbar::backdrop { + display: none +} + +[popover] { + border: 0 +} + +.page { + --_transform: translate(0, 0); + opacity: 0; + position: absolute; + display: none +} + +.page.active { + opacity: 1; + position: inherit; + display: inherit; + animation: var(--speed4) to-page ease +} + +.page.active.top { + --_transform: translate(0, -4rem) +} + +.page.active.bottom { + --_transform: translate(0, 4rem) +} + +.page.active.left { + --_transform: translate(-4rem, 0) +} + +.page.active.right { + --_transform: translate(4rem, 0) +} + +@keyframes to-page { + 0% { + opacity: 0; + transform: var(--_transform) + } + to { + opacity: 1; + transform: translate(0) + } +} + +progress { + --_light: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4AWJiYGAQBgAAAP//ZyYa+wAAAAZJREFUAwAAIgAWeX9MsQAAAABJRU5ErkJggg==); + --_dark: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR4AWL6//+/FAAAAP//qSv5QwAAAAZJREFUAwAJIAMaJWjIvQAAAABJRU5ErkJggg==); + --_size: .25rem; + position: relative; + inline-size: 100%; + block-size: var(--_size); + color: var(--primary); + background: var(--_light); + border-radius: 1rem; + flex: none; + border: none; + overflow: hidden; + writing-mode: horizontal-tb; + direction: ltr; + -webkit-appearance: none; + -moz-appearance: none; + appearance: none +} + +.dark progress { + background: var(--_dark) +} + +progress.small { + --_size: .25rem +} + +progress.medium { + --_size: .35rem +} + +progress.large { + --_size: .45rem +} + +progress.indeterminate { + --_value: 100; + animation: 3.2s to-indeterminate ease infinite +} + +progress:not(.circle,[value]):after { + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + inline-size: 100%; + block-size: 100%; + clip-path: none; + background: currentcolor; + animation: 3.2s to-linear ease infinite +} + +progress:not(.circle,[value])::-moz-progress-bar { + animation: 3.2s to-linear ease infinite +} + +progress:not(.circle,[value])::-webkit-progress-value { + animation: 3.2s to-linear ease infinite +} + +progress::-webkit-progress-bar { + background: none +} + +progress::-webkit-progress-value { + background: currentColor +} + +progress::-moz-progress-bar { + background: currentColor +} + +progress.wavy { + block-size: calc(var(--_size) * 2); + background: none; + background-image: var(--_light); + background-repeat: repeat-x; + background-position: 0 50%; + background-size: auto calc(var(--_size) / 2) +} + +.dark progress.wavy { + background-image: var(--_dark) +} + +progress.wavy::-webkit-progress-value, progress.wavy:not(.circle,[value]):after { + -webkit-mask-image: url(wavy.svg); + mask-image: url(wavy.svg); + -webkit-mask-position: 0 50%; + mask-position: 0 50%; + -webkit-mask-repeat: repeat-x; + mask-repeat: repeat-x; + -webkit-mask-size: auto 100%; + mask-size: auto 100% +} + +progress.wavy::-moz-progress-bar { + -webkit-mask-image: url(wavy.svg); + mask-image: url(wavy.svg); + -webkit-mask-position: 0 50%; + mask-position: 0 50%; + -webkit-mask-repeat: repeat-x; + mask-repeat: repeat-x; + -webkit-mask-size: auto 100%; + mask-size: auto 100% +} + +progress.circle { + --_value: attr(value type(< number >), 50); + inline-size: 2.5rem; + block-size: 2.5rem; + background: conic-gradient(currentColor calc(var(--_value) * 1%), var(--active) 0%); + border-radius: 50%; + -webkit-mask-image: radial-gradient(circle at center, transparent 57%, currentColor 60%); + mask-image: radial-gradient(circle at center, transparent 57%, currentColor 60%) +} + +progress.circle::-webkit-progress-value { + background: none +} + +progress.circle::-moz-progress-bar { + background: none +} + +progress.circle.wavy { + background: conic-gradient(currentColor calc(var(--_value) * 1%), var(--active) 0); + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; + -webkit-mask-position: center; + mask-position: center; + -webkit-mask-size: contain; + mask-size: contain; + -webkit-mask-image: url(wavy-circle.svg); + mask-image: url(wavy-circle.svg) +} + +progress.circle.small { + inline-size: 1.5rem; + block-size: 1.5rem +} + +progress.circle.large { + inline-size: 3.5rem; + block-size: 3.5rem +} + +progress.circle:not([value]), progress.circle.indeterminate { + --_value: 50; + animation: to-rotate 1s infinite linear +} + +:is(nav,.row,.field) > progress:not(.circle,.small,.medium,.large) { + flex: auto +} + +progress.max { + display: unset; + position: absolute; + inline-size: 100% !important; + block-size: 100% !important; + color: currentColor; + background: none; + top: 0; + right: 0; + bottom: 0; + left: 0; + border-radius: inherit; + animation: none; + writing-mode: horizontal-tb; + opacity: .33 +} + +progress.max[class*=-text] { + opacity: 1 +} + +progress.max + * { + margin-block-start: 0 +} + +:is(.button,button,.chip) > progress.circle { + color: inherit +} + +@keyframes to-linear { + 0% { + margin: 0 0 0 -100% + } + 50% { + margin: 0 + } + to { + margin: 0 0 0 100% + } +} + +@keyframes to-indeterminate { + 0% { + padding: 0 100% 0 0 + } + 50% { + padding: 0 + } + to { + padding: 0 0 0 100% + } +} + +@keyframes to-rotate { + 0% { + transform: rotate(0) + } + to { + transform: rotate(360deg) + } +} + +.shape { + display: flex; + align-items: center; + justify-content: center; + color: var(--on-primary); + background-color: var(--primary); + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; + -webkit-mask-position: center; + mask-position: center; + -webkit-mask-size: contain; + mask-size: contain; + border-radius: 0; + block-size: 3.5rem; + inline-size: 3.5rem; + margin: 0 !important; + padding: 0 !important; + border: 0 !important +} + +.transparent > .shape > i { + filter: invert(1) +} + +.shape.tiny-space { + -webkit-mask-size: 90%; + mask-size: 90% +} + +.shape.space, .shape.small-space { + -webkit-mask-size: 80%; + mask-size: 80% +} + +.shape.medium-space { + -webkit-mask-size: 70%; + mask-size: 70% +} + +.shape.large-space { + -webkit-mask-size: 60%; + mask-size: 60% +} + +.shape.extra-space { + -webkit-mask-size: 50%; + mask-size: 50% +} + +.shape.tiny { + block-size: 2.5rem; + inline-size: 2.5rem +} + +.shape.medium { + block-size: 4.5rem; + inline-size: 4.5rem +} + +.shape.large { + block-size: 5.5rem; + inline-size: 5.5rem +} + +.shape.extra { + block-size: 6.5rem; + inline-size: 6.5rem +} + +.shape.max, .shape > .responsive, .shape > .responsive > .responsive { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + block-size: 100%; + inline-size: 100%; + margin: 0 !important; + padding: 0 !important; + border: 0 !important +} + +.shape > .responsive { + background: inherit; + color: inherit +} + +.shape.rotate { + animation: linear to-shape-rotate infinite 12s +} + +.shape.rotate > * { + animation: linear to-shape-rotate infinite 12s reverse +} + +.shape.fast-rotate { + animation: linear to-shape-rotate infinite 6s +} + +.shape.fast-rotate > * { + animation: linear to-shape-rotate infinite 6s reverse +} + +.shape.slow-rotate { + animation: linear to-shape-rotate infinite 24s +} + +.shape.slow-rotate > * { + animation: linear to-shape-rotate infinite 24s reverse +} + +:is(button,.button,.chip):has(>.shape) > .responsive { + border: none +} + +.shape.arch { + -webkit-mask-image: url(arch.svg); + mask-image: url(arch.svg) +} + +.shape.arrow { + -webkit-mask-image: url(arrow.svg); + mask-image: url(arrow.svg) +} + +.shape.boom { + -webkit-mask-image: url(boom.svg); + mask-image: url(boom.svg) +} + +.shape.bun { + -webkit-mask-image: url(bun.svg); + mask-image: url(bun.svg) +} + +.shape.burst { + -webkit-mask-image: url(burst.svg); + mask-image: url(burst.svg) +} + +.shape.circle { + -webkit-mask-image: url(circle.svg); + mask-image: url(circle.svg) +} + +.shape.clamshell { + -webkit-mask-image: url(clamshell.svg); + mask-image: url(clamshell.svg) +} + +.shape.diamond { + -webkit-mask-image: url(diamond.svg); + mask-image: url(diamond.svg) +} + +.shape.fan { + -webkit-mask-image: url(fan.svg); + mask-image: url(fan.svg) +} + +.shape.flower { + -webkit-mask-image: url(flower.svg); + mask-image: url(flower.svg) +} + +.shape.gem { + -webkit-mask-image: url(gem.svg); + mask-image: url(gem.svg) +} + +.shape.ghost-ish { + -webkit-mask-image: url(ghost-ish.svg); + mask-image: url(ghost-ish.svg) +} + +.shape.heart { + -webkit-mask-image: url(heart.svg); + mask-image: url(heart.svg) +} + +.shape.leaf-clover4 { + -webkit-mask-image: url(leaf-clover4.svg); + mask-image: url(leaf-clover4.svg) +} + +.shape.leaft-clover8 { + -webkit-mask-image: url(leaf-clover8.svg); + mask-image: url(leaf-clover8.svg) +} + +.shape.loading-indicator { + -webkit-mask-image: url(loading-indicator.svg); + mask-image: url(loading-indicator.svg) +} + +.shape.oval { + -webkit-mask-image: url(oval.svg); + mask-image: url(oval.svg) +} + +.shape.pentagon { + -webkit-mask-image: url(pentagon.svg); + mask-image: url(pentagon.svg) +} + +.shape.pill { + -webkit-mask-image: url(pill.svg); + mask-image: url(pill.svg) +} + +.shape.pixel-circle { + -webkit-mask-image: url(pixel-circle.svg); + mask-image: url(pixel-circle.svg) +} + +.shape.pixel-triangle { + -webkit-mask-image: url(pixel-triangle.svg); + mask-image: url(pixel-triangle.svg) +} + +.shape.puffy { + -webkit-mask-image: url(puffy.svg); + mask-image: url(puffy.svg) +} + +.shape.puffy-diamond { + -webkit-mask-image: url(puffy-diamond.svg); + mask-image: url(puffy-diamond.svg) +} + +.shape.semicircle { + -webkit-mask-image: url(semicircle.svg); + mask-image: url(semicircle.svg) +} + +.shape.sided-cookie4 { + -webkit-mask-image: url(sided-cookie4.svg); + mask-image: url(sided-cookie4.svg) +} + +.shape.sided-cookie6 { + -webkit-mask-image: url(sided-cookie6.svg); + mask-image: url(sided-cookie6.svg) +} + +.shape.sided-cookie7 { + -webkit-mask-image: url(sided-cookie7.svg); + mask-image: url(sided-cookie7.svg) +} + +.shape.sided-cookie9 { + -webkit-mask-image: url(sided-cookie9.svg); + mask-image: url(sided-cookie9.svg) +} + +.shape.sided-cookie12 { + -webkit-mask-image: url(sided-cookie12.svg); + mask-image: url(sided-cookie12.svg) +} + +.shape.slanted { + -webkit-mask-image: url(slanted.svg); + mask-image: url(slanted.svg) +} + +.shape.soft-boom { + -webkit-mask-image: url(soft-boom.svg); + mask-image: url(soft-boom.svg) +} + +.shape.soft-burst { + -webkit-mask-image: url(soft-burst.svg); + mask-image: url(soft-burst.svg) +} + +.shape.square { + -webkit-mask-image: url(square.svg); + mask-image: url(square.svg) +} + +.shape.sunny { + -webkit-mask-image: url(sunny.svg); + mask-image: url(sunny.svg) +} + +.shape.triangle { + -webkit-mask-image: url(triangle.svg); + mask-image: url(triangle.svg) +} + +.shape.very-sunny { + -webkit-mask-image: url(very-sunny.svg); + mask-image: url(very-sunny.svg) +} + +@keyframes to-shape-rotate { + 0% { + transform: rotate(0) + } + to { + transform: rotate(360deg) + } +} + +.checkbox, .radio, .switch { + --_size: 1.5rem; + inline-size: auto; + block-size: auto; + line-height: normal; + white-space: nowrap; + cursor: pointer; + display: inline-flex; + align-items: center +} + +.switch { + direction: ltr +} + +:is(.checkbox,.radio,.switch).small { + --_size: 1rem +} + +:is(.checkbox,.radio,.switch).large { + --_size: 2rem +} + +:is(.checkbox,.radio,.switch).extra { + --_size: 2.5rem +} + +:is(.checkbox,.radio) > input { + inline-size: var(--_size); + block-size: var(--_size); + opacity: 0 +} + +.switch > input { + inline-size: 3.25rem; + block-size: 2rem; + opacity: 0 +} + +:is(.checkbox,.radio,.switch) > span { + display: inline-flex; + align-items: center; + color: var(--on-surface); + font-size: .875rem +} + +:is(.checkbox,.radio) > span:not(:empty) { + padding-inline-start: .25rem +} + +:is(.checkbox,.radio,.switch) > span:before, :is(.checkbox,.radio,.switch) > span > i, :is(.checkbox,.radio) > span:after { + --_size: inherit; + content: ""; + inline-size: var(--_size); + block-size: var(--_size); + box-sizing: border-box; + margin: 0 auto; + outline: none; + color: var(--primary); + position: absolute; + inset: auto auto auto calc(var(--_size) * -1); + border-radius: 50%; + -webkit-user-select: none; + user-select: none; + z-index: 1 +} + +[dir=rtl] :is(.checkbox,.radio) > span:before, [dir=rtl] :is(.checkbox,.radio) > span > i, [dir=rtl] :is(.checkbox,.radio) > span:after { + --_size: inherit; + inset: auto calc(var(--_size) * -1) auto auto +} + +.switch > span:before, .switch.icon > span > i { + position: absolute; + inset: 50% auto auto 0; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 50%; + transition: all var(--speed2); + font-size: calc(var(--_size) - .5rem); + -webkit-user-select: none; + user-select: none; + min-inline-size: var(--_size); + min-block-size: var(--_size); + content: ""; + color: var(--surface-variant); + background-color: var(--outline) +} + +.switch > span:before, .switch.icon > span > i { + transform: translate(-3rem, -50%) scale(.6) +} + +.switch.icon > span > i { + transform: translate(-3rem, -50%) scale(1) +} + +.checkbox > span:before { + content: "check_box_outline_blank" +} + +.checkbox > input:checked + span:before { + content: "check_box"; + font-variation-settings: "FILL" 1 +} + +.checkbox > input:indeterminate + span:before { + content: "indeterminate_check_box" +} + +.radio > span:before { + content: "radio_button_unchecked" +} + +.radio > input:checked + span:before { + content: "radio_button_checked" +} + +:is(.radio,.checkbox,.switch).icon > span:before { + content: "" !important; + font-variation-settings: unset !important +} + +:is(.checkbox,.radio) > span:after { + transition: all var(--speed1); + background-color: currentColor; + box-shadow: 0 0 0 0 currentColor; + opacity: 0 +} + +:is(.checkbox,.radio):is(:hover) > input:not(:disabled) + span:after, :is(.checkbox,.radio) > input:not(:disabled):is(:focus) + span:after { + box-shadow: 0 0 0 .5rem currentColor; + opacity: .1 +} + +.switch > input:not(:disabled):is(:focus,:hover) + span:before, .switch.icon > input:not(:disabled):is(:focus,:hover) + span > i { + box-shadow: 0 0 0 .5rem var(--active) +} + +:is(.checkbox,.radio) > input:checked + span:before, :is(.checkbox,.radio).icon > input:checked + span > i { + color: var(--primary) +} + +.icon > input:checked + span > i:first-child, .icon > span > i:last-child { + opacity: 0 +} + +.icon > input:checked + span > i:last-child, .icon > span > i:first-child { + opacity: 1 +} + +.switch > input:checked + span:after { + border: none; + background-color: var(--primary) +} + +.switch > input:checked + span:before, .switch.icon > input:checked + span > i { + content: "check"; + color: var(--primary); + background-color: var(--on-primary); + transform: translate(-1.75rem, -50%) scale(1) +} + +.switch > input:active:not(:disabled) + span:before, .switch.icon > input:active:not(:disabled) + span > i { + transform: translate(-3rem, -50%) scale(1.2) +} + +[dir=rtl] .switch > input:active:not(:disabled) + span:before, [dir=rtl] .switch.icon > input:active:not(:disabled) + span > i { + transform: translate(-3rem, -50%) scale(-1.2) +} + +.switch > input:active:checked:not(:disabled) + span:before, .switch.icon > input:active:checked:not(:disabled) + span > i { + transform: translate(-1.75rem, -50%) scale(1.2) +} + +[dir=rtl] .switch > input:active:checked:not(:disabled) + span:before, [dir=rtl] .switch.icon > input:active:checked:not(:disabled) + span > i { + transform: translate(-1.75rem, -50%) scale(-1.2) +} + +:is(.checkbox,.radio,.switch) > input:disabled + span { + opacity: .5; + cursor: not-allowed +} + +.switch > span:after { + content: ""; + position: absolute; + inset: 50% auto auto 0; + background-color: var(--active); + border: .125rem solid var(--outline); + box-sizing: border-box; + inline-size: 3.25rem; + block-size: 2rem; + border-radius: 2rem; + transform: translate(-3.25rem, -50%) +} + +.field > :is(nav,.row) { + flex-grow: 1; + padding: 0 1rem +} + +.field.round > :is(nav,.row) { + flex-grow: 1; + padding: 0 1.5rem +} + +[dir=rtl] .switch { + transform: scale(-1) +} + +[dir=rtl] .switch > span:before, [dir=rtl] .switch.icon > span > i { + transform: translate(-3rem, -50%) scale(-.6) +} + +[dir=rtl] .switch.icon > span > i { + transform: translate(-3rem, -50%) scale(-1) +} + +[dir=rtl] .switch > input:checked + span:before, [dir=rtl] .switch.icon > input:checked + span > i { + transform: translate(-1.75rem, -50%) scale(-1) +} + +.switch > :focus-visible + span:after { + outline: .125rem solid var(--primary); + outline-offset: .25rem +} + +:is(.checkbox,.radio) > :focus-visible + span:before { + outline: .125rem solid var(--primary); + outline-offset: .375rem +} + +.slider { + --_start: 0%; + --_end: 0%; + --_value1: ""; + --_value2: ""; + --_track: 1rem; + --_thumb: max(2.5rem, calc(var(--_track) + .5rem)); + display: flex; + align-items: center !important; + inline-size: auto; + block-size: var(--_thumb); + flex: none; + direction: ltr; + margin: 0 1.25rem +} + +[dir=rtl] .slider { + transform: scaleX(-1) +} + +.slider.vertical { + flex-direction: row !important; + margin: .5rem auto !important; + padding: 50% 0; + transform: rotate(-90deg); + inline-size: 100% +} + +.slider.tiny { + --_track: 1rem +} + +.slider.small { + --_track: 1.5rem +} + +.slider.medium { + --_track: 2.5rem +} + +.slider.large { + --_track: 3.5rem +} + +.slider.extra { + --_track: 6rem +} + +.slider > input { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + box-shadow: none; + border: none; + outline: none; + pointer-events: none; + inline-size: 100%; + block-size: var(--_track); + background: none; + z-index: 1; + padding: 0; + margin: 0; + transform: rotate(0) +} + +.slider > input:only-of-type { + pointer-events: all +} + +.slider > input + input { + position: absolute +} + +.slider > input::-webkit-slider-thumb { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + box-shadow: none; + border: none; + outline: none; + pointer-events: all; + block-size: var(--_thumb); + inline-size: .25rem; + border-radius: .25rem; + background: var(--primary); + cursor: grab; + margin: 0; + z-index: 1 +} + +.slider > input::-webkit-slider-thumb:active { + cursor: grabbing +} + +.slider > input::-moz-range-thumb { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + box-shadow: none; + border: none; + outline: none; + pointer-events: all; + block-size: 2.75rem; + inline-size: .25rem; + border-radius: .25rem; + background: var(--primary); + cursor: grab; + margin: 0 +} + +.slider > input::-moz-range-thumb:active { + cursor: grabbing +} + +.slider > input:not(:disabled):is(:focus)::-webkit-slider-thumb { + transform: scaleX(.6) +} + +.slider > input:not(:disabled):is(:focus)::-moz-range-thumb { + transform: scaleX(.6) +} + +.slider > input:disabled { + cursor: not-allowed; + opacity: 1 +} + +.slider > input:disabled::-webkit-slider-thumb { + background: var(--outline); + cursor: not-allowed +} + +.slider > input:disabled::-moz-range-thumb { + background: var(--outline); + cursor: not-allowed +} + +.slider > input:disabled ~ span { + background: var(--outline) +} + +.slider > span { + position: absolute; + block-size: var(--_track); + border-radius: 1rem 0 0 1rem; + background: var(--primary); + color: var(--on-primary); + z-index: 0; + inset: calc(50% - (var(--_track) / 2)) var(--_end) auto var(--_start); + clip-path: polygon(0 0, calc(100% - .5rem) 0, calc(100% - .5rem) 100%, 0 100%) +} + +.slider > input[type=range] + input[type=range] ~ span { + border-radius: 0; + clip-path: polygon(.5rem 0, max(.5rem, calc(100% - .5rem)) 0, max(.5rem, calc(100% - .5rem)) 100%, .5rem 100%) +} + +.field > .slider { + inline-size: 100% +} + +.slider:before { + content: ""; + position: absolute; + inline-size: 100%; + block-size: var(--_track); + border-radius: 1rem; + background: var(--secondary-container); + clip-path: polygon(calc(var(--_start) - .5rem) 0, 0 0, 0 100%, calc(var(--_start) - .5rem) 100%, calc(var(--_start) - .5rem) 0, calc(100% - var(--_end) + .5rem) 0, 100% 0, 100% 100%, calc(100% - var(--_end) + .5rem) 100%, calc(100% - var(--_end) + .5rem) 0) +} + +.slider:has(>[disabled]):before { + background: var(--outline-variant) +} + +.slider:has([disabled]) { + opacity: .62 +} + +.slider > span > i { + position: absolute; + block-size: auto; + inset: 0 auto 0 .5rem; + color: currentColor; + z-index: 1 +} + +.slider:not(.medium,.large,.extra) > span > i { + display: none +} + +.slider.vertical > i { + transform: rotate(90deg) +} + +.slider > .tooltip { + visibility: hidden !important; + opacity: 0 !important; + inset: 0 auto auto calc(100% - var(--_end)); + border-radius: 2rem; + transition: top var(--speed2) ease, opacity var(--speed2) ease; + transform: translate(-50%, -25%) !important; + padding: .75rem 1rem +} + +.slider > .tooltip.bottom { + inset: auto auto 0 calc(100% - var(--_end)); + transition: bottom var(--speed2) ease, opacity var(--speed2) ease; + transform: translate(-50%, 25%) !important +} + +[dir=rtl] .slider > .tooltip { + transform: translate(-50%, -25%) scaleX(-1) !important +} + +[dir=rtl] .slider > .tooltip.bottom { + transform: translate(-50%, 25%) scaleX(-1) !important +} + +.slider > .tooltip + .tooltip { + inset: .25rem calc(100% - var(--_start)) auto auto; + transform: translate(50%, -25%) !important +} + +.slider > .tooltip + .tooltip.bottom { + inset: auto calc(100% - var(--_start)) -.25rem auto; + transform: translate(50%, 25%) !important +} + +[dir=rtl] .slider > .tooltip + .tooltip { + transform: translate(50%, -25%) scaleX(-1) !important +} + +[dir=rtl] .slider > .tooltip + .tooltip.bottom { + transform: translate(50%, 25%) scaleX(-1) !important +} + +.slider > .tooltip:before { + content: var(--_value1) +} + +.slider > .tooltip + .tooltip:before { + content: var(--_value2) +} + +.slider > :focus ~ .tooltip { + inset-block: -1rem auto; + opacity: 1 !important; + visibility: visible !important +} + +.slider > :focus ~ .tooltip.bottom { + inset-block: auto -1rem +} + +.slider.vertical > .tooltip { + inset-block: auto; + block-size: 2.5rem; + inline-size: 2.5rem; + margin-block: calc(-1 * var(--_thumb)) 0 !important; + transform: rotate(90deg) translate(-75%, 50%) !important +} + +.slider.vertical > .tooltip.bottom { + inset-block: auto; + margin-block: 0 calc(-1 * var(--_thumb)) !important; + transform: rotate(90deg) translate(75%, 50%) !important +} + +.slider.vertical > .tooltip + .tooltip { + transform: rotate(90deg) translate(-75%, -50%) !important +} + +.slider.vertical > .tooltip + .tooltip.bottom { + transform: rotate(90deg) translate(75%, -50%) !important +} + +:is(nav,.row,.field) > .slider:not(.circle,.small,.medium,.large) { + flex: auto +} + +.slider.max, .slider.max.vertical, .slider.max > input, .slider.max.vertical > input { + all: unset; + margin: 0 !important; + position: absolute; + color: var(--primary); + top: 0; + right: 0; + bottom: 0; + left: 0; + border-radius: inherit; + overflow: hidden; + z-index: 2; + cursor: grab; + inline-size: 100%; + block-size: 100% +} + +.slider.max:before { + display: none +} + +.slider.max.vertical > input { + writing-mode: vertical-lr; + transform: rotate(-180deg) +} + +.slider.max > input::-webkit-slider-thumb { + opacity: 0; + inline-size: 1rem; + block-size: 100vh; + transform: none !important +} + +.slider.max > input::-moz-range-thumb { + opacity: 0; + inline-size: 1rem; + block-size: 100vh; + transform: none !important +} + +.slider.max > span { + block-size: auto !important; + inset: 0 var(--_end) 0 var(--_start); + clip-path: none; + background: currentcolor; + color: inherit; + border-radius: 0 +} + +.slider.max.vertical > span { + inset: var(--_end) 0 var(--_start) 0 +} + +.slider > input:focus-visible::-webkit-slider-thumb { + outline: .1875rem solid var(--primary); + outline-offset: .25rem +} + +.slider > input:focus-visible::-moz-range-thumb { + outline: .1875rem solid var(--primary); + outline-offset: .25rem +} + +.slider.max > input:focus-visible { + outline: .1875rem solid var(--primary); + outline-offset: -.125rem +} + +@media (pointer: coarse) { + .slider > :hover ~ .tooltip { + inset-block: -1rem auto !important; + opacity: 1 !important; + visibility: visible !important + } + + .slider > :hover ~ .tooltip.bottom { + inset-block: auto -1rem !important + } + + body:has(input[type=range]:focus) { + overflow: hidden + } +} + +.snackbar { + position: fixed; + inset: auto auto 6rem 50%; + inline-size: 80%; + block-size: auto; + z-index: 200; + visibility: hidden; + display: flex; + box-shadow: var(--elevate2); + color: var(--inverse-on-surface); + background-color: var(--inverse-surface); + padding: 1rem; + cursor: pointer; + text-align: start; + align-items: center; + border-radius: .25rem; + gap: .5rem; + transition: all var(--speed2); + transform: translate(-50%, 1rem); + opacity: 0 +} + +.snackbar.top { + inset: 6rem auto auto 50% +} + +.snackbar:is(.active) { + visibility: visible; + transform: translate(-50%); + opacity: 1 +} + +.snackbar:popover-open { + visibility: visible; + transform: translate(-50%); + opacity: 1 +} + +.snackbar > .max { + flex: auto +} + +@media only screen and (min-width: 993px) { + .snackbar { + inline-size: 40% + } +} + +table { + inline-size: 100%; + border-spacing: 0; + font-size: .875rem; + text-align: start +} + +.scroll > table, table :is(thead,tbody,tfoot,tr,th,td) { + background-color: inherit; + color: inherit +} + +:is(th,td) { + inline-size: auto; + text-align: inherit; + padding: .5rem; + border-radius: 0 +} + +:is(th,td) > * { + vertical-align: middle +} + +table.border > tbody > tr:not(:last-child) > td, thead > tr > th { + border-block-end: .0625rem solid var(--outline) +} + +tfoot > tr > th { + border-block-start: .0625rem solid var(--outline) +} + +table.stripes > tbody > tr:nth-child(odd) { + background-color: var(--active) +} + +table.no-space :is(th,td) { + padding: 0 +} + +table.medium-space :is(th,td) { + padding: .75rem +} + +table.large-space :is(th,td) { + padding: 1rem +} + +table > .fixed, th.fixed { + position: sticky; + z-index: 1; + inset-block-start: 0 +} + +tfoot.fixed, tfoot th.fixed { + inset-block-end: 0 +} + +:is(td,th).min { + inline-size: .1%; + white-space: nowrap +} + +.tabs { + display: flex; + white-space: nowrap; + border-block-end: .0625rem solid var(--surface-variant); + border-radius: 0 +} + +.tabs:not(.left-align,.right-align,.center-align) { + justify-content: space-around +} + +.tabs > a { + display: flex; + font-size: .875rem; + font-weight: 500; + color: var(--on-surface-variant); + padding: .5rem 1rem; + text-align: center; + min-block-size: 3rem; + inline-size: 100%; + gap: .25rem +} + +.tabs.small > a { + min-block-size: 2rem +} + +.tabs.large > a { + min-block-size: 4rem +} + +.tabs > a.active, .tabs > a.active > i { + color: var(--primary) +} + +.tabs > a.active:before { + content: ""; + position: absolute; + inset: auto 0 0 0; + block-size: .125rem; + background-color: var(--primary) +} + +.tabs.min > a.active:before { + margin: 0 auto; + max-inline-size: min(100%, 4rem) +} + +.tabs:is(.left-align,.center-align,.right-align) > a { + inline-size: auto +} + +.tooltip { + --_space: -.5rem; + visibility: hidden; + display: flex; + align-items: center; + justify-content: center; + gap: .5rem; + background-color: var(--inverse-surface); + color: var(--inverse-on-surface); + font-size: .75rem; + text-align: center; + border-radius: .25rem; + padding: .5rem; + position: absolute; + z-index: 200; + inset: 0 auto auto 50%; + inline-size: auto; + white-space: nowrap; + font-weight: 500; + opacity: 0; + transition: all var(--speed2); + line-height: normal; + transform: translate(-50%, -100%) scale(.9) +} + +.tooltip:not(.max):hover { + visibility: hidden; + opacity: 0 +} + +.tooltip.left { + inset: 50% auto auto 0; + transform: translate(-100%, -50%) scale(.9) +} + +.tooltip.right { + inset: 50% 0 auto auto; + transform: translate(100%, -50%) scale(.9) +} + +.tooltip.bottom { + inset: auto auto 0 50%; + transform: translate(-50%, 100%) scale(.9) +} + +.tooltip.small { + inline-size: 8rem; + white-space: normal +} + +.tooltip.medium { + inline-size: 12rem; + white-space: normal +} + +.tooltip.large { + inline-size: 16rem; + white-space: normal +} + +:hover > .tooltip { + visibility: visible; + opacity: 1; + transform: translate(-50%, -100%) scale(1) +} + +:hover > .tooltip.left { + transform: translate(-100%, -50%) scale(1) +} + +:hover > .tooltip.right { + transform: translate(100%, -50%) scale(1) +} + +:hover > .tooltip.bottom { + transform: translate(-50%, 100%) scale(1) +} + +.tooltip.no-space { + --_space: 0 +} + +.tooltip.medium-space { + --_space: -1rem +} + +.tooltip.large-space { + --_space: -1.5rem +} + +.tooltip:not(.left,.right,.bottom) { + margin-block-start: var(--_space) !important +} + +.tooltip.left, .tooltip.right { + margin-inline: var(--_space) !important +} + +.tooltip.bottom { + margin-block-end: var(--_space) !important +} + +menu:active ~ .tooltip, :is(button,.button):focus > menu ~ .tooltip, .field > :focus ~ menu ~ .tooltip { + visibility: hidden +} + +.slider > .tooltip { + --_space: -1.25rem +} + +.slider.vertical > .tooltip { + --_space: -.75rem +} + +.slider.vertical > .tooltip:is(.left,.right) { + --_space: -.5rem +} + +.tooltip.max { + display: block; + font-size: inherit; + white-space: normal; + text-align: start; + inline-size: 20rem; + border-radius: .5rem; + padding: 1rem; + box-shadow: var(--elevate2) +} diff --git a/ShareGuard.Web/wwwroot/beer.js b/ShareGuard.Web/wwwroot/beer.js new file mode 100644 index 0000000..518efbb --- /dev/null +++ b/ShareGuard.Web/wwwroot/beer.js @@ -0,0 +1 @@ +const V=[],Z=new WeakSet,P=navigator.userAgent.includes("Chrome");navigator.userAgent.includes("Firefox");navigator.userAgent.includes("Safari");navigator.userAgent.includes("Windows");const G=navigator.userAgent.includes("Macintosh");navigator.userAgent.includes("Linux");navigator.userAgent.includes("Android");const Q=/iPad|iPhone|iPod/.test(navigator.userAgent);function bt(){return window==null?void 0:window.matchMedia("(prefers-color-scheme: dark)").matches}async function J(t){await new Promise(n=>setTimeout(n,t))}function kt(){return"fxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{const n=Math.random()*16|0;return(t==="x"?n:n&3|8).toString(16)})}function h(t,n){try{return typeof t=="string"?(n??document).querySelector(t):t}catch{return null}}function a(t,n){try{return typeof t=="string"?(n??document).querySelectorAll(t):t??V}catch{return V}}function u(t,n){return(t==null?void 0:t.classList.contains(n))??!1}function y(t,n){var e;return((e=t==null?void 0:t.tagName)==null?void 0:e.toLowerCase())===n}function w(t,n){var e;return((e=t==null?void 0:t.type)==null?void 0:e.toLowerCase())===n}function g(t,n){if(t instanceof NodeList)for(let e=0;e label");for(let n=0;n input:not([type=file], [type=color], [type=range])");for(let n=0;n select");for(let n=0;n input[type=file]");for(let n=0;n input[type=color]");for(let n=0;n textarea");for(let n=0;ni.name).join(", "):"",e.readOnly=!0,r(e,"keydown",Pt,!1),L(e))}function W(t,n){if((n==null?void 0:n.key)==="Enter"){const i=F(t);if(!w(i,"color"))return;i.click();return}const e=$(t);w(e,"text")&&(e.readOnly=!0,e.value=t.value,r(e,"keydown",It,!1),L(e))}function et(t){if(nt(t),t.hasAttribute("rows"))return;const n=tt();t.style.blockSize="auto",t.style.blockSize=`${t.scrollHeight-n}px`}function Rt(){_t(),Ot(),Ft(),$t(),zt(),Nt(),qt()}function H(t){const n=t.target;!y(n,"input")&&!y(n,"select")||(n.type==="range"?(n.focus(),ot(n)):it())}function Wt(t){const n=t.target;requestAnimationFrame(()=>n.blur())}function it(){const t=document.body,n=a(".slider > input[type=range]");n.length?C(t,"input",H,!1):I(t,"input",H,!1);for(let e=0;e1&&(f=Math.abs(o[1]-o[0]),v=o[1]>o[0]?o[0]:o[1],E=100-v-f,m>k&&(k=l[1]||0,m=l[0])),n.style.setProperty("--_start",`${v}%`),n.style.setProperty("--_end",`${E}%`),n.style.setProperty("--_value1",`'${k}'`),n.style.setProperty("--_value2",`'${m}'`)}function jt(){it()}const d={light:"",dark:""};function O(){var t;return(t=document==null?void 0:document.body)!=null&&t.classList.contains("dark")?"dark":"light"}function Bt(){if(d.light&&d.dark)return d;const t=document.body,n=document.createElement("body");n.className="light",t.appendChild(n);const e=document.createElement("body");e.className="dark",t.appendChild(e);const i=getComputedStyle(n),s=getComputedStyle(e),c=["--primary","--on-primary","--primary-container","--on-primary-container","--secondary","--on-secondary","--secondary-container","--on-secondary-container","--tertiary","--on-tertiary","--tertiary-container","--on-tertiary-container","--error","--on-error","--error-container","--on-error-container","--background","--on-background","--surface","--on-surface","--surface-variant","--on-surface-variant","--outline","--outline-variant","--shadow","--scrim","--inverse-surface","--inverse-on-surface","--inverse-primary","--surface-dim","--surface-bright","--surface-container-lowest","--surface-container-low","--surface-container","--surface-container-high","--surface-container-highest"];for(let o=0,l=c.length;o{const c=o=>{let l="";for(let f=0,v=Object.keys(o),E=v.length;f{const n=h(".field > input",t);n?n.focus():t.focus()},90)}function lt(t,n,e){D&&clearTimeout(D),D=setTimeout(()=>{C(document.body,"click",ut),y(document.activeElement,"input")||z();const i=u(n,"active"),s=(e==null?void 0:e.target)===t,c=!!t.closest("menu");if(!i&&c||i&&s){p(n,"active");return}p(a("menu.active"),"active"),g(n,"active"),Ht(n)},90)}let A;function Xt(t){const n=t.currentTarget;p(n,"active"),A&&clearTimeout(A)}function Yt(t,n){z();const e=a(".snackbar.active");for(let i=0;i{p(t,"active")},n??6e3))}function Gt(t){const n=T(t);n&&p(a(":scope > .page",n),"active"),g(t,"active")}function Qt(t){Jt(t)}function Jt(t){const n=t.currentTarget,e=n.getBoundingClientRect(),i=Math.max(e.width,e.height),s=i/2,c=t.clientX-e.left-s,o=t.clientY-e.top-s,l=document.createElement("div");l.className="ripple-js";const f=document.createElement("div");f.style.inlineSize=f.style.blockSize=`${i}px`,f.style.left=`${c}px`,f.style.top=`${o}px`,f.addEventListener("animationend",()=>{l.remove()}),l.appendChild(f),n.appendChild(l)}function tn(){const t=a(".slow-ripple, .ripple, .fast-ripple");for(let n=0;nawait yt(),180)}async function j(t,n,e,i){if(!n&&(n=h(t.getAttribute("data-ui")),!n)){t.classList.toggle("active");return}if(Ct(t),y(n,"dialog")){await st(t,n);return}if(y(n,"menu")){lt(t,n,i);return}if(u(n,"snackbar")){Yt(n,e);return}if(u(n,"page")){Gt(n);return}if(u(n,"active")){p(t,"active"),p(n,"active");return}g(n,"active")}function nn(t){j(t.currentTarget,null,null,t)}function en(t){t.key==="Enter"&&j(t.currentTarget,null,null,t)}function pt(){x.ui||_||!x.MutationObserver||(_=new MutationObserver(Y),_.observe(document.body,{childList:!0,subtree:!0}),Y())}function on(){const t=a("[data-ui]");for(let n=0,e=t.length;n. All Rights Reserved. + */ + +(function () { + function gf(init) { + var r = new Float64Array(16); + if (init) { + for (var i = 0; i < init.length; ++i) + r[i] = init[i]; + } + return r; + } + + function pack(o, n) { + var b, m = gf(), t = gf(); + for (var i = 0; i < 16; ++i) + t[i] = n[i]; + carry(t); + carry(t); + carry(t); + for (var j = 0; j < 2; ++j) { + m[0] = t[0] - 0xffed; + for (var i = 1; i < 15; ++i) { + m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1); + m[i - 1] &= 0xffff; + } + m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1); + b = (m[15] >> 16) & 1; + m[14] &= 0xffff; + cswap(t, m, 1 - b); + } + for (var i = 0; i < 16; ++i) { + o[2 * i] = t[i] & 0xff; + o[2 * i + 1] = t[i] >> 8; + } + } + + function carry(o) { + var c; + for (var i = 0; i < 16; ++i) { + o[(i + 1) % 16] += (i < 15 ? 1 : 38) * Math.floor(o[i] / 65536); + o[i] &= 0xffff; + } + } + + function cswap(p, q, b) { + var t, c = ~(b - 1); + for (var i = 0; i < 16; ++i) { + t = c & (p[i] ^ q[i]); + p[i] ^= t; + q[i] ^= t; + } + } + + function add(o, a, b) { + for (var i = 0; i < 16; ++i) + o[i] = (a[i] + b[i]) | 0; + } + + function subtract(o, a, b) { + for (var i = 0; i < 16; ++i) + o[i] = (a[i] - b[i]) | 0; + } + + function multmod(o, a, b) { + var t = new Float64Array(31); + for (var i = 0; i < 16; ++i) { + for (var j = 0; j < 16; ++j) + t[i + j] += a[i] * b[j]; + } + for (var i = 0; i < 15; ++i) + t[i] += 38 * t[i + 16]; + for (var i = 0; i < 16; ++i) + o[i] = t[i]; + carry(o); + carry(o); + } + + function invert(o, i) { + var c = gf(); + for (var a = 0; a < 16; ++a) + c[a] = i[a]; + for (var a = 253; a >= 0; --a) { + multmod(c, c, c); + if (a !== 2 && a !== 4) + multmod(c, c, i); + } + for (var a = 0; a < 16; ++a) + o[a] = c[a]; + } + + function clamp(z) { + z[31] = (z[31] & 127) | 64; + z[0] &= 248; + } + + function generatePublicKey(privateKey) { + var r, z = new Uint8Array(32); + var a = gf([1]), + b = gf([9]), + c = gf(), + d = gf([1]), + e = gf(), + f = gf(), + _121665 = gf([0xdb41, 1]), + _9 = gf([9]); + for (var i = 0; i < 32; ++i) + z[i] = privateKey[i]; + clamp(z); + for (var i = 254; i >= 0; --i) { + r = (z[i >>> 3] >>> (i & 7)) & 1; + cswap(a, b, r); + cswap(c, d, r); + add(e, a, c); + subtract(a, a, c); + add(c, b, d); + subtract(b, b, d); + multmod(d, e, e); + multmod(f, a, a); + multmod(a, c, a); + multmod(c, b, e); + add(e, a, c); + subtract(a, a, c); + multmod(b, a, a); + subtract(c, d, f); + multmod(a, c, _121665); + add(a, a, d); + multmod(c, c, a); + multmod(a, d, f); + multmod(d, b, _9); + multmod(b, e, e); + cswap(a, b, r); + cswap(c, d, r); + } + invert(c, c); + multmod(a, a, c); + pack(z, a); + return z; + } + + function generatePresharedKey() { + var privateKey = new Uint8Array(32); + window.crypto.getRandomValues(privateKey); + return privateKey; + } + + function generatePrivateKey() { + var privateKey = generatePresharedKey(); + clamp(privateKey); + return privateKey; + } + + function encodeBase64(dest, src) { + var input = Uint8Array.from([(src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63]); + for (var i = 0; i < 4; ++i) + dest[i] = input[i] + 65 + + (((25 - input[i]) >> 8) & 6) - + (((51 - input[i]) >> 8) & 75) - + (((61 - input[i]) >> 8) & 15) + + (((62 - input[i]) >> 8) & 3); + } + + function keyToBase64(key) { + var i, base64 = new Uint8Array(44); + for (i = 0; i < 32 / 3; ++i) + encodeBase64(base64.subarray(i * 4), key.subarray(i * 3)); + encodeBase64(base64.subarray(i * 4), Uint8Array.from([key[i * 3 + 0], key[i * 3 + 1], 0])); + base64[43] = 61; + return String.fromCharCode.apply(null, base64); + } + + window.wireguard = { + generateKeypair: function () { + var privateKey = generatePrivateKey(); + var publicKey = generatePublicKey(privateKey); + return { + publicKey: keyToBase64(publicKey), + privateKey: keyToBase64(privateKey) + }; + } + }; +})(); diff --git a/ShareGuard.sln b/ShareGuard.sln new file mode 100644 index 0000000..d1c513d --- /dev/null +++ b/ShareGuard.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShareGuard.Web", "ShareGuard.Web\ShareGuard.Web.csproj", "{0D999063-9CC8-4279-B299-35E49818887B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ShareGuard.Web.Client", "ShareGuard.Web.Client\ShareGuard.Web.Client.csproj", "{E1A15FC5-B83D-4E83-8BF8-8FDA6B955F11}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0D999063-9CC8-4279-B299-35E49818887B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D999063-9CC8-4279-B299-35E49818887B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D999063-9CC8-4279-B299-35E49818887B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D999063-9CC8-4279-B299-35E49818887B}.Release|Any CPU.Build.0 = Release|Any CPU + {E1A15FC5-B83D-4E83-8BF8-8FDA6B955F11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1A15FC5-B83D-4E83-8BF8-8FDA6B955F11}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1A15FC5-B83D-4E83-8BF8-8FDA6B955F11}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1A15FC5-B83D-4E83-8BF8-8FDA6B955F11}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal -- cgit v1.2.3