/* Luxen UI standalone bundle — concatenated by scripts/build-standalone.mjs */

/* preset.css (tokens + base) */
/* Luxen CSS preset — opinionated default, take the wheel anytime.

   Each layer below is independently overridable: swap one for your own
   implementation, override individual `--l-*` properties at :root, or
   `npx luxen-ui import <thing>` to copy a layer into your project for full
   ownership. */

/* Runtime helpers (a11y, custom-element FOUC prevention). */

/* Luxen runtime base — the 15 lines required for the library to function:
   - .l-visually-hidden a11y helper
   - Custom element FOUC prevention (l-dialog:not(:defined), etc.)
   - css-extras CSS utility functions

   Imports zero tokens. Use this if you want to swap the token layer entirely
   (e.g. plug in Radix UI primitives + your own aliases). */

/**
CSS Extras - A collection of useful CSS custom functions

@author Sindre Sorhus
@license (MIT OR CC0-1.0)
*/

/* ===================================
	 Math & Number Functions
	 =================================== */

/**
Negates a value (returns the negative).

@param {Number} --value - The value to negate.
@returns {Number} The negated value.
@example padding: --negate(1em);
*/

@function --negate(--value) {
	result: calc(-1 * var(--value));
}

/**
Linear interpolation between two values.

@param {Number} --from - Start value.
@param {Number} --to - End value.
@param {Number} --progress - Progress between 0 and 1.
@returns {Number} Interpolated value.
@example width: --lerp(100px, 200px, 0.5);
*/

@function --lerp(--from, --to, --progress) {
	result: calc(var(--from) + (var(--to) - var(--from)) * var(--progress));
}

/**
Maps a value from one range to another.

@param {Number} --value - Input value.
@param {Number} --in-min - Input range minimum.
@param {Number} --in-max - Input range maximum.
@param {Number} --out-min - Output range minimum.
@param {Number} --out-max - Output range maximum.
@returns {Number} Mapped value.
@example font-size: --map-range(50vw, 320px, 1920px, 14px, 24px);
*/

@function --map-range(--value, --in-min, --in-max, --out-min, --out-max) {
	--progress: clamp(0, calc((var(--value) - var(--in-min)) / (var(--in-max) - var(--in-min))), 1);
	result: calc(var(--out-min) + (var(--out-max) - var(--out-min)) * var(--progress));
}

/**
Returns the ratio of two values. Supports values with different units, unlike regular division.

@param {CalcSum} --value - Input value.
@param {CalcSum} --to-value - Another input value.
@returns {Number} The ratio between two values.
@example scale: --ratio(16px, 1em);
*/

@function --ratio(--value, --to-value) {
	result: tan(atan2(var(--value), var(--to-value)));
}

/* ===================================
	 Color Functions
	 =================================== */

/**
Returns a semi-transparent version of any color.

@param {Color} --color - The base color.
@param {Number} --opacity - Opacity value (0-100% or 0-1).
@returns {Color} Color with opacity.
@example background: --opacity(blue, 50%);
*/

@function --opacity(--color, --opacity) {
	result: rgb(from var(--color) r g b / var(--opacity));
}

/**
Lightens a color by mixing with white.

Uses OKLab color space for perceptually uniform mixing.

@param {Color} --color - The base color.
@param {Number} --amount - Amount to lighten (0-100%).
@returns {Color} Lightened color.
@example background: --tint(blue, 20%);
*/

@function --tint(--color, --amount: 10%) {
	result: color-mix(in oklab, var(--color), white var(--amount));
}

/**
Darkens a color by mixing with black.

Uses OKLab color space for perceptually uniform mixing.

@param {Color} --color - The base color.
@param {Number} --amount - Amount to darken (0-100%).
@returns {Color} Darkened color.
@example background: --shade(blue, 20%);
*/

@function --shade(--color, --amount: 10%) {
	result: color-mix(in oklab, var(--color), black var(--amount));
}

/**
Adjusts color saturation.

Uses OKLCH color space for perceptually uniform chroma adjustment. Chroma is clamped to 0.4 for safe display.

@param {Color} --color - The base color.
@param {Number} --amount - Chroma multiplier.
@returns {Color} Adjusted color.
@example color: --saturate(red, 1.5);
*/

@function --saturate(--color, --amount: 1.2) {
	result: oklch(from var(--color) l clamp(0, calc(c * var(--amount)), 0.4) h);
}

/**
Adjusts color lightness.

Uses OKLCH color space for perceptually uniform lightness adjustment. Maintains chroma independently.

@param {Color} --color - The base color.
@param {Number} --amount - Lightness adjustment (-100% to 100%).
@returns {Color} Adjusted color.
@example background: --lighten(blue, 20%);
*/

@function --lighten(--color, --amount: 10%) {
	result: oklch(from var(--color) clamp(0, calc(l + var(--amount) / 100%), 1) c h);
}

/**
Darkens a color by reducing lightness.

Uses OKLCH color space for perceptually uniform lightness adjustment. Unlike `--shade()` which mixes with black, this directly reduces the lightness value.

@param {Color} --color - The base color.
@param {Number} --amount - Lightness reduction (0-100%).
@returns {Color} Darkened color.
@example background: --darken(blue, 20%);
*/

@function --darken(--color, --amount: 10%) {
	result: oklch(from var(--color) clamp(0, calc(l - var(--amount) / 100%), 1) c h);
}

/**
Rotates the hue of a color.

Uses OKLCH color space for perceptually uniform hue rotation.

@param {Color} --color - The base color.
@param {Angle} --degrees - Degrees to rotate hue.
@returns {Color} Color with rotated hue.
@example background: --rotate-hue(blue, 180deg);
*/

@function --rotate-hue(--color, --degrees: 30deg) {
	result: oklch(from var(--color) l c calc(h + var(--degrees)));
}

/**
Returns the complementary color.

Uses OKLCH color space for perceptually accurate complementary colors.

@param {Color} --color - The base color.
@returns {Color} Complementary color.
@example border-color: --complement(blue);
*/

@function --complement(--color) {
	result: oklch(from var(--color) l c calc(h + 180deg));
}

/**
Inverts a color.

@param {Color} --color - The color to invert.
@returns {Color} Inverted color.
@example background: --invert(white);
*/

@function --invert(--color) {
	result: rgb(from var(--color) calc(255 - r) calc(255 - g) calc(255 - b) / alpha);
}

/**
Converts a color to grayscale.

Uses OKLCH color space by setting chroma to 0.

@param {Color} --color - The color to convert.
@returns {Color} Grayscale color.
@example filter: --grayscale(var(--brand-color));
*/

@function --grayscale(--color) {
	result: oklch(from var(--color) l 0 h);
}

/*
Uses HWB color space trick: converts to grayscale in OKLCH, then uses HWB's blackness/whiteness values amplified by a large multiplier to create a binary black/white decision based on brightness. This provides excellent contrast decisions without needing numeric luminance extraction.
*/

/**
Returns black or white text color for optimal contrast on a background.

@param {Color} --bg - Background color.
@returns {Color} Black or white for optimal readability.
@example color: --text-on(var(--bg-color));
*/

@function --text-on(--bg) {
	result: hwb(from oklch(from var(--bg) l 0 0) h calc((b - 50) * 999) calc((w - 50) * 999));
}

/**
Removes transparency from a color, making it fully opaque.

@param {Color} --color - Color with alpha channel.
@returns {Color} Fully opaque version of the color.
@example background: --opaque(var(--semi-transparent-bg));
*/

@function --opaque(--color) {
	result: rgb(from var(--color) r g b / 1);
}

/**
Mixes two colors in OKLab color space.

Uses perceptually uniform OKLab color space for natural-looking color mixing.

@param {Color} --color1 - First color.
@param {Color} --color2 - Second color.
@param {Number} --amount - Amount of second color to mix (0-100%).
@returns {Color} Mixed color.
@example background: --mix(red, blue, 30%);
*/

@function --mix(--color1, --color2, --amount: 50%) {
	result: color-mix(in oklab, var(--color1), var(--color2) var(--amount));
}

/**
Returns a triadic color harmony.

Triadic colors are evenly spaced around the color wheel (120° apart).

@param {Color} --color - Base color.
@param {Number} --index - Which triadic color (1 or 2).
@returns {Color} Triadic color.
@example color: --triadic(blue, 1);
*/

@function --triadic(--color, --index: 1) {
	result: oklch(from var(--color) l c calc(h + 120deg * var(--index)));
}

/**
Returns a tetradic (square) color harmony.

Tetradic colors are evenly spaced around the color wheel (90° apart).

@param {Color} --color - Base color.
@param {Number} --index - Which tetradic color (1, 2, or 3).
@returns {Color} Tetradic color.
@example color: --tetradic(blue, 2);
*/

@function --tetradic(--color, --index: 1) {
	result: oklch(from var(--color) l c calc(h + 90deg * var(--index)));
}

/**
Creates a semi-transparent black.

@param {Number} --opacity - Opacity value (0-100% or 0-1).
@returns {Color} Semi-transparent black.
@example box-shadow: 0 2px 4px --black(20%);
*/

@function --black(--opacity: 50%) {
	result: rgb(0 0 0 / var(--opacity));
}

/**
Creates a semi-transparent white.

@param {Number} --opacity - Opacity value (0-100% or 0-1).
@returns {Color} Semi-transparent white.
@example background: --white(90%);
*/

@function --white(--opacity: 50%) {
	result: rgb(255 255 255 / var(--opacity));
}

/* ===================================
	 Typography Functions
	 =================================== */

/**
Creates fluid typography that scales with viewport.

NOTE: This function is mathematically equivalent to `--responsive-value()` but optimized for typography. Use this for `font-size`, `--responsive-value()` for other properties.

@param {Length} --min - Minimum font size.
@param {Length} --max - Maximum font size.
@param {Length} --min-viewport - Minimum viewport width.
@param {Length} --max-viewport - Maximum viewport width.
@returns {Length} Fluid font size.
@example font-size: --fluid-type(16px, 24px, 320px, 1280px);
*/

@function --fluid-type(--min, --max, --min-viewport: 320px, --max-viewport: 1280px) {
	--slope: calc((var(--max) - var(--min)) / (var(--max-viewport) - var(--min-viewport)));
	--intercept: calc(var(--min) - var(--slope) * var(--min-viewport));
	result: clamp(var(--min), calc(var(--intercept) + var(--slope) * 100vw), var(--max));
}

/**
Creates a modular scale value.

@param {Number} --base - Base size.
@param {Number} --ratio - Scale ratio.
@param {Number} --step - Step in the scale.
@returns {Length} Scaled value.
@example font-size: --modular-scale(1rem, 1.25, 3);
*/

@function --modular-scale(--base: 1rem, --ratio: 1.25, --step: 0) {
	result: calc(var(--base) * pow(var(--ratio), var(--step)));
}

/**
Calculates line height as a length value based on font size.

Returns a length (e.g., 24px) rather than a unitless ratio. Use this when you need an absolute line height value.

@param {Length} --font-size - The font size.
@param {Number} --multiplier - Line height multiplier.
@returns {Length} Line height as a length.
@example line-height: --line-height-length(16px, 1.6);
*/

@function --line-height-length(--font-size, --multiplier: 1.5) {
	result: calc(var(--font-size) * var(--multiplier));
}

/**
Calculates line height as a unitless ratio.

Returns a number (e.g., 1.5) which is recommended for better inheritance in CSS.

@param {Length} --line-height - The desired line height as a length.
@param {Length} --font-size - The font size.
@returns {Number} Unitless line height ratio.
@example line-height: --line-height-ratio(24px, 16px);
*/

@function --line-height-ratio(--line-height, --font-size) {
	result: calc(var(--line-height) / var(--font-size));
}

/**
Creates unitless line height from font size (recommended for better inheritance).

NOTE: Only works correctly with pixel font sizes. For rem/em values, use `--line-height-length()` or `--line-height-ratio()` instead.

@param {Length} --font-size - Font size in pixels.
@param {Number} --multiplier - Line height multiplier.
@returns {Number} Unitless line height.
@example line-height: --line-height-unitless(16px, 1.5);
*/

@function --line-height-unitless(--font-size: 16px, --multiplier: 1.5) {
	result: calc(var(--font-size) * var(--multiplier) / 1px);
}

/* ===================================
	 Layout Functions
	 =================================== */

/**
Creates responsive sidebar layout columns.

@param {Length} --sidebar-width - Width of sidebar.
@param {Length} --content-min - Minimum width of content area.
@returns {Length} Grid template columns value.
@example grid-template-columns: --sidebar-layout(250px, 20ch);
*/

@function --sidebar-layout(--sidebar-width: 20ch, --content-min: 20ch) {
	result: minmax(var(--sidebar-width), 1fr) minmax(var(--content-min), 3fr);
}

/**
Conditional border radius that removes at viewport edges.

@param {Length} --radius - Border radius value.
@param {Length} --edge-dist - Distance from viewport edge.
@returns {Length} Computed border radius.
@example border-radius: --conditional-radius(1rem, 8px);
*/

@function --conditional-radius(--radius, --edge-dist: 4px) {
	/* Multiply by large number to amplify small differences, creating binary 0/radius effect */
	result: clamp(0px, ((100vw - var(--edge-dist)) - 100%) * 1e5, var(--radius));
}

/**
Creates a responsive value that scales between two sizes.

NOTE: This function is mathematically equivalent to `--fluid-type()` but uses a simpler lerp-based approach. Use this for spacing/sizing, `--fluid-type()` for typography.

@param {Length} --small - Minimum value.
@param {Length} --large - Maximum value.
@param {Length} --viewport-min - Minimum viewport width.
@param {Length} --viewport-max - Maximum viewport width.
@returns {Length} Responsive value.
@example padding: --responsive-value(1rem, 2rem, 320px, 1200px);
*/

@function --responsive-value(--small, --large, --viewport-min: 320px, --viewport-max: 1200px) {
	--progress: calc((100vw - var(--viewport-min)) / (var(--viewport-max) - var(--viewport-min)));
	--clamped-progress: clamp(0, var(--progress), 1);
	result: calc(var(--small) + (var(--large) - var(--small)) * var(--clamped-progress));
}

/**
Calculates height from aspect ratio and maximum constraints.

@param {Number} --ratio - Aspect ratio (e.g., 16/9).
@param {Length} --max-width - Maximum width.
@param {Length} --max-height - Maximum height.
@returns {Length} Computed height.
@example height: --aspect-height(16/9, 100vw, 100vh);
*/

@function --aspect-height(--ratio: 1, --max-width: 100%, --max-height: 100%) {
	--computed-height: calc(var(--max-width) / var(--ratio));
	result: min(var(--computed-height), var(--max-height));
}

/**
Calculates width from aspect ratio and maximum constraints.

@param {Number} --ratio - Aspect ratio (e.g., 16/9).
@param {Length} --max-height - Maximum height.
@param {Length} --max-width - Maximum width.
@returns {Length} Computed width.
@example width: --aspect-width(16/9, 100vh, 100vw);
*/

@function --aspect-width(--ratio: 1, --max-height: 100%, --max-width: 100%) {
	--computed-width: calc(var(--max-height) * var(--ratio));
	result: min(var(--computed-width), var(--max-width));
}

/* ===================================
	 Spacing Functions
	 =================================== */

/**
Creates consistent spacing based on a scale.

Recommended range: 0-10. Higher values create exponentially larger spacing.

@param {Number} --level - Spacing level (0-10).
@param {Length} --base - Base spacing unit.
@returns {Length} Computed spacing.
@example margin: --spacing(3);
*/

@function --spacing(--level: 1, --base: 0.25rem) {
	result: calc(var(--base) * pow(2, var(--level)));
}

/**
Creates inset spacing for containers.

@param {Length} --padding - Base padding.
@param {Length} --max-width - Maximum container width.
@returns {Length} Responsive padding.
@example padding: --container-padding(2rem, 1200px);
*/

@function --container-padding(--padding: 1rem, --max-width: 1200px) {
	--available: calc(100vw - var(--max-width));
	--side-space: max(var(--padding), calc(var(--available) / 2));
	result: var(--side-space);
}

/* ===================================
	 Animation Functions
	 =================================== */

/**
Creates a simple easing curve value.

@param {Number} --progress - Animation progress (0-1).
@returns {Number} Eased value.
@example transform: translateY(--ease-out(var(--progress)));
*/

@function --ease-out(--progress) {
	--inverse: calc(1 - var(--progress));
	result: calc(1 - var(--inverse) * var(--inverse));
}

/**
Creates elastic easing.

@param {Number} --progress - Animation progress (0-1).
@param {Number} --amplitude - Amplitude of elasticity.
@returns {Number} Eased value.
@example transform: scale(--elastic-ease(var(--progress), 1.2));
*/

@function --elastic-ease(--progress, --amplitude: 1) {
	--p: calc(var(--progress) * 3.14159);
	result: calc(var(--amplitude) * sin(var(--p) * 10) * exp(calc(-5 * var(--progress))));
}

/* ===================================
	 Utility Functions
	 =================================== */

/**
Converts pixels to rem.

@param {Length} --pixels - Pixel value.
@param {Length} --base - Base font size.
@returns {Length} Rem value.
@example font-size: --px-to-rem(24px);
*/

@function --px-to-rem(--pixels, --base: 16px) {
	result: calc(var(--pixels) / var(--base) * 1rem);
}

/**
Converts rem to pixels.

@param {Length} --rems - Rem value.
@param {Length} --base - Base font size.
@returns {Length} Pixel value.
@example width: --rem-to-px(2rem);
*/

@function --rem-to-px(--rems, --base: 16px) {
	result: calc(var(--rems) / 1rem * var(--base));
}

/* ===================================
	 Grid Functions
	 =================================== */

/**
Creates responsive grid columns.

@param {Number} --min-width - Minimum column width.
@param {Number} --max-cols - Maximum number of columns.
@returns {Grid} Grid template columns value.
@example grid-template-columns: --auto-grid(250px, 4);
*/

@function --auto-grid(--min-width: 250px, --max-cols: 4) {
	result: repeat(
		auto-fit,
		minmax(max(var(--min-width), calc(100% / var(--max-cols))), 1fr)
	);
}

/**
Creates a CSS grid span value.

Ensures the span is an integer value.

@param {Number} --columns - Number of columns to span.
@param {Number} --total - Total columns in grid.
@returns {Span} Grid column span (rounded to integer).
@example grid-column: --grid-span(3, 12);
*/

@function --grid-span(--columns: 1, --total: 12) {
	result: span round(clamp(1, var(--columns), var(--total)));
}

/* ===================================
	 Filter Functions
	 =================================== */

/**
Creates a smooth shadow.

Generates three shadow layers. The spread-factor controls how distributed the shadows are.

@param {Color} --color - Shadow color.
@param {Length} --size - Shadow size.
@param {Number} --spread-factor - Controls shadow distribution (higher = tighter shadows).
@returns {Shadow} Layered box shadow.
@example box-shadow: --smooth-shadow(black, 20px, 3);
*/

@function --smooth-shadow(--color: rgb(0 0 0 / 0.2), --size: 12px, --spread-factor: 3) {
	--step: calc(var(--size) / var(--spread-factor));
	result:
		0 var(--step) calc(var(--step) * 2) rgb(from var(--color) r g b / 0.25),
		0 calc(var(--step) * 2) calc(var(--step) * 3) rgb(from var(--color) r g b / 0.18),
		0 calc(var(--step) * 3) calc(var(--step) * 4) rgb(from var(--color) r g b / 0.12);
}

/**
Creates a glow effect.

@param {Color} --color - Glow color.
@param {Length} --size - Glow size.
@param {Number} --intensity - Glow intensity (0-1).
@returns {Shadow} Glow shadow.
@example box-shadow: --glow(cyan, 10px, 0.5);
*/

@function --glow(--color: white, --size: 10px, --intensity: 0.5) {
	result: 0 0 var(--size) rgb(from var(--color) r g b / var(--intensity));
}

/* ===================================
	 Theme Functions
	 =================================== */

/**
Theme-aware value switcher for light/dark mode.

Uses CSS `if()` with color-scheme query. Requires `color-scheme: light dark` on `:root`.
Works with ANY value type (colors, lengths, etc.), not just colors.

> [!NOTE]
> CSS has a native [`light-dark()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark) function for colors. The custom `--light-dark()` function is more powerful as it works with any value type, not just colors.

@param {Any} --light - Value for light mode.
@param {Any} --dark - Value for dark mode.
@returns {Any} Theme-appropriate value.
@example color: --light-dark(black, white);
@example background-image: --light-dark(url(light.svg), url(dark.svg));
@example padding: --light-dark(0.75rem, 1rem);
*/

@function --light-dark(--light, --dark) {
	result: if(color-scheme(dark): var(--dark); else: var(--light));
}

/**
Creates a theme-aware color with automatic adjustment.

Uses CSS `if()` with color-scheme query. Requires `color-scheme: light dark` on `:root`.

In light mode, mixes the base color with white (default 85% white).
In dark mode, mixes the base color with black (default 15% black).

@param {Color} --base - Base color.
@param {Number} --light-mix - Percentage of white to mix in light mode.
@param {Number} --dark-mix - Percentage of black to mix in dark mode.
@returns {Color} Theme-adjusted color.
@example background: --theme-color(blue, 80%, 20%);
*/

