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 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/cli/SimpleModule.Cli/Templates/HostTemplates.cs b/cli/SimpleModule.Cli/Templates/HostTemplates.cs index 19613065..2b9deb2b 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,16 @@ 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). + /// @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. @@ -183,7 +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("_scan/", StringComparison.Ordinal)); + lines.RemoveAll(line => line.Contains("docs/design-system", StringComparison.Ordinal)); var result = new List(lines.Count); foreach (var line in lines) 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,63 @@ 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; + + // The default encoder escapes '<', '>' and '&', so a module or assembly name + // can never break out of the "; + } + + 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..b5eb98b1 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 @@ -38,20 +44,50 @@ $(IntermediateOutputPath)extractdto.stamp $(IntermediateOutputPath)extractroutes.stamp - + + + + + + - + + + + + 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/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..0c8e5ef3 100644 --- a/packages/SimpleModule.Client/src/resolve-page.ts +++ b/packages/SimpleModule.Client/src/resolve-page.ts @@ -4,25 +4,68 @@ // 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; + let parsed: unknown; + try { + parsed = json ? JSON.parse(json) : undefined; + } catch { + // A malformed map is not worth failing navigation over — fall back to probing. + parsed = undefined; + } + // 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; +} + +/** + * 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) ?.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`. + // 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 candidates = cached ? [cached] : [`SimpleModule.${moduleName}`, moduleName]; + const declared = getDeclaredAssembly(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]; let lastError: unknown; + let declaredError: unknown; for (const candidate of candidates) { try { mod = await import( @@ -34,13 +77,17 @@ export async function resolvePage(name: string) { break; } catch (err) { lastError = err; + if (candidate === declared) declaredError = err; } } if (!mod) { + // When the server declared an assembly, that bundle failing is the real cause — + // report it rather than the 404 from a fallback probe that was never going to + // resolve, which would bury e.g. a syntax error inside the module's own bundle. throw new Error( `Could not load pages bundle for module "${moduleName}". ` + - `Tried ${candidates.join(', ')}. Last error: ${String(lastError)}`, + `Tried ${candidates.join(', ')}. Error: ${String(declaredError ?? lastError)}`, ); } 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/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.Cli.Tests/HostTemplatesAppCssTests.cs b/tests/SimpleModule.Cli.Tests/HostTemplatesAppCssTests.cs new file mode 100644 index 00000000..ec7b65b9 --- /dev/null +++ b/tests/SimpleModule.Cli.Tests/HostTemplatesAppCssTests.cs @@ -0,0 +1,50 @@ +using FluentAssertions; +using SimpleModule.Cli.Templates; + +namespace SimpleModule.Cli.Tests; + +public sealed class HostTemplatesAppCssTests +{ + [Fact] + public void AppCss_KeepsScanSource_SoPackagedModuleClassesAreCompiled() + { + // SimpleModule.Hosting.targets stages every module's built .pages.js into + // Styles/_scan/, and that is the only way Tailwind sees classes from a + // packaged module or from components living outside Pages/. The path is + // already relative to Styles/, so it needs no rewriting for the scaffold — + // dropping it left scaffolded apps missing those utility classes (#290). + HostTemplates.AppCss().Should().Contain("@source \"./_scan/\";"); + } + + [Fact] + public void AppCss_RewritesFrameworkPackageRoots_ToNodeModules() + { + var css = HostTemplates.AppCss(); + + css.Should().Contain("../../../node_modules/@simplemodule/theme-default/theme.css"); + css.Should().Contain("../../../node_modules/@simplemodule/ui/"); + css.Should().Contain("../../../node_modules/@simplemodule/client/"); + // A scaffold has no packages/ directory, so no in-repo root may survive. + 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() + { + var css = HostTemplates.AppCss(); + + // Modules sit at src/modules/ in a scaffold: two ups from Styles/, not three. + css.Should().Contain("@source \"../../modules/**/Pages/**/*.tsx\";"); + css.Should().NotContain("../../../modules/"); + } +} diff --git a/tests/SimpleModule.Core.Tests/Inertia/InertiaResultTests.cs b/tests/SimpleModule.Core.Tests/Inertia/InertiaResultTests.cs index 6383d4df..415b8f76 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,55 @@ 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). 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.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 + // 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() { 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)'); + }); +});