From bbf09e266b96965ad50ae9c1ac7d9898fc9520c0 Mon Sep 17 00:00:00 2001 From: Anto Subash Date: Sun, 9 Aug 2026 15:51:20 +0200 Subject: [PATCH 1/9] fix: scaffolded-project path assumptions (#283, #284, #287, #288) Four reports from a `sm new project` scaffold, all the same root cause: the framework assumed its own monorepo layout, where a module's directory and assembly are both `SimpleModule.`. `sm new module` scaffolds a bare `` for both. Page bundle 404 (#287) The client guessed `_content/SimpleModule./`, but an RCL serves its assets under its AssemblyName, so every scaffolded module 404'd on first load. The renderer now declares the module name -> assembly name mapping in the page shell and the resolver reads it, so the first request goes to the path that actually serves the bundle. Probing remains as a fallback for shells that predate the map. The same prefix assumption also kept a bare-named module's stylesheet from ever being linked; module CSS discovery now consults the same mapping. types.ts written to a directory no project owns (#283, #284) extract-ts-types.mjs hardcoded `SimpleModule.` as the output project and created it unconditionally. It now resolves the project directory that actually exists and skips modules with no local source, so NuGet-installed modules no longer materialise phantom source trees in a consumer's repo and deleting a module no longer resurrects one. Removes the orphan modules/Identity/src/SimpleModule.Identity/, which held only a generated types.ts for a contracts-only module and belonged to no project. Tailwind never recompiled for new pages (#288) Styles/app.css declares module pages as @source globs, but the MSBuild target did not treat them as inputs, so a new page's utility classes were silently never generated. Module Pages/Views, the host ClientApp, and the staged module bundles are now inputs, with @(TailwindExtraSourceFiles) as an extension point. Verified: reproduced the stale-CSS failure on unmodified main and confirmed the fix compiles the class; exercised the extraction script against both layouts and a package-only module; confirmed the shell mapping and a single 200 for the page bundle in a browser. Full suite green (20 assemblies), npm run check clean. --- docs/site/advanced/type-generation.md | 15 +++- docs/site/frontend/overview.md | 11 ++- .../Inertia/HtmlFileInertiaPageRenderer.cs | 83 +++++++++++++++++-- .../build/SimpleModule.Hosting.targets | 22 ++++- .../src/SimpleModule.Identity/types.ts | 10 --- .../SimpleModule.Client/src/resolve-page.ts | 39 +++++++-- scripts/extract-ts-types.mjs | 67 ++++++++++++--- .../Inertia/InertiaResultTests.cs | 49 +++++++++++ 8 files changed, 252 insertions(+), 44 deletions(-) delete mode 100644 modules/Identity/src/SimpleModule.Identity/types.ts diff --git a/docs/site/advanced/type-generation.md b/docs/site/advanced/type-generation.md index cc20116d..b370b3b7 100644 --- a/docs/site/advanced/type-generation.md +++ b/docs/site/advanced/type-generation.md @@ -124,10 +124,14 @@ You must build the .NET project before running type generation, since the tool r ## Output Location -Each module gets its own `types.ts` file at: +Each module gets its own `types.ts` file inside its primary source project, so the +generated types sit next to the `Pages/` that consume them and are covered by that +project's `tsconfig.json`. The project directory is resolved from what is on disk, +which differs between layouts: ``` -modules/{ModuleName}/src/SimpleModule.{ModuleName}/types.ts +modules/{ModuleName}/src/SimpleModule.{ModuleName}/types.ts # framework repo +src/modules/{ModuleName}/src/{ModuleName}/types.ts # sm new module ``` For example, the Customers module produces: @@ -136,6 +140,10 @@ For example, the Customers module produces: modules/Customers/src/SimpleModule.Customers/types.ts ``` +Modules that arrived as NuGet packages are skipped — they have no source project in +your repo, so nothing is written and no directory is created for them. The tool +reports how many it skipped. + The file is marked as auto-generated and should not be edited manually: ```typescript @@ -264,7 +272,8 @@ The `extract-ts-types.mjs` tool then: 1. Reads all `DtoTypeScript_*.g.cs` files from the generated output directory 2. Extracts the module name from the `// @module` comment 3. Parses the TypeScript interfaces from the comment block -4. Writes a `types.ts` file to the module's source directory +4. Locates the module's existing source project, and writes a `types.ts` into it — + skipping the module when it has no source project locally Property names are automatically converted from `PascalCase` (C#) to `camelCase` (TypeScript) during generation, matching the default `System.Text.Json` serialization behavior. diff --git a/docs/site/frontend/overview.md b/docs/site/frontend/overview.md index 56c0d6b0..3fd14ac6 100644 --- a/docs/site/frontend/overview.md +++ b/docs/site/frontend/overview.md @@ -104,9 +104,12 @@ export async function resolvePage(name: string) { For a route name like `Customers/Browse`: 1. The module name `Customers` is extracted from the first segment -2. The bundle `/_content/SimpleModule.Customers/SimpleModule.Customers.pages.js` is dynamically imported -3. The `pages` record is looked up for the key `Customers/Browse` -4. Lazy entries (functions) are resolved, eager entries are returned directly +2. The assembly serving that module is read from the `", devScript, after.Replace(NoncePlaceholder, nonce, StringComparison.Ordinal) @@ -152,18 +165,78 @@ private static async Task BuildHeadContributionsAsync(HttpContext httpCo return sb?.ToString() ?? string.Empty; } - private static string BuildModuleCssLinks(IWebHostEnvironment env, string version) + /// + /// Maps each module's [Module] name to the assembly its static web assets + /// are served under. The Inertia page name's first segment is the module name, so + /// this is what the client needs to build a bundle URL that resolves. + /// + private static Dictionary BuildModuleAssemblyMap(IEnumerable modules) + { + var map = new Dictionary(StringComparer.Ordinal); + foreach (var module in modules) + { + var type = module.GetType(); + var name = type.GetCustomAttribute()?.Name; + var assembly = type.Assembly.GetName().Name; + if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(assembly)) + continue; + + map[name] = assembly; + } + return map; + } + + private static string BuildModuleAssembliesScript(Dictionary moduleAssemblies) + { + if (moduleAssemblies.Count == 0) + return string.Empty; + + var sb = new StringBuilder( + "").ToString(); + } + + private static string BuildModuleCssLinks( + IWebHostEnvironment env, + string version, + IEnumerable moduleAssemblies + ) { var contents = env.WebRootFileProvider.GetDirectoryContents("_content"); if (!contents.Exists) return string.Empty; + // An RCL serves its assets under its AssemblyName, so a module scaffolded as a + // bare (rather than SimpleModule.) lands in a directory the + // prefix check alone would skip — and its stylesheet would never be linked. + var known = new HashSet(moduleAssemblies, StringComparer.Ordinal); + var sb = new StringBuilder(); foreach (var entry in contents) { + if (!entry.IsDirectory) + continue; + if ( - !entry.IsDirectory - || !entry.Name.StartsWith("SimpleModule.", StringComparison.Ordinal) + !entry.Name.StartsWith("SimpleModule.", StringComparison.Ordinal) + && !known.Contains(entry.Name) ) continue; diff --git a/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets b/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets index 7949125a..efcd4756 100644 --- a/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets +++ b/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets @@ -38,13 +38,31 @@ $(IntermediateOutputPath)extractdto.stamp $(IntermediateOutputPath)extractroutes.stamp - + + + + + @@ -83,7 +101,7 @@ Name="TailwindBuild" BeforeTargets="Build" Condition="Exists('$(TailwindInput)') And Exists('$(RepoRoot)node_modules')" - Inputs="@(TailwindSourceFiles)" + Inputs="@(TailwindSourceFiles);@(TailwindExtraSourceFiles)" Outputs="$(TailwindOutput)" > diff --git a/modules/Identity/src/SimpleModule.Identity/types.ts b/modules/Identity/src/SimpleModule.Identity/types.ts deleted file mode 100644 index d50b7439..00000000 --- a/modules/Identity/src/SimpleModule.Identity/types.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Auto-generated from [Dto] types — do not edit -export interface SessionDto { - tokenId: string; - type: string; - applicationName: string; - creationDate: string | null; - expirationDate: string | null; - isCurrent: boolean; -} - diff --git a/packages/SimpleModule.Client/src/resolve-page.ts b/packages/SimpleModule.Client/src/resolve-page.ts index 753208be..30a8193b 100644 --- a/packages/SimpleModule.Client/src/resolve-page.ts +++ b/packages/SimpleModule.Client/src/resolve-page.ts @@ -4,21 +4,44 @@ // module. We only ever store a name that actually resolved. const resolvedAssemblies = new Map(); +// The server emits the module name -> RCL assembly name mapping into the page shell +// (see HtmlFileInertiaPageRenderer). Reading it means the very first request for a +// module's bundle goes to the path that actually serves it, instead of guessing and +// eating a 404 (#287). Parsed once — the shell is static for the life of the document. +let declaredAssemblies: Record | undefined; + +function getDeclaredAssemblies(): Record { + if (declaredAssemblies) return declaredAssemblies; + + const json = document.querySelector('script[data-module-assemblies]')?.textContent; + try { + declaredAssemblies = json ? JSON.parse(json) : {}; + } catch { + // A malformed map is not worth failing navigation over — fall back to probing. + declaredAssemblies = {}; + } + return declaredAssemblies as Record; +} + export async function resolvePage(name: string) { const moduleName = name.split('/')[0]; const cacheBuster = (document.querySelector('meta[name="cache-buster"]') as HTMLMetaElement) ?.content; const suffix = cacheBuster ? `?v=${cacheBuster}` : ''; - // Framework modules serve their bundle under the assembly-qualified path - // "SimpleModule." (their RCL AssemblyName), whereas downstream apps - // frequently ship modules under a bare assembly name (e.g. "Customers"). Try - // the assembly-qualified form first so framework modules — the common, always- - // present case — resolve without a 404 (#224), then fall back to the bare name - // for consumer modules. Once a module resolves, reuse that name directly so - // later navigations never re-probe. + // A module's bundle is served under its RCL AssemblyName: "SimpleModule." + // for framework modules, a bare "" for ones scaffolded by `sm new module`. + // Prefer the name the server declared; only guess when the shell predates that + // mapping, and then try the assembly-qualified form first (#224). Once a module + // resolves, reuse that name directly so later navigations never re-probe. const cached = resolvedAssemblies.get(moduleName); - const candidates = cached ? [cached] : [`SimpleModule.${moduleName}`, moduleName]; + const declared = getDeclaredAssemblies()[moduleName]; + const guesses = [`SimpleModule.${moduleName}`, moduleName]; + const candidates = cached + ? [cached] + : declared + ? [declared, ...guesses.filter((guess) => guess !== declared)] + : guesses; // biome-ignore lint/suspicious/noExplicitAny: matches existing dynamic-import shape let mod: any; let assemblyName = candidates[0]; diff --git a/scripts/extract-ts-types.mjs b/scripts/extract-ts-types.mjs index 644f0a32..e0f78b38 100644 --- a/scripts/extract-ts-types.mjs +++ b/scripts/extract-ts-types.mjs @@ -6,7 +6,7 @@ import { readdirSync, readFileSync, writeFileSync, - mkdirSync, + statSync, existsSync, } from 'fs'; import { resolve, join } from 'path'; @@ -19,6 +19,45 @@ function writeIfChanged(path, contents) { return true; } +function isDirectory(path) { + try { + return statSync(path).isDirectory(); + } catch { + return false; + } +} + +/** + * Resolves the directory of a module's primary source project, or null when the + * module has no source under `modulesDir`. + * + * The generator emits a DtoTypeScript_*.g.cs for every module in the compilation, + * which includes modules that arrived as NuGet packages. Those have no source in + * the consumer's repo, and the directory layout is not guessable either: framework + * modules use `src/SimpleModule./` while `sm new module` scaffolds a bare + * `src//`. So only ever write into a project directory that already exists — + * never create one. + */ +function findProjectDir(modulesDir, moduleName) { + const srcDir = resolve(modulesDir, moduleName, 'src'); + if (!isDirectory(srcDir)) return null; + + for (const candidate of [`SimpleModule.${moduleName}`, moduleName]) { + const dir = join(srcDir, candidate); + if (existsSync(join(dir, `${candidate}.csproj`))) return dir; + } + + // Non-conventional project name: accept it when there is exactly one candidate + // left after excluding the companion Contracts and Tests projects. + const projects = readdirSync(srcDir, { withFileTypes: true }) + .filter((entry) => entry.isDirectory()) + .map((entry) => entry.name) + .filter((name) => !name.endsWith('.Contracts') && !name.endsWith('.Tests')) + .filter((name) => existsSync(join(srcDir, name, `${name}.csproj`))); + + return projects.length === 1 ? join(srcDir, projects[0]) : null; +} + const generatedDir = process.argv[2]; const modulesDir = process.argv[3] || 'modules'; @@ -38,6 +77,8 @@ if (files.length === 0) { process.exit(0); } +const skipped = []; + for (const file of files) { const content = readFileSync(join(generatedDir, file), 'utf-8').replace( /\r\n/g, @@ -54,20 +95,22 @@ for (const file of files) { if (!tsMatch) continue; const tsContent = tsMatch[1]; - const projectDir = `SimpleModule.${moduleName}`; - const outPath = resolve( - modulesDir, - moduleName, - 'src', - projectDir, - 'types.ts', - ); + const projectDir = findProjectDir(modulesDir, moduleName); + + if (!projectDir) { + skipped.push(moduleName); + continue; + } - mkdirSync(resolve(modulesDir, moduleName, 'src', projectDir), { - recursive: true, - }); + const outPath = join(projectDir, 'types.ts'); const next = `// Auto-generated from [Dto] types \u2014 do not edit\n${tsContent}`; if (writeIfChanged(outPath, next)) { console.log(`Wrote ${moduleName} types to ${outPath}`); } } + +if (skipped.length > 0) { + console.log( + `Skipped ${skipped.length} module(s) with no source project under ${modulesDir}: ${skipped.join(', ')}`, + ); +} diff --git a/tests/SimpleModule.Core.Tests/Inertia/InertiaResultTests.cs b/tests/SimpleModule.Core.Tests/Inertia/InertiaResultTests.cs index 6383d4df..23ab6327 100644 --- a/tests/SimpleModule.Core.Tests/Inertia/InertiaResultTests.cs +++ b/tests/SimpleModule.Core.Tests/Inertia/InertiaResultTests.cs @@ -1,8 +1,11 @@ using System.Net; using System.Net.Http.Json; using System.Text.Json; +using System.Text.RegularExpressions; using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; using SimpleModule.Core.Inertia; +using SimpleModule.Core.Menu; using SimpleModule.Tests.Shared.Fixtures; namespace SimpleModule.Core.Tests.Inertia; @@ -91,6 +94,52 @@ public async Task InertiaMiddleware_AddsVersionHeader() response.Headers.Contains("X-Inertia-Version").Should().BeTrue(); } + [Fact] + public async Task InertiaEndpoint_DeclaresAssemblyNameThatServesEachModuleBundle() + { + using var client = _factory.CreateClient(); + var response = await client.GetAsync("/"); + var html = await response.Content.ReadAsStringAsync(); + + var declared = ParseModuleAssemblies(html); + declared.Should().NotBeEmpty("the client needs the mapping to build a bundle URL"); + + // Every module that renders pages needs an entry, or the client is back to + // guessing the bundle URL for it (#287). + var pages = _factory.Services.GetRequiredService>(); + declared + .Keys.Should() + .Contain(pages.Select(p => p.Module).Distinct(StringComparer.Ordinal)); + + // A module's RCL serves its assets under its AssemblyName. Declaring anything + // else — such as the "SimpleModule."-prefixed module name, which is not an + // assembly at all in a scaffolded app — 404s on first load of its pages. + var loaded = AppDomain + .CurrentDomain.GetAssemblies() + .Select(a => a.GetName().Name) + .ToHashSet(StringComparer.Ordinal); + declared + .Values.Should() + .OnlyContain( + assembly => loaded.Contains(assembly), + "declared names must be assemblies" + ); + } + + private static Dictionary ParseModuleAssemblies(string html) + { + var match = Regex.Match( + html, + "", + RegexOptions.Singleline, + TimeSpan.FromSeconds(5) + ); + match.Success.Should().BeTrue("the page shell must declare the module assemblies"); + + return JsonSerializer.Deserialize>(match.Groups["json"].Value) + ?? []; + } + [Fact] public async Task InertiaMiddleware_VersionMismatch_Returns409() { From 10bc18b185f27233075fd34b5283762f41050401 Mon Sep 17 00:00:00 2001 From: Anto Subash Date: Sun, 9 Aug 2026 21:18:32 +0200 Subject: [PATCH 2/9] fix: complete the Tailwind input set and surface the declared bundle's error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to the review on #289. Tailwind up-to-date check was still incomplete. `Styles/app.css` declares five @source roots; only the module pages and ClientApp were tracked, so editing `packages/SimpleModule.UI` — the most-edited shared code in the repo — left the compiled CSS stale with exactly the #288 symptom. Adds the UI and client package roots (via `SimpleModuleUiDir`/`SimpleModuleClientDir`, which resolve to the npm packages in a scaffold and to `packages/` in the monorepo) and tracks `docs/design-system/` through the `@(TailwindExtraSourceFiles)` extension point. Moves the `Styles/_scan/**/*.js` glob into `CollectModuleAssets`. It was in an evaluation-time ItemGroup, but those files are staged during execution, so on the first build after installing a packaged module the glob expanded to nothing and `TailwindBuild` was judged up to date. Declaring the item in the target that stages them means the Inputs check, which runs afterwards, sees them. Anchors the module glob to `*/src/*/Pages/**` instead of `**/Pages/**` so evaluation no longer walks every module's bin/ and obj/ on every build, including design-time builds. `resolvePage` now reports the declared candidate's error rather than the last probe's. With the declaration authoritative, a real failure in the module's own bundle was being buried under a 404 from a fallback URL that was never going to resolve. Notes the scaffold's stripped `@source "./_scan/"` (#290) where the staging target could otherwise be misread as covering packaged modules everywhere. --- Directory.Build.props | 7 +++ .../build/SimpleModule.Hosting.targets | 45 ++++++++++++++----- .../SimpleModule.Client/src/resolve-page.ts | 7 ++- 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 869a1b4c..59a75106 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -59,7 +59,14 @@ $(RepoRoot)packages/SimpleModule.Theme.Default/theme.css $(RepoRoot)modules $(RepoRoot)packages/SimpleModule.Client/src/routes.ts + $(RepoRoot)packages/SimpleModule.UI + $(RepoRoot)packages/SimpleModule.Client + + + + latest-all diff --git a/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets b/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets index efcd4756..3024bdea 100644 --- a/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets +++ b/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets @@ -23,6 +23,12 @@ $(MSBuildProjectDirectory)/ClientApp/routes.ts + $(RepoRoot)node_modules/@simplemodule/ui + $(RepoRoot)node_modules/@simplemodule/client @@ -39,11 +45,12 @@ $(IntermediateOutputPath)extractroutes.stamp + scans, not just the stylesheets. Styles/app.css declares the module pages, the + UI and client packages, and the design system as @source roots; unless those are + inputs here, editing one leaves the compiled CSS untouched and its utility + classes are silently never generated (#288). Globs are anchored rather than + rooted at ** so evaluation does not walk every module's bin/ and obj/. Hosts with + @source directives of their own can add to @(TailwindExtraSourceFiles). --> + + - - - + + + + + Date: Mon, 10 Aug 2026 13:51:13 +0200 Subject: [PATCH 3/9] fix: harden the declared-assembly map and drop a second orphaned types.ts - resolve-page: validate the parsed module-assembly map is a plain object, so a null/non-object payload memoizes as empty instead of throwing on every lookup - renderer: emit the map with JsonSerializer instead of hand-rolled JSON writing - targets: track ClientApp *.ts alongside *.tsx in the Tailwind input set - delete modules/Core/src/SimpleModule.Core/types.ts, a generator phantom with no csproj that nothing imports --- .../Inertia/HtmlFileInertiaPageRenderer.cs | 23 ++++--------------- .../build/SimpleModule.Hosting.targets | 2 +- modules/Core/src/SimpleModule.Core/types.ts | 19 --------------- .../SimpleModule.Client/src/resolve-page.ts | 12 +++++++--- 4 files changed, 14 insertions(+), 42 deletions(-) delete mode 100644 modules/Core/src/SimpleModule.Core/types.ts diff --git a/framework/SimpleModule.Hosting/Inertia/HtmlFileInertiaPageRenderer.cs b/framework/SimpleModule.Hosting/Inertia/HtmlFileInertiaPageRenderer.cs index c68930f3..50183118 100644 --- a/framework/SimpleModule.Hosting/Inertia/HtmlFileInertiaPageRenderer.cs +++ b/framework/SimpleModule.Hosting/Inertia/HtmlFileInertiaPageRenderer.cs @@ -191,26 +191,11 @@ private static string BuildModuleAssembliesScript(Dictionary mod if (moduleAssemblies.Count == 0) return string.Empty; - var sb = new StringBuilder( - "").ToString(); + return $""; } private static string BuildModuleCssLinks( diff --git a/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets b/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets index 3024bdea..ee85384d 100644 --- a/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets +++ b/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets @@ -72,7 +72,7 @@ Condition="Exists('$(SimpleModuleClientDir)')" /> diff --git a/modules/Core/src/SimpleModule.Core/types.ts b/modules/Core/src/SimpleModule.Core/types.ts deleted file mode 100644 index 8e9fb0a0..00000000 --- a/modules/Core/src/SimpleModule.Core/types.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Auto-generated from [Dto] types — do not edit -export interface SettingDefinition { - key: string; - displayName: string; - description: string; - group: string; - scope: any; - defaultValue: string; - type: any; - allowedValues: string[]; - min: number | null; - max: number | null; - pattern: string; - required: boolean; - sensitive: boolean; - order: number; - placeholder: string; -} - diff --git a/packages/SimpleModule.Client/src/resolve-page.ts b/packages/SimpleModule.Client/src/resolve-page.ts index b654b312..fe2356bb 100644 --- a/packages/SimpleModule.Client/src/resolve-page.ts +++ b/packages/SimpleModule.Client/src/resolve-page.ts @@ -14,13 +14,19 @@ function getDeclaredAssemblies(): Record { if (declaredAssemblies) return declaredAssemblies; const json = document.querySelector('script[data-module-assemblies]')?.textContent; + let parsed: unknown; try { - declaredAssemblies = json ? JSON.parse(json) : {}; + parsed = json ? JSON.parse(json) : undefined; } catch { // A malformed map is not worth failing navigation over — fall back to probing. - declaredAssemblies = {}; + parsed = undefined; } - return declaredAssemblies as Record; + // Anything that isn't a plain object (including `null`, which is truthy-checked + // away below but would still throw on property access) becomes an empty map, so + // the memo always sticks and lookups never blow up. + declaredAssemblies = + typeof parsed === 'object' && parsed !== null ? (parsed as Record) : {}; + return declaredAssemblies; } export async function resolvePage(name: string) { From 004079905c2e2918b5ea6acf35733ff0f1796391 Mon Sep 17 00:00:00 2001 From: Anto Subash Date: Mon, 10 Aug 2026 14:12:15 +0200 Subject: [PATCH 4/9] fix: stop tracking generated app.css and tighten the assembly-map lookup The Tailwind output under wwwroot/ is already covered by .gitignore's "Tailwind CSS build output" rule, which allowlists only favicon.svg, index.html and error.html; app.css was tracked by accident. Widening the TailwindBuild input set made the target rerun on nearly every frontend edit, so the stale committed copy was rewritten constantly and every build produced a ~5000-line spurious diff. CI and Docker both compile the CSS themselves and the Host is IsPackable=false, so nothing depended on it. - resolve-page: narrow declared assembly names to non-empty strings so a module named after an Object.prototype member cannot build a junk URL - targets: track .js/.jsx under the UI and client packages, which are declared as directory @source roots and so scanned for every extension - test: key the assembly-map assertion on the page name's first segment, which is what the client looks up, instead of restating how it is built --- .../build/SimpleModule.Hosting.targets | 7 +- .../SimpleModule.Client/src/resolve-page.ts | 19 +- .../SimpleModule.Host/wwwroot/css/app.css | 5351 ----------------- .../Inertia/InertiaResultTests.cs | 7 +- 4 files changed, 25 insertions(+), 5359 deletions(-) delete mode 100644 template/SimpleModule.Host/wwwroot/css/app.css diff --git a/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets b/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets index ee85384d..ff6ce5a9 100644 --- a/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets +++ b/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets @@ -61,13 +61,16 @@ Include="$(SimpleModuleModulesDir)/*/src/*/Pages/**/*.tsx;$(SimpleModuleModulesDir)/*/src/*/Views/**/*.tsx" Condition="Exists('$(SimpleModuleModulesDir)')" /> + diff --git a/packages/SimpleModule.Client/src/resolve-page.ts b/packages/SimpleModule.Client/src/resolve-page.ts index fe2356bb..06da4495 100644 --- a/packages/SimpleModule.Client/src/resolve-page.ts +++ b/packages/SimpleModule.Client/src/resolve-page.ts @@ -8,9 +8,9 @@ const resolvedAssemblies = new Map(); // (see HtmlFileInertiaPageRenderer). Reading it means the very first request for a // module's bundle goes to the path that actually serves it, instead of guessing and // eating a 404 (#287). Parsed once — the shell is static for the life of the document. -let declaredAssemblies: Record | undefined; +let declaredAssemblies: Record | undefined; -function getDeclaredAssemblies(): Record { +function getDeclaredAssemblies(): Record { if (declaredAssemblies) return declaredAssemblies; const json = document.querySelector('script[data-module-assemblies]')?.textContent; @@ -25,10 +25,21 @@ function getDeclaredAssemblies(): Record { // away below but would still throw on property access) becomes an empty map, so // the memo always sticks and lookups never blow up. declaredAssemblies = - typeof parsed === 'object' && parsed !== null ? (parsed as Record) : {}; + typeof parsed === 'object' && parsed !== null ? (parsed as Record) : {}; return declaredAssemblies; } +/** + * Reads a module's declared assembly. Values are typed `unknown` and narrowed here + * because the map is a plain JSON object: a module name that collides with an + * inherited `Object.prototype` member ("constructor", "toString", …) would otherwise + * hand back a function and build a nonsense bundle URL. + */ +function getDeclaredAssembly(moduleName: string): string | undefined { + const value = getDeclaredAssemblies()[moduleName]; + return typeof value === 'string' && value.length > 0 ? value : undefined; +} + export async function resolvePage(name: string) { const moduleName = name.split('/')[0]; const cacheBuster = (document.querySelector('meta[name="cache-buster"]') as HTMLMetaElement) @@ -41,7 +52,7 @@ export async function resolvePage(name: string) { // mapping, and then try the assembly-qualified form first (#224). Once a module // resolves, reuse that name directly so later navigations never re-probe. const cached = resolvedAssemblies.get(moduleName); - const declared = getDeclaredAssemblies()[moduleName]; + const declared = getDeclaredAssembly(moduleName); const guesses = [`SimpleModule.${moduleName}`, moduleName]; const candidates = cached ? [cached] diff --git a/template/SimpleModule.Host/wwwroot/css/app.css b/template/SimpleModule.Host/wwwroot/css/app.css deleted file mode 100644 index 042d66f1..00000000 --- a/template/SimpleModule.Host/wwwroot/css/app.css +++ /dev/null @@ -1,5351 +0,0 @@ -/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */ -@layer properties; -@layer theme, base, components, utilities; -@layer theme { - :root, :host { - --font-sans: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", - "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; - --font-mono: "JetBrains Mono", "Fira Code", ui-monospace, "SF Mono", Menlo, monospace; - --color-red-50: oklch(97.1% 0.013 17.38); - --color-red-200: oklch(88.5% 0.062 18.334); - --color-red-400: oklch(70.4% 0.191 22.216); - --color-red-500: oklch(63.7% 0.237 25.331); - --color-red-600: oklch(57.7% 0.245 27.325); - --color-red-700: oklch(50.5% 0.213 27.518); - --color-red-800: oklch(44.4% 0.177 26.899); - --color-amber-400: oklch(82.8% 0.189 84.429); - --color-amber-600: oklch(66.6% 0.179 58.318); - --color-yellow-600: oklch(68.1% 0.162 75.834); - --color-green-400: oklch(79.2% 0.209 151.711); - --color-green-600: oklch(62.7% 0.194 149.214); - --color-green-700: oklch(52.7% 0.154 150.069); - --color-blue-50: oklch(97% 0.014 254.604); - --color-blue-100: oklch(93.2% 0.032 255.585); - --color-blue-300: oklch(80.9% 0.105 251.813); - --color-blue-400: oklch(70.7% 0.165 254.624); - --color-blue-500: oklch(62.3% 0.214 259.815); - --color-blue-600: oklch(54.6% 0.245 262.881); - --color-blue-700: oklch(48.8% 0.243 264.376); - --color-blue-800: oklch(42.4% 0.199 265.638); - --color-blue-900: oklch(37.9% 0.146 265.522); - --color-gray-50: oklch(98.5% 0.002 247.839); - --color-gray-100: oklch(96.7% 0.003 264.542); - --color-gray-200: oklch(92.8% 0.006 264.531); - --color-gray-300: oklch(87.2% 0.01 258.338); - --color-gray-400: oklch(70.7% 0.022 261.325); - --color-gray-500: oklch(55.1% 0.027 264.364); - --color-gray-600: oklch(44.6% 0.03 256.802); - --color-gray-700: oklch(37.3% 0.034 259.733); - --color-gray-800: oklch(27.8% 0.033 256.848); - --color-black: #000; - --color-white: #fff; - --spacing: 0.25rem; - --container-sm: 24rem; - --container-md: 28rem; - --container-lg: 32rem; - --container-xl: 36rem; - --container-2xl: 42rem; - --container-3xl: 48rem; - --container-4xl: 56rem; - --container-6xl: 72rem; - --container-7xl: 80rem; - --text-xs: 0.75rem; - --text-xs--line-height: calc(1 / 0.75); - --text-sm: 0.875rem; - --text-sm--line-height: calc(1.25 / 0.875); - --text-base: 1rem; - --text-base--line-height: calc(1.5 / 1); - --text-lg: 1.125rem; - --text-lg--line-height: calc(1.75 / 1.125); - --text-xl: 1.25rem; - --text-xl--line-height: calc(1.75 / 1.25); - --text-2xl: 1.5rem; - --text-2xl--line-height: calc(2 / 1.5); - --text-3xl: 1.875rem; - --text-3xl--line-height: calc(2.25 / 1.875); - --text-4xl: 2.25rem; - --text-4xl--line-height: calc(2.5 / 2.25); - --text-5xl: 3rem; - --text-5xl--line-height: 1; - --text-6xl: 3.75rem; - --text-6xl--line-height: 1; - --font-weight-normal: 400; - --font-weight-medium: 500; - --font-weight-semibold: 600; - --font-weight-bold: 700; - --font-weight-extrabold: 800; - --tracking-tight: -0.025em; - --tracking-wide: 0.025em; - --tracking-wider: 0.05em; - --tracking-widest: 0.1em; - --leading-tight: 1.25; - --leading-relaxed: 1.625; - --radius-sm: 0.25rem; - --radius-md: 0.375rem; - --radius-lg: 0.5rem; - --radius-xl: 0.75rem; - --radius-2xl: 1rem; - --shadow-xs: 0 1px 2px 0 oklch(0.18 0.012 260 / 0.05); - --shadow-sm: 0 1px 2px 0 oklch(0.18 0.012 260 / 0.06), 0 1px 3px 0 oklch(0.18 0.012 260 / 0.04); - --shadow-md: 0 4px 8px -2px oklch(0.18 0.012 260 / 0.08), 0 2px 4px -2px oklch(0.18 0.012 260 / 0.05); - --shadow-lg: 0 12px 24px -8px oklch(0.18 0.012 260 / 0.12), 0 4px 8px -4px oklch(0.18 0.012 260 / 0.06); - --shadow-xl: 0 24px 48px -12px oklch(0.18 0.012 260 / 0.18), 0 8px 16px -8px oklch(0.18 0.012 260 / 0.08); - --ease-out: cubic-bezier(0.16, 1, 0.3, 1); - --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1); - --animate-spin: spin 1s linear infinite; - --animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; - --blur-sm: 8px; - --blur-lg: 16px; - --default-transition-duration: 150ms; - --default-transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); - --default-font-family: var(--font-sans); - --default-mono-font-family: var(--font-mono); - --color-primary: oklch(0.62 0.16 158); - --color-primary-hover: oklch(0.55 0.17 158); - --color-primary-light: oklch(0.78 0.16 158); - --color-primary-subtle: oklch(0.62 0.16 158 / 0.09); - --color-primary-ring: oklch(0.62 0.16 158 / 0.32); - --color-accent: oklch(0.5 0.11 192); - --color-accent-hover: oklch(0.42 0.11 192); - --color-success: oklch(0.62 0.16 158); - --color-success-light: oklch(0.78 0.16 158); - --color-success-bg: oklch(0.62 0.16 158 / 0.1); - --color-success-text: oklch(0.48 0.15 158); - --color-danger: oklch(0.6 0.21 17); - --color-danger-hover: oklch(0.52 0.21 17); - --color-danger-bg: oklch(0.6 0.21 17 / 0.09); - --color-danger-text: oklch(0.5 0.2 17); - --color-warning: oklch(0.72 0.16 60); - --color-warning-bg: oklch(0.72 0.16 60 / 0.1); - --color-warning-border: oklch(0.72 0.16 60 / 0.25); - --color-warning-text: oklch(0.5 0.13 60); - --color-info: oklch(0.65 0.16 232); - --color-info-bg: oklch(0.65 0.16 232 / 0.1); - --color-surface: oklch(1 0 0); - --color-surface-raised: oklch(0.985 0.003 80); - --color-surface-sunken: oklch(0.965 0.005 80); - --color-surface-overlay: oklch(1 0 0 / 0.78); - --color-text: oklch(0.22 0.015 250); - --color-text-secondary: oklch(0.45 0.015 250); - --color-text-muted: oklch(0.62 0.012 250); - --color-text-inverse: oklch(1 0 0); - --color-border: oklch(0.92 0.005 80); - --color-border-strong: oklch(0.85 0.008 80); - --color-code-bg: oklch(0.22 0.02 260); - --color-code-text: oklch(0.92 0.01 80); - --color-dark: oklch(0.18 0.012 260); - --color-muted: oklch(0.62 0.012 250); - --color-ring: oklch(0.62 0.16 158 / 0.45); - --shadow-primary: 0 6px 18px -4px oklch(0.62 0.16 158 / 0.42), 0 2px 4px -2px oklch(0.62 0.16 158 / 0.25); - --shadow-primary-hover: 0 10px 28px -6px oklch(0.62 0.16 158 / 0.55), 0 4px 8px -4px oklch(0.62 0.16 158 / 0.3); - --shadow-danger: 0 6px 18px -4px oklch(0.6 0.21 17 / 0.32); - --shadow-danger-hover: 0 10px 28px -6px oklch(0.6 0.21 17 / 0.45); - --font-display: "Fraunces", "Iowan Old Style", "Apple Garamond", "Baskerville", "Times New Roman", Times, - "Source Serif Pro", serif; - --font-body: "Geist", "Inter", system-ui, -apple-system, sans-serif; - --duration-instant: 80ms; - --duration-fast: 150ms; - --duration-base: 200ms; - --duration-moderate: 300ms; - --duration-slow: 500ms; - --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1); - --z-base: 0; - --z-sticky: 10; - --z-overlay: 20; - --z-sidebar: 40; - --z-dropdown: 50; - --z-modal: 60; - --z-toast: 80; - --z-tooltip: 90; - --opacity-disabled: 0.45; - --opacity-muted: 0.65; - --opacity-overlay: 0.78; - --opacity-backdrop: 0.5; - } -} -@layer base { - *, ::after, ::before, ::backdrop, ::file-selector-button { - box-sizing: border-box; - margin: 0; - padding: 0; - border: 0 solid; - } - html, :host { - line-height: 1.5; - -webkit-text-size-adjust: 100%; - tab-size: 4; - font-family: var(--default-font-family, ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"); - font-feature-settings: var(--default-font-feature-settings, normal); - font-variation-settings: var(--default-font-variation-settings, normal); - -webkit-tap-highlight-color: transparent; - } - hr { - height: 0; - color: inherit; - border-top-width: 1px; - } - abbr:where([title]) { - -webkit-text-decoration: underline dotted; - text-decoration: underline dotted; - } - h1, h2, h3, h4, h5, h6 { - font-size: inherit; - font-weight: inherit; - } - a { - color: inherit; - -webkit-text-decoration: inherit; - text-decoration: inherit; - } - b, strong { - font-weight: bolder; - } - code, kbd, samp, pre { - font-family: var(--default-mono-font-family, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace); - font-feature-settings: var(--default-mono-font-feature-settings, normal); - font-variation-settings: var(--default-mono-font-variation-settings, normal); - font-size: 1em; - } - small { - font-size: 80%; - } - sub, sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; - } - sub { - bottom: -0.25em; - } - sup { - top: -0.5em; - } - table { - text-indent: 0; - border-color: inherit; - border-collapse: collapse; - } - :-moz-focusring { - outline: auto; - } - progress { - vertical-align: baseline; - } - summary { - display: list-item; - } - ol, ul, menu { - list-style: none; - } - img, svg, video, canvas, audio, iframe, embed, object { - display: block; - vertical-align: middle; - } - img, video { - max-width: 100%; - height: auto; - } - button, input, select, optgroup, textarea, ::file-selector-button { - font: inherit; - font-feature-settings: inherit; - font-variation-settings: inherit; - letter-spacing: inherit; - color: inherit; - border-radius: 0; - background-color: transparent; - opacity: 1; - } - :where(select:is([multiple], [size])) optgroup { - font-weight: bolder; - } - :where(select:is([multiple], [size])) optgroup option { - padding-inline-start: 20px; - } - ::file-selector-button { - margin-inline-end: 4px; - } - ::placeholder { - opacity: 1; - } - @supports (not (-webkit-appearance: -apple-pay-button)) or (contain-intrinsic-size: 1px) { - ::placeholder { - color: currentcolor; - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, currentcolor 50%, transparent); - } - } - } - textarea { - resize: vertical; - } - ::-webkit-search-decoration { - -webkit-appearance: none; - } - ::-webkit-date-and-time-value { - min-height: 1lh; - text-align: inherit; - } - ::-webkit-datetime-edit { - display: inline-flex; - } - ::-webkit-datetime-edit-fields-wrapper { - padding: 0; - } - ::-webkit-datetime-edit, ::-webkit-datetime-edit-year-field, ::-webkit-datetime-edit-month-field, ::-webkit-datetime-edit-day-field, ::-webkit-datetime-edit-hour-field, ::-webkit-datetime-edit-minute-field, ::-webkit-datetime-edit-second-field, ::-webkit-datetime-edit-millisecond-field, ::-webkit-datetime-edit-meridiem-field { - padding-block: 0; - } - ::-webkit-calendar-picker-indicator { - line-height: 1; - } - :-moz-ui-invalid { - box-shadow: none; - } - button, input:where([type="button"], [type="reset"], [type="submit"]), ::file-selector-button { - appearance: button; - } - ::-webkit-inner-spin-button, ::-webkit-outer-spin-button { - height: auto; - } - [hidden]:where(:not([hidden="until-found"])) { - display: none !important; - } -} -@layer utilities { - .pointer-events-auto { - pointer-events: auto; - } - .pointer-events-none { - pointer-events: none; - } - .collapse { - visibility: collapse; - } - .invisible { - visibility: hidden; - } - .visible { - visibility: visible; - } - .sr-only { - position: absolute; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - overflow: hidden; - clip-path: inset(50%); - white-space: nowrap; - border-width: 0; - } - .absolute { - position: absolute; - } - .fixed { - position: fixed; - } - .relative { - position: relative; - } - .static { - position: static; - } - .sticky { - position: sticky; - } - .inset-0 { - inset: calc(var(--spacing) * 0); - } - .inset-x-0 { - inset-inline: calc(var(--spacing) * 0); - } - .inset-y-0 { - inset-block: calc(var(--spacing) * 0); - } - .start { - inset-inline-start: var(--spacing); - } - .end { - inset-inline-end: var(--spacing); - } - .top-0 { - top: calc(var(--spacing) * 0); - } - .top-1 { - top: calc(var(--spacing) * 1); - } - .top-1\/2 { - top: calc(1 / 2 * 100%); - } - .top-4 { - top: calc(var(--spacing) * 4); - } - .top-20 { - top: calc(var(--spacing) * 20); - } - .top-\[50\%\] { - top: 50%; - } - .top-full { - top: 100%; - } - .right-0 { - right: calc(var(--spacing) * 0); - } - .right-1 { - right: calc(var(--spacing) * 1); - } - .right-4 { - right: calc(var(--spacing) * 4); - } - .bottom-0 { - bottom: calc(var(--spacing) * 0); - } - .bottom-4 { - bottom: calc(var(--spacing) * 4); - } - .left-0 { - left: calc(var(--spacing) * 0); - } - .left-1 { - left: calc(var(--spacing) * 1); - } - .left-1\/2 { - left: calc(1 / 2 * 100%); - } - .left-2 { - left: calc(var(--spacing) * 2); - } - .left-\[50\%\] { - left: 50%; - } - .left-full { - left: 100%; - } - .isolate { - isolation: isolate; - } - .z-10 { - z-index: 10; - } - .z-30 { - z-index: 30; - } - .z-40 { - z-index: 40; - } - .z-50 { - z-index: 50; - } - .z-\[60\] { - z-index: 60; - } - .z-\[90\] { - z-index: 90; - } - .z-\[100\] { - z-index: 100; - } - .container { - width: 100%; - @media (width >= 40rem) { - max-width: 40rem; - } - @media (width >= 48rem) { - max-width: 48rem; - } - @media (width >= 64rem) { - max-width: 64rem; - } - @media (width >= 80rem) { - max-width: 80rem; - } - @media (width >= 96rem) { - max-width: 96rem; - } - } - .m-0 { - margin: calc(var(--spacing) * 0); - } - .m-2 { - margin: calc(var(--spacing) * 2); - } - .-mx-1 { - margin-inline: calc(var(--spacing) * -1); - } - .-mx-4 { - margin-inline: calc(var(--spacing) * -4); - } - .mx-auto { - margin-inline: auto; - } - .my-0\.5 { - margin-block: calc(var(--spacing) * 0.5); - } - .my-1 { - margin-block: calc(var(--spacing) * 1); - } - .my-1\.5 { - margin-block: calc(var(--spacing) * 1.5); - } - .my-4 { - margin-block: calc(var(--spacing) * 4); - } - .my-6 { - margin-block: calc(var(--spacing) * 6); - } - .my-8 { - margin-block: calc(var(--spacing) * 8); - } - .-mt-4 { - margin-top: calc(var(--spacing) * -4); - } - .mt-0\.5 { - margin-top: calc(var(--spacing) * 0.5); - } - .mt-1 { - margin-top: calc(var(--spacing) * 1); - } - .mt-1\.5 { - margin-top: calc(var(--spacing) * 1.5); - } - .mt-2 { - margin-top: calc(var(--spacing) * 2); - } - .mt-3 { - margin-top: calc(var(--spacing) * 3); - } - .mt-4 { - margin-top: calc(var(--spacing) * 4); - } - .mt-6 { - margin-top: calc(var(--spacing) * 6); - } - .mt-7 { - margin-top: calc(var(--spacing) * 7); - } - .mt-8 { - margin-top: calc(var(--spacing) * 8); - } - .mt-10 { - margin-top: calc(var(--spacing) * 10); - } - .mt-15 { - margin-top: calc(var(--spacing) * 15); - } - .mr-1\.5 { - margin-right: calc(var(--spacing) * 1.5); - } - .mr-2 { - margin-right: calc(var(--spacing) * 2); - } - .\!mb-0 { - margin-bottom: calc(var(--spacing) * 0) !important; - } - .-mb-px { - margin-bottom: -1px; - } - .mb-0 { - margin-bottom: calc(var(--spacing) * 0); - } - .mb-0\.5 { - margin-bottom: calc(var(--spacing) * 0.5); - } - .mb-1 { - margin-bottom: calc(var(--spacing) * 1); - } - .mb-1\.5 { - margin-bottom: calc(var(--spacing) * 1.5); - } - .mb-2 { - margin-bottom: calc(var(--spacing) * 2); - } - .mb-3 { - margin-bottom: calc(var(--spacing) * 3); - } - .mb-4 { - margin-bottom: calc(var(--spacing) * 4); - } - .mb-5 { - margin-bottom: calc(var(--spacing) * 5); - } - .mb-6 { - margin-bottom: calc(var(--spacing) * 6); - } - .mb-7 { - margin-bottom: calc(var(--spacing) * 7); - } - .mb-8 { - margin-bottom: calc(var(--spacing) * 8); - } - .mb-10 { - margin-bottom: calc(var(--spacing) * 10); - } - .mb-14 { - margin-bottom: calc(var(--spacing) * 14); - } - .mb-16 { - margin-bottom: calc(var(--spacing) * 16); - } - .-ml-1 { - margin-left: calc(var(--spacing) * -1); - } - .ml-0 { - margin-left: calc(var(--spacing) * 0); - } - .ml-0\.5 { - margin-left: calc(var(--spacing) * 0.5); - } - .ml-1 { - margin-left: calc(var(--spacing) * 1); - } - .ml-1\.5 { - margin-left: calc(var(--spacing) * 1.5); - } - .ml-2 { - margin-left: calc(var(--spacing) * 2); - } - .ml-4 { - margin-left: calc(var(--spacing) * 4); - } - .ml-7 { - margin-left: calc(var(--spacing) * 7); - } - .ml-auto { - margin-left: auto; - } - .block { - display: block; - } - .contents { - display: contents; - } - .flex { - display: flex; - } - .grid { - display: grid; - } - .hidden { - display: none; - } - .inline { - display: inline; - } - .inline-block { - display: inline-block; - } - .inline-flex { - display: inline-flex; - } - .table { - display: table; - } - .aspect-square { - aspect-ratio: 1 / 1; - } - .h-1\.5 { - height: calc(var(--spacing) * 1.5); - } - .h-2 { - height: calc(var(--spacing) * 2); - } - .h-2\.5 { - height: calc(var(--spacing) * 2.5); - } - .h-3 { - height: calc(var(--spacing) * 3); - } - .h-3\.5 { - height: calc(var(--spacing) * 3.5); - } - .h-4 { - height: calc(var(--spacing) * 4); - } - .h-5 { - height: calc(var(--spacing) * 5); - } - .h-6 { - height: calc(var(--spacing) * 6); - } - .h-7 { - height: calc(var(--spacing) * 7); - } - .h-8 { - height: calc(var(--spacing) * 8); - } - .h-9 { - height: calc(var(--spacing) * 9); - } - .h-10 { - height: calc(var(--spacing) * 10); - } - .h-11 { - height: calc(var(--spacing) * 11); - } - .h-12 { - height: calc(var(--spacing) * 12); - } - .h-14 { - height: calc(var(--spacing) * 14); - } - .h-16 { - height: calc(var(--spacing) * 16); - } - .h-36 { - height: calc(var(--spacing) * 36); - } - .h-\[1\.25rem\] { - height: 1.25rem; - } - .h-\[18px\] { - height: 18px; - } - .h-\[var\(--radix-select-trigger-height\)\] { - height: var(--radix-select-trigger-height); - } - .h-full { - height: 100%; - } - .h-px { - height: 1px; - } - .h-screen { - height: 100vh; - } - .max-h-30 { - max-height: calc(var(--spacing) * 30); - } - .max-h-50 { - max-height: calc(var(--spacing) * 50); - } - .max-h-96 { - max-height: calc(var(--spacing) * 96); - } - .max-h-\[300px\] { - max-height: 300px; - } - .max-h-\[500px\] { - max-height: 500px; - } - .max-h-\[calc\(100vh-2rem\)\] { - max-height: calc(100vh - 2rem); - } - .max-h-screen { - max-height: 100vh; - } - .min-h-\[60vh\] { - min-height: 60vh; - } - .min-h-\[80px\] { - min-height: 80px; - } - .min-h-\[180px\] { - min-height: 180px; - } - .min-h-\[200px\] { - min-height: 200px; - } - .min-h-\[220px\] { - min-height: 220px; - } - .min-h-\[calc\(100vh-12rem\)\] { - min-height: calc(100vh - 12rem); - } - .min-h-\[calc\(100vh-16rem\)\] { - min-height: calc(100vh - 16rem); - } - .min-h-screen { - min-height: 100vh; - } - .w-0 { - width: calc(var(--spacing) * 0); - } - .w-1 { - width: calc(var(--spacing) * 1); - } - .w-1\.5 { - width: calc(var(--spacing) * 1.5); - } - .w-2 { - width: calc(var(--spacing) * 2); - } - .w-2\.5 { - width: calc(var(--spacing) * 2.5); - } - .w-3 { - width: calc(var(--spacing) * 3); - } - .w-3\.5 { - width: calc(var(--spacing) * 3.5); - } - .w-3\/4 { - width: calc(3 / 4 * 100%); - } - .w-4 { - width: calc(var(--spacing) * 4); - } - .w-5 { - width: calc(var(--spacing) * 5); - } - .w-6 { - width: calc(var(--spacing) * 6); - } - .w-7 { - width: calc(var(--spacing) * 7); - } - .w-8 { - width: calc(var(--spacing) * 8); - } - .w-9 { - width: calc(var(--spacing) * 9); - } - .w-10 { - width: calc(var(--spacing) * 10); - } - .w-11 { - width: calc(var(--spacing) * 11); - } - .w-12 { - width: calc(var(--spacing) * 12); - } - .w-14 { - width: calc(var(--spacing) * 14); - } - .w-16 { - width: calc(var(--spacing) * 16); - } - .w-20 { - width: calc(var(--spacing) * 20); - } - .w-24 { - width: calc(var(--spacing) * 24); - } - .w-36 { - width: calc(var(--spacing) * 36); - } - .w-64 { - width: calc(var(--spacing) * 64); - } - .w-72 { - width: calc(var(--spacing) * 72); - } - .w-100 { - width: calc(var(--spacing) * 100); - } - .w-\[18px\] { - width: 18px; - } - .w-\[70px\] { - width: 70px; - } - .w-\[100px\] { - width: 100px; - } - .w-\[160px\] { - width: 160px; - } - .w-auto { - width: auto; - } - .w-full { - width: 100%; - } - .w-px { - width: 1px; - } - .max-w-2xl { - max-width: var(--container-2xl); - } - .max-w-3xl { - max-width: var(--container-3xl); - } - .max-w-4xl { - max-width: var(--container-4xl); - } - .max-w-6xl { - max-width: var(--container-6xl); - } - .max-w-7xl { - max-width: var(--container-7xl); - } - .max-w-\[140px\] { - max-width: 140px; - } - .max-w-\[200px\] { - max-width: 200px; - } - .max-w-\[calc\(100vw-2rem\)\] { - max-width: calc(100vw - 2rem); - } - .max-w-full { - max-width: 100%; - } - .max-w-lg { - max-width: var(--container-lg); - } - .max-w-md { - max-width: var(--container-md); - } - .max-w-prose { - max-width: 65ch; - } - .max-w-sm { - max-width: var(--container-sm); - } - .max-w-xl { - max-width: var(--container-xl); - } - .min-w-0 { - min-width: calc(var(--spacing) * 0); - } - .min-w-\[1\.25rem\] { - min-width: 1.25rem; - } - .min-w-\[8rem\] { - min-width: 8rem; - } - .min-w-\[16rem\] { - min-width: 16rem; - } - .min-w-\[160px\] { - min-width: 160px; - } - .min-w-\[180px\] { - min-width: 180px; - } - .min-w-\[640px\] { - min-width: 640px; - } - .min-w-\[var\(--radix-select-trigger-width\)\] { - min-width: var(--radix-select-trigger-width); - } - .flex-1 { - flex: 1; - } - .flex-shrink { - flex-shrink: 1; - } - .flex-shrink-0 { - flex-shrink: 0; - } - .shrink { - flex-shrink: 1; - } - .shrink-0 { - flex-shrink: 0; - } - .flex-grow { - flex-grow: 1; - } - .grow { - flex-grow: 1; - } - .caption-bottom { - caption-side: bottom; - } - .border-collapse { - border-collapse: collapse; - } - .-translate-x-1\/2 { - --tw-translate-x: calc(calc(1 / 2 * 100%) * -1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - .-translate-x-full { - --tw-translate-x: -100%; - translate: var(--tw-translate-x) var(--tw-translate-y); - } - .translate-x-0 { - --tw-translate-x: calc(var(--spacing) * 0); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - .translate-x-\[-50\%\] { - --tw-translate-x: -50%; - translate: var(--tw-translate-x) var(--tw-translate-y); - } - .-translate-y-1\/2 { - --tw-translate-y: calc(calc(1 / 2 * 100%) * -1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - .translate-y-\[-50\%\] { - --tw-translate-y: -50%; - translate: var(--tw-translate-x) var(--tw-translate-y); - } - .rotate-90 { - rotate: 90deg; - } - .rotate-180 { - rotate: 180deg; - } - .transform { - transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,); - } - .animate-pulse { - animation: var(--animate-pulse); - } - .animate-spin { - animation: var(--animate-spin); - } - .cursor-default { - cursor: default; - } - .cursor-pointer { - cursor: pointer; - } - .touch-none { - touch-action: none; - } - .resize { - resize: both; - } - .resize-y { - resize: vertical; - } - .list-decimal { - list-style-type: decimal; - } - .list-none { - list-style-type: none; - } - .\[appearance\:textfield\] { - appearance: textfield; - } - .grid-cols-1 { - grid-template-columns: repeat(1, minmax(0, 1fr)); - } - .grid-cols-2 { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - .grid-cols-3 { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - .flex-col { - flex-direction: column; - } - .flex-col-reverse { - flex-direction: column-reverse; - } - .flex-row { - flex-direction: row; - } - .flex-nowrap { - flex-wrap: nowrap; - } - .flex-wrap { - flex-wrap: wrap; - } - .items-baseline { - align-items: baseline; - } - .items-center { - align-items: center; - } - .items-end { - align-items: flex-end; - } - .items-start { - align-items: flex-start; - } - .items-stretch { - align-items: stretch; - } - .justify-between { - justify-content: space-between; - } - .justify-center { - justify-content: center; - } - .justify-end { - justify-content: flex-end; - } - .justify-start { - justify-content: flex-start; - } - .gap-0 { - gap: calc(var(--spacing) * 0); - } - .gap-0\.5 { - gap: calc(var(--spacing) * 0.5); - } - .gap-1 { - gap: calc(var(--spacing) * 1); - } - .gap-1\.5 { - gap: calc(var(--spacing) * 1.5); - } - .gap-2 { - gap: calc(var(--spacing) * 2); - } - .gap-2\.5 { - gap: calc(var(--spacing) * 2.5); - } - .gap-3 { - gap: calc(var(--spacing) * 3); - } - .gap-4 { - gap: calc(var(--spacing) * 4); - } - .gap-5 { - gap: calc(var(--spacing) * 5); - } - .gap-6 { - gap: calc(var(--spacing) * 6); - } - .gap-8 { - gap: calc(var(--spacing) * 8); - } - .gap-12 { - gap: calc(var(--spacing) * 12); - } - .space-y-0 { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-y-reverse))); - } - } - .space-y-0\.5 { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 0.5) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 0.5) * calc(1 - var(--tw-space-y-reverse))); - } - } - .space-y-1 { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse))); - } - } - .space-y-2 { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse))); - } - } - .space-y-3 { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse))); - } - } - .space-y-4 { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse))); - } - } - .space-y-5 { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 5) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 5) * calc(1 - var(--tw-space-y-reverse))); - } - } - .space-y-6 { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse))); - } - } - .space-y-8 { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 8) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 8) * calc(1 - var(--tw-space-y-reverse))); - } - } - .gap-x-6 { - column-gap: calc(var(--spacing) * 6); - } - .space-x-1 { - :where(& > :not(:last-child)) { - --tw-space-x-reverse: 0; - margin-inline-start: calc(calc(var(--spacing) * 1) * var(--tw-space-x-reverse)); - margin-inline-end: calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-x-reverse))); - } - } - .gap-y-2 { - row-gap: calc(var(--spacing) * 2); - } - .divide-y { - :where(& > :not(:last-child)) { - --tw-divide-y-reverse: 0; - border-bottom-style: var(--tw-border-style); - border-top-style: var(--tw-border-style); - border-top-width: calc(1px * var(--tw-divide-y-reverse)); - border-bottom-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); - } - } - .divide-gray-200 { - :where(& > :not(:last-child)) { - border-color: var(--color-gray-200); - } - } - .truncate { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - .overflow-auto { - overflow: auto; - } - .overflow-hidden { - overflow: hidden; - } - .overflow-visible { - overflow: visible; - } - .overflow-x-auto { - overflow-x: auto; - } - .overflow-x-hidden { - overflow-x: hidden; - } - .overflow-y-auto { - overflow-y: auto; - } - .rounded { - border-radius: 0.25rem; - } - .rounded-2xl { - border-radius: var(--radius-2xl); - } - .rounded-\[2px\] { - border-radius: 2px; - } - .rounded-\[18px\] { - border-radius: 18px; - } - .rounded-\[inherit\] { - border-radius: inherit; - } - .rounded-full { - border-radius: calc(infinity * 1px); - } - .rounded-lg { - border-radius: var(--radius-lg); - } - .rounded-md { - border-radius: var(--radius-md); - } - .rounded-none { - border-radius: 0; - } - .rounded-sm { - border-radius: var(--radius-sm); - } - .rounded-xl { - border-radius: var(--radius-xl); - } - .rounded-l-none { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - .rounded-r-none { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - .border { - border-style: var(--tw-border-style); - border-width: 1px; - } - .border-0 { - border-style: var(--tw-border-style); - border-width: 0px; - } - .border-2 { - border-style: var(--tw-border-style); - border-width: 2px; - } - .border-\[1\.5px\] { - border-style: var(--tw-border-style); - border-width: 1.5px; - } - .border-t { - border-top-style: var(--tw-border-style); - border-top-width: 1px; - } - .border-r { - border-right-style: var(--tw-border-style); - border-right-width: 1px; - } - .border-r-0 { - border-right-style: var(--tw-border-style); - border-right-width: 0px; - } - .border-b { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 1px; - } - .border-b-2 { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 2px; - } - .border-l { - border-left-style: var(--tw-border-style); - border-left-width: 1px; - } - .border-l-0 { - border-left-style: var(--tw-border-style); - border-left-width: 0px; - } - .border-l-2 { - border-left-style: var(--tw-border-style); - border-left-width: 2px; - } - .border-dashed { - --tw-border-style: dashed; - border-style: dashed; - } - .border-none { - --tw-border-style: none; - border-style: none; - } - .border-\[--color-border\] { - border-color: --color-border; - } - .border-accent { - border-color: var(--color-accent); - } - .border-border { - border-color: var(--color-border); - } - .border-border-strong { - border-color: var(--color-border-strong); - } - .border-border\/50 { - border-color: color-mix(in srgb, oklch(0.92 0.005 80) 50%, transparent); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-border) 50%, transparent); - } - } - .border-current\/30 { - border-color: currentcolor; - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, currentcolor 30%, transparent); - } - } - .border-danger { - border-color: var(--color-danger); - } - .border-danger\/20 { - border-color: color-mix(in srgb, oklch(0.6 0.21 17) 20%, transparent); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-danger) 20%, transparent); - } - } - .border-danger\/30 { - border-color: color-mix(in srgb, oklch(0.6 0.21 17) 30%, transparent); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-danger) 30%, transparent); - } - } - .border-gray-200 { - border-color: var(--color-gray-200); - } - .border-gray-300 { - border-color: var(--color-gray-300); - } - .border-info\/20 { - border-color: color-mix(in srgb, oklch(0.65 0.16 232) 20%, transparent); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-info) 20%, transparent); - } - } - .border-primary { - border-color: var(--color-primary); - } - .border-primary\/30 { - border-color: color-mix(in srgb, oklch(0.62 0.16 158) 30%, transparent); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-primary) 30%, transparent); - } - } - .border-red-200 { - border-color: var(--color-red-200); - } - .border-success\/20 { - border-color: color-mix(in srgb, oklch(0.62 0.16 158) 20%, transparent); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-success) 20%, transparent); - } - } - .border-transparent { - border-color: transparent; - } - .border-warning-border { - border-color: var(--color-warning-border); - } - .border-warning\/30 { - border-color: color-mix(in srgb, oklch(0.72 0.16 60) 30%, transparent); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-warning) 30%, transparent); - } - } - .border-t-current { - border-top-color: currentcolor; - } - .border-t-primary { - border-top-color: var(--color-primary); - } - .border-t-transparent { - border-top-color: transparent; - } - .border-l-transparent { - border-left-color: transparent; - } - .bg-\[--color-bg\] { - background-color: --color-bg; - } - .bg-black\/50 { - background-color: color-mix(in srgb, #000 50%, transparent); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-black) 50%, transparent); - } - } - .bg-blue-50 { - background-color: var(--color-blue-50); - } - .bg-blue-100 { - background-color: var(--color-blue-100); - } - .bg-blue-600 { - background-color: var(--color-blue-600); - } - .bg-border { - background-color: var(--color-border); - } - .bg-border-strong { - background-color: var(--color-border-strong); - } - .bg-code-bg { - background-color: var(--color-code-bg); - } - .bg-danger { - background-color: var(--color-danger); - } - .bg-danger-bg { - background-color: var(--color-danger-bg); - } - .bg-danger\/10 { - background-color: color-mix(in srgb, oklch(0.6 0.21 17) 10%, transparent); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-danger) 10%, transparent); - } - } - .bg-dark { - background-color: var(--color-dark); - } - .bg-dark\/60 { - background-color: color-mix(in srgb, oklch(0.18 0.012 260) 60%, transparent); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-dark) 60%, transparent); - } - } - .bg-gray-50 { - background-color: var(--color-gray-50); - } - .bg-gray-100 { - background-color: var(--color-gray-100); - } - .bg-green-600 { - background-color: var(--color-green-600); - } - .bg-info-bg { - background-color: var(--color-info-bg); - } - .bg-muted { - background-color: var(--color-muted); - } - .bg-primary { - background-color: var(--color-primary); - } - .bg-primary-subtle { - background-color: var(--color-primary-subtle); - } - .bg-primary\/5 { - background-color: color-mix(in srgb, oklch(0.62 0.16 158) 5%, transparent); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-primary) 5%, transparent); - } - } - .bg-red-50 { - background-color: var(--color-red-50); - } - .bg-red-600 { - background-color: var(--color-red-600); - } - .bg-success-bg { - background-color: var(--color-success-bg); - } - .bg-success\/10 { - background-color: color-mix(in srgb, oklch(0.62 0.16 158) 10%, transparent); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-success) 10%, transparent); - } - } - .bg-surface { - background-color: var(--color-surface); - } - .bg-surface-overlay { - background-color: var(--color-surface-overlay); - } - .bg-surface-raised { - background-color: var(--color-surface-raised); - } - .bg-surface-sunken { - background-color: var(--color-surface-sunken); - } - .bg-surface\/80 { - background-color: color-mix(in srgb, oklch(1 0 0) 80%, transparent); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-surface) 80%, transparent); - } - } - .bg-transparent { - background-color: transparent; - } - .bg-warning-bg { - background-color: var(--color-warning-bg); - } - .bg-white { - background-color: var(--color-white); - } - .bg-gradient-to-br { - --tw-gradient-position: to bottom right in oklab; - background-image: linear-gradient(var(--tw-gradient-stops)); - } - .from-primary { - --tw-gradient-from: var(--color-primary); - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } - .to-accent { - --tw-gradient-to: var(--color-accent); - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } - .fill-current { - fill: currentcolor; - } - .p-0 { - padding: calc(var(--spacing) * 0); - } - .p-1 { - padding: calc(var(--spacing) * 1); - } - .p-1\.5 { - padding: calc(var(--spacing) * 1.5); - } - .p-2 { - padding: calc(var(--spacing) * 2); - } - .p-2\.5 { - padding: calc(var(--spacing) * 2.5); - } - .p-3 { - padding: calc(var(--spacing) * 3); - } - .p-4 { - padding: calc(var(--spacing) * 4); - } - .p-5 { - padding: calc(var(--spacing) * 5); - } - .p-6 { - padding: calc(var(--spacing) * 6); - } - .p-8 { - padding: calc(var(--spacing) * 8); - } - .p-\[1px\] { - padding: 1px; - } - .px-0 { - padding-inline: calc(var(--spacing) * 0); - } - .px-1 { - padding-inline: calc(var(--spacing) * 1); - } - .px-1\.5 { - padding-inline: calc(var(--spacing) * 1.5); - } - .px-2 { - padding-inline: calc(var(--spacing) * 2); - } - .px-2\.5 { - padding-inline: calc(var(--spacing) * 2.5); - } - .px-3 { - padding-inline: calc(var(--spacing) * 3); - } - .px-3\.5 { - padding-inline: calc(var(--spacing) * 3.5); - } - .px-4 { - padding-inline: calc(var(--spacing) * 4); - } - .px-5 { - padding-inline: calc(var(--spacing) * 5); - } - .px-6 { - padding-inline: calc(var(--spacing) * 6); - } - .px-8 { - padding-inline: calc(var(--spacing) * 8); - } - .py-0\.5 { - padding-block: calc(var(--spacing) * 0.5); - } - .py-1 { - padding-block: calc(var(--spacing) * 1); - } - .py-1\.5 { - padding-block: calc(var(--spacing) * 1.5); - } - .py-2 { - padding-block: calc(var(--spacing) * 2); - } - .py-2\.5 { - padding-block: calc(var(--spacing) * 2.5); - } - .py-3 { - padding-block: calc(var(--spacing) * 3); - } - .py-3\.5 { - padding-block: calc(var(--spacing) * 3.5); - } - .py-4 { - padding-block: calc(var(--spacing) * 4); - } - .py-6 { - padding-block: calc(var(--spacing) * 6); - } - .py-8 { - padding-block: calc(var(--spacing) * 8); - } - .py-10 { - padding-block: calc(var(--spacing) * 10); - } - .py-12 { - padding-block: calc(var(--spacing) * 12); - } - .py-20 { - padding-block: calc(var(--spacing) * 20); - } - .pt-0 { - padding-top: calc(var(--spacing) * 0); - } - .pt-1 { - padding-top: calc(var(--spacing) * 1); - } - .pt-2 { - padding-top: calc(var(--spacing) * 2); - } - .pt-3 { - padding-top: calc(var(--spacing) * 3); - } - .pt-4 { - padding-top: calc(var(--spacing) * 4); - } - .pt-8 { - padding-top: calc(var(--spacing) * 8); - } - .pt-10 { - padding-top: calc(var(--spacing) * 10); - } - .pt-12 { - padding-top: calc(var(--spacing) * 12); - } - .pt-32 { - padding-top: calc(var(--spacing) * 32); - } - .pr-2 { - padding-right: calc(var(--spacing) * 2); - } - .pr-10 { - padding-right: calc(var(--spacing) * 10); - } - .pb-2 { - padding-bottom: calc(var(--spacing) * 2); - } - .pb-3 { - padding-bottom: calc(var(--spacing) * 3); - } - .pb-4 { - padding-bottom: calc(var(--spacing) * 4); - } - .pb-8 { - padding-bottom: calc(var(--spacing) * 8); - } - .pb-20 { - padding-bottom: calc(var(--spacing) * 20); - } - .pb-32 { - padding-bottom: calc(var(--spacing) * 32); - } - .pl-4 { - padding-left: calc(var(--spacing) * 4); - } - .pl-5 { - padding-left: calc(var(--spacing) * 5); - } - .pl-8 { - padding-left: calc(var(--spacing) * 8); - } - .pl-10 { - padding-left: calc(var(--spacing) * 10); - } - .text-center { - text-align: center; - } - .text-left { - text-align: left; - } - .text-right { - text-align: right; - } - .align-middle { - vertical-align: middle; - } - .font-display { - font-family: var(--font-display); - } - .font-mono { - font-family: var(--font-mono); - } - .text-2xl { - font-size: var(--text-2xl); - line-height: var(--tw-leading, var(--text-2xl--line-height)); - } - .text-3xl { - font-size: var(--text-3xl); - line-height: var(--tw-leading, var(--text-3xl--line-height)); - } - .text-4xl { - font-size: var(--text-4xl); - line-height: var(--tw-leading, var(--text-4xl--line-height)); - } - .text-base { - font-size: var(--text-base); - line-height: var(--tw-leading, var(--text-base--line-height)); - } - .text-lg { - font-size: var(--text-lg); - line-height: var(--tw-leading, var(--text-lg--line-height)); - } - .text-sm { - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - } - .text-xl { - font-size: var(--text-xl); - line-height: var(--tw-leading, var(--text-xl--line-height)); - } - .text-xs { - font-size: var(--text-xs); - line-height: var(--tw-leading, var(--text-xs--line-height)); - } - .text-\[0\.6rem\] { - font-size: 0.6rem; - } - .text-\[0\.8rem\] { - font-size: 0.8rem; - } - .text-\[0\.65rem\] { - font-size: 0.65rem; - } - .text-\[8rem\] { - font-size: 8rem; - } - .leading-none { - --tw-leading: 1; - line-height: 1; - } - .leading-relaxed { - --tw-leading: var(--leading-relaxed); - line-height: var(--leading-relaxed); - } - .leading-tight { - --tw-leading: var(--leading-tight); - line-height: var(--leading-tight); - } - .font-bold { - --tw-font-weight: var(--font-weight-bold); - font-weight: var(--font-weight-bold); - } - .font-extrabold { - --tw-font-weight: var(--font-weight-extrabold); - font-weight: var(--font-weight-extrabold); - } - .font-medium { - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - } - .font-normal { - --tw-font-weight: var(--font-weight-normal); - font-weight: var(--font-weight-normal); - } - .font-semibold { - --tw-font-weight: var(--font-weight-semibold); - font-weight: var(--font-weight-semibold); - } - .tracking-tight { - --tw-tracking: var(--tracking-tight); - letter-spacing: var(--tracking-tight); - } - .tracking-wide { - --tw-tracking: var(--tracking-wide); - letter-spacing: var(--tracking-wide); - } - .tracking-wider { - --tw-tracking: var(--tracking-wider); - letter-spacing: var(--tracking-wider); - } - .tracking-widest { - --tw-tracking: var(--tracking-widest); - letter-spacing: var(--tracking-widest); - } - .break-words { - overflow-wrap: break-word; - } - .break-all { - word-break: break-all; - } - .whitespace-nowrap { - white-space: nowrap; - } - .whitespace-pre-wrap { - white-space: pre-wrap; - } - .text-accent { - color: var(--color-accent); - } - .text-amber-600 { - color: var(--color-amber-600); - } - .text-blue-500 { - color: var(--color-blue-500); - } - .text-blue-600 { - color: var(--color-blue-600); - } - .text-blue-800 { - color: var(--color-blue-800); - } - .text-border { - color: var(--color-border); - } - .text-code-text { - color: var(--color-code-text); - } - .text-current { - color: currentcolor; - } - .text-danger { - color: var(--color-danger); - } - .text-danger-text { - color: var(--color-danger-text); - } - .text-gray-500 { - color: var(--color-gray-500); - } - .text-gray-600 { - color: var(--color-gray-600); - } - .text-gray-700 { - color: var(--color-gray-700); - } - .text-green-600 { - color: var(--color-green-600); - } - .text-info { - color: var(--color-info); - } - .text-muted { - color: var(--color-muted); - } - .text-primary { - color: var(--color-primary); - } - .text-red-500 { - color: var(--color-red-500); - } - .text-red-600 { - color: var(--color-red-600); - } - .text-red-800 { - color: var(--color-red-800); - } - .text-success { - color: var(--color-success); - } - .text-success-text { - color: var(--color-success-text); - } - .text-text { - color: var(--color-text); - } - .text-text-inverse { - color: var(--color-text-inverse); - } - .text-text-muted { - color: var(--color-text-muted); - } - .text-text-muted\/20 { - color: color-mix(in srgb, oklch(0.62 0.012 250) 20%, transparent); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-text-muted) 20%, transparent); - } - } - .text-text-muted\/50 { - color: color-mix(in srgb, oklch(0.62 0.012 250) 50%, transparent); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-text-muted) 50%, transparent); - } - } - .text-text-secondary { - color: var(--color-text-secondary); - } - .text-warning { - color: var(--color-warning); - } - .text-warning-text { - color: var(--color-warning-text); - } - .text-white { - color: var(--color-white); - } - .text-white\/80 { - color: color-mix(in srgb, #fff 80%, transparent); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-white) 80%, transparent); - } - } - .text-yellow-600 { - color: var(--color-yellow-600); - } - .lowercase { - text-transform: lowercase; - } - .uppercase { - text-transform: uppercase; - } - .tabular-nums { - --tw-numeric-spacing: tabular-nums; - font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,); - } - .no-underline { - text-decoration-line: none; - } - .underline { - text-decoration-line: underline; - } - .placeholder-text-muted { - &::placeholder { - color: var(--color-text-muted); - } - } - .accent-primary { - accent-color: var(--color-primary); - } - .opacity-0 { - opacity: 0%; - } - .opacity-40 { - opacity: 40%; - } - .opacity-50 { - opacity: 50%; - } - .opacity-60 { - opacity: 60%; - } - .opacity-90 { - opacity: 90%; - } - .opacity-100 { - opacity: 100%; - } - .shadow { - --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .shadow-\(--shadow-danger\) { - --tw-shadow: var(--shadow-danger); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .shadow-\(--shadow-primary\) { - --tw-shadow: var(--shadow-primary); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .shadow-lg { - --tw-shadow: 0 12px 24px -8px var(--tw-shadow-color, oklch(0.18 0.012 260 / 0.12)), 0 4px 8px -4px var(--tw-shadow-color, oklch(0.18 0.012 260 / 0.06)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .shadow-md { - --tw-shadow: 0 4px 8px -2px var(--tw-shadow-color, oklch(0.18 0.012 260 / 0.08)), 0 2px 4px -2px var(--tw-shadow-color, oklch(0.18 0.012 260 / 0.05)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .shadow-sm { - --tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, oklch(0.18 0.012 260 / 0.06)), 0 1px 3px 0 var(--tw-shadow-color, oklch(0.18 0.012 260 / 0.04)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .shadow-xl { - --tw-shadow: 0 24px 48px -12px var(--tw-shadow-color, oklch(0.18 0.012 260 / 0.18)), 0 8px 16px -8px var(--tw-shadow-color, oklch(0.18 0.012 260 / 0.08)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .ring { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .ring-0 { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - .ring-offset-surface { - --tw-ring-offset-color: var(--color-surface); - } - .outline { - outline-style: var(--tw-outline-style); - outline-width: 1px; - } - .blur { - --tw-blur: blur(8px); - filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); - } - .filter { - filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,); - } - .backdrop-blur-lg { - --tw-backdrop-blur: blur(var(--blur-lg)); - -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); - backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); - } - .backdrop-blur-sm { - --tw-backdrop-blur: blur(var(--blur-sm)); - -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); - backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); - } - .backdrop-filter { - -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); - backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,); - } - .transition { - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to, opacity, box-shadow, transform, translate, scale, rotate, filter, -webkit-backdrop-filter, backdrop-filter, display, content-visibility, overlay, pointer-events; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - .transition-all { - transition-property: all; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - .transition-colors { - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - .transition-opacity { - transition-property: opacity; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - .transition-transform { - transition-property: transform, translate, scale, rotate; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - } - .duration-150 { - --tw-duration: 150ms; - transition-duration: 150ms; - } - .duration-200 { - --tw-duration: 200ms; - transition-duration: 200ms; - } - .duration-300 { - --tw-duration: 300ms; - transition-duration: 300ms; - } - .duration-500 { - --tw-duration: 500ms; - transition-duration: 500ms; - } - .ease-in-out { - --tw-ease: var(--ease-in-out); - transition-timing-function: var(--ease-in-out); - } - .ease-out { - --tw-ease: var(--ease-out); - transition-timing-function: var(--ease-out); - } - .outline-none { - --tw-outline-style: none; - outline-style: none; - } - .select-all { - -webkit-user-select: all; - user-select: all; - } - .select-none { - -webkit-user-select: none; - user-select: none; - } - .group-hover\:block { - &:is(:where(.group):hover *) { - @media (hover: hover) { - display: block; - } - } - } - .group-hover\:scale-105 { - &:is(:where(.group):hover *) { - @media (hover: hover) { - --tw-scale-x: 105%; - --tw-scale-y: 105%; - --tw-scale-z: 105%; - scale: var(--tw-scale-x) var(--tw-scale-y); - } - } - } - .group-hover\:bg-primary\/10 { - &:is(:where(.group):hover *) { - @media (hover: hover) { - background-color: color-mix(in srgb, oklch(0.62 0.16 158) 10%, transparent); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-primary) 10%, transparent); - } - } - } - } - .group-hover\:text-primary { - &:is(:where(.group):hover *) { - @media (hover: hover) { - color: var(--color-primary); - } - } - } - .group-hover\:opacity-100 { - &:is(:where(.group):hover *) { - @media (hover: hover) { - opacity: 100%; - } - } - } - .group-hover\/sub\:block { - &:is(:where(.group\/sub):hover *) { - @media (hover: hover) { - display: block; - } - } - } - .placeholder\:text-text-muted { - &::placeholder { - color: var(--color-text-muted); - } - } - .before\:h-5 { - &::before { - content: var(--tw-content); - height: calc(var(--spacing) * 5); - } - } - .before\:w-1 { - &::before { - content: var(--tw-content); - width: calc(var(--spacing) * 1); - } - } - .before\:rounded-full { - &::before { - content: var(--tw-content); - border-radius: calc(infinity * 1px); - } - } - .before\:bg-gradient-to-b { - &::before { - content: var(--tw-content); - --tw-gradient-position: to bottom in oklab; - background-image: linear-gradient(var(--tw-gradient-stops)); - } - } - .before\:from-primary { - &::before { - content: var(--tw-content); - --tw-gradient-from: var(--color-primary); - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } - } - .before\:to-accent { - &::before { - content: var(--tw-content); - --tw-gradient-to: var(--color-accent); - --tw-gradient-stops: var(--tw-gradient-via-stops, var(--tw-gradient-position), var(--tw-gradient-from) var(--tw-gradient-from-position), var(--tw-gradient-to) var(--tw-gradient-to-position)); - } - } - .before\:content-\[\'\'\] { - &::before { - --tw-content: ''; - content: var(--tw-content); - } - } - .after\:absolute { - &::after { - content: var(--tw-content); - position: absolute; - } - } - .after\:inset-y-0 { - &::after { - content: var(--tw-content); - inset-block: calc(var(--spacing) * 0); - } - } - .after\:left-1\/2 { - &::after { - content: var(--tw-content); - left: calc(1 / 2 * 100%); - } - } - .after\:w-1 { - &::after { - content: var(--tw-content); - width: calc(var(--spacing) * 1); - } - } - .after\:-translate-x-1\/2 { - &::after { - content: var(--tw-content); - --tw-translate-x: calc(calc(1 / 2 * 100%) * -1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - } - .last\:border-0 { - &:last-child { - border-style: var(--tw-border-style); - border-width: 0px; - } - } - .last\:border-b-0 { - &:last-child { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 0px; - } - } - .focus-within\:relative { - &:focus-within { - position: relative; - } - } - .focus-within\:z-20 { - &:focus-within { - z-index: 20; - } - } - .hover\:-translate-y-px { - &:hover { - @media (hover: hover) { - --tw-translate-y: -1px; - translate: var(--tw-translate-x) var(--tw-translate-y); - } - } - } - .hover\:border-border { - &:hover { - @media (hover: hover) { - border-color: var(--color-border); - } - } - } - .hover\:border-border-strong { - &:hover { - @media (hover: hover) { - border-color: var(--color-border-strong); - } - } - } - .hover\:border-primary { - &:hover { - @media (hover: hover) { - border-color: var(--color-primary); - } - } - } - .hover\:border-primary\/30 { - &:hover { - @media (hover: hover) { - border-color: color-mix(in srgb, oklch(0.62 0.16 158) 30%, transparent); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-primary) 30%, transparent); - } - } - } - } - .hover\:bg-blue-700 { - &:hover { - @media (hover: hover) { - background-color: var(--color-blue-700); - } - } - } - .hover\:bg-danger-hover { - &:hover { - @media (hover: hover) { - background-color: var(--color-danger-hover); - } - } - } - .hover\:bg-gray-50 { - &:hover { - @media (hover: hover) { - background-color: var(--color-gray-50); - } - } - } - .hover\:bg-gray-100 { - &:hover { - @media (hover: hover) { - background-color: var(--color-gray-100); - } - } - } - .hover\:bg-green-700 { - &:hover { - @media (hover: hover) { - background-color: var(--color-green-700); - } - } - } - .hover\:bg-muted\/50 { - &:hover { - @media (hover: hover) { - background-color: color-mix(in srgb, oklch(0.62 0.012 250) 50%, transparent); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-muted) 50%, transparent); - } - } - } - } - .hover\:bg-primary { - &:hover { - @media (hover: hover) { - background-color: var(--color-primary); - } - } - } - .hover\:bg-primary-hover { - &:hover { - @media (hover: hover) { - background-color: var(--color-primary-hover); - } - } - } - .hover\:bg-primary-subtle { - &:hover { - @media (hover: hover) { - background-color: var(--color-primary-subtle); - } - } - } - .hover\:bg-primary\/90 { - &:hover { - @media (hover: hover) { - background-color: color-mix(in srgb, oklch(0.62 0.16 158) 90%, transparent); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-primary) 90%, transparent); - } - } - } - } - .hover\:bg-red-50 { - &:hover { - @media (hover: hover) { - background-color: var(--color-red-50); - } - } - } - .hover\:bg-red-700 { - &:hover { - @media (hover: hover) { - background-color: var(--color-red-700); - } - } - } - .hover\:bg-surface-raised { - &:hover { - @media (hover: hover) { - background-color: var(--color-surface-raised); - } - } - } - .hover\:bg-surface-sunken { - &:hover { - @media (hover: hover) { - background-color: var(--color-surface-sunken); - } - } - } - .hover\:text-blue-800 { - &:hover { - @media (hover: hover) { - color: var(--color-blue-800); - } - } - } - .hover\:text-danger\/80 { - &:hover { - @media (hover: hover) { - color: color-mix(in srgb, oklch(0.6 0.21 17) 80%, transparent); - @supports (color: color-mix(in lab, red, red)) { - color: color-mix(in oklab, var(--color-danger) 80%, transparent); - } - } - } - } - .hover\:text-gray-700 { - &:hover { - @media (hover: hover) { - color: var(--color-gray-700); - } - } - } - .hover\:text-primary { - &:hover { - @media (hover: hover) { - color: var(--color-primary); - } - } - } - .hover\:text-red-800 { - &:hover { - @media (hover: hover) { - color: var(--color-red-800); - } - } - } - .hover\:text-text { - &:hover { - @media (hover: hover) { - color: var(--color-text); - } - } - } - .hover\:text-text-inverse { - &:hover { - @media (hover: hover) { - color: var(--color-text-inverse); - } - } - } - .hover\:text-white { - &:hover { - @media (hover: hover) { - color: var(--color-white); - } - } - } - .hover\:no-underline { - &:hover { - @media (hover: hover) { - text-decoration-line: none; - } - } - } - .hover\:underline { - &:hover { - @media (hover: hover) { - text-decoration-line: underline; - } - } - } - .hover\:opacity-100 { - &:hover { - @media (hover: hover) { - opacity: 100%; - } - } - } - .hover\:shadow-\(--shadow-danger-hover\) { - &:hover { - @media (hover: hover) { - --tw-shadow: var(--shadow-danger-hover); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - } - } - .hover\:shadow-\(--shadow-primary-hover\) { - &:hover { - @media (hover: hover) { - --tw-shadow: var(--shadow-primary-hover); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - } - } - .hover\:shadow-md { - &:hover { - @media (hover: hover) { - --tw-shadow: 0 4px 8px -2px var(--tw-shadow-color, oklch(0.18 0.012 260 / 0.08)), 0 2px 4px -2px var(--tw-shadow-color, oklch(0.18 0.012 260 / 0.05)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - } - } - .hover\:shadow-xl { - &:hover { - @media (hover: hover) { - --tw-shadow: 0 24px 48px -12px var(--tw-shadow-color, oklch(0.18 0.012 260 / 0.18)), 0 8px 16px -8px var(--tw-shadow-color, oklch(0.18 0.012 260 / 0.08)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - } - } - .focus\:border-danger { - &:focus { - border-color: var(--color-danger); - } - } - .focus\:border-primary { - &:focus { - border-color: var(--color-primary); - } - } - .focus\:bg-primary { - &:focus { - background-color: var(--color-primary); - } - } - .focus\:bg-primary-subtle { - &:focus { - background-color: var(--color-primary-subtle); - } - } - .focus\:bg-surface-raised { - &:focus { - background-color: var(--color-surface-raised); - } - } - .focus\:text-primary { - &:focus { - color: var(--color-primary); - } - } - .focus\:text-text { - &:focus { - color: var(--color-text); - } - } - .focus\:text-text-inverse { - &:focus { - color: var(--color-text-inverse); - } - } - .focus\:opacity-100 { - &:focus { - opacity: 100%; - } - } - .focus\:ring-2 { - &:focus { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - } - .focus\:ring-4 { - &:focus { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - } - .focus\:ring-blue-500 { - &:focus { - --tw-ring-color: var(--color-blue-500); - } - } - .focus\:ring-danger-bg { - &:focus { - --tw-ring-color: var(--color-danger-bg); - } - } - .focus\:ring-primary-ring { - &:focus { - --tw-ring-color: var(--color-primary-ring); - } - } - .focus\:ring-primary\/15 { - &:focus { - --tw-ring-color: color-mix(in srgb, oklch(0.62 0.16 158) 15%, transparent); - @supports (color: color-mix(in lab, red, red)) { - --tw-ring-color: color-mix(in oklab, var(--color-primary) 15%, transparent); - } - } - } - .focus\:outline-none { - &:focus { - --tw-outline-style: none; - outline-style: none; - } - } - .focus-visible\:ring-2 { - &:focus-visible { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - } - .focus-visible\:ring-4 { - &:focus-visible { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - } - .focus-visible\:ring-primary-ring { - &:focus-visible { - --tw-ring-color: var(--color-primary-ring); - } - } - .focus-visible\:ring-ring { - &:focus-visible { - --tw-ring-color: var(--color-ring); - } - } - .focus-visible\:ring-offset-2 { - &:focus-visible { - --tw-ring-offset-width: 2px; - --tw-ring-offset-shadow: var(--tw-ring-inset,) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); - } - } - .focus-visible\:outline-none { - &:focus-visible { - --tw-outline-style: none; - outline-style: none; - } - } - .active\:scale-\[0\.97\] { - &:active { - scale: 0.97; - } - } - .disabled\:pointer-events-none { - &:disabled { - pointer-events: none; - } - } - .disabled\:cursor-not-allowed { - &:disabled { - cursor: not-allowed; - } - } - .disabled\:opacity-50 { - &:disabled { - opacity: 50%; - } - } - .aria-selected\:bg-primary-subtle { - &[aria-selected="true"] { - background-color: var(--color-primary-subtle); - } - } - .aria-selected\:bg-primary-subtle\/50 { - &[aria-selected="true"] { - background-color: color-mix(in srgb, oklch(0.62 0.16 158 / 0.09) 50%, transparent); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-primary-subtle) 50%, transparent); - } - } - } - .aria-selected\:text-text { - &[aria-selected="true"] { - color: var(--color-text); - } - } - .aria-selected\:text-text-muted { - &[aria-selected="true"] { - color: var(--color-text-muted); - } - } - .aria-selected\:opacity-100 { - &[aria-selected="true"] { - opacity: 100%; - } - } - .data-\[active\]\:bg-primary-subtle { - &[data-active] { - background-color: var(--color-primary-subtle); - } - } - .data-\[active\]\:font-semibold { - &[data-active] { - --tw-font-weight: var(--font-weight-semibold); - font-weight: var(--font-weight-semibold); - } - } - .data-\[active\]\:text-primary { - &[data-active] { - color: var(--color-primary); - } - } - .data-\[direction\=vertical\]\:flex-col { - &[data-direction="vertical"] { - flex-direction: column; - } - } - .data-\[disabled\]\:pointer-events-none { - &[data-disabled] { - pointer-events: none; - } - } - .data-\[disabled\]\:opacity-50 { - &[data-disabled] { - opacity: 50%; - } - } - .data-\[disabled\=true\]\:pointer-events-none { - &[data-disabled="true"] { - pointer-events: none; - } - } - .data-\[disabled\=true\]\:opacity-50 { - &[data-disabled="true"] { - opacity: 50%; - } - } - .data-\[selected\=true\]\:bg-primary-subtle { - &[data-selected="true"] { - background-color: var(--color-primary-subtle); - } - } - .data-\[selected\=true\]\:text-primary { - &[data-selected="true"] { - color: var(--color-primary); - } - } - .data-\[side\=bottom\]\:translate-y-1 { - &[data-side="bottom"] { - --tw-translate-y: calc(var(--spacing) * 1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - } - .data-\[side\=top\]\:-translate-y-1 { - &[data-side="top"] { - --tw-translate-y: calc(var(--spacing) * -1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - } - .data-\[state\=active\]\:bg-surface { - &[data-state="active"] { - background-color: var(--color-surface); - } - } - .data-\[state\=active\]\:text-text { - &[data-state="active"] { - color: var(--color-text); - } - } - .data-\[state\=active\]\:shadow-sm { - &[data-state="active"] { - --tw-shadow: 0 1px 2px 0 var(--tw-shadow-color, oklch(0.18 0.012 260 / 0.06)), 0 1px 3px 0 var(--tw-shadow-color, oklch(0.18 0.012 260 / 0.04)); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - } - .data-\[state\=checked\]\:translate-x-5 { - &[data-state="checked"] { - --tw-translate-x: calc(var(--spacing) * 5); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - } - .data-\[state\=checked\]\:border-primary { - &[data-state="checked"] { - border-color: var(--color-primary); - } - } - .data-\[state\=checked\]\:bg-primary { - &[data-state="checked"] { - background-color: var(--color-primary); - } - } - .data-\[state\=checked\]\:text-text-inverse { - &[data-state="checked"] { - color: var(--color-text-inverse); - } - } - .data-\[state\=closed\]\:duration-300 { - &[data-state="closed"] { - --tw-duration: 300ms; - transition-duration: 300ms; - } - } - .data-\[state\=on\]\:bg-primary-subtle { - &[data-state="on"] { - background-color: var(--color-primary-subtle); - } - } - .data-\[state\=on\]\:text-primary { - &[data-state="on"] { - color: var(--color-primary); - } - } - .data-\[state\=open\]\:duration-500 { - &[data-state="open"] { - --tw-duration: 500ms; - transition-duration: 500ms; - } - } - .data-\[state\=unchecked\]\:translate-x-0 { - &[data-state="unchecked"] { - --tw-translate-x: calc(var(--spacing) * 0); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - } - .data-\[state\=unchecked\]\:bg-border-strong { - &[data-state="unchecked"] { - background-color: var(--color-border-strong); - } - } - .data-\[swipe\=cancel\]\:translate-x-0 { - &[data-swipe="cancel"] { - --tw-translate-x: calc(var(--spacing) * 0); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - } - .data-\[swipe\=end\]\:translate-x-\[var\(--radix-toast-swipe-end-x\)\] { - &[data-swipe="end"] { - --tw-translate-x: var(--radix-toast-swipe-end-x); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - } - .data-\[swipe\=move\]\:translate-x-\[var\(--radix-toast-swipe-move-x\)\] { - &[data-swipe="move"] { - --tw-translate-x: var(--radix-toast-swipe-move-x); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - } - .data-\[swipe\=move\]\:transition-none { - &[data-swipe="move"] { - transition-property: none; - } - } - .sm\:sticky { - @media (width >= 40rem) { - position: sticky; - } - } - .sm\:top-20 { - @media (width >= 40rem) { - top: calc(var(--spacing) * 20); - } - } - .sm\:top-auto { - @media (width >= 40rem) { - top: auto; - } - } - .sm\:right-0 { - @media (width >= 40rem) { - right: calc(var(--spacing) * 0); - } - } - .sm\:bottom-0 { - @media (width >= 40rem) { - bottom: calc(var(--spacing) * 0); - } - } - .sm\:mx-0 { - @media (width >= 40rem) { - margin-inline: calc(var(--spacing) * 0); - } - } - .sm\:mt-6 { - @media (width >= 40rem) { - margin-top: calc(var(--spacing) * 6); - } - } - .sm\:mb-4 { - @media (width >= 40rem) { - margin-bottom: calc(var(--spacing) * 4); - } - } - .sm\:mb-6 { - @media (width >= 40rem) { - margin-bottom: calc(var(--spacing) * 6); - } - } - .sm\:mb-8 { - @media (width >= 40rem) { - margin-bottom: calc(var(--spacing) * 8); - } - } - .sm\:block { - @media (width >= 40rem) { - display: block; - } - } - .sm\:flex { - @media (width >= 40rem) { - display: flex; - } - } - .sm\:inline-flex { - @media (width >= 40rem) { - display: inline-flex; - } - } - .sm\:h-16 { - @media (width >= 40rem) { - height: calc(var(--spacing) * 16); - } - } - .sm\:h-48 { - @media (width >= 40rem) { - height: calc(var(--spacing) * 48); - } - } - .sm\:min-h-\[200px\] { - @media (width >= 40rem) { - min-height: 200px; - } - } - .sm\:min-h-\[220px\] { - @media (width >= 40rem) { - min-height: 220px; - } - } - .sm\:min-h-\[250px\] { - @media (width >= 40rem) { - min-height: 250px; - } - } - .sm\:min-h-\[280px\] { - @media (width >= 40rem) { - min-height: 280px; - } - } - .sm\:w-16 { - @media (width >= 40rem) { - width: calc(var(--spacing) * 16); - } - } - .sm\:w-48 { - @media (width >= 40rem) { - width: calc(var(--spacing) * 48); - } - } - .sm\:w-56 { - @media (width >= 40rem) { - width: calc(var(--spacing) * 56); - } - } - .sm\:w-\[160px\] { - @media (width >= 40rem) { - width: 160px; - } - } - .sm\:w-\[180px\] { - @media (width >= 40rem) { - width: 180px; - } - } - .sm\:w-auto { - @media (width >= 40rem) { - width: auto; - } - } - .sm\:max-w-lg { - @media (width >= 40rem) { - max-width: var(--container-lg); - } - } - .sm\:max-w-sm { - @media (width >= 40rem) { - max-width: var(--container-sm); - } - } - .sm\:grid-cols-2 { - @media (width >= 40rem) { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - } - .sm\:grid-cols-3 { - @media (width >= 40rem) { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - } - .sm\:flex-col { - @media (width >= 40rem) { - flex-direction: column; - } - } - .sm\:flex-row { - @media (width >= 40rem) { - flex-direction: row; - } - } - .sm\:flex-wrap { - @media (width >= 40rem) { - flex-wrap: wrap; - } - } - .sm\:items-center { - @media (width >= 40rem) { - align-items: center; - } - } - .sm\:items-end { - @media (width >= 40rem) { - align-items: flex-end; - } - } - .sm\:items-start { - @media (width >= 40rem) { - align-items: flex-start; - } - } - .sm\:justify-between { - @media (width >= 40rem) { - justify-content: space-between; - } - } - .sm\:justify-end { - @media (width >= 40rem) { - justify-content: flex-end; - } - } - .sm\:gap-0 { - @media (width >= 40rem) { - gap: calc(var(--spacing) * 0); - } - } - .sm\:gap-2 { - @media (width >= 40rem) { - gap: calc(var(--spacing) * 2); - } - } - .sm\:gap-3 { - @media (width >= 40rem) { - gap: calc(var(--spacing) * 3); - } - } - .sm\:gap-4 { - @media (width >= 40rem) { - gap: calc(var(--spacing) * 4); - } - } - .sm\:gap-6 { - @media (width >= 40rem) { - gap: calc(var(--spacing) * 6); - } - } - .sm\:gap-8 { - @media (width >= 40rem) { - gap: calc(var(--spacing) * 8); - } - } - .sm\:space-y-0 { - @media (width >= 40rem) { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 0) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 0) * calc(1 - var(--tw-space-y-reverse))); - } - } - } - .sm\:space-y-4 { - @media (width >= 40rem) { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse))); - } - } - } - .sm\:space-y-6 { - @media (width >= 40rem) { - :where(& > :not(:last-child)) { - --tw-space-y-reverse: 0; - margin-block-start: calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse)); - margin-block-end: calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse))); - } - } - } - .sm\:space-x-4 { - @media (width >= 40rem) { - :where(& > :not(:last-child)) { - --tw-space-x-reverse: 0; - margin-inline-start: calc(calc(var(--spacing) * 4) * var(--tw-space-x-reverse)); - margin-inline-end: calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-x-reverse))); - } - } - } - .sm\:overflow-x-visible { - @media (width >= 40rem) { - overflow-x: visible; - } - } - .sm\:rounded-2xl { - @media (width >= 40rem) { - border-radius: var(--radius-2xl); - } - } - .sm\:p-3 { - @media (width >= 40rem) { - padding: calc(var(--spacing) * 3); - } - } - .sm\:p-4 { - @media (width >= 40rem) { - padding: calc(var(--spacing) * 4); - } - } - .sm\:p-5 { - @media (width >= 40rem) { - padding: calc(var(--spacing) * 5); - } - } - .sm\:p-6 { - @media (width >= 40rem) { - padding: calc(var(--spacing) * 6); - } - } - .sm\:p-8 { - @media (width >= 40rem) { - padding: calc(var(--spacing) * 8); - } - } - .sm\:px-0 { - @media (width >= 40rem) { - padding-inline: calc(var(--spacing) * 0); - } - } - .sm\:px-4 { - @media (width >= 40rem) { - padding-inline: calc(var(--spacing) * 4); - } - } - .sm\:px-6 { - @media (width >= 40rem) { - padding-inline: calc(var(--spacing) * 6); - } - } - .sm\:px-8 { - @media (width >= 40rem) { - padding-inline: calc(var(--spacing) * 8); - } - } - .sm\:px-10 { - @media (width >= 40rem) { - padding-inline: calc(var(--spacing) * 10); - } - } - .sm\:py-3 { - @media (width >= 40rem) { - padding-block: calc(var(--spacing) * 3); - } - } - .sm\:py-6 { - @media (width >= 40rem) { - padding-block: calc(var(--spacing) * 6); - } - } - .sm\:py-16 { - @media (width >= 40rem) { - padding-block: calc(var(--spacing) * 16); - } - } - .sm\:pt-4 { - @media (width >= 40rem) { - padding-top: calc(var(--spacing) * 4); - } - } - .sm\:pt-6 { - @media (width >= 40rem) { - padding-top: calc(var(--spacing) * 6); - } - } - .sm\:pt-14 { - @media (width >= 40rem) { - padding-top: calc(var(--spacing) * 14); - } - } - .sm\:text-2xl { - @media (width >= 40rem) { - font-size: var(--text-2xl); - line-height: var(--tw-leading, var(--text-2xl--line-height)); - } - } - .sm\:text-3xl { - @media (width >= 40rem) { - font-size: var(--text-3xl); - line-height: var(--tw-leading, var(--text-3xl--line-height)); - } - } - .sm\:text-4xl { - @media (width >= 40rem) { - font-size: var(--text-4xl); - line-height: var(--tw-leading, var(--text-4xl--line-height)); - } - } - .sm\:text-5xl { - @media (width >= 40rem) { - font-size: var(--text-5xl); - line-height: var(--tw-leading, var(--text-5xl--line-height)); - } - } - .sm\:text-base { - @media (width >= 40rem) { - font-size: var(--text-base); - line-height: var(--tw-leading, var(--text-base--line-height)); - } - } - .sm\:text-xl { - @media (width >= 40rem) { - font-size: var(--text-xl); - line-height: var(--tw-leading, var(--text-xl--line-height)); - } - } - .md\:block { - @media (width >= 48rem) { - display: block; - } - } - .md\:flex { - @media (width >= 48rem) { - display: flex; - } - } - .md\:hidden { - @media (width >= 48rem) { - display: none; - } - } - .md\:inline-flex { - @media (width >= 48rem) { - display: inline-flex; - } - } - .md\:w-56 { - @media (width >= 48rem) { - width: calc(var(--spacing) * 56); - } - } - .md\:max-w-\[420px\] { - @media (width >= 48rem) { - max-width: 420px; - } - } - .md\:grid-cols-2 { - @media (width >= 48rem) { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - } - .md\:grid-cols-3 { - @media (width >= 48rem) { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - } - .md\:grid-cols-4 { - @media (width >= 48rem) { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - } - .md\:grid-cols-\[1fr_18rem\] { - @media (width >= 48rem) { - grid-template-columns: 1fr 18rem; - } - } - .md\:grid-cols-\[2fr_3fr\] { - @media (width >= 48rem) { - grid-template-columns: 2fr 3fr; - } - } - .md\:flex-row { - @media (width >= 48rem) { - flex-direction: row; - } - } - .md\:gap-6 { - @media (width >= 48rem) { - gap: calc(var(--spacing) * 6); - } - } - .md\:border-r { - @media (width >= 48rem) { - border-right-style: var(--tw-border-style); - border-right-width: 1px; - } - } - .md\:border-b-0 { - @media (width >= 48rem) { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 0px; - } - } - .md\:p-8 { - @media (width >= 48rem) { - padding: calc(var(--spacing) * 8); - } - } - .md\:px-6 { - @media (width >= 48rem) { - padding-inline: calc(var(--spacing) * 6); - } - } - .md\:text-2xl { - @media (width >= 48rem) { - font-size: var(--text-2xl); - line-height: var(--tw-leading, var(--text-2xl--line-height)); - } - } - .lg\:col-span-2 { - @media (width >= 64rem) { - grid-column: span 2 / span 2; - } - } - .lg\:block { - @media (width >= 64rem) { - display: block; - } - } - .lg\:grid-cols-2 { - @media (width >= 64rem) { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - } - .lg\:grid-cols-3 { - @media (width >= 64rem) { - grid-template-columns: repeat(3, minmax(0, 1fr)); - } - } - .lg\:grid-cols-4 { - @media (width >= 64rem) { - grid-template-columns: repeat(4, minmax(0, 1fr)); - } - } - .lg\:grid-cols-\[1fr_13rem\] { - @media (width >= 64rem) { - grid-template-columns: 1fr 13rem; - } - } - .lg\:gap-6 { - @media (width >= 64rem) { - gap: calc(var(--spacing) * 6); - } - } - .lg\:px-8 { - @media (width >= 64rem) { - padding-inline: calc(var(--spacing) * 8); - } - } - .lg\:text-6xl { - @media (width >= 64rem) { - font-size: var(--text-6xl); - line-height: var(--tw-leading, var(--text-6xl--line-height)); - } - } - .xl\:grid-cols-5 { - @media (width >= 80rem) { - grid-template-columns: repeat(5, minmax(0, 1fr)); - } - } - .xl\:grid-cols-6 { - @media (width >= 80rem) { - grid-template-columns: repeat(6, minmax(0, 1fr)); - } - } - .dark\:block { - @media (prefers-color-scheme: dark) { - display: block; - } - } - .dark\:hidden { - @media (prefers-color-scheme: dark) { - display: none; - } - } - .dark\:divide-gray-700 { - @media (prefers-color-scheme: dark) { - :where(& > :not(:last-child)) { - border-color: var(--color-gray-700); - } - } - } - .dark\:border-gray-600 { - @media (prefers-color-scheme: dark) { - border-color: var(--color-gray-600); - } - } - .dark\:border-gray-700 { - @media (prefers-color-scheme: dark) { - border-color: var(--color-gray-700); - } - } - .dark\:bg-blue-900\/30 { - @media (prefers-color-scheme: dark) { - background-color: color-mix(in srgb, oklch(37.9% 0.146 265.522) 30%, transparent); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-blue-900) 30%, transparent); - } - } - } - .dark\:bg-gray-800 { - @media (prefers-color-scheme: dark) { - background-color: var(--color-gray-800); - } - } - .dark\:text-amber-400 { - @media (prefers-color-scheme: dark) { - color: var(--color-amber-400); - } - } - .dark\:text-blue-300 { - @media (prefers-color-scheme: dark) { - color: var(--color-blue-300); - } - } - .dark\:text-blue-400 { - @media (prefers-color-scheme: dark) { - color: var(--color-blue-400); - } - } - .dark\:text-gray-400 { - @media (prefers-color-scheme: dark) { - color: var(--color-gray-400); - } - } - .dark\:text-green-400 { - @media (prefers-color-scheme: dark) { - color: var(--color-green-400); - } - } - .dark\:text-red-400 { - @media (prefers-color-scheme: dark) { - color: var(--color-red-400); - } - } - .dark\:hover\:bg-gray-800\/50 { - @media (prefers-color-scheme: dark) { - &:hover { - @media (hover: hover) { - background-color: color-mix(in srgb, oklch(27.8% 0.033 256.848) 50%, transparent); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-gray-800) 50%, transparent); - } - } - } - } - } - .dark\:hover\:text-blue-300 { - @media (prefers-color-scheme: dark) { - &:hover { - @media (hover: hover) { - color: var(--color-blue-300); - } - } - } - } - .dark\:hover\:text-gray-300 { - @media (prefers-color-scheme: dark) { - &:hover { - @media (hover: hover) { - color: var(--color-gray-300); - } - } - } - } - .print\:block { - @media print { - display: block; - } - } - .print\:bg-transparent { - @media print { - background-color: transparent; - } - } - .print\:text-black { - @media print { - color: var(--color-black); - } - } - .\[\&_\.recharts-cartesian-axis-tick_text\]\:fill-text-muted { - & .recharts-cartesian-axis-tick text { - fill: var(--color-text-muted); - } - } - .\[\&_\.recharts-cartesian-grid_line\[stroke\]\]\:stroke-border\/50 { - & .recharts-cartesian-grid line[stroke] { - stroke: color-mix(in srgb, oklch(0.92 0.005 80) 50%, transparent); - @supports (color: color-mix(in lab, red, red)) { - stroke: color-mix(in oklab, var(--color-border) 50%, transparent); - } - } - } - .\[\&_\.recharts-curve\.recharts-tooltip-cursor\]\:stroke-border { - & .recharts-curve.recharts-tooltip-cursor { - stroke: var(--color-border); - } - } - .\[\&_\.recharts-dot\[stroke\]\]\:stroke-transparent { - & .recharts-dot[stroke] { - stroke: transparent; - } - } - .\[\&_\.recharts-layer\]\:outline-none { - & .recharts-layer { - --tw-outline-style: none; - outline-style: none; - } - } - .\[\&_\.recharts-polar-grid_\[stroke\]\]\:stroke-border { - & .recharts-polar-grid [stroke] { - stroke: var(--color-border); - } - } - .\[\&_\.recharts-radial-bar-background-sector\]\:fill-surface-sunken { - & .recharts-radial-bar-background-sector { - fill: var(--color-surface-sunken); - } - } - .\[\&_\.recharts-rectangle\.recharts-tooltip-cursor\]\:fill-surface-sunken { - & .recharts-rectangle.recharts-tooltip-cursor { - fill: var(--color-surface-sunken); - } - } - .\[\&_\.recharts-reference-line_\[stroke\]\]\:stroke-border { - & .recharts-reference-line [stroke] { - stroke: var(--color-border); - } - } - .\[\&_\.recharts-sector\]\:outline-none { - & .recharts-sector { - --tw-outline-style: none; - outline-style: none; - } - } - .\[\&_\.recharts-sector\[stroke\]\]\:stroke-transparent { - & .recharts-sector[stroke] { - stroke: transparent; - } - } - .\[\&_\.recharts-surface\]\:outline-none { - & .recharts-surface { - --tw-outline-style: none; - outline-style: none; - } - } - .\[\&_\[cmdk-group-heading\]\]\:px-2 { - & [cmdk-group-heading] { - padding-inline: calc(var(--spacing) * 2); - } - } - .\[\&_\[cmdk-group-heading\]\]\:py-1\.5 { - & [cmdk-group-heading] { - padding-block: calc(var(--spacing) * 1.5); - } - } - .\[\&_\[cmdk-group-heading\]\]\:text-xs { - & [cmdk-group-heading] { - font-size: var(--text-xs); - line-height: var(--tw-leading, var(--text-xs--line-height)); - } - } - .\[\&_\[cmdk-group-heading\]\]\:font-medium { - & [cmdk-group-heading] { - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - } - } - .\[\&_\[cmdk-group-heading\]\]\:text-text-muted { - & [cmdk-group-heading] { - color: var(--color-text-muted); - } - } - .\[\&_p\]\:leading-relaxed { - & p { - --tw-leading: var(--leading-relaxed); - line-height: var(--leading-relaxed); - } - } - .\[\&_tr\]\:border-b { - & tr { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 1px; - } - } - .\[\&_tr\:last-child\]\:border-0 { - & tr:last-child { - border-style: var(--tw-border-style); - border-width: 0px; - } - } - .\[\&\:\:-webkit-inner-spin-button\]\:appearance-none { - &::-webkit-inner-spin-button { - appearance: none; - } - } - .\[\&\:\:-webkit-outer-spin-button\]\:appearance-none { - &::-webkit-outer-spin-button { - appearance: none; - } - } - .\[\&\:\:-webkit-search-cancel-button\]\:appearance-none { - &::-webkit-search-cancel-button { - appearance: none; - } - } - .\[\&\:\:-webkit-search-decoration\]\:appearance-none { - &::-webkit-search-decoration { - appearance: none; - } - } - .\[\&\:has\(\[aria-selected\]\)\]\:bg-primary-subtle { - &:has([aria-selected]) { - background-color: var(--color-primary-subtle); - } - } - .first\:\[\&\:has\(\[aria-selected\]\)\]\:rounded-l-md { - &:first-child { - &:has([aria-selected]) { - border-top-left-radius: var(--radius-md); - border-bottom-left-radius: var(--radius-md); - } - } - } - .last\:\[\&\:has\(\[aria-selected\]\)\]\:rounded-r-md { - &:last-child { - &:has([aria-selected]) { - border-top-right-radius: var(--radius-md); - border-bottom-right-radius: var(--radius-md); - } - } - } - .\[\&\:has\(\[aria-selected\]\.day-outside\)\]\:bg-primary-subtle\/50 { - &:has([aria-selected].day-outside) { - background-color: color-mix(in srgb, oklch(0.62 0.16 158 / 0.09) 50%, transparent); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-primary-subtle) 50%, transparent); - } - } - } - .\[\&\:has\(\[aria-selected\]\.day-range-end\)\]\:rounded-r-md { - &:has([aria-selected].day-range-end) { - border-top-right-radius: var(--radius-md); - border-bottom-right-radius: var(--radius-md); - } - } - .\[\&\:has\(\[role\=checkbox\]\)\]\:pr-0 { - &:has([role=checkbox]) { - padding-right: calc(var(--spacing) * 0); - } - } - .\[\&\>\[data-slot\=prefix\]\]\:pointer-events-none { - &>[data-slot=prefix] { - pointer-events: none; - } - } - .\[\&\>\[data-slot\=prefix\]\]\:absolute { - &>[data-slot=prefix] { - position: absolute; - } - } - .\[\&\>\[data-slot\=prefix\]\]\:left-3 { - &>[data-slot=prefix] { - left: calc(var(--spacing) * 3); - } - } - .\[\&\>\[data-slot\=prefix\]\]\:text-text-muted { - &>[data-slot=prefix] { - color: var(--color-text-muted); - } - } - .\[\&\>\[data-slot\=suffix\]\]\:absolute { - &>[data-slot=suffix] { - position: absolute; - } - } - .\[\&\>\[data-slot\=suffix\]\]\:right-3 { - &>[data-slot=suffix] { - right: calc(var(--spacing) * 3); - } - } - .\[\&\>\[data-slot\=suffix\]\]\:text-text-muted { - &>[data-slot=suffix] { - color: var(--color-text-muted); - } - } - .\[\&\>span\]\:line-clamp-1 { - &>span { - overflow: hidden; - display: -webkit-box; - -webkit-box-orient: vertical; - -webkit-line-clamp: 1; - } - } - .\[\&\>svg\]\:h-2\.5 { - &>svg { - height: calc(var(--spacing) * 2.5); - } - } - .\[\&\>svg\]\:h-3 { - &>svg { - height: calc(var(--spacing) * 3); - } - } - .\[\&\>svg\]\:h-3\.5 { - &>svg { - height: calc(var(--spacing) * 3.5); - } - } - .\[\&\>svg\]\:h-5 { - &>svg { - height: calc(var(--spacing) * 5); - } - } - .\[\&\>svg\]\:w-2\.5 { - &>svg { - width: calc(var(--spacing) * 2.5); - } - } - .\[\&\>svg\]\:w-3 { - &>svg { - width: calc(var(--spacing) * 3); - } - } - .\[\&\>svg\]\:w-3\.5 { - &>svg { - width: calc(var(--spacing) * 3.5); - } - } - .\[\&\>svg\]\:w-5 { - &>svg { - width: calc(var(--spacing) * 5); - } - } - .\[\&\>svg\]\:text-text-muted { - &>svg { - color: var(--color-text-muted); - } - } - .\[\&\[data-state\=open\]\>svg\]\:rotate-180 { - &[data-state=open]>svg { - rotate: 180deg; - } - } - .\[\[data-direction\=vertical\]_\&\]\:h-px { - [data-direction=vertical] & { - height: 1px; - } - } - .\[\[data-direction\=vertical\]_\&\]\:w-full { - [data-direction=vertical] & { - width: 100%; - } - } - .\[\[data-direction\=vertical\]_\&\]\:after\:left-0 { - [data-direction=vertical] & { - &::after { - content: var(--tw-content); - left: calc(var(--spacing) * 0); - } - } - } - .\[\[data-direction\=vertical\]_\&\]\:after\:h-1 { - [data-direction=vertical] & { - &::after { - content: var(--tw-content); - height: calc(var(--spacing) * 1); - } - } - } - .\[\[data-direction\=vertical\]_\&\]\:after\:w-full { - [data-direction=vertical] & { - &::after { - content: var(--tw-content); - width: 100%; - } - } - } - .\[\[data-direction\=vertical\]_\&\]\:after\:translate-x-0 { - [data-direction=vertical] & { - &::after { - content: var(--tw-content); - --tw-translate-x: calc(var(--spacing) * 0); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - } - } - .\[\[data-direction\=vertical\]_\&\]\:after\:-translate-y-1\/2 { - [data-direction=vertical] & { - &::after { - content: var(--tw-content); - --tw-translate-y: calc(calc(1 / 2 * 100%) * -1); - translate: var(--tw-translate-x) var(--tw-translate-y); - } - } - } -} -.dark { - --color-primary: oklch(0.72 0.16 158); - --color-primary-hover: oklch(0.78 0.16 158); - --color-primary-light: oklch(0.85 0.13 158); - --color-primary-subtle: oklch(0.72 0.16 158 / 0.14); - --color-primary-ring: oklch(0.72 0.16 158 / 0.4); - --color-accent: oklch(0.62 0.11 192); - --color-accent-hover: oklch(0.7 0.11 192); - --color-success: oklch(0.78 0.16 158); - --color-success-bg: oklch(0.72 0.16 158 / 0.14); - --color-success-text: oklch(0.85 0.13 158); - --color-danger: oklch(0.7 0.2 17); - --color-danger-hover: oklch(0.78 0.2 17); - --color-danger-bg: oklch(0.7 0.2 17 / 0.14); - --color-danger-text: oklch(0.85 0.13 17); - --color-warning-bg: oklch(0.72 0.16 60 / 0.15); - --color-warning-border: oklch(0.72 0.16 60 / 0.35); - --color-warning-text: oklch(0.86 0.14 60); - --color-info: oklch(0.75 0.13 232); - --color-info-bg: oklch(0.75 0.13 232 / 0.14); - --color-surface: oklch(0.205 0.006 60); - --color-surface-raised: oklch(0.245 0.007 60); - --color-surface-sunken: oklch(0.16 0.006 60); - --color-surface-overlay: oklch(0.205 0.006 60 / 0.78); - --color-text: oklch(0.96 0.005 80); - --color-text-secondary: oklch(0.74 0.008 80); - --color-text-muted: oklch(0.55 0.008 80); - --color-text-inverse: oklch(0.16 0.006 60); - --color-border: oklch(0.3 0.008 60); - --color-border-strong: oklch(0.4 0.01 60); - --color-code-bg: oklch(0.135 0.006 60); - --color-code-text: oklch(0.92 0.008 80); - --shadow-xs: 0 1px 2px 0 oklch(0 0 0 / 0.4); - --shadow-sm: 0 1px 2px 0 oklch(0 0 0 / 0.45), 0 1px 3px 0 oklch(0 0 0 / 0.3); - --shadow-md: 0 4px 8px -2px oklch(0 0 0 / 0.5), 0 2px 4px -2px oklch(0 0 0 / 0.35); - --shadow-lg: 0 14px 28px -8px oklch(0 0 0 / 0.6), 0 4px 8px -4px oklch(0 0 0 / 0.4); - --shadow-xl: 0 32px 64px -16px oklch(0 0 0 / 0.7), 0 8px 16px -8px oklch(0 0 0 / 0.45); - --shadow-primary: 0 6px 22px -4px oklch(0.72 0.16 158 / 0.42), 0 0 0 1px oklch(0.72 0.16 158 / 0.2); - --shadow-primary-hover: 0 12px 34px -8px oklch(0.72 0.16 158 / 0.55), 0 0 0 1px oklch(0.72 0.16 158 / 0.3); -} -@layer base { - html { - scroll-behavior: smooth; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - text-rendering: optimizeLegibility; - } - body { - background-color: var(--color-surface-sunken); - color: var(--color-text); - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - --tw-duration: 300ms; - transition-duration: 300ms; - font-family: var(--font-body); - font-feature-settings: "ss01", "cv11", "cv05"; - font-optical-sizing: auto; - letter-spacing: -0.005em; - } - h1, h2, h3, h4, h5, h6 { - margin-top: 0; - font-family: var(--font-display); - font-optical-sizing: auto; - font-variation-settings: "opsz" 144, "SOFT" 30, "WONK" 0; - letter-spacing: -0.025em; - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - color: var(--color-text); - } - h1 { - font-size: var(--text-2xl); - line-height: var(--tw-leading, var(--text-2xl--line-height)); - @media (width >= 40rem) { - font-size: var(--text-3xl); - line-height: var(--tw-leading, var(--text-3xl--line-height)); - } - letter-spacing: -0.035em; - font-weight: 500; - } - h2 { - font-size: var(--text-xl); - line-height: var(--tw-leading, var(--text-xl--line-height)); - @media (width >= 40rem) { - font-size: var(--text-2xl); - line-height: var(--tw-leading, var(--text-2xl--line-height)); - } - letter-spacing: -0.03em; - } - h3 { - font-size: var(--text-lg); - line-height: var(--tw-leading, var(--text-lg--line-height)); - @media (width >= 40rem) { - font-size: var(--text-xl); - line-height: var(--tw-leading, var(--text-xl--line-height)); - } - } - label { - margin-bottom: calc(var(--spacing) * 1.5); - display: block; - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - color: var(--color-text-secondary); - font-family: var(--font-body); - letter-spacing: 0; - } - input[type="email"], input[type="password"], input[type="text"], input[type="tel"] { - width: 100%; - border-radius: var(--radius-xl); - border-style: var(--tw-border-style); - border-width: 1px; - border-color: var(--color-border); - background-color: var(--color-surface); - padding-inline: calc(var(--spacing) * 3); - padding-block: calc(var(--spacing) * 2.5); - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - color: var(--color-text); - &::placeholder { - color: var(--color-text-muted); - } - transition-property: all; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - --tw-duration: 200ms; - transition-duration: 200ms; - @media (width >= 40rem) { - padding-inline: calc(var(--spacing) * 4); - } - @media (width >= 40rem) { - padding-block: calc(var(--spacing) * 3); - } - box-shadow: var(--shadow-xs); - } - input[type="email"]:focus, input[type="password"]:focus, input[type="text"]:focus, input[type="tel"]:focus { - border-color: var(--color-primary); - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - --tw-ring-color: var(--color-primary-ring); - --tw-outline-style: none; - outline-style: none; - } - a { - color: var(--color-primary); - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - --tw-duration: 150ms; - transition-duration: 150ms; - } - a:hover { - color: var(--color-primary-hover); - } - hr { - margin-block: calc(var(--spacing) * 6); - border-top-style: var(--tw-border-style); - border-top-width: 1px; - border-color: var(--color-border); - } - ::selection { - background: oklch(0.62 0.16 158 / 0.25); - color: var(--color-text); - } - .dark ::selection { - background: oklch(0.72 0.16 158 / 0.3); - } -} -@layer components { - .glass-card { - border-radius: var(--radius-2xl); - border-style: var(--tw-border-style); - border-width: 1px; - border-color: var(--color-border); - background-color: var(--color-surface-overlay); - backdrop-filter: blur(24px) saturate(180%); - -webkit-backdrop-filter: blur(24px) saturate(180%); - box-shadow: var(--shadow-lg); - } - .dark .glass-card { - box-shadow: inset 0 1px 0 0 oklch(1 0 0 / 0.04), var(--shadow-xl); - } - .btn-primary, .btn-secondary, .btn-ghost, .btn-danger, .btn-outline { - display: inline-flex; - cursor: pointer; - align-items: center; - justify-content: center; - gap: calc(var(--spacing) * 2); - border-radius: var(--radius-xl); - --tw-border-style: none; - border-style: none; - padding-inline: calc(var(--spacing) * 5); - padding-block: calc(var(--spacing) * 2.5); - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - text-decoration-line: none; - transition-property: all; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - --tw-duration: 200ms; - transition-duration: 200ms; - --tw-ease: var(--ease-out); - transition-timing-function: var(--ease-out); - font-family: var(--font-body); - letter-spacing: -0.005em; - } - .btn-primary:active, .btn-secondary:active, .btn-ghost:active, .btn-danger:active, .btn-outline:active { - transform: scale(0.97); - } - .btn-primary { - background-color: var(--color-primary); - color: var(--color-white); - box-shadow: var(--shadow-primary); - } - .btn-primary:hover { - background: var(--color-primary-hover); - box-shadow: var(--shadow-primary-hover); - transform: translateY(-1px); - } - .btn-secondary { - border-style: var(--tw-border-style); - border-width: 1px; - border-color: var(--color-border); - background-color: var(--color-surface); - color: var(--color-text); - box-shadow: var(--shadow-xs); - } - .btn-secondary:hover { - border-color: var(--color-border-strong); - background-color: var(--color-surface-raised); - box-shadow: var(--shadow-sm); - } - .btn-ghost { - background-color: transparent; - color: var(--color-text-secondary); - } - .btn-ghost:hover { - background-color: var(--color-primary-subtle); - color: var(--color-primary); - } - .btn-danger { - background-color: var(--color-danger); - color: var(--color-white); - box-shadow: var(--shadow-danger); - } - .btn-danger:hover { - background-color: var(--color-danger-hover); - box-shadow: var(--shadow-danger-hover); - transform: translateY(-1px); - } - .btn-outline { - border-style: var(--tw-border-style); - border-width: 2px; - border-color: color-mix(in srgb, oklch(0.62 0.16 158) 30%, transparent); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-primary) 30%, transparent); - } - background-color: transparent; - color: var(--color-primary); - } - .btn-outline:hover { - border-color: var(--color-primary); - background-color: var(--color-primary-subtle); - } - .btn-sm { - border-radius: var(--radius-lg); - padding-inline: calc(var(--spacing) * 3.5); - padding-block: calc(var(--spacing) * 1.5); - font-size: var(--text-xs); - line-height: var(--tw-leading, var(--text-xs--line-height)); - } - .btn-lg { - padding-inline: calc(var(--spacing) * 8); - padding-block: calc(var(--spacing) * 3.5); - font-size: var(--text-base); - line-height: var(--tw-leading, var(--text-base--line-height)); - } - .form-group { - margin-bottom: calc(var(--spacing) * 5); - } - .badge-success, .badge-danger, .badge-warning, .badge-info { - display: inline-flex; - align-items: center; - border-radius: calc(infinity * 1px); - padding-inline: calc(var(--spacing) * 2.5); - padding-block: calc(var(--spacing) * 1); - font-size: var(--text-xs); - line-height: var(--tw-leading, var(--text-xs--line-height)); - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - letter-spacing: 0; - } - .badge-success { - background-color: var(--color-success-bg); - color: var(--color-success-text); - } - .badge-danger { - background-color: var(--color-danger-bg); - color: var(--color-danger-text); - } - .badge-warning { - background-color: var(--color-warning-bg); - color: var(--color-warning-text); - } - .badge-info { - background-color: var(--color-info-bg); - color: var(--color-primary); - } - .alert-success, .alert-danger, .alert-warning, .alert-info { - border-radius: var(--radius-xl); - border-style: var(--tw-border-style); - border-width: 1px; - padding: calc(var(--spacing) * 4); - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - } - .alert-success { - border-color: color-mix(in srgb, oklch(0.62 0.16 158) 20%, transparent); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-success) 20%, transparent); - } - background-color: var(--color-success-bg); - color: var(--color-success-text); - } - .alert-danger { - border-color: color-mix(in srgb, oklch(0.6 0.21 17) 20%, transparent); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-danger) 20%, transparent); - } - background-color: var(--color-danger-bg); - color: var(--color-danger-text); - } - .alert-warning { - border-color: var(--color-warning-border); - background-color: var(--color-warning-bg); - color: var(--color-warning-text); - } - .alert-info { - border-color: color-mix(in srgb, oklch(0.65 0.16 232) 20%, transparent); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-info) 20%, transparent); - } - background-color: var(--color-info-bg); - color: var(--color-primary); - } - .code-block { - overflow: auto; - border-radius: var(--radius-xl); - border-style: var(--tw-border-style); - border-width: 1px; - border-color: var(--color-border); - background-color: var(--color-code-bg); - padding: calc(var(--spacing) * 4); - font-size: var(--text-xs); - line-height: var(--tw-leading, var(--text-xs--line-height)); - color: var(--color-code-text); - font-family: var(--font-mono); - font-feature-settings: "calt", "liga"; - } - .panel { - margin-bottom: calc(var(--spacing) * 8); - } - .panel-title { - margin-bottom: calc(var(--spacing) * 4); - display: flex; - align-items: center; - gap: calc(var(--spacing) * 2.5); - padding-bottom: calc(var(--spacing) * 3); - font-size: var(--text-base); - line-height: var(--tw-leading, var(--text-base--line-height)); - --tw-font-weight: var(--font-weight-semibold); - font-weight: var(--font-weight-semibold); - font-family: var(--font-display); - font-variation-settings: "opsz" 144, "SOFT" 0, "WONK" 0; - letter-spacing: -0.02em; - } - .panel-title::before { - content: ""; - height: calc(var(--spacing) * 5); - width: calc(var(--spacing) * 1); - border-radius: calc(infinity * 1px); - background-color: var(--color-primary); - } - .card { - border-radius: var(--radius-xl); - border-style: var(--tw-border-style); - border-width: 1px; - border-color: var(--color-border); - background-color: var(--color-surface); - padding: calc(var(--spacing) * 4); - transition-property: all; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - --tw-duration: 200ms; - transition-duration: 200ms; - @media (width >= 40rem) { - border-radius: var(--radius-2xl); - } - @media (width >= 40rem) { - padding: calc(var(--spacing) * 5); - } - box-shadow: var(--shadow-xs); - } - .card:hover { - border-color: var(--color-border-strong); - box-shadow: var(--shadow-sm); - } - .table-responsive { - margin-inline: calc(var(--spacing) * -4); - overflow-x: auto; - @media (width >= 40rem) { - margin-inline: calc(var(--spacing) * 0); - } - } - .table-responsive > table { - min-width: 600px; - @media (width >= 40rem) { - min-width: calc(var(--spacing) * 0); - } - } - .scrollbar-hide { - -ms-overflow-style: none; - scrollbar-width: none; - } - .scrollbar-hide::-webkit-scrollbar { - display: none; - } - .manage-tab { - display: inline-flex; - flex-shrink: 0; - align-items: center; - gap: calc(var(--spacing) * 2); - border-bottom-style: var(--tw-border-style); - border-bottom-width: 2px; - border-color: transparent; - padding-inline: calc(var(--spacing) * 4); - padding-block: calc(var(--spacing) * 2.5); - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - white-space: nowrap; - color: var(--color-text-muted); - text-decoration-line: none; - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - --tw-duration: 150ms; - transition-duration: 150ms; - } - .manage-tab:hover { - color: var(--color-text); - } - .manage-tab-active { - display: inline-flex; - flex-shrink: 0; - align-items: center; - gap: calc(var(--spacing) * 2); - border-bottom-style: var(--tw-border-style); - border-bottom-width: 2px; - border-color: var(--color-primary); - padding-inline: calc(var(--spacing) * 4); - padding-block: calc(var(--spacing) * 2.5); - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - --tw-font-weight: var(--font-weight-semibold); - font-weight: var(--font-weight-semibold); - white-space: nowrap; - color: var(--color-primary); - text-decoration-line: none; - } - .public-overlay { - position: fixed; - inset: calc(var(--spacing) * 0); - z-index: 50; - background-color: var(--color-surface); - opacity: 0; - pointer-events: none; - transition: opacity 0.25s ease; - } - .public-overlay.open { - opacity: 1; - pointer-events: auto; - } - .nav-link-active, .nav-link-inactive { - display: block; - border-radius: var(--radius-xl); - padding-inline: calc(var(--spacing) * 4); - padding-block: calc(var(--spacing) * 2.5); - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - text-decoration-line: none; - transition-property: all; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - --tw-duration: 150ms; - transition-duration: 150ms; - } - .nav-link-active { - background-color: var(--color-primary-subtle); - --tw-font-weight: var(--font-weight-semibold); - font-weight: var(--font-weight-semibold); - color: var(--color-primary); - } - .nav-link-inactive { - color: var(--color-text-secondary); - } - .nav-link-inactive:hover { - background-color: var(--color-surface-raised); - color: var(--color-text); - } - .sidebar-nav-link-active, .sidebar-nav-link-inactive { - position: relative; - display: flex; - align-items: center; - gap: calc(var(--spacing) * 3); - border-radius: var(--radius-xl); - padding-inline: calc(var(--spacing) * 3); - padding-block: calc(var(--spacing) * 2); - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - text-decoration-line: none; - transition-property: all; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - --tw-duration: 150ms; - transition-duration: 150ms; - --tw-outline-style: none; - outline-style: none; - &:focus-visible { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - &:focus-visible { - --tw-ring-color: var(--color-primary-ring); - } - } - .sidebar-nav-link-active { - background-color: var(--color-primary-subtle); - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - color: var(--color-primary); - } - .sidebar-nav-link-active::before { - content: ""; - position: absolute; - left: -0.75rem; - top: 50%; - transform: translateY(-50%); - width: 3px; - height: 1.25rem; - border-radius: 0 3px 3px 0; - background: var(--color-primary); - } - .sidebar-nav-link-inactive { - color: var(--color-text-secondary); - } - .sidebar-nav-link-inactive:hover { - background-color: var(--color-surface-raised); - color: var(--color-text); - } -} -@layer utilities { - .focus-ring { - --tw-outline-style: none; - outline-style: none; - &:focus-visible { - border-color: var(--color-primary); - } - &:focus-visible { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - &:focus-visible { - --tw-ring-color: var(--color-primary-ring); - } - } - .focus-ring-danger { - --tw-outline-style: none; - outline-style: none; - &:focus-visible { - border-color: var(--color-danger); - } - &:focus-visible { - --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(4px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor); - box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); - } - &:focus-visible { - --tw-ring-color: var(--color-danger-bg); - } - } -} -@layer utilities { - .gradient-text { - color: var(--color-primary); - } - .gradient-border { - position: relative; - } - .gradient-border::before { - content: ""; - position: absolute; - inset: 0; - border-radius: inherit; - padding: 1px; - background: var(--color-primary); - mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0); - mask-composite: exclude; - -webkit-mask-composite: xor; - pointer-events: none; - } - .font-display { - font-family: var(--font-display); - font-optical-sizing: auto; - } - .font-body { - font-family: var(--font-body); - } -} -@layer components { - .bg-mesh { - position: fixed; - inset: 0; - z-index: -1; - background: var(--color-surface-sunken); - } - .dark body::before { - content: ""; - position: fixed; - inset: 0; - pointer-events: none; - z-index: 0; - opacity: 0.025; - mix-blend-mode: overlay; - background-image: url("data:image/svg+xml;utf8,"); - } - .spinner { - margin-left: calc(var(--spacing) * 2); - display: inline-block; - height: calc(var(--spacing) * 4); - width: calc(var(--spacing) * 4); - animation: var(--animate-spin); - border-radius: calc(infinity * 1px); - border-style: var(--tw-border-style); - border-width: 2px; - border-color: var(--color-border); - border-top-color: var(--color-primary); - vertical-align: middle; - } - .user-dropdown-wrap { - position: relative; - } - .user-dropdown-trigger { - display: flex; - cursor: pointer; - align-items: center; - gap: calc(var(--spacing) * 2); - border-radius: var(--radius-xl); - --tw-border-style: none; - border-style: none; - background-color: transparent; - padding-inline: calc(var(--spacing) * 2); - padding-block: calc(var(--spacing) * 1.5); - transition-property: all; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - --tw-duration: 200ms; - transition-duration: 200ms; - } - .user-dropdown-trigger:hover { - background-color: var(--color-surface-raised); - } - .user-dropdown-trigger[aria-expanded="true"] { - background-color: var(--color-surface-raised); - } - .user-dropdown { - position: absolute; - right: 0; - top: calc(100% + 6px); - width: 240px; - border-radius: var(--radius-xl); - border-style: var(--tw-border-style); - border-width: 1px; - border-color: var(--color-border); - background-color: var(--color-surface); - box-shadow: var(--shadow-lg); - opacity: 0; - transform: translateY(-4px) scale(0.97); - pointer-events: none; - transition: opacity 0.18s ease, transform 0.18s ease; - z-index: 100; - } - .user-dropdown.open { - opacity: 1; - transform: translateY(0) scale(1); - pointer-events: auto; - } - .app-sidebar .user-dropdown { - top: auto; - bottom: calc(100% + 6px); - left: 0; - right: auto; - transform: translateY(4px) scale(0.97); - } - .app-sidebar .user-dropdown.open { - transform: translateY(0) scale(1); - } - .dark .user-dropdown { - box-shadow: inset 0 1px 0 0 oklch(1 0 0 / 0.04), var(--shadow-xl); - } - .user-dropdown-header { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 1px; - border-color: var(--color-border); - padding-inline: calc(var(--spacing) * 4); - padding-block: calc(var(--spacing) * 3); - } - .user-dropdown-body { - padding-block: calc(var(--spacing) * 1.5); - } - .user-dropdown-item { - display: flex; - cursor: pointer; - align-items: center; - gap: calc(var(--spacing) * 2.5); - padding-inline: calc(var(--spacing) * 4); - padding-block: calc(var(--spacing) * 2); - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - color: var(--color-text-secondary); - text-decoration-line: none; - transition-property: color, background-color, border-color, outline-color, text-decoration-color, fill, stroke, --tw-gradient-from, --tw-gradient-via, --tw-gradient-to; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - --tw-duration: 150ms; - transition-duration: 150ms; - } - .user-dropdown-item:hover { - background-color: var(--color-surface-raised); - color: var(--color-text); - } - .user-dropdown-divider { - margin-block: calc(var(--spacing) * 1.5); - border-top-style: var(--tw-border-style); - border-top-width: 1px; - border-color: var(--color-border); - } - .user-dropdown-item.danger { - color: var(--color-danger); - } - .user-dropdown-item.danger:hover { - background-color: var(--color-danger-bg); - color: var(--color-danger); - } - .dash-card { - border-radius: var(--radius-xl); - border-style: var(--tw-border-style); - border-width: 1px; - border-color: var(--color-border); - background-color: var(--color-surface); - padding: calc(var(--spacing) * 4); - transition-property: all; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - --tw-duration: 200ms; - transition-duration: 200ms; - @media (width >= 40rem) { - border-radius: var(--radius-2xl); - } - @media (width >= 40rem) { - padding: calc(var(--spacing) * 5); - } - box-shadow: var(--shadow-xs); - } - .dash-card:hover { - border-color: var(--color-border-strong); - box-shadow: var(--shadow-md); - transform: translateY(-1px); - } - .dash-stat { - font-size: var(--text-2xl); - line-height: var(--tw-leading, var(--text-2xl--line-height)); - --tw-tracking: var(--tracking-tight); - letter-spacing: var(--tracking-tight); - @media (width >= 40rem) { - font-size: var(--text-3xl); - line-height: var(--tw-leading, var(--text-3xl--line-height)); - } - font-family: var(--font-display); - font-variation-settings: "opsz" 144, "SOFT" 30, "WONK" 0; - font-weight: 500; - letter-spacing: -0.035em; - } - .dash-label { - margin-top: calc(var(--spacing) * 1); - font-size: var(--text-xs); - line-height: var(--tw-leading, var(--text-xs--line-height)); - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - --tw-tracking: var(--tracking-wider); - letter-spacing: var(--tracking-wider); - color: var(--color-text-muted); - text-transform: uppercase; - letter-spacing: 0.08em; - } - .validation-message { - margin-top: calc(var(--spacing) * 1.5); - font-size: var(--text-xs); - line-height: var(--tw-leading, var(--text-xs--line-height)); - --tw-font-weight: var(--font-weight-medium); - font-weight: var(--font-weight-medium); - color: var(--color-danger); - } - .app-layout { - display: flex; - flex-direction: column; - min-height: 100vh; - } - .app-sidebar { - flex-shrink: 0; - border-right-style: var(--tw-border-style); - border-right-width: 1px; - border-color: var(--color-border); - background-color: var(--color-surface); - position: fixed; - top: 0; - left: 0; - bottom: 0; - z-index: 40; - transition: width 0.2s ease, transform 0.25s ease; - width: 16rem; - transform: translateX(-100%); - } - .dark .app-sidebar { - background: var(--color-surface); - } - .app-sidebar.app-sidebar-open { - transform: translateX(0); - } - .app-sidebar.app-sidebar-collapsed { - width: 4rem; - } - .app-sidebar.app-sidebar-collapsed .sidebar-label { - display: none; - } - .app-sidebar.app-sidebar-collapsed .sidebar-icon { - margin: 0 auto; - } - .app-content { - flex: 1; - margin-left: 0; - transition: margin-left 0.2s ease; - } - .app-sidebar-backdrop { - position: fixed; - inset: calc(var(--spacing) * 0); - z-index: 30; - background-color: color-mix(in srgb, #000 50%, transparent); - @supports (color: color-mix(in lab, red, red)) { - background-color: color-mix(in oklab, var(--color-black) 50%, transparent); - } - opacity: 0; - pointer-events: none; - transition: opacity 0.25s ease; - } - .app-sidebar-backdrop.visible { - opacity: 1; - pointer-events: auto; - } - .app-sidebar-toggle { - position: fixed; - bottom: calc(var(--spacing) * 4); - z-index: 50; - height: calc(var(--spacing) * 7); - width: calc(var(--spacing) * 7); - cursor: pointer; - align-items: center; - justify-content: center; - border-radius: calc(infinity * 1px); - border-style: var(--tw-border-style); - border-width: 1px; - border-color: var(--color-border); - background-color: var(--color-surface); - color: var(--color-text-muted); - transition-property: all; - transition-timing-function: var(--tw-ease, var(--default-transition-timing-function)); - transition-duration: var(--tw-duration, var(--default-transition-duration)); - --tw-duration: 200ms; - transition-duration: 200ms; - &:hover { - @media (hover: hover) { - background-color: var(--color-surface-raised); - } - } - &:hover { - @media (hover: hover) { - color: var(--color-text); - } - } - left: 1rem; - display: none; - box-shadow: var(--shadow-sm); - } - .app-mobile-header { - position: sticky; - top: calc(var(--spacing) * 0); - z-index: 20; - display: flex; - align-items: center; - gap: calc(var(--spacing) * 3); - border-bottom-style: var(--tw-border-style); - border-bottom-width: 1px; - border-color: var(--color-border); - background-color: var(--color-surface); - padding-inline: calc(var(--spacing) * 4); - padding-block: calc(var(--spacing) * 3); - backdrop-filter: blur(12px) saturate(180%); - -webkit-backdrop-filter: blur(12px) saturate(180%); - background: var(--color-surface-overlay); - } - @media (min-width: 768px) { - .app-sidebar { - transform: translateX(0); - } - .app-content { - margin-left: 16rem; - } - .app-sidebar-collapsed ~ .app-content { - margin-left: 4rem; - } - .app-sidebar-backdrop { - display: none; - } - .app-sidebar-toggle { - display: flex; - left: 15rem; - } - .app-sidebar-collapsed ~ .app-sidebar-toggle { - left: 3rem; - } - .app-mobile-header { - display: none; - } - .public-overlay { - display: none; - } - } - .markdown-content h1 { - margin-top: calc(var(--spacing) * 6); - margin-bottom: calc(var(--spacing) * 3); - font-size: var(--text-3xl); - line-height: var(--tw-leading, var(--text-3xl--line-height)); - color: var(--color-text); - font-family: var(--font-display); - font-variation-settings: "opsz" 144, "SOFT" 0; - letter-spacing: -0.035em; - font-weight: 500; - } - .markdown-content h2 { - margin-top: calc(var(--spacing) * 5); - margin-bottom: calc(var(--spacing) * 2); - font-size: var(--text-2xl); - line-height: var(--tw-leading, var(--text-2xl--line-height)); - color: var(--color-text); - font-family: var(--font-display); - letter-spacing: -0.03em; - font-weight: 500; - } - .markdown-content h3 { - margin-top: calc(var(--spacing) * 4); - margin-bottom: calc(var(--spacing) * 2); - font-size: var(--text-xl); - line-height: var(--tw-leading, var(--text-xl--line-height)); - color: var(--color-text); - font-family: var(--font-display); - font-weight: 500; - } - .markdown-content h4 { - margin-top: calc(var(--spacing) * 3); - margin-bottom: calc(var(--spacing) * 1); - font-size: var(--text-base); - line-height: var(--tw-leading, var(--text-base--line-height)); - --tw-font-weight: var(--font-weight-semibold); - font-weight: var(--font-weight-semibold); - color: var(--color-text); - } - .markdown-content p { - margin-bottom: calc(var(--spacing) * 3); - --tw-leading: var(--leading-relaxed); - line-height: var(--leading-relaxed); - color: var(--color-text-secondary); - } - .markdown-content ul { - margin-bottom: calc(var(--spacing) * 3); - list-style-type: disc; - padding-left: calc(var(--spacing) * 6); - color: var(--color-text-secondary); - } - .markdown-content ol { - margin-bottom: calc(var(--spacing) * 3); - list-style-type: decimal; - padding-left: calc(var(--spacing) * 6); - color: var(--color-text-secondary); - } - .markdown-content li { - margin-bottom: calc(var(--spacing) * 1); - } - .markdown-content code { - border-radius: 0.25rem; - background-color: var(--color-surface-sunken); - padding-inline: calc(var(--spacing) * 1.5); - padding-block: calc(var(--spacing) * 0.5); - font-size: var(--text-xs); - line-height: var(--tw-leading, var(--text-xs--line-height)); - color: var(--color-text); - font-family: var(--font-mono); - } - .markdown-content pre { - margin-bottom: calc(var(--spacing) * 3); - overflow: auto; - border-radius: var(--radius-xl); - background-color: var(--color-code-bg); - padding: calc(var(--spacing) * 4); - font-size: var(--text-xs); - line-height: var(--tw-leading, var(--text-xs--line-height)); - color: var(--color-code-text); - } - .markdown-content pre code { - background-color: transparent; - padding: calc(var(--spacing) * 0); - color: var(--color-code-text); - } - .markdown-content a { - color: var(--color-primary); - &:hover { - @media (hover: hover) { - text-decoration-line: underline; - } - } - } - .markdown-content blockquote { - margin-block: calc(var(--spacing) * 3); - border-left-style: var(--tw-border-style); - border-left-width: 4px; - border-color: color-mix(in srgb, oklch(0.62 0.16 158) 30%, transparent); - @supports (color: color-mix(in lab, red, red)) { - border-color: color-mix(in oklab, var(--color-primary) 30%, transparent); - } - padding-left: calc(var(--spacing) * 4); - color: var(--color-text-muted); - font-style: italic; - font-family: var(--font-display); - font-variation-settings: "opsz" 144, "SOFT" 50; - } - .markdown-content img { - margin-block: calc(var(--spacing) * 3); - max-width: 100%; - border-radius: var(--radius-lg); - } - .markdown-content table { - margin-block: calc(var(--spacing) * 3); - width: 100%; - border-collapse: collapse; - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - } - .markdown-content th { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 1px; - border-color: var(--color-border); - padding: calc(var(--spacing) * 2); - text-align: left; - --tw-font-weight: var(--font-weight-semibold); - font-weight: var(--font-weight-semibold); - color: var(--color-text); - } - .markdown-content td { - border-bottom-style: var(--tw-border-style); - border-bottom-width: 1px; - border-color: var(--color-border); - padding: calc(var(--spacing) * 2); - color: var(--color-text-secondary); - } - .markdown-content hr { - margin-block: calc(var(--spacing) * 6); - border-color: var(--color-border); - } - .markdown-content > *:first-child { - margin-top: calc(var(--spacing) * 0); - } -} -@layer base { - table { - width: 100%; - font-size: var(--text-sm); - line-height: var(--tw-leading, var(--text-sm--line-height)); - } - table th { - padding-inline: calc(var(--spacing) * 4); - padding-top: calc(var(--spacing) * 4); - padding-bottom: calc(var(--spacing) * 3); - text-align: left; - font-size: var(--text-xs); - line-height: var(--tw-leading, var(--text-xs--line-height)); - --tw-font-weight: var(--font-weight-semibold); - font-weight: var(--font-weight-semibold); - color: var(--color-text-muted); - text-transform: uppercase; - letter-spacing: 0.08em; - background: var(--color-surface-sunken); - vertical-align: middle; - white-space: nowrap; - } - table td { - border-top-style: var(--tw-border-style); - border-top-width: 1px; - border-color: var(--color-border); - padding-inline: calc(var(--spacing) * 4); - padding-block: calc(var(--spacing) * 3); - color: var(--color-text-secondary); - vertical-align: middle; - } - table th:first-child, table td:first-child { - padding-left: calc(var(--spacing) * 5); - } - table th:last-child, table td:last-child { - padding-right: calc(var(--spacing) * 5); - } - table td.num, table th.num { - text-align: right; - font-family: var(--font-mono); - font-variant-numeric: tabular-nums; - } - table[data-interactive] tbody tr { - transition: background-color var(--duration-fast) var(--ease-out); - } - table[data-interactive] tbody tr:hover { - background: var(--color-surface-raised); - } - ::-webkit-scrollbar { - width: 6px; - height: 6px; - } - ::-webkit-scrollbar-track { - background: transparent; - } - ::-webkit-scrollbar-thumb { - border-radius: calc(infinity * 1px); - background-color: var(--color-border-strong); - } - ::-webkit-scrollbar-thumb:hover { - background-color: var(--color-text-muted); - } -} -@media (prefers-reduced-motion: reduce) { - .app-sidebar, .app-sidebar-backdrop, .app-sidebar-toggle, .public-overlay { - transition: none !important; - } -} -@property --tw-translate-x { - syntax: "*"; - inherits: false; - initial-value: 0; -} -@property --tw-translate-y { - syntax: "*"; - inherits: false; - initial-value: 0; -} -@property --tw-translate-z { - syntax: "*"; - inherits: false; - initial-value: 0; -} -@property --tw-rotate-x { - syntax: "*"; - inherits: false; -} -@property --tw-rotate-y { - syntax: "*"; - inherits: false; -} -@property --tw-rotate-z { - syntax: "*"; - inherits: false; -} -@property --tw-skew-x { - syntax: "*"; - inherits: false; -} -@property --tw-skew-y { - syntax: "*"; - inherits: false; -} -@property --tw-space-y-reverse { - syntax: "*"; - inherits: false; - initial-value: 0; -} -@property --tw-space-x-reverse { - syntax: "*"; - inherits: false; - initial-value: 0; -} -@property --tw-divide-y-reverse { - syntax: "*"; - inherits: false; - initial-value: 0; -} -@property --tw-border-style { - syntax: "*"; - inherits: false; - initial-value: solid; -} -@property --tw-gradient-position { - syntax: "*"; - inherits: false; -} -@property --tw-gradient-from { - syntax: ""; - inherits: false; - initial-value: #0000; -} -@property --tw-gradient-via { - syntax: ""; - inherits: false; - initial-value: #0000; -} -@property --tw-gradient-to { - syntax: ""; - inherits: false; - initial-value: #0000; -} -@property --tw-gradient-stops { - syntax: "*"; - inherits: false; -} -@property --tw-gradient-via-stops { - syntax: "*"; - inherits: false; -} -@property --tw-gradient-from-position { - syntax: ""; - inherits: false; - initial-value: 0%; -} -@property --tw-gradient-via-position { - syntax: ""; - inherits: false; - initial-value: 50%; -} -@property --tw-gradient-to-position { - syntax: ""; - inherits: false; - initial-value: 100%; -} -@property --tw-leading { - syntax: "*"; - inherits: false; -} -@property --tw-font-weight { - syntax: "*"; - inherits: false; -} -@property --tw-tracking { - syntax: "*"; - inherits: false; -} -@property --tw-ordinal { - syntax: "*"; - inherits: false; -} -@property --tw-slashed-zero { - syntax: "*"; - inherits: false; -} -@property --tw-numeric-figure { - syntax: "*"; - inherits: false; -} -@property --tw-numeric-spacing { - syntax: "*"; - inherits: false; -} -@property --tw-numeric-fraction { - syntax: "*"; - inherits: false; -} -@property --tw-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-shadow-color { - syntax: "*"; - inherits: false; -} -@property --tw-shadow-alpha { - syntax: ""; - inherits: false; - initial-value: 100%; -} -@property --tw-inset-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-inset-shadow-color { - syntax: "*"; - inherits: false; -} -@property --tw-inset-shadow-alpha { - syntax: ""; - inherits: false; - initial-value: 100%; -} -@property --tw-ring-color { - syntax: "*"; - inherits: false; -} -@property --tw-ring-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-inset-ring-color { - syntax: "*"; - inherits: false; -} -@property --tw-inset-ring-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-ring-inset { - syntax: "*"; - inherits: false; -} -@property --tw-ring-offset-width { - syntax: ""; - inherits: false; - initial-value: 0px; -} -@property --tw-ring-offset-color { - syntax: "*"; - inherits: false; - initial-value: #fff; -} -@property --tw-ring-offset-shadow { - syntax: "*"; - inherits: false; - initial-value: 0 0 #0000; -} -@property --tw-outline-style { - syntax: "*"; - inherits: false; - initial-value: solid; -} -@property --tw-blur { - syntax: "*"; - inherits: false; -} -@property --tw-brightness { - syntax: "*"; - inherits: false; -} -@property --tw-contrast { - syntax: "*"; - inherits: false; -} -@property --tw-grayscale { - syntax: "*"; - inherits: false; -} -@property --tw-hue-rotate { - syntax: "*"; - inherits: false; -} -@property --tw-invert { - syntax: "*"; - inherits: false; -} -@property --tw-opacity { - syntax: "*"; - inherits: false; -} -@property --tw-saturate { - syntax: "*"; - inherits: false; -} -@property --tw-sepia { - syntax: "*"; - inherits: false; -} -@property --tw-drop-shadow { - syntax: "*"; - inherits: false; -} -@property --tw-drop-shadow-color { - syntax: "*"; - inherits: false; -} -@property --tw-drop-shadow-alpha { - syntax: ""; - inherits: false; - initial-value: 100%; -} -@property --tw-drop-shadow-size { - syntax: "*"; - inherits: false; -} -@property --tw-backdrop-blur { - syntax: "*"; - inherits: false; -} -@property --tw-backdrop-brightness { - syntax: "*"; - inherits: false; -} -@property --tw-backdrop-contrast { - syntax: "*"; - inherits: false; -} -@property --tw-backdrop-grayscale { - syntax: "*"; - inherits: false; -} -@property --tw-backdrop-hue-rotate { - syntax: "*"; - inherits: false; -} -@property --tw-backdrop-invert { - syntax: "*"; - inherits: false; -} -@property --tw-backdrop-opacity { - syntax: "*"; - inherits: false; -} -@property --tw-backdrop-saturate { - syntax: "*"; - inherits: false; -} -@property --tw-backdrop-sepia { - syntax: "*"; - inherits: false; -} -@property --tw-duration { - syntax: "*"; - inherits: false; -} -@property --tw-ease { - syntax: "*"; - inherits: false; -} -@property --tw-scale-x { - syntax: "*"; - inherits: false; - initial-value: 1; -} -@property --tw-scale-y { - syntax: "*"; - inherits: false; - initial-value: 1; -} -@property --tw-scale-z { - syntax: "*"; - inherits: false; - initial-value: 1; -} -@property --tw-content { - syntax: "*"; - initial-value: ""; - inherits: false; -} -@keyframes spin { - to { - transform: rotate(360deg); - } -} -@keyframes pulse { - 50% { - opacity: 0.5; - } -} -@layer properties { - @supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) { - *, ::before, ::after, ::backdrop { - --tw-translate-x: 0; - --tw-translate-y: 0; - --tw-translate-z: 0; - --tw-rotate-x: initial; - --tw-rotate-y: initial; - --tw-rotate-z: initial; - --tw-skew-x: initial; - --tw-skew-y: initial; - --tw-space-y-reverse: 0; - --tw-space-x-reverse: 0; - --tw-divide-y-reverse: 0; - --tw-border-style: solid; - --tw-gradient-position: initial; - --tw-gradient-from: #0000; - --tw-gradient-via: #0000; - --tw-gradient-to: #0000; - --tw-gradient-stops: initial; - --tw-gradient-via-stops: initial; - --tw-gradient-from-position: 0%; - --tw-gradient-via-position: 50%; - --tw-gradient-to-position: 100%; - --tw-leading: initial; - --tw-font-weight: initial; - --tw-tracking: initial; - --tw-ordinal: initial; - --tw-slashed-zero: initial; - --tw-numeric-figure: initial; - --tw-numeric-spacing: initial; - --tw-numeric-fraction: initial; - --tw-shadow: 0 0 #0000; - --tw-shadow-color: initial; - --tw-shadow-alpha: 100%; - --tw-inset-shadow: 0 0 #0000; - --tw-inset-shadow-color: initial; - --tw-inset-shadow-alpha: 100%; - --tw-ring-color: initial; - --tw-ring-shadow: 0 0 #0000; - --tw-inset-ring-color: initial; - --tw-inset-ring-shadow: 0 0 #0000; - --tw-ring-inset: initial; - --tw-ring-offset-width: 0px; - --tw-ring-offset-color: #fff; - --tw-ring-offset-shadow: 0 0 #0000; - --tw-outline-style: solid; - --tw-blur: initial; - --tw-brightness: initial; - --tw-contrast: initial; - --tw-grayscale: initial; - --tw-hue-rotate: initial; - --tw-invert: initial; - --tw-opacity: initial; - --tw-saturate: initial; - --tw-sepia: initial; - --tw-drop-shadow: initial; - --tw-drop-shadow-color: initial; - --tw-drop-shadow-alpha: 100%; - --tw-drop-shadow-size: initial; - --tw-backdrop-blur: initial; - --tw-backdrop-brightness: initial; - --tw-backdrop-contrast: initial; - --tw-backdrop-grayscale: initial; - --tw-backdrop-hue-rotate: initial; - --tw-backdrop-invert: initial; - --tw-backdrop-opacity: initial; - --tw-backdrop-saturate: initial; - --tw-backdrop-sepia: initial; - --tw-duration: initial; - --tw-ease: initial; - --tw-scale-x: 1; - --tw-scale-y: 1; - --tw-scale-z: 1; - --tw-content: ""; - } - } -} diff --git a/tests/SimpleModule.Core.Tests/Inertia/InertiaResultTests.cs b/tests/SimpleModule.Core.Tests/Inertia/InertiaResultTests.cs index 23ab6327..415b8f76 100644 --- a/tests/SimpleModule.Core.Tests/Inertia/InertiaResultTests.cs +++ b/tests/SimpleModule.Core.Tests/Inertia/InertiaResultTests.cs @@ -105,11 +105,14 @@ public async Task InertiaEndpoint_DeclaresAssemblyNameThatServesEachModuleBundle declared.Should().NotBeEmpty("the client needs the mapping to build a bundle URL"); // Every module that renders pages needs an entry, or the client is back to - // guessing the bundle URL for it (#287). + // guessing the bundle URL for it (#287). Key on the first segment of the page + // name — that is what the client looks up, and it is only the [Module] name the + // map is built from by convention, so asserting on the module name directly + // would just restate how the map is built. var pages = _factory.Services.GetRequiredService>(); declared .Keys.Should() - .Contain(pages.Select(p => p.Module).Distinct(StringComparer.Ordinal)); + .Contain(pages.Select(p => p.PageRoute.Split('/')[0]).Distinct(StringComparer.Ordinal)); // A module's RCL serves its assets under its AssemblyName. Declaring anything // else — such as the "SimpleModule."-prefixed module name, which is not an From 59dcce3a0afead605c0c0c458f2fdc3dfae7c7f4 Mon Sep 17 00:00:00 2001 From: Anto Subash Date: Mon, 10 Aug 2026 14:24:50 +0200 Subject: [PATCH 5/9] docs: correct the page-resolution comment to match the fallback behaviour MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The comment said the guesses only apply when the shell predates the assembly map, but they are always appended after the declared name, so a declared bundle that fails still falls back to probing. That is deliberate — the declaredError reporting depends on it — so only the comment was wrong. --- packages/SimpleModule.Client/src/resolve-page.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/SimpleModule.Client/src/resolve-page.ts b/packages/SimpleModule.Client/src/resolve-page.ts index 06da4495..0c8e5ef3 100644 --- a/packages/SimpleModule.Client/src/resolve-page.ts +++ b/packages/SimpleModule.Client/src/resolve-page.ts @@ -48,9 +48,11 @@ export async function resolvePage(name: string) { // A module's bundle is served under its RCL AssemblyName: "SimpleModule." // for framework modules, a bare "" for ones scaffolded by `sm new module`. - // Prefer the name the server declared; only guess when the shell predates that - // mapping, and then try the assembly-qualified form first (#224). Once a module - // resolves, reuse that name directly so later navigations never re-probe. + // Try the name the server declared first so the common case costs zero 404s; the + // guesses stay on as a fallback for shells that predate the mapping (and for a + // declared name that somehow does not serve the bundle), assembly-qualified form + // first (#224). Once a module resolves, reuse that name directly so later + // navigations never re-probe. const cached = resolvedAssemblies.get(moduleName); const declared = getDeclaredAssembly(moduleName); const guesses = [`SimpleModule.${moduleName}`, moduleName]; From a1063f27d3a3721fecfcf90799ffb004c48b1e55 Mon Sep 17 00:00:00 2001 From: Anto Subash Date: Mon, 10 Aug 2026 14:42:57 +0200 Subject: [PATCH 6/9] fix: keep @source "./_scan/" in scaffolded app.css (#290) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SimpleModule.Hosting.targets stages every module's built .pages.js into Styles/_scan/, which is the only way Tailwind sees utility classes coming from a packaged module or from components outside Pages/. The CLI stripped the matching @source line, so scaffolded apps rendered those classes unstyled. The path is relative to Styles/, so the same line is correct in both the monorepo and a scaffold. The line was originally left in place out of a concern that Tailwind would fail on a missing directory in a fresh scaffold; it does not — v4 skips absent @source roots without error, and CollectModuleAssets creates the folder before TailwindBuild runs regardless. Adds HostTemplatesAppCssTests covering the retained _scan source and the existing package/module path rewrites. SimpleModule.Cli.Tests sets IsTestProject=false, so run it with: dotnet run --project tests/SimpleModule.Cli.Tests --- .../Templates/HostTemplates.cs | 8 +++- .../build/SimpleModule.Hosting.targets | 6 +-- .../HostTemplatesAppCssTests.cs | 40 +++++++++++++++++++ 3 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 tests/SimpleModule.Cli.Tests/HostTemplatesAppCssTests.cs diff --git a/cli/SimpleModule.Cli/Templates/HostTemplates.cs b/cli/SimpleModule.Cli/Templates/HostTemplates.cs index 19613065..2ab306f4 100644 --- a/cli/SimpleModule.Cli/Templates/HostTemplates.cs +++ b/cli/SimpleModule.Cli/Templates/HostTemplates.cs @@ -166,7 +166,7 @@ public static string ClientAppPackageJson(string projectName) /// /// Return a clean Styles/app.css with tailwindcss import, theme import, - /// and @source directives for modules. Strip _scan/ import if present. + /// and @source directives for modules. /// /// /// The embedded template lives in template/SimpleModule.Host/Styles/ and uses @@ -176,6 +176,11 @@ public static string ClientAppPackageJson(string projectName) /// Framework packages live in node_modules/@simplemodule/* at the project root /// (3 ups from Styles/) — installed via npm. /// Modules live at src/modules/ (2 ups from Styles/). + /// @source "./_scan/" is already relative to Styles/ and is kept as-is: + /// SimpleModule.Hosting.targets stages every module's built .pages.js there, and + /// that is the only way Tailwind sees classes from packaged modules (and from module + /// components that live outside Pages/). Stripping it left scaffolded apps + /// missing those utility classes (#290). /// /// We rewrite each @import/@source directive directly rather than blindly /// rewriting path prefixes, since the original substrings overlap. @@ -183,7 +188,6 @@ public static string ClientAppPackageJson(string projectName) public static string AppCss() { var lines = EmbeddedResourceReader.ReadTemplateLines("Templates.Host.Styles.app.css"); - lines.RemoveAll(line => line.Contains("_scan/", StringComparison.Ordinal)); var result = new List(lines.Count); foreach (var line in lines) diff --git a/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets b/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets index ff6ce5a9..b5eb98b1 100644 --- a/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets +++ b/framework/SimpleModule.Hosting/build/SimpleModule.Hosting.targets @@ -85,9 +85,9 @@ - + Date: Mon, 10 Aug 2026 14:59:33 +0200 Subject: [PATCH 7/9] fix: drop the monorepo-only design-system @source from scaffolded app.css MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs/design-system is tracked in the monorepo through @(TailwindExtraSourceFiles) and has no consumer equivalent, so copying the directive verbatim left a scaffold pointing at a directory that never exists — and would have silently started scanning unrelated files if a user ever created that path. Tailwind skips absent roots, so this was dead config rather than a build break. Also makes the sibling test's stated invariant true: no in-repo root survives into a scaffold. --- cli/SimpleModule.Cli/Templates/HostTemplates.cs | 6 ++++++ .../SimpleModule.Cli.Tests/HostTemplatesAppCssTests.cs | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/cli/SimpleModule.Cli/Templates/HostTemplates.cs b/cli/SimpleModule.Cli/Templates/HostTemplates.cs index 2ab306f4..2b9deb2b 100644 --- a/cli/SimpleModule.Cli/Templates/HostTemplates.cs +++ b/cli/SimpleModule.Cli/Templates/HostTemplates.cs @@ -181,6 +181,11 @@ public static string ClientAppPackageJson(string projectName) /// that is the only way Tailwind sees classes from packaged modules (and from module /// components that live outside Pages/). Stripping it left scaffolded apps /// missing those utility classes (#290). + /// @source "../../../docs/design-system/" is dropped: it is a monorepo-only + /// root with no consumer equivalent (the monorepo tracks it through + /// @(TailwindExtraSourceFiles)), so in a scaffold it would dangle at a + /// directory that never exists — and would silently start scanning unrelated files + /// if one were ever created there. /// /// We rewrite each @import/@source directive directly rather than blindly /// rewriting path prefixes, since the original substrings overlap. @@ -188,6 +193,7 @@ public static string ClientAppPackageJson(string projectName) public static string AppCss() { var lines = EmbeddedResourceReader.ReadTemplateLines("Templates.Host.Styles.app.css"); + lines.RemoveAll(line => line.Contains("docs/design-system", StringComparison.Ordinal)); var result = new List(lines.Count); foreach (var line in lines) diff --git a/tests/SimpleModule.Cli.Tests/HostTemplatesAppCssTests.cs b/tests/SimpleModule.Cli.Tests/HostTemplatesAppCssTests.cs index a5ae38e1..ec7b65b9 100644 --- a/tests/SimpleModule.Cli.Tests/HostTemplatesAppCssTests.cs +++ b/tests/SimpleModule.Cli.Tests/HostTemplatesAppCssTests.cs @@ -28,6 +28,16 @@ public void AppCss_RewritesFrameworkPackageRoots_ToNodeModules() css.Should().NotContain("../../../packages/"); } + [Fact] + public void AppCss_DropsMonorepoOnlyDesignSystemSource() + { + // docs/design-system is a monorepo-only @source root (tracked there through + // @(TailwindExtraSourceFiles)). A scaffold has no such directory, so keeping + // the directive would leave a dangling root — and would silently start + // scanning unrelated files if the user ever created that path. + HostTemplates.AppCss().Should().NotContain("docs/design-system"); + } + [Fact] public void AppCss_RewritesModuleRoots_ToScaffoldDepth() { From 90e7ab270437ebbce9278ceedf5185971c26c868 Mon Sep 17 00:00:00 2001 From: Anto Subash Date: Mon, 10 Aug 2026 15:19:27 +0200 Subject: [PATCH 8/9] ci: run the CLI template tests, which dotnet test skips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SimpleModule.Cli.Tests sets IsTestProject=false so its slow scaffolding tests stay out of dotnet test, which also excluded the fast template tests added for #288/#290 — they guarded nothing in CI. Run just that class explicitly in the build job. --- .github/workflows/ci.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ac3a64d0..f0092e4e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,6 +50,15 @@ jobs: - name: Test run: dotnet test --no-build + # SimpleModule.Cli.Tests sets IsTestProject=false (its scaffolding tests + # spawn `dotnet build` and take ~15min), so `dotnet test` above skips the + # whole project. Run the fast, pure template tests explicitly — otherwise + # they guard nothing. + - name: Test CLI templates + run: >- + dotnet run --project tests/SimpleModule.Cli.Tests --no-build -- + -class SimpleModule.Cli.Tests.HostTemplatesAppCssTests + vulnerable-packages: runs-on: ubuntu-latest needs: lint From 325ea24449b0cb2b3d55fa7b2753cef9151207cf Mon Sep 17 00:00:00 2001 From: Anto Subash Date: Mon, 10 Aug 2026 15:24:50 +0200 Subject: [PATCH 9/9] test(e2e): assert module pages load their declared bundle in one request MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Covers the #287 contract end to end: for each module route the shell must declare an assembly for that page's module, and the browser must fetch exactly that bundle, once, with a 200 — a second request would mean the client fell back to probing after a 404. Expectations are derived from the shell's own Inertia component name and assembly map rather than a hardcoded route table, so the test checks the contract instead of restating today's wiring. Also asserts Tailwind utilities used by module pages survive compilation, with an unused control class to prove the check discriminates (#288). Verified the spec fails when the map is emptied (5/5 route cases) and passes once restored. --- tests/e2e/tests/smoke/module-bundles.spec.ts | 85 ++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 tests/e2e/tests/smoke/module-bundles.spec.ts diff --git a/tests/e2e/tests/smoke/module-bundles.spec.ts b/tests/e2e/tests/smoke/module-bundles.spec.ts new file mode 100644 index 00000000..5a6bfcdd --- /dev/null +++ b/tests/e2e/tests/smoke/module-bundles.spec.ts @@ -0,0 +1,85 @@ +import { expect, test } from '../../fixtures/base'; + +// One route per module that renders its own React pages, so each case exercises a +// different RCL assembly. +const MODULE_ROUTES = ['/', '/files', '/settings/me', '/admin', '/branding/manage'] as const; + +/** + * The server declares module -> RCL assembly in the page shell, and the client resolves a + * page bundle from it instead of guessing "SimpleModule." and eating a 404 when the + * guess is wrong (#287). These assertions are deliberately derived from the shell itself + * rather than a hardcoded route->assembly table, so they check the actual contract: whatever + * the server declared for this page's module is what the browser requested, once, and it + * served. + */ +test.describe('Module page bundles', () => { + for (const route of MODULE_ROUTES) { + test(`${route} loads its declared bundle in a single request`, async ({ page }) => { + const bundles: { status: number; url: string }[] = []; + const failures: string[] = []; + + page.on('response', (res) => { + if (res.url().includes('.pages.js')) { + bundles.push({ status: res.status(), url: res.url() }); + } + if (res.status() >= 400) { + failures.push(`${res.status()} ${res.url()}`); + } + }); + + await page.goto(route); + await expect.poll(() => bundles.length, { timeout: 10_000 }).toBeGreaterThan(0); + + const { module, declaredAssembly } = await page.evaluate(() => { + const map = JSON.parse( + document.querySelector('script[data-module-assemblies]')?.textContent ?? '{}', + ); + const pageData = JSON.parse( + document.querySelector('script[data-page="app"]')?.textContent ?? '{}', + ); + const name: string = pageData.component ?? ''; + const mod = name.split('/')[0]; + return { module: mod, declaredAssembly: map[mod] as string | undefined }; + }); + + expect(module, 'shell should name the Inertia component').not.toBe(''); + expect(declaredAssembly, `no assembly declared for module "${module}"`).toBeTruthy(); + + // Exactly one bundle request: a second would mean the first candidate 404'd and the + // client fell back to probing, which is the regression this map removes. + expect(bundles).toHaveLength(1); + expect(bundles[0].url).toContain( + `/_content/${declaredAssembly}/${declaredAssembly}.pages.js`, + ); + expect(bundles[0].status).toBe(200); + expect(failures, 'no request should fail').toEqual([]); + }); + } + + test('compiled stylesheet carries the utility classes module pages use', async ({ page }) => { + await page.goto('/'); + + const styles = await page.evaluate(() => { + const probe = (cls: string, prop: string) => { + const el = document.createElement('div'); + el.className = cls; + document.body.appendChild(el); + const value = getComputedStyle(el).getPropertyValue(prop); + el.remove(); + return value; + }; + return { + flex: probe('flex', 'display'), + padding: probe('p-4', 'padding'), + // No module uses this one, so it must stay unstyled — otherwise the assertions + // above would pass even against a stylesheet containing every possible utility. + control: probe('bg-lime-700', 'background-color'), + }; + }); + + // If Tailwind's input set misses a source root, these silently vanish (#288). + expect(styles.flex).toBe('flex'); + expect(styles.padding).toBe('16px'); + expect(styles.control).toBe('rgba(0, 0, 0, 0)'); + }); +});