@function --theme-color(--base, --light-mix: 85%, --dark-mix: 15%) {
	--light-result: color-mix(in oklab, white var(--light-mix), var(--base));
	--dark-result: color-mix(in oklab, black var(--dark-mix), var(--base));
	result: if(color-scheme(dark): var(--dark-result); else: var(--light-result));
}

@layer base {
  .l-visually-hidden {
    /* Visually hide this, but keep it accessible to keyboard,
       screen reader and other assistive technologies.

      Also know as CSS helper: `.visually-hidden` / `.sr-only`

      References:
        - https://www.tpgi.com/the-anatomy-of-visually-hidden/
        - https://youtu.be/ob_M_qXeDVE?t=677
    */
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0); /* for IE only */
    white-space: nowrap;
    border: 0;
  }

  /* Hide Shadow-DOM overlay elements until their custom element upgrades.
     Without this, slotted/inner content flashes inline before the upgrade. */
  l-dialog:not(:defined),
  l-drawer:not(:defined),
  l-dropdown:not(:defined),
  l-popover:not(:defined),
  l-tooltip:not(:defined) {
    display: none;
  }
}

/* Design tokens — primitives (palette + scales) and semantic aliases. */

/* Luxen design tokens — primitives (core 5 color families + spacing + radius
   + text + …) and semantic aliases (text-primary, bg-fill-brand, …) in one
   import. Equivalent to combining:

     @import 'luxen-ui/css/tokens/primitives';
     @import 'luxen-ui/css/tokens/aliases';
*/

/* Luxen design-tokens entry — imports primitives + semantic aliases. */

/* Luxen design-tokens — primitives (palette). */

:root {
  --l-color-white: #ffffff;
  --l-color-black: #000000;
  --l-color-transparent: transparent;
  --l-color-blue-50: oklch(97% 0.014 254.604);
  --l-color-blue-100: oklch(93.2% 0.032 255.585);
  --l-color-blue-300: oklch(80.9% 0.105 251.813);
  --l-color-blue-500: oklch(62.3% 0.214 259.815);
  --l-color-blue-600: oklch(54.6% 0.245 262.881);
  --l-color-blue-700: oklch(48.8% 0.243 264.376);
  --l-color-blue-900: oklch(37.9% 0.146 265.522);
  --l-color-blue-950: oklch(28.2% 0.091 267.935);
  --l-color-gray-50: oklch(98.5% 0.002 247.839);
  --l-color-gray-100: oklch(96.7% 0.003 264.542);
  --l-color-gray-200: oklch(92.8% 0.006 264.531);
  --l-color-gray-300: oklch(87.2% 0.01 258.338);
  --l-color-gray-400: oklch(70.7% 0.022 261.325);
  --l-color-gray-500: oklch(55.1% 0.027 264.364);
  --l-color-gray-600: oklch(44.6% 0.03 256.802);
  --l-color-gray-700: oklch(37.3% 0.034 259.733);
  --l-color-gray-800: oklch(27.8% 0.033 256.848);
  --l-color-gray-900: oklch(21% 0.034 264.665);
  --l-color-green-50: oklch(98.2% 0.018 155.826);
  --l-color-green-100: oklch(96.2% 0.044 156.743);
  --l-color-green-300: oklch(87.1% 0.15 154.449);
  --l-color-green-500: oklch(72.3% 0.219 149.579);
  --l-color-green-600: oklch(62.7% 0.194 149.214);
  --l-color-green-700: oklch(52.7% 0.154 150.069);
  --l-color-green-900: oklch(39.3% 0.095 152.535);
  --l-color-green-950: oklch(26.6% 0.065 152.934);
  --l-color-red-50: oklch(97.1% 0.013 17.38);
  --l-color-red-100: oklch(93.6% 0.032 17.717);
  --l-color-red-300: oklch(80.8% 0.114 19.571);
  --l-color-red-600: oklch(57.7% 0.245 27.325);
  --l-color-red-700: oklch(50.5% 0.213 27.518);
  --l-color-red-900: oklch(39.6% 0.141 25.723);
  --l-color-red-950: oklch(25.8% 0.092 26.042);
  --l-color-yellow-50: oklch(98.7% 0.026 102.212);
  --l-color-yellow-100: oklch(97.3% 0.071 103.193);
  --l-color-yellow-300: oklch(90.5% 0.182 98.111);
  --l-color-yellow-500: oklch(79.5% 0.184 86.047);
  --l-color-yellow-600: oklch(68.1% 0.162 75.834);
  --l-color-yellow-700: oklch(55.4% 0.135 66.442);
  --l-color-yellow-900: oklch(42.1% 0.095 57.708);
  --l-color-yellow-950: oklch(28.6% 0.066 53.813);
  --l-font-weight-thin: 100;
  --l-font-weight-extralight: 200;
  --l-font-weight-light: 300;
  --l-font-weight-normal: 400;
  --l-font-weight-medium: 500;
  --l-font-weight-semibold: 600;
  --l-font-weight-bold: 700;
  --l-font-weight-extrabold: 800;
  --l-font-weight-black: 900;
  --l-font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
  --l-font-serif: ui-serif, Georgia, Cambria, 'Times New Roman', Times, serif;
  --l-font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
  /* Luxen-specific neo-grotesque stack (badge, labels). Override at :root to use a project font. */
  --l-font-neo-grotesque: Inter, ui-sans-serif, system-ui, sans-serif;
  --l-leading-tight: 1.25;
  --l-leading-snug: 1.375;
  --l-leading-normal: 1.5;
  --l-leading-relaxed: 1.625;
  --l-leading-loose: 2;
  --l-radius: 0.25rem;
  --l-radius-xs: 0.125rem;
  --l-radius-sm: 0.25rem;
  --l-radius-md: 0.375rem;
  --l-radius-lg: 0.5rem;
  --l-radius-xl: 0.75rem;
  --l-radius-2xl: 1rem;
  --l-radius-3xl: 1.5rem;
  --l-radius-4xl: 2rem;
  /* Fully round border-radius for pill shapes, avatars, and circular elements. Luxen-specific — Tailwind v4 does not ship this. */
  --l-radius-full: calc(infinity * 1px);
  --l-spacing: 0.25rem;
  --l-spacing-0: 0;
  --l-spacing-px: 1px;
  --l-spacing-0_5: calc(var(--l-spacing) * 0.5);
  --l-spacing-1: calc(var(--l-spacing) * 1);
  --l-spacing-1_5: calc(var(--l-spacing) * 1.5);
  --l-spacing-2: calc(var(--l-spacing) * 2);
  --l-spacing-2_5: calc(var(--l-spacing) * 2.5);
  --l-spacing-3: calc(var(--l-spacing) * 3);
  --l-spacing-4: calc(var(--l-spacing) * 4);
  --l-spacing-5: calc(var(--l-spacing) * 5);
  --l-spacing-6: calc(var(--l-spacing) * 6);
  --l-spacing-8: calc(var(--l-spacing) * 8);
  --l-spacing-10: calc(var(--l-spacing) * 10);
  --l-spacing-12: calc(var(--l-spacing) * 12);
  --l-spacing-16: calc(var(--l-spacing) * 16);
  --l-spacing-20: calc(var(--l-spacing) * 20);
  --l-spacing-24: calc(var(--l-spacing) * 24);
  --l-spacing-32: calc(var(--l-spacing) * 32);
  --l-text-xs: 0.75rem;
  --l-text-xs--line-height: calc(1 / 0.75);
  --l-text-sm: 0.875rem;
  --l-text-sm--line-height: calc(1.25 / 0.875);
  --l-text-base: 1rem;
  --l-text-base--line-height: calc(1.5 / 1);
  --l-text-lg: 1.125rem;
  --l-text-lg--line-height: calc(1.75 / 1.125);
  --l-text-xl: 1.25rem;
  --l-text-xl--line-height: calc(1.75 / 1.25);
  --l-text-2xl: 1.5rem;
  --l-text-2xl--line-height: calc(2 / 1.5);
  --l-text-3xl: 1.875rem;
  --l-text-3xl--line-height: calc(2.25 / 1.875);
  --l-text-4xl: 2.25rem;
  --l-text-4xl--line-height: calc(2.5 / 2.25);
  --l-text-5xl: 3rem;
  --l-text-5xl--line-height: 1;
  --l-text-6xl: 3.75rem;
  --l-text-6xl--line-height: 1;
  --l-text-7xl: 4.5rem;
  --l-text-7xl--line-height: 1;
  --l-text-8xl: 6rem;
  --l-text-8xl--line-height: 1;
  --l-text-9xl: 8rem;
  --l-text-9xl--line-height: 1;
  --l-tracking-tighter: -0.05em;
  --l-tracking-tight: -0.025em;
  --l-tracking-normal: 0em;
  --l-tracking-wide: 0.025em;
  --l-tracking-wider: 0.05em;
  --l-tracking-widest: 0.1em;
}

/* Luxen design-tokens — semantic aliases. */

:root {
  /* Main body text, headings, and high-emphasis content. Use as the default text color throughout the UI. */
  --l-color-text-primary: light-dark(var(--l-color-gray-700), var(--l-color-gray-200));
  /* Supporting text, descriptions, captions, and medium-emphasis content like helper text beneath form fields. */
  --l-color-text-secondary: light-dark(var(--l-color-gray-600), var(--l-color-gray-400));
  /* Placeholder text, disabled labels, and low-emphasis content like timestamps or metadata. */
  --l-color-text-tertiary: light-dark(var(--l-color-gray-500), var(--l-color-gray-500));
  /* Neutral semantic text for default-state badges, tags, and status labels with no specific severity. */
  --l-color-text-neutral: light-dark(var(--l-color-gray-700), var(--l-color-gray-300));
  /* Informational semantic text for badges, alerts, banners, and status indicators conveying neutral information. */
  --l-color-text-info: light-dark(var(--l-color-blue-700), var(--l-color-blue-300));
  /* Warning semantic text for badges, alerts, and status indicators conveying caution or non-blocking issues. */
  --l-color-text-warning: light-dark(var(--l-color-yellow-700), var(--l-color-yellow-300));
  /* Danger/error semantic text for badges, alerts, validation messages, and destructive action labels. */
  --l-color-text-danger: light-dark(var(--l-color-red-700), var(--l-color-red-300));
  /* Success semantic text for badges, alerts, and status indicators conveying completion or positive outcomes. */
  --l-color-text-success: light-dark(var(--l-color-green-700), var(--l-color-green-300));
  /* Text color for disabled labels, values, and icons in form controls, buttons, and interactive elements. */
  --l-color-text-disabled: light-dark(var(--l-color-gray-400), var(--l-color-gray-500));
  /* Text on brand-filled surfaces like primary buttons. Maximum contrast against --l-color-bg-fill-brand. */
  --l-color-text-on-fill-brand: light-dark(var(--l-color-white), var(--l-color-gray-900));
  /* Default page-level background. Use on <body>, main content areas, and cards at the base layer. */
  --l-color-surface: light-dark(var(--l-color-white), var(--l-color-gray-900));
  /* Elevated overlay background for modals, dialogs, drawers, popovers, and any surface that floats above the page with a backdrop. Slightly lighter than --l-color-surface in dark mode to create visual separation from the page beneath. */
  --l-color-surface-overlay: light-dark(var(--l-color-white), var(--l-color-gray-800));
  /* Default border for form controls, secondary buttons, inputs, selects, and any element that needs a thin neutral outline. Visible enough to delimit the element without competing with surrounding content. */
  --l-color-border: light-dark(var(--l-color-gray-300), var(--l-color-gray-600));
  /* Subtle border or ring for interactive elements like close buttons, toggles, and icon buttons on hover. Provides low-contrast visual feedback without competing with primary content. */
  --l-color-border-interactive: light-dark(var(--l-color-gray-300), var(--l-color-gray-600));
  /* Border for floating surfaces like popovers, dropdowns, menus, tooltips, and combobox listboxes. Aliases --l-color-border so overlays stay visually consistent with form controls, but exposed as a separate semantic so consumers can soften overlay borders independently if needed. Pairs with --l-color-surface-overlay. */
  --l-color-border-overlay: var(--l-color-border);
  /* Border color for disabled form controls, buttons, and interactive elements. Faded to signal non-interactivity. */
  --l-color-border-disabled: light-dark(var(--l-color-gray-300), var(--l-color-gray-700));
  /* Subtle divider line for separating content sections, list items, and card groups. Lighter than --l-color-border to avoid visual competition with interactive element borders. */
  --l-color-divider: light-dark(var(--l-color-gray-200), var(--l-color-gray-700));
  /* Background color for disabled form controls, buttons, and interactive elements. Subtle tint to reinforce non-interactivity. */
  --l-color-bg-disabled: light-dark(var(--l-color-gray-100), var(--l-color-gray-800));
  /* Translucent background tint for hovered rows in lists, menus, trees, tables, and similar roving collections. Alpha-based so underlying indent guides, borders, or zebra stripes stay visible. */
  --l-color-bg-state-hover: light-dark(rgb(0 0 0 / 5%), rgb(255 255 255 / 6%));
  /* Translucent background tint for the selected (persistent) row in lists, trees, menus, listboxes, and tables. Alpha-based so underlying indent guides, borders, or zebra stripes stay visible. Semantically paired with `aria-selected="true"` / `:state(selected)`. */
  --l-color-bg-state-selected: light-dark(rgb(0 0 0 / 7%), rgb(255 255 255 / 9%));
  /* Primary brand fill for CTA buttons, active toggles, and primary action backgrounds. */
  --l-color-bg-fill-brand: light-dark(var(--l-color-gray-900), var(--l-color-gray-50));
  /* Hover state of brand-filled elements like primary buttons and active toggles. */
  --l-color-bg-fill-brand-hover: light-dark(var(--l-color-gray-800), var(--l-color-gray-200));
  /* Active/pressed state of brand-filled elements like primary buttons during click or tap. */
  --l-color-bg-fill-brand-active: light-dark(var(--l-color-gray-700), var(--l-color-gray-300));
  /* Secondary control fill for buttons, toggles, and non-primary interactive surfaces. */
  --l-color-bg-fill-secondary: light-dark(var(--l-color-white), var(--l-color-gray-700));
  /* Soft neutral fill — palest tint for toast backgrounds, alert containers, and callout boxes. */
  --l-color-bg-fill-neutral-soft: light-dark(var(--l-color-gray-50), var(--l-color-gray-800));
  /* Subtle neutral fill — tinted background for badges, tags, selected states, and secondary highlights. */
  --l-color-bg-fill-neutral-subtle: light-dark(var(--l-color-gray-100), var(--l-color-gray-700));
  /* Strong neutral fill — solid background for solid badges and high-emphasis status indicators. Pair with --l-color-surface for foreground. */
  --l-color-bg-fill-neutral-strong: light-dark(var(--l-color-gray-800), var(--l-color-gray-200));
  /* Soft info fill — palest tint for toast backgrounds, alert containers, and callout boxes. */
  --l-color-bg-fill-info-soft: light-dark(var(--l-color-blue-50), var(--l-color-blue-950));
  /* Subtle info fill — tinted background for badges, tags, selected states, and secondary highlights. */
  --l-color-bg-fill-info-subtle: light-dark(var(--l-color-blue-100), var(--l-color-blue-900));
  /* Strong info fill — solid background for solid badges and high-emphasis status indicators. */
  --l-color-bg-fill-info-strong: light-dark(var(--l-color-blue-600), var(--l-color-blue-500));
  /* Soft warning fill — palest tint for toast backgrounds, alert containers, and callout boxes. */
  --l-color-bg-fill-warning-soft: light-dark(var(--l-color-yellow-50), var(--l-color-yellow-950));
  /* Subtle warning fill — tinted background for badges, tags, selected states, and secondary highlights. */
  --l-color-bg-fill-warning-subtle: light-dark(var(--l-color-yellow-100), var(--l-color-yellow-900));
  /* Strong warning fill — solid background for solid badges and high-emphasis status indicators. */
  --l-color-bg-fill-warning-strong: light-dark(var(--l-color-yellow-600), var(--l-color-yellow-500));
  /* Soft danger fill — palest tint for toast backgrounds, alert containers, and callout boxes. */
  --l-color-bg-fill-danger-soft: light-dark(var(--l-color-red-50), var(--l-color-red-950));
  /* Subtle danger fill — tinted background for badges, tags, selected states, and secondary highlights. */
  --l-color-bg-fill-danger-subtle: light-dark(var(--l-color-red-100), var(--l-color-red-900));
  /* Strong danger fill — solid background for solid badges, high-emphasis status indicators, and destructive buttons. Dark uses red-600 (not red-500) so white text clears WCAG AA 4.5:1. */
  --l-color-bg-fill-danger-strong: light-dark(var(--l-color-red-600), var(--l-color-red-600));
  /* Soft success fill — palest tint for toast backgrounds, alert containers, and callout boxes. */
  --l-color-bg-fill-success-soft: light-dark(var(--l-color-green-50), var(--l-color-green-950));
  /* Subtle success fill — tinted background for badges, tags, selected states, and secondary highlights. */
  --l-color-bg-fill-success-subtle: light-dark(var(--l-color-green-100), var(--l-color-green-900));
  /* Strong success fill — solid background for solid badges and high-emphasis status indicators. */
  --l-color-bg-fill-success-strong: light-dark(var(--l-color-green-600), var(--l-color-green-500));
  /* Semi-transparent backdrop behind modals, dialogs, drawers, and any overlay that dims the page content. Darker in dark mode for better contrast against dark surfaces. */
  --l-backdrop: light-dark(oklch(46% 0.01 260 / 33%), oklch(15% 0.01 260 / 60%));
  /* Stronger backdrop for immersive overlays where the underlying page should fade well into the background — media viewers (`<l-stories-viewer>`), lightboxes, and full-screen players. The page stays barely perceptible but visual focus is unambiguously on the foreground. */
  --l-backdrop-strong: light-dark(oklch(15% 0.01 260 / 78%), oklch(5% 0 0 / 88%));
  /* Subtle elevation for resting surfaces that lift only slightly off the page — cards, raised list items, sticky bars. The shadow color uses light-dark() so it deepens in dark mode, where a faint black shadow would otherwise vanish against a dark surface. */
  --l-shadow-sm: 0 1px 2px 0 light-dark(rgb(0 0 0 / 8%), rgb(0 0 0 / 40%));
  /* Medium elevation for floating overlays anchored to a trigger — popovers, dropdown menus, comboboxes. The shadow color uses light-dark() so it deepens in dark mode. */
  --l-shadow-md: 0 4px 6px -1px light-dark(rgb(0 0 0 / 8%), rgb(0 0 0 / 40%)), 0 2px 4px -2px light-dark(rgb(0 0 0 / 6%), rgb(0 0 0 / 34%));
  /* Large elevation for prominent detached surfaces that float free of any edge — inset drawers, floating panels, large menus. The shadow color uses light-dark() so it deepens in dark mode. */
  --l-shadow-lg: 0 10px 15px -3px light-dark(rgb(0 0 0 / 10%), rgb(0 0 0 / 50%)), 0 4px 6px -4px light-dark(rgb(0 0 0 / 10%), rgb(0 0 0 / 44%));
  /* Focus ring color for :focus-visible outlines on interactive elements like buttons, inputs, and links. */
  --l-focus-ring: #d9461f;
  /* Standard interactive control height (28px) for extra-small elements like compact buttons, tags, and inline controls. */
  --l-size-control-xs: 1.75rem;
  /* Standard interactive control height (32px) for small elements like secondary buttons, small selects, and tight-layout controls. */
  --l-size-control-sm: 2rem;
  /* Standard interactive control height (36px) for medium (default) elements like buttons, inputs, selects, and form controls. */
  --l-size-control-md: 2.25rem;
  /* Standard interactive control height (40px) for large elements like prominent buttons, inputs in spacious layouts, and hero CTAs. */
  --l-size-control-lg: 2.5rem;
  /* Standard interactive control height (44px) for extra-large elements like oversized buttons, landing page CTAs, and touch-optimized controls — meets the 44px WCAG 2.5.5 / Apple HIG touch-target minimum. */
  --l-size-control-xl: 2.75rem;
}

/* Form component tokens (--l-form-control-*, --l-form-field-*). */

/* Luxen design-tokens — form component tokens.
   Opt-in via `@import "luxen-ui/css/tokens/forms"` (already bundled in the
   Luxen preset). Reference semantic aliases, so dark mode is inherited. */

:root {
  /* Color of a control when activated (checked checkbox, selected radio, on switch). Form-wide accent, akin to native `accent-color`. A stable, saturated color that does not invert between light and dark, so the baked white glyph stays legible in both modes — override per brand. */
  --l-form-control-activated-color: var(--l-color-blue-600);
  /* Color of the glyph on the activated fill (checkmark, radio dot, switch thumb). Pairs with the stable accent. */
  --l-form-control-activated-content-color: var(--l-color-white);
  /* Control background (input, textarea, select, unchecked toggle box). */
  --l-form-control-background-color: var(--l-color-surface);
  /* Color of the user-entered value text. */
  --l-form-control-value-color: var(--l-color-text-primary);
  /* Placeholder text color. */
  --l-form-control-placeholder-color: var(--l-color-text-tertiary);
  /* Control border color. */
  --l-form-control-border-color: var(--l-color-border);
  /* Control border width. */
  --l-form-control-border-width: 1px;
  /* Control border radius. */
  --l-form-control-border-radius: var(--l-radius-md);
  /* Control border color on hover. */
  --l-form-control-border-color-hover: var(--l-color-text-tertiary);
  /* Control border color when invalid. */
  --l-form-control-border-color-invalid: var(--l-color-bg-fill-danger-strong);
  /* Background of a disabled text control — a subtle grey fill (reinforces non-interactivity, instead of fading the whole control with opacity). */
  --l-form-control-disabled-background: var(--l-color-bg-disabled);
  /* Border color of a disabled text control. */
  --l-form-control-disabled-border: var(--l-color-border-disabled);
  /* Value/placeholder text color of a disabled text control. */
  --l-form-control-disabled-color: var(--l-color-text-disabled);
  /* Box size of toggle controls (checkbox, radio, switch). Relative to font size so it scales with the label. */
  --l-form-control-toggle-size: 1.25em;
  /* Thickness of the hover halo ring on toggle controls (checkbox, radio, switch), as a fraction of the toggle size. Single source for the shared toggle hover language. */
  --l-form-control-toggle-halo-ratio: 0.22;
  /* Off-state track fill of the switch. A mid grey that holds in both light and dark so the white thumb keeps ~5:1 contrast either way — decoupled from text tokens so re-theming body text never moves the track. */
  --l-form-control-track-off-color: var(--l-color-gray-500);
  /* Height of text controls (input, select, textarea). */
  --l-form-control-height: var(--l-size-control-md);
  /* Horizontal padding of text controls. */
  --l-form-control-padding-inline: var(--l-spacing-3);
  /* Field label color. */
  --l-form-control-label-color: var(--l-color-text-primary);
  /* Helper/description text color (`l-hint`). */
  --l-form-control-hint-color: var(--l-color-text-secondary);
  /* Error message text color (`l-error`). */
  --l-form-control-error-color: var(--l-color-text-danger);
  /* Marker appended to a required field's label. */
  --l-form-control-required-content: '*';
  /* Color of the required marker. */
  --l-form-control-required-color: var(--l-color-text-danger);
  /* Vertical gap between label, control, and messages in a stacked field. Tight, so the label and helper text sit close to the control. */
  --l-form-field-gap: var(--l-spacing-1);
  /* Horizontal gap between a toggle control and its label in an inline field. */
  --l-form-field-choice-gap: var(--l-spacing-2);
}

/* Tooltip component token (--l-tooltip-background-color). */

/* Luxen design-tokens — tooltip component token.
   Bundled in the Luxen preset via `luxen-ui/css/tokens`. A neutral inverse
   surface, decoupled from the brand fill so re-theming primary does not
   recolor tooltips. */

:root {
  /* Tooltip surface color — dark in light mode, light in dark mode. Neutral by design; not tied to --l-color-bg-fill-brand, so changing the brand color does not recolor tooltips. Override globally to re-skin every tooltip, or use the per-instance --background-color custom property on a single `<l-tooltip>`. */
  --l-tooltip-background-color: light-dark(var(--l-color-gray-900), var(--l-color-gray-50));
}

/* alert.css */
@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   🅰🅻🅴🆁🆃
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  - Flex row: [icon] [content] [close]. Authored children are moved into the
    injected `.l-alert-content` wrapper (a text-node body cannot be placed by
    CSS), which stacks them vertically.
  - Variants tint the whole callout (text + icon + border) from a single
    `--_color`, over a soft background — mirrors the toast look.
  */

  l-alert {
    /* Public knobs */
    --gap: 0.75rem;
    --padding: 1rem;
    --border-radius: 8px;

    /* Neutral primitives — overridden per variant */
    --_color: var(--l-color-text-neutral);
    --_bg: var(--l-color-bg-fill-neutral-subtle);
    --_border: var(--l-color-border);

    display: flex;
    align-items: flex-start;
    gap: var(--gap);
    padding: var(--padding);
    border: 1px solid var(--_border);
    border-radius: var(--border-radius);
    background: var(--_bg);
    color: var(--l-color-text-primary);
    font-family: var(--l-font-neo-grotesque);
    font-size: var(--l-text-sm);
    line-height: 1.5;

    transition:
      opacity 150ms ease,
      translate 150ms ease;

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆅🅰🆁🅸🅰🅽🆃🆂
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    &[variant='info'] {
      --_color: var(--l-color-text-info);
      --_bg: var(--l-color-bg-fill-info-soft);
    }
    &[variant='success'] {
      --_color: var(--l-color-text-success);
      --_bg: var(--l-color-bg-fill-success-soft);
    }
    &[variant='warning'] {
      --_color: var(--l-color-text-warning);
      --_bg: var(--l-color-bg-fill-warning-soft);
    }
    &[variant='danger'] {
      --_color: var(--l-color-text-danger);
      --_bg: var(--l-color-bg-fill-danger-soft);
    }

    &[variant] {
      --_border: color-mix(in oklab, var(--_color) 25%, transparent);
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆄🅸: icon
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    > .l-alert-icon {
      flex-shrink: 0;
      margin-top: 0.0625rem;
      font-size: 1.25rem;
      color: var(--_color);
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆄🅸: content (authored children, stacked)
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    > .l-alert-content {
      flex: 1 1 auto;
      min-width: 0;
      display: flex;
      flex-direction: column;
      gap: 0.25rem;
      /* Body copy stays in the readable primary text color; only the icon,
         title, and border carry the semantic tint, so prose contrast holds
         and links keep a distinct affordance. */
      color: var(--l-color-text-primary);

      > * {
        margin: 0;
      }

      /* Links keep an underline (and the semantic tint on variants) so they
         remain distinguishable from the body text. */
      :where(a) {
        color: var(--_color);
        text-decoration: underline;
      }
    }

    .l-alert-title {
      font-weight: var(--l-font-weight-medium);
      line-height: 1.4;
    }

    &[variant] .l-alert-title {
      color: var(--_color);
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆄🅸: close button (shared .l-close, ring appearance)
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    > .l-close {
      --size: 28px;
      --icon-size: 14px;
      --icon-color: var(--_color);
      --ring-color: color-mix(in oklab, var(--_color) 40%, transparent);

      flex-shrink: 0;
      margin: -0.25rem -0.25rem 0 0;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅰🅽🅸🅼🅰🆃🅸🅾🅽: dismiss
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    &[data-dismissing] {
      opacity: 0;
      translate: 0 -0.25rem;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    l-alert {
      transition: none;
    }
  }
}

/* avatar.css */
@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   🅰🆅🅰🆃🅰🆁
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  .l-avatar-group {
    display: flex;
    isolation: isolate;

    > * + * {
      margin-inline-start: -0.5rem;
    }

    > l-avatar {
      box-shadow: 0 0 0 2px var(--l-color-surface);
    }
  }
}

/* badge.css */
@layer components {
  /*
    Autonomous custom elements
    https://blog.jim-nielsen.com/2021/custom-elements-without-js/
  */
  l-badge {
    /* Per-variant primitives — set by [variant], consumed by appearance */
    --_text-color: var(--l-color-text-neutral);
    --_background-color: transparent;
    --_border-color: color-mix(in oklab, var(--_text-color) 30%, transparent);

    /* Size defaults (md) */
    --_height: 1.375rem;
    --_font-size: var(--l-text-xs);
    --_padding-inline: 0.375rem;
    --_gap: 0.25rem;

    display: inline-flex;
    align-items: center;
    gap: var(--_gap);
    min-height: var(--_height);
    padding-block: 0.25rem;
    padding-inline: var(--_padding-inline);
    border: 1px solid var(--_border-color);
    border-radius: var(--l-radius-md);
    corner-shape: squircle;
    background-color: var(--_background-color);
    color: var(--_text-color);
    font-family: var(--l-font-neo-grotesque);
    font-size: var(--_font-size);
    font-weight: var(--l-font-weight-medium);
    line-height: 1;
    text-box: trim-both cap alphabetic;

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆂🅸🆉🅴🆂
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    &[size='sm'] {
      --_height: 1.25rem;
      --_padding-inline: 0.25rem;
      --_gap: 0.125rem;
    }
    &[size='lg'] {
      --_height: 1.5rem;
      --_font-size: var(--l-text-sm);
      --_padding-inline: 0.5rem;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆅🅰🆁🅸🅰🅽🆃🆂
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    &[variant='info'] {
      --_text-color: var(--l-color-text-info);
    }
    &[variant='success'] {
      --_text-color: var(--l-color-text-success);
    }
    &[variant='warning'] {
      --_text-color: var(--l-color-text-warning);
    }
    &[variant='danger'] {
      --_text-color: var(--l-color-text-danger);
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅰🅿🅿🅴🅰🆁🅰🅽🅲🅴
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    &[appearance='filled'] {
      --_background-color: var(--l-color-bg-fill-neutral-subtle);
      --_border-color: transparent;

      &[variant='info'] {
        --_background-color: var(--l-color-bg-fill-info-subtle);
      }
      &[variant='success'] {
        --_background-color: var(--l-color-bg-fill-success-subtle);
      }
      &[variant='warning'] {
        --_background-color: var(--l-color-bg-fill-warning-subtle);
      }
      &[variant='danger'] {
        --_background-color: var(--l-color-bg-fill-danger-subtle);
      }
    }

    &[appearance='filled-outlined'] {
      --_background-color: var(--l-color-bg-fill-neutral-subtle);

      &[variant='info'] {
        --_background-color: var(--l-color-bg-fill-info-subtle);
      }
      &[variant='success'] {
        --_background-color: var(--l-color-bg-fill-success-subtle);
      }
      &[variant='warning'] {
        --_background-color: var(--l-color-bg-fill-warning-subtle);
      }
      &[variant='danger'] {
        --_background-color: var(--l-color-bg-fill-danger-subtle);
      }
    }

    &[appearance='accent'] {
      --_background-color: var(--l-color-bg-fill-neutral-strong);
      --_text-color: var(--l-color-surface);
      --_border-color: transparent;

      &[variant='info'] {
        --_background-color: var(--l-color-bg-fill-info-strong);
        --_text-color: white;
      }
      &[variant='success'] {
        --_background-color: var(--l-color-bg-fill-success-strong);
      }
      &[variant='warning'] {
        --_background-color: var(--l-color-bg-fill-warning-strong);
      }
      &[variant='danger'] {
        --_background-color: var(--l-color-bg-fill-danger-strong);
        --_text-color: white;
      }
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅿🅸🅻🅻
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    &[pill] {
      border-radius: var(--l-radius-full);
      corner-shape: unset;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅸🅲🅾🅽
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    iconify-icon {
      color: currentColor;
      height: 1em;
    }
  }

  l-badge:has(iconify-icon:first-child) {
    padding-inline-start: 0.25rem;
  }
}

/* breadcrumb.css */
/*
 * Breadcrumb — `.l-breadcrumb` on a native <nav> wrapping an <ol>/<li>/<a>.
 *
 * CSS-only (no JS). Maps 1:1 onto the WAI-ARIA APG breadcrumb pattern:
 * https://www.w3.org/WAI/ARIA/apg/patterns/breadcrumb/
 *
 * Markup contract:
 *   <nav class="l-breadcrumb" aria-label="Breadcrumb">
 *     <ol>
 *       <li><a href="/">Home</a></li>
 *       <li><a href="/products">Products</a></li>
 *       <li><a href="/products/bags" aria-current="page">Bags</a></li>
 *     </ol>
 *   </nav>
 *
 * - Links are underlined; the current crumb (`aria-current="page"`) is rendered
 *   subtler than the rest and non-interactive.
 * - The trail never wraps: it scrolls horizontally with touch momentum when it
 *   overflows (the default mobile behavior), scrollbar hidden.
 * - Separators are CSS `::before` pseudo-elements (an oblique `/` by default) —
 *   decorative and hidden from assistive tech. Recolor with `--separator-color`;
 *   swap the glyph with any character or `url()` via `--separator`.
 * - Icons (a leading home glyph, etc.) compose via <l-icon> inside the <a>.
 */

@layer components {
  .l-breadcrumb {
    /* Public knobs */
    --gap: 0.5rem;
    --separator: '/';
    /* Tertiary text, not the divider token: a thin `/` glyph needs more weight
       than a 1px line color. Still a step lighter than the link text. */
    --separator-color: var(--l-color-text-tertiary);

    display: block;
    font-size: var(--l-text-sm);
    line-height: 1.4;

    & ol {
      display: flex;
      flex-wrap: nowrap;
      align-items: center;
      gap: var(--gap);
      margin: 0;
      /* Block padding leaves room for focus rings inside the scroll clip. */
      padding-block: 0.25rem;
      padding-inline: 0;
      list-style: none;

      /* Never wrap — scroll horizontally with momentum on touch, no scrollbar. */
      overflow-x: auto;
      overscroll-behavior-inline: contain;
      -webkit-overflow-scrolling: touch;
      scrollbar-width: none;

      &::-webkit-scrollbar {
        display: none;
      }
    }

    & li {
      display: inline-flex;
      align-items: center;
      gap: var(--gap);
      flex: none;
      white-space: nowrap;
    }

    /* Separator before every crumb except the first. */
    & li + li::before {
      content: var(--separator);
      color: var(--separator-color);
    }

    /* Keep the decorative divider out of the accessibility tree where the
       content alt-text syntax is supported (the visible glyph still renders). */
    @supports (content: '/' / '') {
      & li + li::before {
        content: var(--separator) / '';
      }
    }

    &:dir(rtl) li + li::before {
      display: inline-block;
      scale: -1 1;
    }

    /* Structural link bits — always applied so icons align and focus shows,
       even when the consumer brings their own link styling. */
    & a {
      display: inline-flex;
      align-items: center;
      gap: 0.375rem;
      border-radius: var(--l-radius-sm);

      &:focus-visible {
        outline: 2px solid var(--l-focus-ring);
        outline-offset: 2px;
      }
    }

    /* The current crumb is never a destination (APG `aria-current="page"`). */
    & [aria-current='page'] {
      pointer-events: none;
      cursor: default;
    }

    /* Luxen link theming. Opt out with `data-unstyled-links` to bring your own
       link class — layout, separators, scroll, and current-page behavior stay. */
    &:not([data-unstyled-links]) {
      & a {
        color: var(--l-color-text-secondary);
        text-decoration-line: underline;
        text-decoration-thickness: 1px;
        text-underline-offset: 0.2em;
        transition:
          color 150ms,
          text-decoration-thickness 150ms;

        /* Minimal hover: the underline thickens and the text deepens. */
        @media (hover: hover) {
          &:hover {
            color: var(--l-color-text-primary);
            text-decoration-thickness: 2px;
          }
        }

        @media (prefers-reduced-motion: reduce) {
          transition: none;
        }
      }

      /* Current crumb rendered subtler than the links. */
      & [aria-current='page'] {
        color: var(--l-color-text-tertiary);
        text-decoration: none;
      }
    }

    /* Icons inside crumbs follow the text size. */
    & l-icon {
      font-size: 1.125em;
    }
  }
}

/* button-group.css */
@layer components {
  /**
   * Visually joins a set of `.l-button` elements into a single segmented
   * control. Pure CSS: it targets direct `.l-button` children and buttons
   * nested one level inside a wrapper (e.g. an `l-dropdown` trigger, for split
   * buttons), so it works without JavaScript. `<l-button-group>` only adds the
   * `role="group"` / `aria-orientation` semantics on top.
   *
   * - https://webawesome.com/docs/components/button-group/
   * - https://ui.shadcn.com/docs/components/button-group
   */

  l-button-group {
    display: inline-flex;
    align-items: stretch;
  }

  l-button-group[orientation='vertical'] {
    flex-direction: column;
  }

  /* Segments overlap their neighbour's border, so raise the interacted one to
     keep its border and focus ring from being clipped. */
  l-button-group .l-button {
    position: relative;
  }

  l-button-group .l-button:hover,
  l-button-group .l-button:active {
    z-index: 1;
  }

  l-button-group .l-button:focus-visible {
    z-index: 2;
  }

  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  Horizontal (default): collapse the inline (start ↔ end) seams
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  l-button-group:not([orientation='vertical']) {
    /* Overlap the shared 1px border. Margin sits on the flex item — either the
       button itself or the wrapper around it. */
    & > *:not(:first-child):is(.l-button, :has(.l-button)) {
      margin-inline-start: -1px;
    }

    /* Non-first segment: square off the leading (start) corners. */
    & > .l-button:not(:first-child),
    & > *:not(:first-child) .l-button {
      border-start-start-radius: 0;
      border-end-start-radius: 0;
    }

    /* Non-last segment: square off the trailing (end) corners. */
    & > .l-button:not(:last-child),
    & > *:not(:last-child) .l-button {
      border-start-end-radius: 0;
      border-end-end-radius: 0;
    }
  }

  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  Vertical: collapse the block (top ↔ bottom) seams
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  l-button-group[orientation='vertical'] {
    & > *:not(:first-child):is(.l-button, :has(.l-button)) {
      margin-block-start: -1px;
    }

    & > .l-button:not(:first-child),
    & > *:not(:first-child) .l-button {
      border-start-start-radius: 0;
      border-start-end-radius: 0;
    }

    & > .l-button:not(:last-child),
    & > *:not(:last-child) .l-button {
      border-end-start-radius: 0;
      border-end-end-radius: 0;
    }
  }
}

/* button.css */
/**
 * - https://component.gallery/components/button
 * - https://designsystems.surf/components/button
 * - https://www.uiguideline.com/components/button
 * - https://codepen.io/jh3y/pen/XJdLrZV
 *
 * Base appearance + primary/destructive variants are shared with shadow-DOM
 * elements (l-alert-dialog) from button-core.css; layered here into
 * `@layer components`. Size/icon-only/press-effect variants are button-only.
 */
@layer components {
/*
 * Shared `.l-button` appearance — the single source of truth for the button's
 * base look, primary, and destructive variants. Lives in the HTML tree so it is
 * both:
 *   - `@import`ed (and layered) by src/css/elements/button.css for the light-DOM
 *     `.l-button`, and
 *   - imported `?inline` into shadow-DOM elements (l-alert-dialog) that render
 *     their own `.l-button` actions and can't reach the global stylesheet.
 *
 * Intentionally UNLAYERED: the light-DOM import wraps it in `@layer components`
 * via `@import … layer(components)`, while shadow consumers want it unlayered to
 * sit alongside the (also unlayered) dialog styles. Size/icon-only/press-effect
 * variants stay in button.css — shadow actions don't need them.
 */
.l-button {
  /* Local size scaffolding (referenced by the size variants in button.css). */
  --_button-size-xs: var(--l-size-control-xs); /* 28px */
  --_button-size-sm: var(--l-size-control-sm); /* 32px */
  --_button-size-md: var(--l-size-control-md); /* 36px */
  --_button-size-lg: var(--l-size-control-lg); /* 40px */
  --_button-size-xl: var(--l-size-control-xl); /* 44px */

  /* Appearance (default: secondary) */
  --height: var(--_button-size-md);
  --padding-inline: 0.625rem;
  --background-color: var(--l-color-bg-fill-secondary);
  --background-color-active: color-mix(
    in oklab,
    var(--l-color-bg-fill-secondary) 90%,
    var(--l-color-text-primary)
  );
  --background-color-hover: var(--background-color-active);
  --font-size: var(--l-text-sm);
  --text-color: var(--l-color-text-primary);
  --text-color-hover: var(--text-color);
  --border-color: var(--l-color-border);
  --border-color-hover: color-mix(in oklab, var(--l-color-border) 75%, var(--l-color-text-primary));

  position: relative;
  display: inline-flex;
  place-items: center;
  justify-content: center;
  gap: calc(var(--spacing) * 2);
  height: var(--height);
  padding-inline: var(--padding-inline);
  border: 1px solid var(--border-color);
  border-radius: calc(var(--height) / 8);
  background: var(--background-color);
  color: var(--text-color);
  outline: none;
  font-size: var(--font-size);
  font-weight: var(--l-font-weight-semibold);
  text-decoration: none;
  -webkit-user-select: none;
          user-select: none;
  white-space: nowrap;
  appearance: none;
  -webkit-tap-highlight-color: transparent;

  transition-property: background-color, color, text-decoration, border-color;
  transition-duration: 150ms;

  /* `:disabled` (native) and `[aria-disabled]` (a busy/blocked control kept in
     the tab order) read the same. */
  &:not(:disabled):not([aria-disabled='true']) {
    cursor: pointer;
  }

  &:disabled,
  &[aria-disabled='true'] {
    cursor: not-allowed;
    opacity: 0.4;
  }

  &:not(:disabled):not([aria-disabled='true']):hover {
    background-color: var(--background-color-hover);
    border-color: var(--border-color-hover);
    color: var(--text-color-hover);
  }

  &:not(:disabled):not([aria-disabled='true']):active {
    background-color: var(--background-color-active);
  }

  &:focus-visible {
    outline: 2px solid var(--l-focus-ring);
    outline-offset: 2px;
  }
}

.l-button[data-variant='primary'] {
  --background-color: var(--l-color-bg-fill-brand);
  --background-color-active: var(--l-color-bg-fill-brand-active);
  --background-color-hover: var(--l-color-bg-fill-brand-hover);
  --text-color: var(--l-color-text-on-fill-brand);
  --text-color-hover: var(--l-color-text-on-fill-brand);
  --border-color: transparent;
  --border-color-hover: transparent;
}

/* Soft danger fill: a pale tint with a dark-red label, deepening the tint on
   hover/active. Reads clearly as destructive without the visual shout of a solid
   red — the consequence lives in the copy and the confirmation, not the chrome.
   Derivations mix the soft fill toward the danger text color, so the tint
   deepens in light mode and lightens in dark mode (where soft is near-black). */
.l-button[data-variant='destructive'] {
  --background-color: var(--l-color-bg-fill-danger-soft);
  --background-color-hover: color-mix(
    in oklab,
    var(--l-color-bg-fill-danger-soft) 80%,
    var(--l-color-text-danger)
  );
  --background-color-active: color-mix(
    in oklab,
    var(--l-color-bg-fill-danger-soft) 65%,
    var(--l-color-text-danger)
  );
  --text-color: var(--l-color-text-danger);
  --text-color-hover: var(--l-color-text-danger);
  --border-color: transparent;
  --border-color-hover: transparent;
}
}
@layer components {
  .l-button[data-press-effect]:active {
    scale: 0.98;
    translate: 0 1px;
  }

  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  Button size
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  .l-button[data-icon-only] {
    width: var(--height);
    font-size: var(--icon-only-size, 1.25em);
  }

  .l-button[data-size='sm'] {
    --height: var(--_button-size-sm);
    --padding-inline: 0.5rem;
    --font-size: var(--l-text-sm);
  }

  /*
   * Font-size stays at --l-text-sm (14px) across md/lg/xl: only height and
   * padding scale with the control size, so a taller button reads as a larger
   * touch target, not a louder label. Consumers who want a bigger label can
   * still override the --font-size knob. See RFC-button-font-size-lg-xl.
   */
  .l-button[data-size='lg'] {
    --height: var(--_button-size-lg);
    --padding-inline: 0.75rem;
  }

  .l-button[data-size='xl'] {
    --height: var(--_button-size-xl);
    --padding-inline: 0.875rem;
  }
}

/* checkbox.css */
/* Checkbox — `.l-checkbox` on a native `<input type="checkbox">`.

   The visual skin lives in the shared `checkbox-appearance.css` partial so it
   can be reused by Shadow-DOM elements that render their own checkbox (e.g.
   `<l-tree-item>` in `selection="multiple"`). The `.l-checkbox` class is the
   canonical primitive (works anywhere); `l-form-field` also auto-styles a bare
   checkbox via a zero-specificity `:where()` selector, so inside a field no
   class is needed (`unstyled` opts out). See the partial for details. */

/* Shared checkbox appearance — the visual skin behind `.l-checkbox`.

   Colocated with its `checkbox-appearance.styles.ts` wrapper so Shadow-DOM
   elements that render their own native checkbox (e.g. `<l-tree-item>` in
   `selection="multiple"`) can pull in the exact same look via `static styles`.
   The global light-DOM `checkbox.css` primitive `@import`s this file too, so
   both surfaces stay in sync. The global `.l-checkbox` class cannot pierce a
   shadow boundary, but the `--l-form-control-*` tokens this rule relies on DO
   inherit across shadow roots.

   The checkmark / indeterminate dash are drawn with a `background-image` SVG on
   the input itself — `::before`/`::after` do not paint on replaced elements
   like `<input>`. `background-image` can't read the host's `currentColor`, so
   the glyph color is baked white. This stays legible because the accent
   (`--l-form-control-activated-color`) is a stable color that does not invert
   between light and dark. Override `--checkmark` to swap the icon. */

@layer components {
  .l-checkbox,
  :where(l-form-field:not([unstyled])) > input[type='checkbox']:not([role='switch']) {
    /* Public knobs */
    --size: var(--l-form-control-toggle-size);
    --accent: var(--l-form-control-activated-color);
    --checkmark: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>');
    --_dash: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2.5" stroke-linecap="round"><line x1="5" y1="12" x2="19" y2="12"/></svg>');

    box-sizing: border-box;
    flex: 0 0 auto;
    inline-size: var(--size);
    block-size: var(--size);
    margin: 0;
    padding: 0;
    appearance: none;
    border: var(--l-form-control-border-width) solid var(--l-form-control-border-color);
    border-radius: calc(var(--size) * 0.2);
    background-color: var(--l-form-control-background-color);
    background-repeat: no-repeat;
    background-position: center;
    background-size: 75%;
    vertical-align: middle;
    cursor: pointer;
    transition-property: background-color, border-color;
    transition-duration: 150ms;

    &:checked,
    &:indeterminate {
      border-color: var(--accent);
      background-color: var(--accent);
    }

    &:checked {
      background-image: var(--checkmark);
    }

    &:indeterminate {
      background-image: var(--_dash);
    }

    @media (hover: hover) {
      &:hover:not(:disabled) {
        border-color: var(--l-form-control-border-color-hover);
      }
    }

    &:focus-visible {
      outline: 2px solid var(--l-focus-ring);
      outline-offset: 2px;
    }

    /* High contrast: the checked fill is forced to Canvas, hiding the accent and
       leaving the baked-white glyph invisible — pin the checked box to a system
       color so the checkmark/dash stays legible. */
    @media (forced-colors: active) {
      &:checked,
      &:indeterminate {
        border-color: Highlight;
        background-color: Highlight;
      }
    }

    /* Invalid: only after interaction (`:user-invalid`), or forced via
       `aria-invalid` (set by `l-form-field` / server-side validation).
       Overriding `--accent` makes a checked invalid box fill with the error
       color too (not just the border). */
    &:user-invalid,
    &[aria-invalid='true'] {
      --accent: var(--l-form-control-border-color-invalid);
      border-color: var(--l-form-control-border-color-invalid);
    }

    &:disabled {
      cursor: not-allowed;
      opacity: 0.4;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .l-checkbox,
    :where(l-form-field:not([unstyled])) > input[type='checkbox']:not([role='switch']) {
      transition-duration: 0ms;
    }
  }
}

/* Hover halo — light-DOM `.l-checkbox` only. Deliberately kept out of the shared
   `checkbox-appearance.css` partial: that partial is also loaded into Shadow-DOM
   consumers (e.g. `l-tree-item`), which never opted into a hover glow. Mirrors
   the radio and switch hover language (same `--_halo` flip, same ratio token). */

@layer components {
  .l-checkbox,
  :where(l-form-field:not([unstyled])) > input[type='checkbox']:not([role='switch']) {
    --_halo: var(--l-form-control-border-color-hover);
    transition-property: background-color, border-color, box-shadow;

    &:checked,
    &:indeterminate {
      --_halo: var(--accent);
    }

    @media (hover: hover) {
      &:hover:not(:disabled) {
        box-shadow: 0 0 0 calc(var(--size) * var(--l-form-control-toggle-halo-ratio))
          color-mix(in oklab, var(--_halo), transparent 84%);
      }
    }
  }
}

/* close-button/circle.css */
@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   🅲🅻🅾🆂🅴 🅱🆄🆃🆃🅾🅽
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  - https://www.benjystanton.co.uk/blog/accessible-close-buttons/
  */

  .l-close {
    --size: 36px;
    --icon-color: var(--l-color-text-primary);
    --icon-size: 24px;

    display: grid;
    place-items: center;
    width: var(--size);
    height: var(--size);
    aspect-ratio: 1;
    cursor: pointer;

    &:focus-visible {
      outline: 2px solid var(--l-focus-ring);
      outline-offset: 2px;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆄🅸: close icon
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    &::after {
      content: '';
      display: block;
      height: var(--icon-size);
      width: var(--icon-size);
      background-color: var(--icon-color);
      /* https://pictogrammers.com/library/mdi/icon/close */
      mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" /></svg>')
        center no-repeat;
    }
  }
}

@layer components {
  .l-close:not([data-appearance]),
  .l-close[data-appearance='circle'] {
    --size: 44px;
    --background-color: light-dark(var(--l-color-gray-100), var(--l-color-gray-700));
    --background-color-hover: light-dark(var(--l-color-gray-200), var(--l-color-gray-600));

    border-radius: var(--l-radius-full);
    background-color: var(--background-color);

    transition-property: background-color;
    transition-duration: 150ms;

    &:hover {
      background-color: var(--background-color-hover);
    }

    &:active {
      background-color: color-mix(in oklab, var(--background-color-hover) 80%, black);
    }
  }
}

/* close-button/ring.css */
@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   🅲🅻🅾🆂🅴 🅱🆄🆃🆃🅾🅽
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  - https://www.benjystanton.co.uk/blog/accessible-close-buttons/
  */

  .l-close {
    --size: 36px;
    --icon-color: var(--l-color-text-primary);
    --icon-size: 24px;

    display: grid;
    place-items: center;
    width: var(--size);
    height: var(--size);
    aspect-ratio: 1;
    cursor: pointer;

    &:focus-visible {
      outline: 2px solid var(--l-focus-ring);
      outline-offset: 2px;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆄🅸: close icon
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    &::after {
      content: '';
      display: block;
      height: var(--icon-size);
      width: var(--icon-size);
      background-color: var(--icon-color);
      /* https://pictogrammers.com/library/mdi/icon/close */
      mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" /></svg>')
        center no-repeat;
    }
  }
}

@layer components {
  .l-close:not([data-appearance]),
  .l-close[data-appearance='ring'] {
    --ring-color: var(--l-color-border-interactive);
    --ring-tickness: 0.25rem;

    /* Private custom properties */
    --_ring-tickness: 0;
    --_ring-color: var(--ring-color);

    border-radius: var(--l-radius-full);
    box-shadow: 0 0 0 var(--_ring-tickness) var(--_ring-color);

    &:hover {
      --_ring-tickness: var(--ring-tickness);

      @media (prefers-reduced-motion: no-preference) {
        transition-duration: 250ms;
      }
    }

    &:active {
      --_ring-color: --darken(var(--ring-color), 5%);
      --_ring-tickness: calc(var(--ring-tickness) * 0.6);
    }
  }
}

/* close-button/square.css */
@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   🅲🅻🅾🆂🅴 🅱🆄🆃🆃🅾🅽
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  - https://www.benjystanton.co.uk/blog/accessible-close-buttons/
  */

  .l-close {
    --size: 36px;
    --icon-color: var(--l-color-text-primary);
    --icon-size: 24px;

    display: grid;
    place-items: center;
    width: var(--size);
    height: var(--size);
    aspect-ratio: 1;
    cursor: pointer;

    &:focus-visible {
      outline: 2px solid var(--l-focus-ring);
      outline-offset: 2px;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆄🅸: close icon
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    &::after {
      content: '';
      display: block;
      height: var(--icon-size);
      width: var(--icon-size);
      background-color: var(--icon-color);
      /* https://pictogrammers.com/library/mdi/icon/close */
      mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z" /></svg>')
        center no-repeat;
    }
  }
}

@layer components {
  .l-close:not([data-appearance]),
  .l-close[data-appearance='square'] {
    --background-color: transparent;
    --background-color-hover: light-dark(var(--l-color-gray-100), var(--l-color-gray-700));

    border-radius: var(--l-radius-sm);
    background-color: var(--background-color);

    transition-property: background-color;
    transition-duration: 150ms;

    &:hover {
      background-color: var(--background-color-hover);
    }

    &:active {
      background-color: color-mix(in oklab, var(--background-color-hover) 80%, black);
    }
  }
}

/* disclosure.css */
@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   Base
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  .l-disclosure {
    --marker-size: 20px;
    --marker-color: var(--l-color-text-tertiary);

    --_duration: 200ms;
    --_easing: cubic-bezier(0.44, 0.36, 0.04, 1);

    interpolate-size: allow-keywords;

    /* Hide default marker */
    & summary::-webkit-details-marker {
      display: none;
    }

    & summary {
      display: flex;
      align-items: center;
      justify-content: space-between;
      padding: calc(var(--spacing) * 3) calc(var(--spacing) * 4);
      font-weight: 600;
      font-size: var(--l-text-base);
      line-height: 1;
      color: var(--l-color-text-primary);
      list-style: none;
      cursor: pointer;

      &:hover {
        background-color: color-mix(
          in oklab,
          var(--l-color-surface) 95%,
          var(--l-color-text-primary)
        );
      }

      &:focus-visible {
        outline: 2px solid var(--l-focus-ring);
        outline-offset: -2px;
      }
    }

    & > :last-child:not(summary) {
      padding: calc(var(--spacing) * 4);
      color: var(--l-color-text-secondary);
    }

    /* Animate with ::details-content */
    & ::details-content {
      transition:
        content-visibility var(--_duration) var(--_easing) allow-discrete,
        block-size var(--_duration) var(--_easing);
      block-size: 0;
    }

    &[open] ::details-content {
      block-size: auto;
    }
  }

  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   State: disabled
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  .l-disclosure:has(summary[disabled]),
  .l-disclosure[disabled] {
    & summary {
      cursor: not-allowed;
      pointer-events: none;
      opacity: 0.4;
    }
  }

  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   Variant: bordered
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  .l-disclosure[data-variant='bordered'] {
    --border-radius: var(--l-radius-md);
    --border-color: var(--l-color-border);

    background-color: var(--l-color-surface);
    border: 1px solid var(--border-color);
    border-radius: var(--border-radius);
    overflow: clip;
  }

  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   Marker
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  .l-disclosure[data-marker] summary::after {
    content: '';
    display: block;
    height: var(--marker-size);
    width: var(--marker-size);
    flex-shrink: 0;
    transition: rotate var(--_duration) var(--_easing);
  }

  /* arrow */
  .l-disclosure[data-marker='arrow'] summary::after {
    background-color: var(--marker-color);
    /* mdi:chevron-down */
    mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z"/></svg>')
      center no-repeat;
    rotate: 0deg;
  }

  .l-disclosure[data-marker='arrow'][open] summary::after {
    rotate: 180deg;
  }

  /* plus */
  .l-disclosure[data-marker='plus'] summary::after {
    background-color: var(--marker-color);
    /* mdi:plus */
    mask: url("data:image/svg+xml;charset=utf-8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z'/></svg>")
      center no-repeat;
    rotate: 0deg;
  }

  .l-disclosure[data-marker='plus'][open] summary::after {
    rotate: 45deg;
  }
}

/* divider.css */
@layer components {
  l-divider {
    --color: var(--l-color-divider);
    --thickness: 1px;
    --spacing: var(--l-spacing-4, 1rem);

    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  /* Line */
  l-divider::before {
    position: absolute;
    display: block;
    content: '';
    background-color: var(--color);
    z-index: 1;
  }

  /* Label text */
  l-divider[label]::after {
    content: attr(label);
    position: relative;
    display: block;
    font-size: 0.875rem;
    line-height: 1.25rem;
    font-weight: 600;
    background-color: var(--l-color-surface);
    color: var(--l-color-text-tertiary);
    z-index: 2;
  }

  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   horizontal (default)
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  l-divider:not([orientation='vertical']) {
    margin-block: var(--spacing);
    width: 100%;
  }

  l-divider:not([orientation='vertical'])::before {
    height: var(--thickness);
    inset: calc(50% - var(--thickness) / 2) 0 0;
  }

  l-divider:not([orientation='vertical'])[label]::after {
    padding-inline: var(--l-spacing-2, 0.5rem);
  }

  /* Compact form when slotted into <l-dropdown>: bleed to panel edges, tighten spacing. */
  l-dropdown l-divider:not([orientation='vertical']) {
    margin-block: var(--padding, 0.25rem);
    margin-inline: calc(var(--padding, 0.25rem) * -1);
    width: auto;
  }

  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   vertical
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  l-divider[orientation='vertical'] {
    padding: 0 4px;
    margin-inline: var(--spacing);
    height: 100%;
  }

  l-divider[orientation='vertical']::before {
    width: var(--thickness);
    inset: 0 0 0 calc(50% - var(--thickness) / 2);
  }

  l-divider[orientation='vertical'][label]::after {
    padding-block: var(--l-spacing-2, 0.5rem);
  }
}

/* form-field.css */
/* Form field — `l-form-field` wraps a label, a control, and optional
   `.l-hint` / `.l-error` messages. The element wires ARIA (id/for,
   aria-describedby, aria-invalid, …) and reflects `layout`,
   `required` / `optional`, and `invalid`. This file only
   styles the anatomy; the control keeps its own styling (`.l-checkbox`, …). */

@layer components {
  l-form-field {
    display: grid;
    gap: var(--l-form-field-gap);
    /* Controls size to their content; text controls (`.l-input`, `.l-textarea`)
       opt into full width via their own CSS — the field never forces width. */
    justify-items: start;

    & > label {
      color: var(--l-form-control-label-color);
      font-size: var(--l-text-sm);
      font-weight: var(--l-font-weight-medium);
    }

    /* Required marker on the label. */
    &[required] > label::after {
      content: var(--l-form-control-required-content);
      margin-inline-start: 0.25ch;
      color: var(--l-form-control-required-color);
    }

    /* Inline layout for toggle controls (checkbox / radio / switch):
       control on the left, label on the right, messages below the label. */
    &[layout='inline'] {
      grid-template-columns: auto 1fr;
      column-gap: var(--l-form-field-choice-gap);
      align-items: center;

      & > :is(input, select, textarea) {
        grid-column: 1;
        grid-row: 1;
      }

      & > label {
        grid-column: 2;
        grid-row: 1;
        cursor: pointer;
      }

      & > :is(.l-hint, .l-error) {
        grid-column: 2;
      }
    }
  }

  .l-hint {
    display: block;
    margin: 0;
    color: var(--l-form-control-hint-color);
    font-size: var(--l-text-sm);
  }

  .l-error {
    display: block;
    margin: 0;
    color: var(--l-form-control-error-color);
    font-size: var(--l-text-sm);
  }

  /* `.l-error` is a standalone message too (e.g. a radio fieldset or a custom
     control the field can't auto-wire): visible by default, hidden via the
     `hidden` attribute the author toggles directly. */
  .l-error[hidden] {
    display: none;
  }

  /* Inside a field, the field owns the error's visibility through its reflected
     `invalid` state — no JS needed for the resting state. Hidden by default, so
     a `.l-error` never flashes on load (even before the element upgrades, or if
     its script never runs), and revealed only once the field is invalid. */
  l-form-field .l-error {
    display: none;
  }

  l-form-field[invalid] .l-error {
    display: block;
  }
}

/* input-otp.css */
@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   🅸🅽🅿🆄🆃 🅾🆃🅿
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  Stripe-style OTP input: a single hidden <input> handles keyboard, paste,
  and autocomplete while visual cells render each digit with individual borders.

  Usage:
    <l-input-otp>
      <input type="text" inputmode="numeric"
             autocomplete="one-time-code" maxlength="6" />
    </l-input-otp>

  @scope keeps these rules from leaking OUT, and the reset below strips UA default
  chrome off the generated cells / separators / hidden input so the component styles
  from a clean slate. Caveat: this lives in @layer components, so it only shields the
  internals from UA defaults and lower-layer rules — *unlayered* host-page rules (a
  CSS reset, Tailwind preflight, naked `input {}`) still outrank every cascade layer
  and are NOT neutralized here.
  */

  @scope (l-input-otp) {
    /* Reset — :where() keeps specificity at (0,0,0) so the component rules below win
       over it. Strips UA default styling off the generated internals for a clean
       slate. Custom properties are NOT affected by `all` — design tokens cascade
       through. :scope is excluded — the host is a unique custom element name and gets
       its styles from the :scope blocks below without interference. */
    *:where(:not(img, svg, l-icon):not(svg *)),
    *::before,
    *::after {
      all: unset;
      display: revert;
    }

    :scope {
      --digits: 6;
      --cell-size: 2.75rem;
      --cell-bg-color: color-mix(in oklab, var(--l-color-text-primary) 4%, var(--l-color-surface));
      --cell-border-color: var(--l-color-border);
      --cell-border-radius: var(--l-radius-md);
      --cell-focus-color: var(--l-focus-ring);
      --cell-focus-ring: 0 0 0 1px var(--cell-focus-color);
      --_cell-font:
        ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono',
        monospace;

      display: inline-block;
      position: relative;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅲🅴🅻🅻🆂 🅲🅾🅽🆃🅰🅸🅽🅴🆁
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    .l-input-otp-cells {
      display: inline-flex;
      align-items: center;
      gap: var(--cell-gap, 0.5rem);
      position: relative;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅲🅴🅻🅻
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    .l-input-otp-cell {
      display: flex;
      align-items: center;
      justify-content: center;
      inline-size: var(--cell-size);
      block-size: var(--cell-size);
      border: 1px solid var(--cell-border-color);
      border-radius: var(--cell-border-radius);
      background: var(--cell-bg-color);
      font-family: var(--_cell-font);
      font-size: calc(var(--cell-size) * 0.45);
      font-variant-numeric: tabular-nums;
      line-height: 1;
      color: var(--l-color-text-primary);
      transition:
        border-color 150ms ease,
        box-shadow 150ms ease;
      pointer-events: none;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅰🅲🆃🅸🆅🅴 🅲🅴🅻🅻 (focus highlight)
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    :scope:focus-within .l-input-otp-cell[data-active] {
      border-color: var(--cell-focus-color);
      box-shadow: var(--cell-focus-ring);
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅲🅰🆁🅴🆃 (fake blinking caret in active empty cell)
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    The native caret is hidden via `caret-color: transparent`; this stand-in
    gives the missing point-of-insertion cue inside the active cell. Only
    visible while empty — once a digit is typed, it replaces the caret.
    */

    :scope:focus-within .l-input-otp-cell[data-active]:not([data-filled])::after {
      content: '';
      inline-size: 1px;
      block-size: 1em;
      background: currentColor;
      animation: l-input-otp-caret 1s steps(2, jump-none) infinite;
    }

    @media (prefers-reduced-motion: reduce) {
      :scope:focus-within .l-input-otp-cell[data-active]:not([data-filled])::after {
        animation: none;
      }
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆂🅴🅿🅰🆁🅰🆃🅾🆁
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    .l-input-otp-separator {
      display: flex;
      align-items: center;
      color: var(--l-color-text-tertiary);
      padding-inline: 0.125rem;
      pointer-events: none;

      &::before {
        content: '\2013'; /* en-dash */
      }
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅷🅸🅳🅳🅴🅽 🅸🅽🅿🆄🆃
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    The native input covers the entire cell area so clicks focus it naturally.
    It is visually transparent — the cells provide all visual feedback.
    */

    :scope:defined .l-input-otp-cells > input {
      position: absolute;
      inset: 0;
      inline-size: 100%;
      block-size: 100%;
      opacity: 0;
      border: 0;
      padding: 0;
      margin: 0;
      caret-color: transparent;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆂🅸🆉🅴🆂
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    :scope[size='sm'] {
      --cell-size: 2rem;
    }

    :scope[size='lg'] {
      --cell-size: 3.5rem;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅳🅸🆂🅰🅱🅻🅴🅳
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    :scope:has(input:disabled) .l-input-otp-cell {
      opacity: 0.4;
      cursor: not-allowed;
      color: var(--l-color-text-disabled);
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅿🆁🅾🅶🆁🅴🆂🆂🅸🆅🅴 🅴🅽🅷🅰🅽🅲🅴🅼🅴🅽🆃 🅵🅰🅻🅻🅱🅰🅲🅺
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    Pre-upgrade, the real <input> stays visible and usable with no JS — styled as
    a single field with the cell tokens. The host still reserves the exact box the
    cells will occupy (width scales with --digits / --cell-size / --cell-gap) so
    layout doesn't shift on hydration.
    */

    :scope:not(:defined) {
      display: inline-block;
      flex-shrink: 0;
      inline-size: calc(
        var(--cell-size) * var(--digits) + var(--cell-gap, 0.5rem) * (var(--digits) - 1)
      );
      block-size: var(--cell-size);
    }

    :scope:not(:defined) > input {
      box-sizing: border-box;
      inline-size: 100%;
      block-size: 100%;
      padding-inline: calc(var(--cell-size) * 0.3);
      border: 0;
      border-radius: var(--cell-border-radius);
      background: var(--cell-bg-color);
      color: var(--l-color-text-primary);
      font-family: var(--_cell-font);
      font-size: calc(var(--cell-size) * 0.45);
      font-variant-numeric: tabular-nums;
      letter-spacing: 0.25em;
      text-align: center;
    }

    /* Swap the UA focus ring for the Luxen outline ring. Inset (like input-stepper)
       so the filled field's ring is never clipped by a tight container. */
    :scope:not(:defined) > input:focus-visible {
      outline: 2px solid var(--l-focus-ring);
      outline-offset: -2px;
    }
  }

  @keyframes l-input-otp-caret {
    50% {
      opacity: 0;
    }
  }
}

/* input-stepper/default.css */
@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   🅸🅽🅿🆄🆃 🆂🆃🅴🅿🅿🅴🆁
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  - https://www.nngroup.com/articles/input-steppers/

  @scope keeps these rules from leaking OUT, and the reset below strips UA default
  chrome off the generated buttons / input / track divs so the component styles from
  a clean slate. Caveat: this lives in @layer components, so it only shields the
  internals from UA defaults and lower-layer rules — *unlayered* host-page rules (a
  CSS reset, Tailwind preflight, naked `button {}`) still outrank every cascade layer
  and are NOT neutralized here.
  */

  @scope (l-input-stepper) {
    /* Reset — :where() keeps specificity at (0,0,0) so the component rules below win
       over it. Strips UA default styling off the generated internals for a clean
       slate. Custom properties are NOT affected by `all` — design tokens cascade
       through. :scope is excluded — the host is a unique custom element name and gets
       its styles from the base + appearance :scope blocks without interference. */
    *:where(:not(img, svg, l-icon):not(svg *)),
    *::before,
    *::after {
      all: unset;
      display: revert;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅷🅾🆂🆃
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    :scope {
      --_button-size: var(--l-size-control-md);
      --border-color: var(--l-form-control-border-color);
      --border-radius: var(--l-form-control-border-radius);
      --_button-bg-hover: var(--l-color-bg-fill-neutral-soft);
      --_button-bg-active: var(--l-color-bg-fill-neutral-subtle);

      display: inline-flex;
      align-items: stretch;
      white-space: nowrap;
      box-sizing: border-box;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅵🅾🆄🅲🅴 🅿🆁🅴🆅🅴🅽🆃🅸🅾🅽
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    Reserve button space before JS loads to avoid layout shift when buttons mount.
    */

    :scope:not(:defined) {
      padding-inline: var(--_button-size);
    }

    :scope:defined {
      padding-inline: 0;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆂🅸🆉🅴🆂
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    :scope[size='xs'] {
      --_button-size: var(--l-size-control-xs);
    }
    :scope[size='sm'] {
      --_button-size: var(--l-size-control-sm);
    }
    :scope[size='lg'] {
      --_button-size: var(--l-size-control-lg);
    }
    :scope[size='xl'] {
      --_button-size: var(--l-size-control-xl);
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅱🆄🆃🆃🅾🅽🆂
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    button {
      padding: 0;
      appearance: none;
      background: none;
      border: 0;
      font-family: inherit;
      font-size: inherit;
      width: var(--_button-size);
      aspect-ratio: 1;
      cursor: pointer;
      touch-action: manipulation;
      -webkit-tap-highlight-color: transparent;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    button:focus-visible {
      outline: 2px solid var(--l-focus-ring);
      outline-offset: -2px;
      z-index: 1;
    }

    button:disabled {
      cursor: not-allowed;
      opacity: 0.4;
    }

    @media (hover: hover) {
      button:hover:not(:disabled) {
        background-color: var(--_button-bg-hover);
      }
    }

    button:active:not(:disabled) {
      background-color: var(--_button-bg-active);
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅸🅽🅿🆄🆃
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    input {
      appearance: textfield;
      box-sizing: border-box;
      font-family: inherit;
      font-size: inherit;
      font-variant-numeric: tabular-nums;
      border: 0;
      text-align: center;
      width: calc(3ch + var(--_button-size) * 2 / 3);
      min-width: 0;
      padding-inline: calc(var(--_button-size) / 3);
      align-self: stretch;
      background: transparent;
      color: inherit;
      caret-shape: underscore;
    }

    input:disabled {
      cursor: not-allowed;
      color: var(--l-color-text-disabled);
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆃🆁🅰🅲🅺 (animated number display)
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    .l-input-stepper-value {
      position: relative;
      display: flex;
      align-items: center;
      min-width: 0;
    }

    .l-input-stepper-track-display {
      position: absolute;
      inset: 0;
      height: var(--_button-size);
      overflow: hidden;
      pointer-events: none;
      font-variant-numeric: tabular-nums;
      background: var(--_track-bg, var(--l-color-surface));
    }

    .l-input-stepper-track {
      transition: translate 0.26s ease-out;
    }

    .l-input-stepper-track div {
      display: flex;
      align-items: center;
      justify-content: center;
      height: var(--_button-size);
    }

    .l-input-stepper-value:focus-within .l-input-stepper-track-display {
      visibility: hidden;
    }

    :scope:not([with-roller]) .l-input-stepper-track-display {
      display: none;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅳🅸🆂🅰🅱🅻🅴🅳
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    :scope:has(input:disabled) {
      color: var(--l-color-text-disabled);
    }

    /* Invalid — mirror the shared form control invalid border (set by
       `l-form-field` via `aria-invalid`, or authored directly). */
    :scope:has(input[aria-invalid='true']) {
      --border-color: var(--l-form-control-border-color-invalid);
    }
  }

  /* Hide native spinners — kept outside @scope because Safari ignores
     ::-webkit-*-spin-button rules nested inside @scope blocks. */
  l-input-stepper input::-webkit-inner-spin-button,
  l-input-stepper input::-webkit-outer-spin-button {
    appearance: none;
    display: none;
    margin: 0;
  }
}

@layer components {
  @scope (l-input-stepper:not([appearance]), l-input-stepper[appearance='default']) {
    :scope {
      border: 1px solid var(--border-color);
      border-radius: var(--border-radius);
      background-color: var(--l-form-control-background-color);
      min-height: var(--_button-size);
    }

    button:first-of-type {
      border-radius: var(--border-radius) 0 0 var(--border-radius);
    }

    button:last-of-type {
      border-radius: 0 var(--border-radius) var(--border-radius) 0;
    }

    :scope:has(input:disabled) {
      --_track-bg: var(--l-color-bg-disabled);
      border-color: var(--l-color-border-disabled);
      background-color: var(--l-color-bg-disabled);
    }
  }

  /* FOUCE: match visual appearance before JS loads.
     min-height adds 2px to account for the 1px border on each side,
     since no buttons exist to push the content area beyond min-height. */
  @scope (l-input-stepper:not(:defined):not([appearance]), l-input-stepper:not(:defined)[appearance='default']) {
    :scope {
      border: 1px solid var(--border-color);
      border-radius: var(--border-radius);
      background-color: var(--l-form-control-background-color);
      min-height: calc(var(--_button-size) + 2px);
    }
  }
}

/* input-stepper/rounded.css */
@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   🅸🅽🅿🆄🆃 🆂🆃🅴🅿🅿🅴🆁
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  - https://www.nngroup.com/articles/input-steppers/

  @scope keeps these rules from leaking OUT, and the reset below strips UA default
  chrome off the generated buttons / input / track divs so the component styles from
  a clean slate. Caveat: this lives in @layer components, so it only shields the
  internals from UA defaults and lower-layer rules — *unlayered* host-page rules (a
  CSS reset, Tailwind preflight, naked `button {}`) still outrank every cascade layer
  and are NOT neutralized here.
  */

  @scope (l-input-stepper) {
    /* Reset — :where() keeps specificity at (0,0,0) so the component rules below win
       over it. Strips UA default styling off the generated internals for a clean
       slate. Custom properties are NOT affected by `all` — design tokens cascade
       through. :scope is excluded — the host is a unique custom element name and gets
       its styles from the base + appearance :scope blocks without interference. */
    *:where(:not(img, svg, l-icon):not(svg *)),
    *::before,
    *::after {
      all: unset;
      display: revert;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅷🅾🆂🆃
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    :scope {
      --_button-size: var(--l-size-control-md);
      --border-color: var(--l-form-control-border-color);
      --border-radius: var(--l-form-control-border-radius);
      --_button-bg-hover: var(--l-color-bg-fill-neutral-soft);
      --_button-bg-active: var(--l-color-bg-fill-neutral-subtle);

      display: inline-flex;
      align-items: stretch;
      white-space: nowrap;
      box-sizing: border-box;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅵🅾🆄🅲🅴 🅿🆁🅴🆅🅴🅽🆃🅸🅾🅽
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    Reserve button space before JS loads to avoid layout shift when buttons mount.
    */

    :scope:not(:defined) {
      padding-inline: var(--_button-size);
    }

    :scope:defined {
      padding-inline: 0;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆂🅸🆉🅴🆂
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    :scope[size='xs'] {
      --_button-size: var(--l-size-control-xs);
    }
    :scope[size='sm'] {
      --_button-size: var(--l-size-control-sm);
    }
    :scope[size='lg'] {
      --_button-size: var(--l-size-control-lg);
    }
    :scope[size='xl'] {
      --_button-size: var(--l-size-control-xl);
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅱🆄🆃🆃🅾🅽🆂
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    button {
      padding: 0;
      appearance: none;
      background: none;
      border: 0;
      font-family: inherit;
      font-size: inherit;
      width: var(--_button-size);
      aspect-ratio: 1;
      cursor: pointer;
      touch-action: manipulation;
      -webkit-tap-highlight-color: transparent;
      display: flex;
      align-items: center;
      justify-content: center;
    }

    button:focus-visible {
      outline: 2px solid var(--l-focus-ring);
      outline-offset: -2px;
      z-index: 1;
    }

    button:disabled {
      cursor: not-allowed;
      opacity: 0.4;
    }

    @media (hover: hover) {
      button:hover:not(:disabled) {
        background-color: var(--_button-bg-hover);
      }
    }

    button:active:not(:disabled) {
      background-color: var(--_button-bg-active);
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅸🅽🅿🆄🆃
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    input {
      appearance: textfield;
      box-sizing: border-box;
      font-family: inherit;
      font-size: inherit;
      font-variant-numeric: tabular-nums;
      border: 0;
      text-align: center;
      width: calc(3ch + var(--_button-size) * 2 / 3);
      min-width: 0;
      padding-inline: calc(var(--_button-size) / 3);
      align-self: stretch;
      background: transparent;
      color: inherit;
      caret-shape: underscore;
    }

    input:disabled {
      cursor: not-allowed;
      color: var(--l-color-text-disabled);
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆃🆁🅰🅲🅺 (animated number display)
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    .l-input-stepper-value {
      position: relative;
      display: flex;
      align-items: center;
      min-width: 0;
    }

    .l-input-stepper-track-display {
      position: absolute;
      inset: 0;
      height: var(--_button-size);
      overflow: hidden;
      pointer-events: none;
      font-variant-numeric: tabular-nums;
      background: var(--_track-bg, var(--l-color-surface));
    }

    .l-input-stepper-track {
      transition: translate 0.26s ease-out;
    }

    .l-input-stepper-track div {
      display: flex;
      align-items: center;
      justify-content: center;
      height: var(--_button-size);
    }

    .l-input-stepper-value:focus-within .l-input-stepper-track-display {
      visibility: hidden;
    }

    :scope:not([with-roller]) .l-input-stepper-track-display {
      display: none;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅳🅸🆂🅰🅱🅻🅴🅳
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    :scope:has(input:disabled) {
      color: var(--l-color-text-disabled);
    }

    /* Invalid — mirror the shared form control invalid border (set by
       `l-form-field` via `aria-invalid`, or authored directly). */
    :scope:has(input[aria-invalid='true']) {
      --border-color: var(--l-form-control-border-color-invalid);
    }
  }

  /* Hide native spinners — kept outside @scope because Safari ignores
     ::-webkit-*-spin-button rules nested inside @scope blocks. */
  l-input-stepper input::-webkit-inner-spin-button,
  l-input-stepper input::-webkit-outer-spin-button {
    appearance: none;
    display: none;
    margin: 0;
  }
}

@layer components {
  @scope (l-input-stepper[appearance='rounded']) {
    :scope {
      gap: 0.25rem;
    }

    button {
      border: 1px solid var(--border-color);
      border-radius: var(--l-radius-full);
      background-color: var(--l-form-control-background-color);
    }

    @media (hover: hover) {
      button:hover:not(:disabled) {
        border-color: var(--l-color-text-primary);
        background-color: transparent;
      }
    }

    button:active:not(:disabled) {
      border-color: var(--l-color-text-primary);
      background-color: var(--_button-bg-active);
    }

    button:disabled {
      border-color: var(--l-color-border-disabled);
    }
  }
}

/* input.css */
/* Input — `.l-input` on a native text-like `<input>` (text, search, number,
   password, email, url, tel, date, time, …).

   Like `.l-checkbox`, the class is the canonical primitive and works anywhere.
   Two contexts auto-style a bare text input with no class needed:
   - `:where(l-form-field:not([unstyled])) > input` (type-filtered to text-like
     types so `range`/`color`/`file`/buttons keep their native look);
   - `:where(l-input-group) > input` — see the group block below.

   Adornments (leading icon, trailing unit, buttons) can't live inside a
   replaced `<input>`, so wrap the control in `<l-input-group>`: the group owns
   the border and the inner input becomes borderless. Layout is pure CSS (DOM
   order = visual order); the element's JS only adds behavior on top
   (`password-toggle`, click-to-focus).

   Native date/time pickers and the search clear button are restyled in place:
   the `::-webkit-calendar-picker-indicator` and `::-webkit-search-cancel-button`
   are recolored and masked with a Lucide glyph (overridable via
   `--calendar-icon` / `--clock-icon` / `--clear-icon`). This is CSS-only —
   Firefox simply keeps its default native controls. */

@layer components {
  .l-input,
  :where(l-form-field:not([unstyled]))
    > input:where(
      :not([type]),
      [type='text'],
      [type='search'],
      [type='email'],
      [type='url'],
      [type='tel'],
      [type='password'],
      [type='number'],
      [type='date'],
      [type='time'],
      [type='datetime-local'],
      [type='month'],
      [type='week']
    ),
  :where(l-input-group) > input {
    /* Public knobs */
    --height: var(--l-form-control-height);
    --border-radius: var(--l-form-control-border-radius);
    /* https://lucide.dev/icons/calendar */
    --calendar-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 2v4"/><path d="M16 2v4"/><rect width="18" height="18" x="3" y="4" rx="2"/><path d="M3 10h18"/></svg>');
    /* https://lucide.dev/icons/clock */
    --clock-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>');
    /* https://lucide.dev/icons/circle-x */
    --clear-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="m15 9-6 6"/><path d="m9 9 6 6"/></svg>');

    box-sizing: border-box;
    inline-size: 100%;
    block-size: var(--height);
    margin: 0;
    padding-block: 0;
    padding-inline: var(--l-form-control-padding-inline);
    appearance: none;
    border: var(--l-form-control-border-width) solid var(--l-form-control-border-color);
    border-radius: var(--border-radius);
    background-color: var(--l-form-control-background-color);
    color: var(--l-form-control-value-color);
    font: inherit;
    line-height: normal;
    transition-property: border-color, box-shadow, background-color;
    transition-duration: 150ms;

    &::placeholder {
      color: var(--l-form-control-placeholder-color);
      opacity: 1; /* Firefox dims placeholders by default */
    }

    /* `:where()` keeps the disabled guard at zero specificity so the focus and
       invalid rules below (equal specificity, later in source) win over hover. */
    @media (hover: hover) {
      &:hover:where(:not(:disabled)) {
        border-color: var(--l-form-control-border-color-hover);
      }
    }

    /* Focus: the border takes the focus-ring color. The soft halo lives in a
       separate rule below (kept off the group's inner input). The transparent
       outline keeps a visible indicator under forced colors. */
    &:focus-visible {
      border-color: var(--l-focus-ring);
      outline: 2px solid transparent;
      outline-offset: 2px;
    }

    /* Invalid: red border + a faint danger wash. After interaction
       (`:user-invalid`) or forced via `aria-invalid` (set by `l-form-field` /
       server-side validation). Ordered after `:focus-visible` so the red border
       wins when a field is both focused and invalid. */
    &:user-invalid,
    &[aria-invalid='true'] {
      border-color: var(--l-form-control-border-color-invalid);
      background-color: color-mix(
        in oklab,
        var(--l-form-control-border-color-invalid) 6%,
        var(--l-form-control-background-color)
      );
    }

    /* Disabled: a solid greyed-out fill (shadcn-style) rather than fading the
       whole control with opacity. `-webkit-text-fill-color` overrides WebKit's
       forced grey on disabled inputs so the token color actually applies. */
    &:disabled {
      cursor: not-allowed;
      border-color: var(--l-form-control-disabled-border);
      background-color: var(--l-form-control-disabled-background);
      color: var(--l-form-control-disabled-color);
      -webkit-text-fill-color: var(--l-form-control-disabled-color);
    }

    &:disabled::placeholder {
      color: var(--l-form-control-disabled-color);
    }
  }

  /* Soft focus halo — on the standalone `.l-input` and a field-styled input
     only. NOT on a group's inner input: nesting `&` inside the base selector
     inflates its specificity (via `:is(.l-input, …)`), which would beat the
     group's reset and paint a halo around the inner input. The group renders
     its own halo on the wrapper instead. */
  :is(.l-input, :where(l-form-field:not([unstyled])) > input):focus-visible {
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--l-focus-ring) 22%, transparent);
  }

  :is(.l-input, :where(l-form-field:not([unstyled])) > input):is(
      :user-invalid,
      [aria-invalid='true']
    ):focus-visible {
    box-shadow: 0 0 0 3px
      color-mix(in oklab, var(--l-form-control-border-color-invalid) 22%, transparent);
  }

  /* Size — `data-size` maps the control height to the shared `--l-size-control-*`
     scale (default `md`). Only the height changes; the label and hint/error are
     unaffected. The group's inner input is excluded — `l-input-group[size]` sizes
     the wrapper instead. */
  :is(.l-input, :where(l-form-field:not([unstyled])) > input) {
    &[data-size='xs'] {
      --height: var(--l-size-control-xs);
    }
    &[data-size='sm'] {
      --height: var(--l-size-control-sm);
    }
    &[data-size='md'] {
      --height: var(--l-size-control-md);
    }
    &[data-size='lg'] {
      --height: var(--l-size-control-lg);
    }
    &[data-size='xl'] {
      --height: var(--l-size-control-xl);
    }
  }

  /*
   * Native picker / clear icons — recolored + masked. Overridable glyphs; the
   * color comes from `background-color: currentColor`, not the image. Applied
   * in every text-control context (class, field, group).
   */
  :is(
    .l-input,
    :where(l-form-field:not([unstyled]), l-input-group) > input
  )::-webkit-calendar-picker-indicator {
    inline-size: 1.15em;
    block-size: 1.15em;
    padding: 0;
    color: var(--l-form-control-placeholder-color);
    background-color: currentColor;
    background-image: none;
    cursor: pointer;
    /* 1em mask-size (not `contain`, which would fill the 1.15em hit area) keeps
       the glyph the same visual size as a 1em `<l-icon>` affix. */
    mask: var(--calendar-icon) center / 1em no-repeat;
  }

  :is(
      .l-input,
      :where(l-form-field:not([unstyled]), l-input-group) > input
    )[type='time']::-webkit-calendar-picker-indicator {
    mask-image: var(--clock-icon);
  }

  @media (hover: hover) {
    :is(
        .l-input,
        :where(l-form-field:not([unstyled]), l-input-group) > input
      )::-webkit-calendar-picker-indicator:hover {
      color: var(--l-form-control-value-color);
    }
  }

  /* Keep the editable date/time segments in the value color. */
  :is(.l-input, :where(l-form-field:not([unstyled]), l-input-group) > input):is(
      [type='date'],
      [type='time'],
      [type='datetime-local'],
      [type='month'],
      [type='week']
    )::-webkit-datetime-edit {
    color: var(--l-form-control-value-color);
  }

  :is(
      .l-input,
      :where(l-form-field:not([unstyled]), l-input-group) > input
    ):disabled::-webkit-datetime-edit {
    color: var(--l-color-text-disabled);
  }

  /* Native search clear — clears the field with zero JS (WebKit/Blink; Firefox
     has no cancel button but Escape clears the field natively). */
  :is(
      .l-input,
      :where(l-form-field:not([unstyled]), l-input-group) > input
    )[type='search']::-webkit-search-cancel-button {
    appearance: none;
    inline-size: 1.15em;
    block-size: 1.15em;
    color: var(--l-form-control-placeholder-color);
    background-color: currentColor;
    cursor: pointer;
    mask: var(--clear-icon) center / 1em no-repeat;
  }

  @media (hover: hover) {
    :is(
        .l-input,
        :where(l-form-field:not([unstyled]), l-input-group) > input
      )[type='search']::-webkit-search-cancel-button:hover {
      color: var(--l-form-control-value-color);
    }
  }

  /*
   * Input group — `<l-input-group>` wraps one text input plus leading/trailing
   * adornments. The group owns the border + chrome; the inner input is reset to
   * a bare field. Layout is DOM order: a child before the input is a leading
   * adornment, after is trailing. Any non-input child (an `<l-icon>`, a unit
   * `<span>`, a `<button>`) is styled as an adornment — no class needed.
   */
  l-input-group {
    /* Public knobs (mirror the input) */
    --height: var(--l-form-control-height);
    --border-radius: var(--l-form-control-border-radius);

    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    gap: 0.5ch;
    inline-size: 100%;
    block-size: var(--height);
    padding-inline: var(--l-form-control-padding-inline);
    border: var(--l-form-control-border-width) solid var(--l-form-control-border-color);
    border-radius: var(--border-radius);
    background-color: var(--l-form-control-background-color);
    color: var(--l-form-control-value-color);
    transition-property: border-color, box-shadow, background-color;
    transition-duration: 150ms;

    /* Size — `size` (reflected) maps the height to the shared scale, like the
       other custom controls (`l-input-stepper`, `l-input-otp`). */
    &[size='xs'] {
      --height: var(--l-size-control-xs);
    }
    &[size='sm'] {
      --height: var(--l-size-control-sm);
    }
    &[size='md'] {
      --height: var(--l-size-control-md);
    }
    &[size='lg'] {
      --height: var(--l-size-control-lg);
    }
    &[size='xl'] {
      --height: var(--l-size-control-xl);
    }

    /* The control inside drops its own chrome — the group provides it.
       `:is(input, .l-input)` outranks the `.l-input` base rule. */
    & > :is(input, .l-input) {
      flex: 1 1 auto;
      min-inline-size: 0;
      inline-size: auto;
      block-size: 100%;
      padding-inline: 0;
      border: 0;
      border-radius: 0;
      background: transparent;
    }

    /* The group owns the state visuals (focus halo, invalid wash); keep the
       inner input neutral so they don't render twice. */
    & > :is(input, .l-input):focus-visible {
      outline: 0;
      box-shadow: none;
    }
    & > :is(input, .l-input):is(:user-invalid, [aria-invalid='true'], :disabled) {
      background: transparent;
    }

    /* Stepping UI inside a group is the adornments' job (`l-input-stepper`
       exists for that) — native spinners would sit between value and unit. */
    & > input[type='number'] {
      appearance: textfield;
    }

    /* Adornment — any non-input child: icon, unit text, or button. */
    & > :not(input) {
      flex: 0 0 auto;
      display: inline-flex;
      align-items: center;
      color: var(--l-form-control-placeholder-color);
      white-space: nowrap;
      -webkit-user-select: none;
              user-select: none;
    }

    /* Interactive adornment (authored or the injected password toggle): a
       full-height 1:1 square hit area with a hover wash, bleeding into the
       group's inline padding so it sits flush against the edge — mirrors the
       stepper's buttons. */
    & > button {
      align-self: stretch;
      aspect-ratio: 1;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      margin: 0;
      padding: 0;
      appearance: none;
      border: 0;
      border-radius: calc(var(--border-radius) - var(--l-form-control-border-width));
      background: none;
      color: var(--l-form-control-placeholder-color);
      font: inherit;
      cursor: pointer;
      transition-property: background-color, color;
      transition-duration: 150ms;

      /* Bleed over the group padding and square off the inner corners so the
         hover wash follows the group's rounded edge. */
      &:first-child {
        margin-inline-start: calc(var(--l-form-control-padding-inline) * -1);
        border-start-end-radius: 0;
        border-end-end-radius: 0;
      }
      &:last-child {
        margin-inline-end: calc(var(--l-form-control-padding-inline) * -1);
        border-start-start-radius: 0;
        border-end-start-radius: 0;
      }

      @media (hover: hover) {
        &:hover:not(:disabled) {
          color: var(--l-form-control-value-color);
          background-color: var(--l-color-bg-fill-neutral-soft);
        }
      }

      &:active:not(:disabled) {
        background-color: var(--l-color-bg-fill-neutral-subtle);
      }

      &:focus-visible {
        outline: 2px solid var(--l-focus-ring);
        outline-offset: -2px;
        z-index: 1;
      }

      &:disabled {
        cursor: not-allowed;
        opacity: 0.4;
      }
    }

    @media (hover: hover) {
      &:hover:where(:not(:has(> input:disabled))) {
        border-color: var(--l-form-control-border-color-hover);
      }
    }

    /* Focus: the group border takes the focus-ring color + soft halo.
       `:has(> input:focus-visible)`, not `:focus-within` — a focused adornment
       button shows its own ring, not the group's. */
    &:has(> input:focus-visible) {
      border-color: var(--l-focus-ring);
      outline: 2px solid transparent;
      outline-offset: 2px;
      box-shadow: 0 0 0 3px color-mix(in oklab, var(--l-focus-ring) 22%, transparent);
    }

    /* Invalid: red border + faint danger wash. Ordered after focus so the red
       border wins when the field is both focused and invalid. */
    &:has(> input:user-invalid),
    &:has(> input[aria-invalid='true']) {
      border-color: var(--l-form-control-border-color-invalid);
      background-color: color-mix(
        in oklab,
        var(--l-form-control-border-color-invalid) 6%,
        var(--l-form-control-background-color)
      );
    }

    &:has(> input:user-invalid):has(> input:focus-visible),
    &:has(> input[aria-invalid='true']):has(> input:focus-visible) {
      box-shadow: 0 0 0 3px
        color-mix(in oklab, var(--l-form-control-border-color-invalid) 22%, transparent);
    }

    &:has(> input:disabled) {
      cursor: not-allowed;
      border-color: var(--l-form-control-disabled-border);
      background-color: var(--l-form-control-disabled-background);
      color: var(--l-form-control-disabled-color);
    }
  }

  /* Hide native spinners / Edge's reveal inside a group — kept top-level
     because Safari ignores some `::-webkit-*` pseudos in nested rules. */
  l-input-group > input[type='number']::-webkit-inner-spin-button,
  l-input-group > input[type='number']::-webkit-outer-spin-button {
    appearance: none;
    display: none;
    margin: 0;
  }

  /* Edge's native password reveal would duplicate the injected toggle. */
  l-input-group > input::-ms-reveal,
  l-input-group > input::-ms-clear {
    display: none;
  }

  @media (prefers-reduced-motion: reduce) {
    .l-input,
    :where(l-form-field:not([unstyled]), l-input-group) > input,
    l-input-group,
    l-input-group > button {
      transition-duration: 0ms;
    }
  }
}

/* kbd.css */
@layer components {
  kbd:has(> .l-kbd) {
    font: inherit;
  }

  .l-kbd {
    display: inline-block;
    padding-block: calc(var(--spacing) * 1);
    padding-inline: calc(var(--spacing) * 2);
    font-family: var(--l-font-mono);
    font-size: var(--l-text-xs);
    font-weight: 600;
    line-height: 1;
    text-box: trim-both cap alphabetic;
    color: var(--l-color-text-primary);
    background-color: var(--l-color-bg-fill-neutral-soft);
    border: 1px solid var(--l-color-border-interactive);
    border-bottom-width: 2px;
    border-radius: var(--l-radius-md);
  }
}

/* progress.css */
@keyframes progress-indeterminate {
  0% {
    transform: translateX(0) scaleX(0);
  }
  40% {
    transform: translateX(0) scaleX(0.4);
  }
  100% {
    transform: translateX(100%) scaleX(0.5);
  }
}

@keyframes progress-indeterminate-bounce {
  0% {
    transform: translateX(0%) scaleX(0.9);
  }

  25% {
    transform: translateX(100%) scaleX(1.1);
  }

  50% {
    transform: translateX(0%) scaleX(0.9);
  }

  75% {
    transform: translateX(100%) scaleX(1.1);
  }

  100% {
    transform: translateX(0%) scaleX(0.9);
  }
}

@keyframes progress-indeterminate-bounce-vertical {
  0% {
    transform: translateY(-20%);
  }

  50% {
    transform: translateY(100%);
  }

  100% {
    transform: translateY(-20%);
  }
}

@layer components {
  .l-progress {
    --size: 4px;
    --track-color: rgba(5, 114, 206, 0.2);
    --indicator-color: rgb(5, 114, 206);
    --indeterminate-animation: progress-indeterminate;
    /*--indeterminate-animation: attr(data-indeterminate-animation type(<custom-ident>), progress-indeterminate);*/

    position: relative;
    display: block;
    width: 100%;
    height: var(--size);
    border-radius: 2px;
    overflow: hidden;
  }

  .l-progress::-moz-progress-bar {
    background-color: var(--indicator-color);
  }

  .l-progress::-webkit-progress-bar {
    background-color: var(--track-color);
  }

  .l-progress::-webkit-progress-value {
    background-color: var(--indicator-color);
  }

  .l-progress:indeterminate::-moz-progress-bar {
    animation: var(--indeterminate-animation) 1s infinite linear;
    transform-origin: 0% 50%;
  }

  /*
    -webkit-progress-value do not accept @keyframes outside the shadow-root
    https://issues.chromium.org/issues/40417491
  */
  .l-progress:indeterminate::after {
    content: '';
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    background-color: var(--indicator-color);
    animation: var(--indeterminate-animation) 1s infinite linear;
    transform-origin: 0% 50%;
  }

  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   🆄🅸: vertical
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  .l-progress[data-orientation='vertical'] {
    --indeterminate-animation: progress-indeterminate-bounce-vertical;

    width: var(--size);
    height: 100%;
  }

  .l-progress[data-orientation='vertical']:indeterminate::after {
    height: 50%;
  }
}

/* prose-editor.css */
/*
 * Styles for the editable content of <l-prose-editor>.
 *
 * The ProseMirror editable element is rendered in light DOM (slotted), not the
 * shadow root, to work around contenteditable caret/selection bugs in Firefox
 * and WebKit inside shadow trees. So its styles live in this published CSS file
 * rather than the element's shadow styles. Import once globally:
 *
 *   @import 'luxen-ui/css/prose-editor';
 */
@layer components {
  l-prose-editor .ProseMirror {
    padding: var(--content-padding, 0.75rem 1rem);
    min-height: var(--content-min-height, 8rem);
    outline: none;
  }

  l-prose-editor .ProseMirror :first-child {
    margin-top: 0;
  }

  l-prose-editor .ProseMirror :last-child {
    margin-bottom: 0;
  }

  /* Block spacing & typography — baseline so content reads correctly without a
     typography framework. A consumer's `prose` class (e.g. Tailwind Typography)
     layers on top via the higher-priority utilities layer. */
  l-prose-editor .ProseMirror p {
    margin-block: 0.5em;
  }

  l-prose-editor .ProseMirror :is(h1, h2, h3, h4) {
    margin-block: 1em 0.5em;
    font-weight: 600;
    line-height: 1.25;
  }

  l-prose-editor .ProseMirror h1 {
    font-size: 1.5em;
  }

  l-prose-editor .ProseMirror h2 {
    font-size: 1.3em;
  }

  l-prose-editor .ProseMirror h3 {
    font-size: 1.15em;
  }

  l-prose-editor .ProseMirror :is(ul, ol) {
    margin-block: 0.5em;
    padding-inline-start: 1.5rem;
  }

  l-prose-editor .ProseMirror ul {
    list-style: disc;
  }

  l-prose-editor .ProseMirror ol {
    list-style: decimal;
  }

  l-prose-editor .ProseMirror li {
    margin-block: 0.25em;
  }

  /* Placeholder — Tiptap Placeholder extension */
  l-prose-editor .ProseMirror p.is-editor-empty:first-child::before {
    content: attr(data-placeholder);
    float: inline-start;
    height: 0;
    color: var(--placeholder-color, var(--l-color-text-tertiary));
    pointer-events: none;
  }

  /* Highlight mark */
  l-prose-editor .ProseMirror mark {
    border-radius: 0.2em;
    background-color: var(--l-color-bg-fill-warning-subtle);
    -webkit-box-decoration-break: clone;
            box-decoration-break: clone;
  }

  /* Links */
  l-prose-editor .ProseMirror a {
    color: var(--l-color-text-info);
    text-decoration: underline;
    cursor: pointer;
  }

  /* Blockquote */
  l-prose-editor .ProseMirror blockquote {
    border-inline-start: 3px solid var(--l-color-border);
    padding-inline-start: 1rem;
    color: var(--l-color-text-secondary);
  }

  /* Inline code & code blocks */
  l-prose-editor .ProseMirror code {
    border-radius: 0.25rem;
    padding: 0.125em 0.3em;
    background-color: var(--l-color-bg-fill-neutral-subtle);
    font-size: 0.9em;
  }

  l-prose-editor .ProseMirror pre {
    border-radius: var(--l-radius-md);
    padding: 0.75rem 1rem;
    background-color: var(--l-color-bg-fill-neutral-subtle);
    overflow-x: auto;
  }

  l-prose-editor .ProseMirror pre code {
    padding: 0;
    background: none;
  }

  /* Horizontal rule */
  l-prose-editor .ProseMirror hr {
    margin-block: 1rem;
    border: none;
    border-top: 1px solid var(--l-color-divider);
  }

  l-prose-editor .ProseMirror hr.ProseMirror-selectednode {
    border-top-color: var(--l-focus-ring);
  }
}

/* radio.css */
/* Radio — `.l-radio` on a native `<input type="radio">`.

   Mirrors the checkbox primitive (same `--l-form-control-*` tokens, same
   states) but reads as a single-choice control: the box is round and the
   selected state paints a centered dot instead of a checkmark. Native radios
   in the same `name` group already enforce single selection and roving focus,
   so there is no JS — this is a CSS-only `appearance: none` skin.

   The dot is drawn with a `background-image` SVG on the input itself —
   `::before`/`::after` do not paint on replaced elements like `<input>`.
   `background-image` can't read the host's `currentColor`, so the glyph color
   is baked white. This stays legible because the accent
   (`--l-form-control-activated-color`) is a stable color that does not invert
   between light and dark. Override `--dot` to swap the icon.

   `l-form-field` auto-styles a bare radio via a zero-specificity `:where()`
   selector, so inside a field no class is needed (`unstyled` opts out). */

@layer components {
  .l-radio,
  :where(l-form-field:not([unstyled])) > input[type='radio'] {
    /* Public knobs */
    --size: var(--l-form-control-toggle-size);
    --accent: var(--l-form-control-activated-color);
    --dot: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="white"><circle cx="12" cy="12" r="6"/></svg>');
    /* Hover-halo tint — neutral when unselected, accent when selected (set
       below); mirrors the switch so the toggle family shares one hover language. */
    --_halo: var(--l-form-control-border-color-hover);

    box-sizing: border-box;
    flex: 0 0 auto;
    inline-size: var(--size);
    block-size: var(--size);
    margin: 0;
    padding: 0;
    appearance: none;
    border: var(--l-form-control-border-width) solid var(--l-form-control-border-color);
    border-radius: 50%;
    background-color: var(--l-form-control-background-color);
    background-repeat: no-repeat;
    background-position: center;
    background-size: 100%;
    vertical-align: middle;
    cursor: pointer;
    transition-property: background-color, border-color, box-shadow;
    transition-duration: 150ms;

    &:checked {
      --_halo: var(--accent);
      border-color: var(--accent);
      background-color: var(--accent);
      background-image: var(--dot);
    }

    @media (hover: hover) {
      &:hover:not(:disabled) {
        border-color: var(--l-form-control-border-color-hover);
        box-shadow: 0 0 0 calc(var(--size) * var(--l-form-control-toggle-halo-ratio))
          color-mix(in oklab, var(--_halo), transparent 84%);
      }
    }

    &:focus-visible {
      outline: 2px solid var(--l-focus-ring);
      outline-offset: 2px;
    }

    /* Invalid: only after interaction (`:user-invalid`), or forced via
       `aria-invalid` (set by `l-form-field` / server-side validation).
       Overriding `--accent` makes a checked invalid radio fill with the error
       color too (not just the border). */
    &:user-invalid,
    &[aria-invalid='true'] {
      --accent: var(--l-form-control-border-color-invalid);
      border-color: var(--l-form-control-border-color-invalid);
    }

    &:disabled {
      cursor: not-allowed;
      opacity: 0.4;
    }

    /* High contrast: the selected fill is forced to Canvas, hiding the accent
       and leaving the baked-white dot invisible — pin the selected box to a
       system color so the dot stays legible. */
    @media (forced-colors: active) {
      &:checked {
        border-color: Highlight;
        background-color: Highlight;
      }
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .l-radio,
    :where(l-form-field:not([unstyled])) > input[type='radio'] {
      transition-duration: 0ms;
    }
  }
}

/* select.css */
/* Select — `.l-select` on a native <select> using the customizable
   `base-select` appearance (the Customizable Select API).

   Progressive enhancement: where `base-select` is unsupported the markup
   degrades to a plain native <select> — the `<button><selectedcontent>` trigger
   is ignored and rich <option> content collapses to its text node. Always keep
   meaningful text in each <option> so the fallback stays usable.

   Two surfaces, two design references kept in sync with the rest of the library:
   - the trigger (the <select> box) mirrors the form controls (l-input /
     l-textarea) through the --l-form-control-* tokens;
   - the picker (::picker(select)) and its items mirror the l-dropdown panel
     (--l-color-surface-overlay / --l-color-border-overlay / --l-shadow-md). */

@layer components {
  .l-select {
    /* Public knobs — mirror l-input so a select sits consistently among the
       other form controls. */
    --height: var(--l-form-control-height);
    --border-radius: var(--l-form-control-border-radius);
    --caret-color: var(--l-form-control-placeholder-color);
    /* mdi:chevron-down — same glyph as l-disclosure's `data-marker="arrow"`.
       Override with any url()/image to re-skin; pairs with --caret-color. */
    --caret-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z"></path></svg>');

    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-block-size: var(--height);
    padding-block: 0;
    padding-inline: var(--l-form-control-padding-inline);
    border: var(--l-form-control-border-width) solid var(--l-form-control-border-color);
    border-radius: var(--border-radius);
    background-color: var(--l-form-control-background-color);
    color: var(--l-form-control-value-color);
    font: inherit;
    line-height: normal;
    cursor: pointer;
    transition-property: border-color, box-shadow, background-color;
    transition-duration: 150ms;

    /* base-select on the control AND its picker unlocks full CSS styling;
       both fall back to the native select where unsupported. */
    &,
    &::picker(select) {
      appearance: base-select;
    }

    /* Size — `data-size` maps the height onto the shared control scale
       (default `md`). Only the height changes. */
    &[data-size='xs'] {
      --height: var(--l-size-control-xs);
    }
    &[data-size='sm'] {
      --height: var(--l-size-control-sm);
    }
    &[data-size='md'] {
      --height: var(--l-size-control-md);
    }
    &[data-size='lg'] {
      --height: var(--l-size-control-lg);
    }
    &[data-size='xl'] {
      --height: var(--l-size-control-xl);
    }

    @media (hover: hover) {
      &:hover:where(:not(:disabled)) {
        border-color: var(--l-form-control-border-color-hover);
      }
    }

    /* Focus: border takes the ring color plus the soft halo, matching l-input. */
    &:focus-visible {
      border-color: var(--l-focus-ring);
      outline: 2px solid transparent;
      outline-offset: 2px;
      box-shadow: 0 0 0 3px color-mix(in oklab, var(--l-focus-ring) 22%, transparent);
    }

    /* Invalid: red border + faint danger wash. Ordered after focus so the red
       border wins when the field is both focused and invalid. */
    &:user-invalid,
    &[aria-invalid='true'] {
      border-color: var(--l-form-control-border-color-invalid);
      background-color: color-mix(
        in oklab,
        var(--l-form-control-border-color-invalid) 6%,
        var(--l-form-control-background-color)
      );
    }

    &:disabled {
      cursor: not-allowed;
      border-color: var(--l-form-control-disabled-border);
      background-color: var(--l-form-control-disabled-background);
      color: var(--l-form-control-disabled-color);
    }

    /* The selected value fills the trigger; the chevron sits at the inline-end.
       <selectedcontent> clones the option's children, not the `.l-select-item`
       flex container — so it must re-establish the row itself, else rich options
       (media + text) stack vertically in the trigger. */
    & selectedcontent {
      display: flex;
      align-items: center;
      gap: 0.5rem;
      flex: 1 1 auto;
      min-inline-size: 0;
      overflow: hidden;
    }

    /* Keep the trigger compact when a rich option is selected: only the title
       is mirrored into the value, the secondary description is dropped. */
    & selectedcontent .l-select-item-description {
      display: none;
    }

    /* Native chevron — its glyph is replaced by a mask so it can be re-skinned
       (--caret-icon) and recolored (--caret-color) independently of the value
       color; rotates on open. `content: ''` drops the UA glyph. */
    &::picker-icon {
      content: '';
      flex: 0 0 auto;
      inline-size: 1.25rem;
      block-size: 1.25rem;
      background-color: var(--caret-color);
      mask: var(--caret-icon) center / contain no-repeat;
      transition: rotate 150ms ease;
    }
    &:open::picker-icon {
      rotate: 180deg;
    }
  }

  /* Picker panel — mirrors the l-dropdown popover. `anchor-size(width)` keeps
     the panel at least as wide as the trigger; ignored (panel sizes to content)
     where unsupported. */
  .l-select::picker(select) {
    box-sizing: border-box;
    /* At least the trigger width, capped at a readable measure — without an
       explicit max the popover sizes to max-content and rich descriptions
       never wrap (they'd overflow with a horizontal scrollbar). */
    min-inline-size: anchor-size(width);
    max-inline-size: min(90vw, 22rem);
    padding: 0.25rem;
    margin-block-start: 0.25rem;
    border: 1px solid var(--l-color-border-overlay);
    border-radius: 6px;
    background: var(--l-color-surface-overlay);
    color: var(--l-color-text-primary);
    box-shadow: var(--l-shadow-md);
    font-size: 0.875rem;
    line-height: 1.5;

    /* Off-stage (also the exit state); the entry starts from @starting-style. */
    opacity: 0;
    transform: scale(0.97);
    transition:
      opacity 150ms ease,
      transform 150ms ease,
      display 150ms allow-discrete,
      overlay 150ms allow-discrete;
  }

  .l-select:open::picker(select) {
    opacity: 1;
    transform: scale(1);

    @starting-style {
      opacity: 0;
      transform: scale(0.97);
    }
  }

  .l-select-item {
    display: flex;
    position: relative;
    align-items: start;
    gap: 0.5rem;
    box-sizing: border-box;
    min-block-size: 36px;
    padding-block: 0.5rem;
    /* Reserve the inline-start checkmark column on every row so titles align
       whether or not the option is selected (like l-dropdown). */
    padding-inline: 1.75rem 0.5rem;
    border-radius: 4px;
    cursor: pointer;
    color: var(--l-color-text-primary);

    /* Selection is shown by the checkmark alone (like l-dropdown) — the
       selected option carries NO background, so it never reads as merged with
       the hovered/active row, which is the only one that takes the tint. */
    @media (hover: hover) {
      &:hover {
        background: var(--l-color-bg-state-hover);
      }
    }

    /* Native selected indicator — absolutely placed in the reserved inline-start
       column and pinned to the first line, so a wrapping description never
       shifts it and unselected rows keep identical text alignment. */
    &::checkmark {
      position: absolute;
      inset-inline-start: 0.5rem;
      /* Sit in the title's line box: same start offset as the padding, and a
         block-size equal to the title's line-height (0.875rem × 1.5) so the
         glyph centers on the title regardless of its cap height. */
      inset-block-start: 0.5rem;
      inline-size: 1rem;
      block-size: 1.3125rem;
      background-color: currentColor;
      /* https://lucide.dev/icons/check */
      mask: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"></path></svg>')
        center / 1rem no-repeat;
    }
  }

  /* Rich option content — image/icon + title + optional description. */
  .l-select-item-media {
    flex: 0 0 auto;
    inline-size: 1.5rem;
    block-size: 1.5rem;
    border-radius: 4px;
    object-fit: cover;
  }

  .l-select-item-text {
    display: flex;
    min-inline-size: 0;
    flex: 1 1 auto;
    flex-direction: column;
  }

  .l-select-item-title {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    font-weight: 600;
  }

  .l-select-item-description {
    /* Override the `white-space: nowrap` inherited from the native <select>, so
       the description wraps within the picker's max-inline-size instead of
       overflowing on one line. */
    white-space: normal;
    text-wrap: pretty;
    color: var(--l-color-text-tertiary);
    font-size: 0.75rem;
    line-height: 1.3;
  }

  @media (prefers-reduced-motion: reduce) {
    .l-select,
    .l-select::picker-icon,
    .l-select::picker(select) {
      transition-duration: 0ms;
    }
  }
}

/* skeleton.css */
@layer components {
  l-skeleton {
    --_fill: light-dark(var(--l-color-gray-200), var(--l-color-gray-700));
    --_shimmer: light-dark(var(--l-color-gray-100), var(--l-color-gray-600));

    position: relative;
    overflow: hidden;
    display: block;
    width: var(--width, 100%);
    height: var(--height, 1.2em);
    background-color: var(--_fill);
    border-radius: var(--l-radius-sm);

    > * {
      visibility: hidden;
    }

    &:not(:empty) {
      max-width: fit-content;
      height: auto;
    }

    /* Shapes */

    &[shape='circle'] {
      border-radius: var(--l-radius-full);
    }

    &[shape='text'] {
      height: auto;
      transform: scale(1, 0.6);
      transform-origin: 0 60%;

      &:empty::before {
        content: '\00a0';
      }
    }

    /* Animations — pulse is default */

    &:not([animation]),
    &[animation='pulse'] {
      animation: l-skeleton-pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) 0.5s infinite;
      animation-fill-mode: backwards;
    }

    &[animation='wave']::after {
      content: '';
      position: absolute;
      inset: 0;
      background-image: linear-gradient(
        270deg,
        var(--_fill) 0%,
        var(--_shimmer) 51%,
        var(--_fill) 100%
      );
      animation: l-skeleton-wave 2s ease-in-out infinite;
      z-index: 1;
    }

    @media (prefers-reduced-motion: reduce) {
      &,
      &::after {
        animation: none;
      }
    }
  }

  @keyframes l-skeleton-pulse {
    0% {
      opacity: 1;
    }
    50% {
      opacity: 0.4;
    }
    100% {
      opacity: 1;
    }
  }

  @keyframes l-skeleton-wave {
    0% {
      transform: translateX(-100%);
    }
    100% {
      transform: translateX(100%);
    }
  }
}

/* slider.css */
@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   🆂🅻🅸🅳🅴🆁 — document-level fallback
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  l-slider is a Shadow-DOM element: its full UI lives in the shadow root. This
  file only reserves layout and paints a static rail BEFORE the element upgrades
  (`:not(:defined)`), so there's no flash or layout shift on hydration. Once
  defined, the shadow styles take over and these rules no longer match.
  */

  l-slider {
    display: block;
    inline-size: 100%;
  }

  /* The host itself IS the rail — a thin solid bar, vertically centred in a
     reserved ~24px box so there's no layout shift on upgrade. A `::before`
     paints a faint filled amorce so it reads as a slider at rest. Solid colors
     (not sized gradients) so it paints reliably everywhere, incl. srcdoc demos.
     The real value/position is unknown pre-upgrade — this is purely cosmetic. */
  l-slider:not(:defined) {
    block-size: 0.375rem;
    margin-block: 0.5625rem; /* (1.5rem − 0.375rem) / 2 → 24px total footprint */
    overflow: hidden;
    background-color: var(--l-color-bg-fill-neutral-subtle);
    border-radius: 999px;
  }

  l-slider:not(:defined)::before {
    content: '';
    display: block;
    block-size: 100%;
    inline-size: 40%;
    background-color: var(--l-form-control-activated-color);
  }
}

/* stories.css */
@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   🆂🆃🅾🆁🅸🅴🆂  (the row)
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  l-stories {
    /* Per-appearance defaults — each appearance overrides these below. */
    --size: 80px;
    --aspect-ratio: 1;
    --radius: var(--l-radius-full);
    --gap: 1rem;

    --ring-color: var(--l-color-bg-fill-brand);
    /* Same as `--ring-color` by default — no visual distinction for "seen" stories. Override `--ring-color-seen` (e.g. `color-mix(in oklab, var(--ring-color), transparent 65%)`) to fade viewed thumbnails Instagram-style. */
    --ring-color-seen: var(--ring-color);
    --ring-width: 2px;
    /* Default 3 px gap between the image and the ring — matches the Instagram Stories pattern that consumers expect from this widget. Filled with `--ring-offset-color` (defaults to the page surface color). For pages with a non-default background, override `--ring-offset-color` to match. */
    --ring-offset: 3px;
    --ring-offset-color: var(--l-color-surface);
    --label-color: var(--l-color-text-primary);

    display: flex;
    flex-wrap: nowrap;
    gap: var(--gap);
    /* The row itself has no overflow viewport — items stay flush at the edges and the focus ring is free to extend into the surrounding container's padding. Consumers wrap in a scroll container if they need horizontal scrolling. */

    /* Squared appearance — no ring, no offset, image edge-to-edge. */
    &[appearance='squared'] {
      --size: 100px;
      --radius: 0.75rem;
      --ring-color: transparent;
      --ring-width: 0;
      --ring-offset: 0px;
    }

    /* Portrait card appearance (Image #19) — no ring, no offset. */
    &[appearance='portrait'] {
      --size: 160px;
      --aspect-ratio: 9 / 16;
      --radius: 1rem;
      --ring-color: transparent;
      --ring-width: 0;
      --ring-offset: 0px;
    }

    /* Landscape appearance — no ring, no offset. */
    &[appearance='landscape'] {
      --size: 200px;
      --aspect-ratio: 16 / 9;
      --radius: 0.75rem;
      --ring-color: transparent;
      --ring-width: 0;
      --ring-offset: 0px;
    }
  }

  /* Hide labels when the row opts out. */
  l-stories[with-labels='false'] l-story .l-story-label {
    display: none;
  }
}

/* story.css */
@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   🆂🆃🅾🆁🆈  (a single thumbnail)
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  l-story renders its own light-DOM subtree (trigger button, thumbnail spans,
  img/video, play icon, label). @scope keeps these rules from leaking OUT, and the
  reset below strips UA default chrome off the generated button / spans so the
  component styles from a clean slate. Caveat: this lives in @layer components, so it
  only shields the internals from UA defaults and lower-layer rules — *unlayered*
  host-page rules (a CSS reset, Tailwind preflight, naked `button {}`) still outrank
  every cascade layer and are NOT neutralized here.
  */

  @scope (l-story) {
    /* Reset — :where() keeps specificity at (0,0,0) so the component rules below win
       over it. Strips UA default styling off the generated internals for a clean
       slate. Custom properties are NOT affected by `all` — design tokens cascade
       through. img / video / l-icon are excluded — they carry their own intrinsic
       sizing and are positioned by the component rules below; :scope is excluded too. */
    *:where(:not(img, svg, video, l-icon):not(svg *)),
    *::before,
    *::after {
      all: unset;
      display: revert;
    }

    :scope {
      display: inline-flex;
      flex: 0 0 auto;
      scroll-snap-align: start;

      /* Inherit row tokens; these can also be set directly per-story. */
      --size: inherit;
      --aspect-ratio: inherit;
      --radius: inherit;
    }

    /* Slot-marked children of l-story are data (cta, header) carried by the story for the viewer to pick up. They must not render in the row. */
    :scope > [slot] {
      display: none;
    }

    .l-story-trigger {
      position: relative;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 0.5rem;
      /* `--size` is the outer thumbnail diameter (image + ring + offset gap). The image inside is `--size − 2×(--ring-width + --ring-offset)`. Matches the convention of avatar/badge libraries: layout footprint stays fixed regardless of ring config. */
      width: var(--size);
      padding: 0;
      border: 0;
      background: transparent;
      color: inherit;
      font: inherit;
      cursor: pointer;
      text-align: center;
      outline: none;
    }

    .l-story-thumb {
      position: relative;
      display: block;
      width: 100%;
      aspect-ratio: var(--aspect-ratio);
      border-radius: var(--radius);
      /* Multi-layer background composition (no pseudo-element):
         - border-box layer: ring color (solid, linear-gradient, conic-gradient, image…)
         - padding-box layer: gap color (a `linear-gradient` wrapper on the offset color so it's a valid background-image and works in non-final layers)
         The image fills content-box and covers the inner area. */
      border: var(--ring-width) solid transparent;
      padding: var(--ring-offset);
      box-sizing: border-box;
      background:
        linear-gradient(var(--ring-offset-color), var(--ring-offset-color)) padding-box,
        var(--ring-color) border-box;
      transition: transform 150ms cubic-bezier(0.44, 0.36, 0.04, 1);
      isolation: isolate;
    }

    :scope[seen] .l-story-thumb {
      --ring-color: var(--ring-color-seen);
    }

    /* Focus ring hugs the thumbnail shape (circle, portrait, etc.) — label stays outside the ring. */
    .l-story-trigger:focus-visible .l-story-thumb {
      outline: 2px solid var(--l-focus-ring);
      outline-offset: 3px;
    }

    .l-story-trigger:hover .l-story-thumb {
      transform: scale(1.03);
    }

    .l-story-thumb img,
    .l-story-thumb video {
      position: absolute;
      /* Inset by the offset to position the image in the thumb's content-box, leaving the padding visible as the gap. */
      inset: var(--ring-offset);
      width: calc(100% - 2 * var(--ring-offset));
      height: calc(100% - 2 * var(--ring-offset));
      object-fit: cover;
      border-radius: inherit;
    }

    .l-story-play {
      position: absolute;
      inset: 0;
      z-index: 1;
      display: grid;
      place-items: center;

      & l-icon {
        display: grid;
        place-items: center;
        width: clamp(28px, 32%, 40px);
        aspect-ratio: 1;
        border-radius: 999px;
        background: rgb(0 0 0 / 35%);
        color: white;
        font-size: clamp(16px, 22%, 26px);
      }
    }

    .l-story-label {
      display: block;
      width: 100%;
      color: var(--label-color);
      font-size: 0.8125rem;
      font-weight: 500;
      line-height: 1.2;
      /* Wrap onto multiple lines, centered. `text-wrap: balance` evens out the line lengths (e.g. "Questions / fréquentes" instead of "Questions fréquentes / "). */
      text-align: center;
      text-wrap: balance;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅿🆄🅻🆂🅴  (attention halo + occasional thumb tap)
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    :scope[pulse] {
      /* Local pulse customization knobs. */
      --pulse-color: var(--ring-color);
      --pulse-scale: 1.2;
      --pulse-duration: 1.6s;
    }

    /* Halo is a scaled clone of the thumb shape backed by `--pulse-color` (defaults to `--ring-color`, so gradient/image rings pulse in their own paint). It sits behind the thumb in the trigger's normal flow — the thumb's opaque background hides the part still inside the original bounds, leaving only the portion that scales beyond visible. */
    :scope[pulse] .l-story-trigger::before {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      aspect-ratio: var(--aspect-ratio);
      border-radius: var(--radius);
      background: var(--pulse-color);
      pointer-events: none;
      animation: l-story-pulse-halo var(--pulse-duration) cubic-bezier(0.4, 0, 0.6, 1) infinite;
    }

    /* Subtle "tap" on the thumb every 3s — the motion happens in the first 0.6s of the cycle (10% in, 10% out), then the thumb sits still for 2.4s before the next tap. Slower than the halo so the two don't feel locked in lockstep. */
    :scope[pulse] .l-story-thumb {
      animation: l-story-pulse-thumb 3s 1.3s linear infinite both;
    }

    /* On hover, kill both pulse animations so the regular hover scale takes over without compounding. */
    :scope[pulse] .l-story-trigger:hover::before,
    :scope[pulse] .l-story-trigger:hover .l-story-thumb {
      animation: none;
    }

    /* Reduced motion */
    @media (prefers-reduced-motion: reduce) {
      .l-story-thumb {
        transition: none;
      }
      .l-story-trigger:hover .l-story-thumb {
        transform: none;
      }
      :scope[pulse] .l-story-trigger::before,
      :scope[pulse] .l-story-thumb {
        animation: none;
      }
    }
  }

  @keyframes l-story-pulse-halo {
    0% {
      transform: scale(1);
      opacity: 0.5;
    }
    70%,
    100% {
      transform: scale(var(--pulse-scale));
      opacity: 0;
    }
  }

  @keyframes l-story-pulse-thumb {
    0%,
    20%,
    100% {
      transform: scale(1);
    }
    10% {
      transform: scale(0.95);
    }
  }
}

/* switch.css */
/* Switch — `.l-switch` on a native `<input type="checkbox" role="switch">`.

   A switch is the *same control* as a checkbox (binary on/off, native form
   participation, Space to toggle) with a different affordance: a sliding thumb
   in a pill track instead of a tick box. So it stays a CSS-only `appearance:
   none` skin on a native `<input type="checkbox">` — no JS. The one required
   addition is `role="switch"`, which makes assistive tech announce it as a
   switch ("on/off") rather than a checkbox ("checked"), and which `l-form-field`
   keys on for inline layout. `checkbox.css` excludes `[role='switch']` from its
   auto-styling so the two skins never collide.

   The thumb is drawn as a `background-image` radial-gradient on the input
   itself — `::before`/`::after` do not paint on replaced elements like
   `<input>`, and an `<input>` can't hold child nodes. That constraint shapes
   the motion: the thumb travels by transitioning `background-position`, and
   reshapes (the hover swell and the press squish) by transitioning
   `background-size` of an `ellipse closest-side` gradient. There is no inner
   node to transform, so the full drag/pill choreography of a JS switch is out
   of scope by design — what remains is a spring-eased slide plus tactile
   hover/press cues, which is the "fluid but simple" target.

   Contrast: the thumb is baked with the stable, non-inverting
   `--l-form-control-activated-content-color` (white). The off-track therefore
   uses `--l-color-text-tertiary` (gray-500 in *both* light and dark), which
   keeps the white thumb at ~5:1 against the off state in either mode — a
   lighter neutral fill would drop the off-state thumb below 3:1.

   `l-form-field` auto-styles a bare switch via a zero-specificity `:where()`
   selector, so inside a field no class is needed (`unstyled` opts out). */

@layer components {
  .l-switch,
  :where(l-form-field:not([unstyled])) > input[type='checkbox'][role='switch'] {
    /* Public knobs */
    --size: var(--l-form-control-toggle-size);
    --accent: var(--l-form-control-activated-color);

    /* Geometry — all derived from the track height so a single `--size`
       rescales the whole control proportionally. */
    --_gap: calc(var(--size) * 0.125);
    --_track-width: calc(var(--size) * 1.75);
    --_thumb: calc(var(--size) - var(--_gap) * 2);

    /* `--_track` is the live track fill; `:checked` flips it to the accent.
       The hover halo reads it back so the glow always matches the state. */
    --_track: var(--l-form-control-track-off-color);
    --_thumb-color: var(--l-form-control-activated-content-color);
    --_thumb-img: radial-gradient(ellipse closest-side, var(--_thumb-color) 94%, #0000);

    /* Box-shadow is composed from two independent layers so they coexist: the
       hover halo (`--_glow`) and the invalid ring (`--_ring`). Each state sets
       only its own layer; both default to nothing. */
    --_glow: 0 0 0 0 #0000;
    --_ring: 0 0 0 0 #0000;

    /* A gentle spring (slight overshoot) for the slide; the ~4% overshoot is a
       sub-pixel of travel and is clipped to the track, so it reads as a settle,
       not a bounce. */
    --_slide-ease: linear(
      0,
      0.045 3.7%,
      0.18 8.5%,
      0.737 24.3%,
      0.92 30.6%,
      1.014 36.1%,
      1.043 42.4%,
      1.032 51.6%,
      1.004 67.7%,
      0.998 85%,
      1
    );

    box-sizing: border-box;
    flex: 0 0 auto;
    inline-size: var(--_track-width);
    block-size: var(--size);
    margin: 0;
    padding: 0;
    appearance: none;
    border: none;
    border-radius: 999px;
    background-color: var(--_track);
    background-image: var(--_thumb-img);
    background-repeat: no-repeat;
    /* The thumb rests at the inline-start (left in LTR); `:dir(rtl)` flips it. */
    background-position: var(--_gap) center;
    background-size: var(--_thumb) var(--_thumb);
    /* Ring on top (crisp), glow behind (soft) — so an invalid switch keeps its
       error ring visible through the hover halo. */
    box-shadow: var(--_ring), var(--_glow);
    vertical-align: middle;
    cursor: pointer;
    transition:
      background-color 160ms ease,
      box-shadow 160ms ease,
      background-position 320ms var(--_slide-ease),
      background-size 200ms ease;

    &:checked {
      --_track: var(--accent);
      /* `calc(100% - gap)` pins the thumb's right edge a gap in from the track
         end, mirroring the off state's left inset. */
      background-position: calc(100% - var(--_gap)) center;
    }

    /* RTL: the thumb rests at the inline-start (right) and slides to the
       inline-end (left) when on — `background-position` is physical, so flip it
       explicitly. */
    &:dir(rtl) {
      background-position: calc(100% - var(--_gap)) center;

      &:checked {
        background-position: var(--_gap) center;
      }
    }

    @media (hover: hover) {
      &:hover:not(:disabled) {
        /* Soft halo proves interactivity without moving anything; it tints from
           the current track color, so it's neutral when off and accent when on.
           The thumb also swells a touch — a quiet "ready" cue. Shares the
           toggle-family halo ratio with checkbox/radio. */
        background-color: color-mix(in oklab, var(--_track), black 6%);
        background-size: calc(var(--_thumb) * 1.06) var(--_thumb);
        --_glow: 0 0 0 calc(var(--size) * var(--l-form-control-toggle-halo-ratio))
          color-mix(in oklab, var(--_track), transparent 84%);
      }
    }

    /* Press: the thumb squishes — wider and shorter — anchored to its resting
       edge (left when off, right when on) thanks to the background-position
       above. Tactile, and it survives without hover on touch. */
    &:active:not(:disabled) {
      background-size: calc(var(--_thumb) * 1.18) calc(var(--_thumb) * 0.9);
    }

    &:focus-visible {
      outline: 2px solid var(--l-focus-ring);
      outline-offset: 2px;
    }

    /* Invalid: after interaction (`:user-invalid`) or forced via `aria-invalid`
       (set by `l-form-field` / server-side validation). The error ring shows in
       both states; overriding `--accent` turns an *on* invalid switch red too. */
    &:user-invalid,
    &[aria-invalid='true'] {
      --accent: var(--l-form-control-border-color-invalid);
      /* Sets only the ring layer, so a hovered invalid switch keeps both its
         error ring and the hover halo. */
      --_ring: 0 0 0 1.5px var(--l-form-control-border-color-invalid);
    }

    &:disabled {
      cursor: not-allowed;
      opacity: 0.4;
      /* Clear the error ring so a disabled control never reads as erroring. */
      box-shadow: none;
    }

    /* In forced-colors the gradient thumb and track fill can flatten, so lean on
       what survives: a track outline plus the thumb's left/right position, and
       distinguish on/off by switching the outline to the system Highlight. */
    @media (forced-colors: active) {
      border: 1px solid CanvasText;
      --_thumb-color: CanvasText;

      &:checked {
        border-color: Highlight;
        --_thumb-color: Highlight;
      }
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .l-switch,
    :where(l-form-field:not([unstyled])) > input[type='checkbox'][role='switch'] {
      transition-duration: 0ms;
    }
  }
}

/* tabs/enclosed.css */
@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   Base
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  l-tabs {
    display: block;
  }

  l-tabs [role='tablist'] {
    --_duration: 150ms;
    --_easing: cubic-bezier(0.25, 0.1, 0.25, 1);

    display: flex;
    position: relative;

    &::before {
      content: '';
      position: absolute;
      left: 0;
      pointer-events: none;
      will-change: transform, width;
      transition:
        transform var(--_duration) var(--_easing),
        width var(--_duration) var(--_easing);
      transform: translateX(var(--_indicator-left, 0));
      width: var(--_indicator-width, 0);
    }
  }

  @media (prefers-reduced-motion: reduce) {
    l-tabs [role='tablist'] {
      --_duration: 0ms;
    }

    l-tabs [role='tab'] {
      transition-duration: 0ms;
    }
  }

  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   Tab trigger
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  l-tabs [role='tab'] {
    appearance: none;
    border: none;
    background: none;
    padding: 0;
    margin: 0;
    cursor: pointer;
    height: var(--l-size-control-xl);
    white-space: nowrap;
    font-family: inherit;
    font-weight: 500;
    font-size: var(--l-text-sm);
    line-height: 1;
    color: var(--l-color-text-secondary);
    transition: color 150ms;
    -webkit-tap-highlight-color: transparent;

    &[aria-selected='true'] {
      color: var(--l-color-text-primary);
    }

    @media (hover: hover) {
      &:hover:not([aria-selected='true']) {
        color: var(--l-color-text-primary);
      }
    }

    &:focus-visible {
      outline: 2px solid var(--l-focus-ring);
      outline-offset: -2px;
      border-radius: var(--l-radius-sm);
    }
  }

  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   Tab panel
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  l-tabs [role='tabpanel'] {
    &:focus-visible {
      outline: 2px solid var(--l-focus-ring);
      outline-offset: 2px;
      border-radius: var(--l-radius-sm);
    }
  }

  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   Full width
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  l-tabs[full-width] [role='tablist'] {
    width: 100%;
  }

  l-tabs[full-width] [role='tab'] {
    flex: 1;
    text-align: center;
  }
}

@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   Variant: enclosed
   A pill-shaped tablist with a sliding background indicator behind the active tab.
   Visual reference: https://ui.shadcn.com/docs/components/radix/tabs
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  l-tabs[variant='enclosed'] {
    & [role='tablist'] {
      background: var(--l-color-bg-fill-neutral-subtle);
      border-radius: var(--l-radius-lg);
      padding: calc(var(--spacing) * 1);
      gap: calc(var(--spacing) * 1);
      box-shadow: inset 0 0 0 1px var(--l-color-divider);

      &::before {
        background: var(--l-color-surface);
        border-radius: var(--l-radius-md);
        box-shadow:
          0 1px 3px 0 rgb(0 0 0 / 0.08),
          0 1px 2px -1px rgb(0 0 0 / 0.08);
        inset-block: calc(var(--spacing) * 1);
        z-index: 0;
      }
    }

    & [role='tab'] {
      position: relative;
      z-index: 1;
      padding: calc(var(--spacing) * 1.5) calc(var(--spacing) * 3);
      border-radius: var(--l-radius-md);
    }
  }
}

/* tabs/line.css */
@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   Base
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  l-tabs {
    display: block;
  }

  l-tabs [role='tablist'] {
    --_duration: 150ms;
    --_easing: cubic-bezier(0.25, 0.1, 0.25, 1);

    display: flex;
    position: relative;

    &::before {
      content: '';
      position: absolute;
      left: 0;
      pointer-events: none;
      will-change: transform, width;
      transition:
        transform var(--_duration) var(--_easing),
        width var(--_duration) var(--_easing);
      transform: translateX(var(--_indicator-left, 0));
      width: var(--_indicator-width, 0);
    }
  }

  @media (prefers-reduced-motion: reduce) {
    l-tabs [role='tablist'] {
      --_duration: 0ms;
    }

    l-tabs [role='tab'] {
      transition-duration: 0ms;
    }
  }

  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   Tab trigger
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  l-tabs [role='tab'] {
    appearance: none;
    border: none;
    background: none;
    padding: 0;
    margin: 0;
    cursor: pointer;
    height: var(--l-size-control-xl);
    white-space: nowrap;
    font-family: inherit;
    font-weight: 500;
    font-size: var(--l-text-sm);
    line-height: 1;
    color: var(--l-color-text-secondary);
    transition: color 150ms;
    -webkit-tap-highlight-color: transparent;

    &[aria-selected='true'] {
      color: var(--l-color-text-primary);
    }

    @media (hover: hover) {
      &:hover:not([aria-selected='true']) {
        color: var(--l-color-text-primary);
      }
    }

    &:focus-visible {
      outline: 2px solid var(--l-focus-ring);
      outline-offset: -2px;
      border-radius: var(--l-radius-sm);
    }
  }

  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   Tab panel
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  l-tabs [role='tabpanel'] {
    &:focus-visible {
      outline: 2px solid var(--l-focus-ring);
      outline-offset: 2px;
      border-radius: var(--l-radius-sm);
    }
  }

  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   Full width
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  l-tabs[full-width] [role='tablist'] {
    width: 100%;
  }

  l-tabs[full-width] [role='tab'] {
    flex: 1;
    text-align: center;
  }
}

@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   Variant: line
   A tablist with a sliding underline indicator below the active tab.
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  */

  l-tabs[variant='line'] {
    & [role='tablist'] {
      border-bottom: var(--track-thickness, 1px) solid var(--track-color, var(--l-color-border));
      gap: 0;

      &::before {
        bottom: calc(-1 * var(--track-thickness, 1px));
        height: var(--indicator-thickness, 2px);
        background: var(--indicator-color, var(--l-color-text-primary));
        border-radius: var(--l-radius-full);
      }
    }

    & [role='tab'] {
      padding: calc(var(--spacing) * 2) calc(var(--spacing) * 3);
    }
  }
}

/* textarea.css */
/* Textarea — `.l-textarea` on a native `<textarea>` for multi-line text.

   The single-line sibling of `.l-input`: same chrome, tokens and states, but
   it grows vertically. Like the input, the class is the canonical primitive and
   a bare `<textarea>` inside `:where(l-form-field:not([unstyled]))` is
   auto-styled with no class needed.

   There is no input-group equivalent — adornments are a single-line concern.
   Height is driven by the native `rows` attribute; `data-resize` controls the
   user resize handle, with `data-resize="auto"` opting into `field-sizing`
   (the box grows with its content, no scrollbar). `field-sizing` is a
   progressive enhancement: where unsupported the textarea simply keeps its
   `rows` height. */

@layer components {
  .l-textarea,
  :where(l-form-field:not([unstyled])) > textarea {
    /* Public knobs */
    --height: var(--l-form-control-height);
    --border-radius: var(--l-form-control-border-radius);

    box-sizing: border-box;
    inline-size: 100%;
    /* One line never shorter than a matching input; `rows` grows it from here. */
    min-block-size: var(--height);
    margin: 0;
    /* Vertically center a single line to the control height, with a comfortable
       floor so multi-line content keeps breathing room at small sizes. */
    padding-block: max(
      calc((var(--height) - 1lh) / 2 - var(--l-form-control-border-width)),
      0.375rem
    );
    padding-inline: var(--l-form-control-padding-inline);
    appearance: none;
    border: var(--l-form-control-border-width) solid var(--l-form-control-border-color);
    border-radius: var(--border-radius);
    background-color: var(--l-form-control-background-color);
    color: var(--l-form-control-value-color);
    font: inherit;
    line-height: 1.5;
    /* Default to a vertical handle only — horizontal resize breaks layouts. */
    resize: vertical;
    transition-property: border-color, box-shadow, background-color;
    transition-duration: 150ms;

    &::placeholder {
      color: var(--l-form-control-placeholder-color);
      opacity: 1; /* Firefox dims placeholders by default */
    }

    /* `:where()` keeps the disabled guard at zero specificity so the focus and
       invalid rules below (equal specificity, later in source) win over hover. */
    @media (hover: hover) {
      &:hover:where(:not(:disabled)) {
        border-color: var(--l-form-control-border-color-hover);
      }
    }

    /* Focus: the border takes the focus-ring color; the soft halo lives in the
       rule below. The transparent outline keeps a visible indicator under
       forced colors. */
    &:focus-visible {
      border-color: var(--l-focus-ring);
      outline: 2px solid transparent;
      outline-offset: 2px;
    }

    /* Invalid: red border + a faint danger wash. After interaction
       (`:user-invalid`) or forced via `aria-invalid` (set by `l-form-field` /
       server-side validation). Ordered after `:focus-visible` so the red border
       wins when a field is both focused and invalid. */
    &:user-invalid,
    &[aria-invalid='true'] {
      border-color: var(--l-form-control-border-color-invalid);
      background-color: color-mix(
        in oklab,
        var(--l-form-control-border-color-invalid) 6%,
        var(--l-form-control-background-color)
      );
    }

    /* Disabled: a solid greyed-out fill (shadcn-style) rather than fading the
       whole control with opacity. `-webkit-text-fill-color` overrides WebKit's
       forced grey on disabled controls so the token color actually applies. */
    &:disabled {
      cursor: not-allowed;
      resize: none;
      border-color: var(--l-form-control-disabled-border);
      background-color: var(--l-form-control-disabled-background);
      color: var(--l-form-control-disabled-color);
      -webkit-text-fill-color: var(--l-form-control-disabled-color);
    }

    &:disabled::placeholder {
      color: var(--l-form-control-disabled-color);
    }
  }

  /* Soft focus halo. */
  :is(.l-textarea, :where(l-form-field:not([unstyled])) > textarea):focus-visible {
    box-shadow: 0 0 0 3px color-mix(in oklab, var(--l-focus-ring) 22%, transparent);
  }

  :is(.l-textarea, :where(l-form-field:not([unstyled])) > textarea):is(
      :user-invalid,
      [aria-invalid='true']
    ):focus-visible {
    box-shadow: 0 0 0 3px
      color-mix(in oklab, var(--l-form-control-border-color-invalid) 22%, transparent);
  }

  :is(.l-textarea, :where(l-form-field:not([unstyled])) > textarea) {
    /* Size — `data-size` maps the single-line min-height to the shared
       `--l-size-control-*` scale (default `md`); the padding scales with it. */
    &[data-size='xs'] {
      --height: var(--l-size-control-xs);
    }
    &[data-size='sm'] {
      --height: var(--l-size-control-sm);
    }
    &[data-size='md'] {
      --height: var(--l-size-control-md);
    }
    &[data-size='lg'] {
      --height: var(--l-size-control-lg);
    }
    &[data-size='xl'] {
      --height: var(--l-size-control-xl);
    }

    /* Resize handle — `data-resize` overrides the vertical default. */
    &[data-resize='none'] {
      resize: none;
    }
    &[data-resize='both'] {
      resize: both;
    }
    &[data-resize='vertical'] {
      resize: vertical;
    }

    /* Auto-grow with content (no manual handle, no scrollbar). Progressive:
       browsers without `field-sizing` keep the `rows` height. */
    &[data-resize='auto'] {
      resize: none;
      field-sizing: content;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .l-textarea,
    :where(l-form-field:not([unstyled])) > textarea {
      transition-duration: 0ms;
    }
  }
}

/* toast.css */
@keyframes l-toast-countdown {
  from {
    transform: scaleX(1);
  }
  to {
    transform: scaleX(0);
  }
}

@layer components {
  /*
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
   🆃🅾🅰🆂🆃
  ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
  - Container element using the Popover API
  - Toast items are <l-toast-item> custom elements generated programmatically
  - Positioned via margin auto on the fixed popover

  @scope keeps these rules from leaking OUT, and the reset below strips UA default
  chrome off the programmatically built internals (accent bar, content/text wrappers,
  timer bar) so the component styles from a clean slate. Caveat: this lives in
  @layer components, so it only shields the internals from UA defaults and lower-layer
  rules — *unlayered* host-page rules (a CSS reset, Tailwind preflight, naked
  `button {}`) still outrank every cascade layer and are NOT neutralized here.
  */

  @scope (l-toast) {
    /* Reset — :where() keeps specificity at (0,0,0) so the component rules below win
       over it. Strips UA default styling off the generated internals for a clean
       slate. Custom properties are NOT affected by `all` — design tokens cascade
       through.
       Exclusions:
       - img / svg / iconify-icon / l-icon carry their own intrinsic content/sizing.
       - .l-close is a self-contained styled sub-component (shared with dialog /
         drawer) that paints its X icon via `::after { mask }`; resetting it — or
         its pseudo — would wipe that icon, so both the element and its pseudos
         are kept out of the reset.
       :scope (the host) is excluded too. */
    *:where(:not(img, svg, iconify-icon, l-icon, .l-close):not(svg *):not(.l-close *)),
    *:where(:not(.l-close))::before,
    *:where(:not(.l-close))::after {
      all: unset;
      display: revert;
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅲🅾🅽🆃🅰🅸🅽🅴🆁: l-toast element
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    :scope {
      --gap: 0.5rem;
      --width: 28rem;
      --show-duration: 200ms;
      --hide-duration: 200ms;

      &:not(:popover-open) {
        display: none;
      }

      &:popover-open {
        overflow-x: clip;
        display: flex;
        flex-direction: column;
        gap: var(--gap);
        width: var(--width);
        max-width: 100%;
        padding: 1rem;
        box-sizing: border-box;
        border: 0;
        background: transparent;
        pointer-events: none;
      }

      > l-toast-item {
        pointer-events: auto;
      }
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🅿🅻🅰🅲🅴🅼🅴🅽🆃: margin auto on fixed popover
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    :scope[placement='top-start'] {
      margin: 0 auto auto 0;
    }
    :scope[placement='top-center'] {
      margin: 0 auto auto;
    }
    :scope[placement='top-end'] {
      margin: 0 0 auto auto;
    }
    :scope[placement='bottom-start'] {
      margin: auto auto 0 0;
    }
    :scope[placement='bottom-center'] {
      margin: auto auto 0;
    }
    :scope[placement='bottom-end'] {
      margin: auto 0 0 auto;
    }

    :scope[placement*='bottom'] {
      flex-direction: column-reverse;
    }

    @media (max-width: 480px) {
      :scope:popover-open {
        width: 100%;
        margin-inline: 0;
      }
    }

    /*
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
     🆃🅾🅰🆂🆃 🅸🆃🅴🅼: l-toast-item
    ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
    */

    l-toast-item {
      --_accent-color: var(--l-color-text-neutral);
      --_shadow-color: oklch(0 0 0 / 3.5%);

      position: relative;
      touch-action: pan-y;
      display: flex;
      align-items: stretch;
      gap: 0.75rem;
      padding: 0.75rem;
      overflow: hidden;
      background: var(--l-color-surface-overlay);
      border-radius: 6px;
      box-shadow:
        0 1px 1px var(--_shadow-color),
        0 2px 2px var(--_shadow-color),
        0 4px 4px var(--_shadow-color),
        0 8px 8px var(--_shadow-color);
      border: 1px solid light-dark(oklch(0 0 0 / 7%), oklch(100% 0 0 / 10%));
      color: var(--l-color-text-primary);
      font-size: 0.875rem;
      line-height: 1.5;

      /*
      ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
       🆅🅰🆁🅸🅰🅽🆃🆂
      ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
      */

      &[variant='info'] {
        --_accent-color: var(--l-color-text-info);
        --_bg: var(--l-color-bg-fill-info-soft);
      }
      &[variant='success'] {
        --_accent-color: var(--l-color-text-success);
        --_bg: var(--l-color-bg-fill-success-soft);
      }
      &[variant='warning'] {
        --_accent-color: var(--l-color-text-warning);
        --_bg: var(--l-color-bg-fill-warning-soft);
      }
      &[variant='danger'] {
        --_accent-color: var(--l-color-text-danger);
        --_bg: var(--l-color-bg-fill-danger-soft);
      }

      &[variant] {
        --_shadow-color: color-mix(in oklab, var(--_accent-color) 8%, transparent);
        background: var(--_bg);
        color: var(--_accent-color);
      }

      /*
      ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
       🆄🅸: accent bar
      ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
      */

      > .l-toast-accent {
        width: 4px;
        flex-shrink: 0;
        border-radius: 2px;
        background: var(--_accent-color);
      }

      /*
      ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
       🆄🅸: icon
      ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
      */

      &:has(> .l-toast-icon) {
        align-items: start;
      }

      > .l-toast-icon {
        flex-shrink: 0;
        width: 20px;
        height: 20px;
        margin-top: 1px;
        color: var(--_accent-color);
      }

      /*
      ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
       🆄🅸: content
      ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
      */

      > .l-toast-content {
        flex: 1;
        display: flex;
        align-items: center;
        min-width: 0;
      }

      /*
      ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
       🆄🅸: close button override
      ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
      */

      > .l-close {
        --size: 28px;
        --icon-size: 14px;
        --ring-color: color-mix(in oklab, var(--_accent-color) 40%, transparent);

        align-self: center;
        flex-shrink: 0;
      }

      /*
      ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
       🆄🅸: timer bar
      ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
      */

      > .l-toast-timer {
        position: absolute;
        bottom: 0;
        left: 0;
        right: 0;
        height: 4px;
        background: color-mix(in oklab, var(--_accent-color) 30%, transparent);
        transform-origin: left;
        animation: l-toast-countdown var(--_timer-duration) linear forwards;
      }

      /*
      ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
       🅰🅽🅸🅼🅰🆃🅸🅾🅽: show / hide transitions
      ▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬▬
      */

      /* EXIT STATE */
      opacity: 0;
      translate: 0 -0.5rem;

      transition-property: opacity, translate, transform;
      transition-duration: var(--hide-duration);

      /* OPEN/SHOWING STATE */
      &[showing] {
        opacity: 1;
        translate: 0 0;
        transition-duration: var(--show-duration);
      }

      /* BEFORE-OPEN STATE (entry animation) */
      @starting-style {
        &[showing] {
          opacity: 0;
          translate: 0 -0.5rem;
        }
      }
    }

    @media (prefers-reduced-motion: reduce) {
      :scope {
        --show-duration: 0ms;
        --hide-duration: 0ms;
      }
    }
  }
}
