From b5c151a8b3463cc9efc09273f8ad2861377bdfd6 Mon Sep 17 00:00:00 2001 From: Foscat Date: Tue, 25 Aug 2026 17:38:00 -0500 Subject: [PATCH 1/6] feat: add contextual layout density --- demo/demo.css | 4 +- demo/demo.js | 43 +++++-- demo/index.html | 6 +- demo/personality-metadata.js | 2 +- dist/core.css | 2 +- dist/foundation.css | 157 +++++++++++++++++++++++-- dist/layout-style-css.css | 157 +++++++++++++++++++++++-- dist/layout-style-css.min.css | 2 +- dist/personalities.css | 2 +- dist/personalities/bauhaus.css | 2 +- dist/personalities/bento.css | 2 +- dist/personalities/brutalism.css | 2 +- dist/personalities/cyberpunk.css | 2 +- dist/personalities/f-pattern.css | 2 +- dist/personalities/maximalist.css | 2 +- dist/personalities/minimal-saas.css | 2 +- dist/personalities/mondrian.css | 2 +- dist/personalities/neumorphism.css | 2 +- dist/personalities/retro-glass.css | 2 +- dist/personalities/retrofuturism.css | 2 +- dist/personalities/split-screen.css | 2 +- dist/personalities/synthwave.css | 2 +- dist/personalities/tactile.css | 2 +- dist/personalities/y2k.css | 2 +- dist/personalities/z-pattern.css | 2 +- dist/primitives.css | 2 +- dist/recipes.css | 2 +- dist/utilities.css | 2 +- dist/wrappers.css | 2 +- personalities.json | 1 + scripts/build.mjs | 17 ++- styles/core.css | 2 +- styles/foundation.css | 157 +++++++++++++++++++++++-- styles/personalities.css | 2 +- styles/personalities/bauhaus.css | 2 +- styles/personalities/bento.css | 2 +- styles/personalities/brutalism.css | 2 +- styles/personalities/cyberpunk.css | 2 +- styles/personalities/f-pattern.css | 2 +- styles/personalities/maximalist.css | 2 +- styles/personalities/minimal-saas.css | 2 +- styles/personalities/mondrian.css | 2 +- styles/personalities/neumorphism.css | 2 +- styles/personalities/retro-glass.css | 2 +- styles/personalities/retrofuturism.css | 2 +- styles/personalities/split-screen.css | 2 +- styles/personalities/synthwave.css | 2 +- styles/personalities/tactile.css | 2 +- styles/personalities/y2k.css | 2 +- styles/personalities/z-pattern.css | 2 +- styles/primitives.css | 2 +- styles/recipes.css | 2 +- styles/utilities.css | 2 +- styles/wrappers.css | 2 +- test/demo-smoke.test.mjs | 105 +++++++++++++++++ test/layout-css-contract.test.mjs | 16 ++- test/manifest-contract.test.mjs | 2 +- 57 files changed, 662 insertions(+), 95 deletions(-) diff --git a/demo/demo.css b/demo/demo.css index 4170c83..dcc9e42 100644 --- a/demo/demo.css +++ b/demo/demo.css @@ -45,12 +45,12 @@ body[data-mode="contrast"] { color-scheme: dark; } -body[data-density="compact"] { +body[data-demo-density="compact"] { --demo-density-gap: 0.5rem; --demo-region-padding: 0.75rem; } -body[data-density="spacious"] { +body[data-demo-density="spacious"] { --demo-density-gap: 1.5rem; --demo-region-padding: 1.5rem; } diff --git a/demo/demo.js b/demo/demo.js index 4f3d3a4..4c42133 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -190,7 +190,7 @@ const ALLOWLISTS = Object.freeze({ ]), height: Object.freeze(["auto", "29rem", "31rem", "43rem", "45rem", "50rem"]), responsive: Object.freeze(["auto", "manual"]), - density: Object.freeze(["compact", "comfortable", "spacious"]), + density: Object.freeze(["compact", "normal", "spacious"]), ui: Object.freeze(UI_STYLE_KIT_MANIFEST.presets.map((preset) => preset.id)), theme: Object.freeze(UI_STYLE_KIT_MANIFEST.themes), mode: Object.freeze(UI_STYLE_KIT_MANIFEST.modes), @@ -205,7 +205,7 @@ const DEFAULT_STATE = Object.freeze({ container: "auto", height: "auto", responsive: "auto", - density: "comfortable", + density: "normal", ui: "minimal-saas", theme: "arctic-indigo", mode: "light", @@ -248,12 +248,6 @@ const DEVICE_PRESETS = Object.freeze({ "desktop-portrait": Object.freeze({ width: 900, height: 1440 }) }); -const DENSITY_GAPS = Object.freeze({ - compact: "0.5rem", - comfortable: "1rem", - spacious: "1.5rem" -}); - const UI_CLASS_PREFIXES = Object.freeze( Object.fromEntries(UI_STYLE_KIT_MANIFEST.presets.map((preset) => [preset.id, preset.prefix])) ); @@ -326,12 +320,28 @@ let querySyncTimer = null; let hasSynchronizedQuery = false; let readoutFrame = null; +/** + * Maps durable demo query aliases to current allowlisted values. + * + * @param {string} key Query-state key. + * @param {string | null} value Raw query value. + * @returns {string | null} Current value or the unchanged input. + */ +function normalizeQueryValue(key, value) { + return key === "density" && value === "comfortable" ? "normal" : value; +} + +/** + * Reads allowlisted demo state from the current query string. + * + * @returns {Record} Normalized demo state. + */ function readStateFromQuery() { const query = new URLSearchParams(window.location.search); return Object.fromEntries( Object.entries(ALLOWLISTS).map(([key, values]) => { - const candidate = query.get(key); + const candidate = normalizeQueryValue(key, query.get(key)); return [key, candidate !== null && values.includes(candidate) ? candidate : DEFAULT_STATE[key]]; }) ); @@ -657,7 +667,15 @@ function updatePreviewReadout() { }); } -function applyState({ updateQuery = true } = {}) { +/** + * Applies current demo state to controls, public layout attributes, preview + * allocation, rendered recipe content, snippets, and URL state. + * + * @param {{updateQuery?: boolean}} [options] State-application options. + * @returns {void} + */ +function applyState(options = {}) { + const { updateQuery = true } = options; for (const [key, control] of Object.entries(controls)) { control.value = state[key]; } @@ -666,11 +684,10 @@ function applyState({ updateQuery = true } = {}) { body.dataset.theme = state.theme; body.dataset.mode = state.mode; body.dataset.lyLayout = state.personality; - body.dataset.density = state.density; + body.dataset.demoDensity = state.density; body.dataset.ecosystem = state.ecosystem; previewRoot.dataset.lyLayout = state.personality; - previewRoot.style.setProperty("--ly-gap", DENSITY_GAPS[state.density]); - previewRoot.style.setProperty("--ly-grid-gap", DENSITY_GAPS[state.density]); + previewRoot.dataset.lyDensity = state.density; previewWrapper.className = state.wrapper === "default" ? "ly-wrapper" : `ly-wrapper ly-wrapper--${state.wrapper}`; diff --git a/demo/index.html b/demo/index.html index a7d3ffc..4a36660 100644 --- a/demo/index.html +++ b/demo/index.html @@ -94,7 +94,7 @@ - + Skip to the layout lab
@@ -208,7 +208,7 @@

Layout controls

@@ -292,7 +292,7 @@

Responsive recipe stage

-
+
diff --git a/demo/personality-metadata.js b/demo/personality-metadata.js index c0d522c..76ed3dd 100644 --- a/demo/personality-metadata.js +++ b/demo/personality-metadata.js @@ -1,2 +1,2 @@ /* Generated from manifest.json by scripts/build.mjs. */ -window.LAYOUT_STYLE_PERSONALITY_METADATA = {"schemaVersion":1,"generatedFrom":"manifest.json","selector":"data-ly-layout","independentSelectors":["data-ly-layout","data-ui","data-theme","data-mode"],"personalities":[{"id":"minimal-saas","label":"Minimal SaaS","visualCompatibility":"native","recommendedVisualPresets":["minimal-saas"]},{"id":"bento","label":"Bento","visualCompatibility":"native","recommendedVisualPresets":["bento"]},{"id":"maximalist","label":"Maximalist","visualCompatibility":"native","recommendedVisualPresets":["maximalist"]},{"id":"bauhaus","label":"Bauhaus","visualCompatibility":"native","recommendedVisualPresets":["bauhaus"]},{"id":"tactile","label":"Tactile","visualCompatibility":"native","recommendedVisualPresets":["tactile"]},{"id":"neumorphism","label":"Neumorphism","visualCompatibility":"native","recommendedVisualPresets":["neumorphism"]},{"id":"retrofuturism","label":"Retrofuturism","visualCompatibility":"native","recommendedVisualPresets":["retrofuturism"]},{"id":"brutalism","label":"Brutalism","visualCompatibility":"native","recommendedVisualPresets":["brutalism"]},{"id":"cyberpunk","label":"Cyberpunk","visualCompatibility":"native","recommendedVisualPresets":["cyberpunk"]},{"id":"y2k","label":"Y2K","visualCompatibility":"native","recommendedVisualPresets":["y2k"]},{"id":"retro-glass","label":"Retro Glass","visualCompatibility":"native","recommendedVisualPresets":["retro-glass"]},{"id":"f-pattern","label":"F-pattern","visualCompatibility":"any","recommendedVisualPresets":[]},{"id":"z-pattern","label":"Z-pattern","visualCompatibility":"any","recommendedVisualPresets":[]},{"id":"split-screen","label":"Split Screen","visualCompatibility":"any","recommendedVisualPresets":[]},{"id":"mondrian","label":"Mondrian","visualCompatibility":"any","recommendedVisualPresets":[]},{"id":"synthwave","label":"Synthwave","visualCompatibility":"recommended","recommendedVisualPresets":["cyberpunk","retrofuturism"],"visualVerification":{"method":"rendered-computed-style","test":"test/demo-smoke.test.mjs","computedProperties":{"cyberpunk":{"boxShadow":"0px 0px 18px"},"retrofuturism":{"boxShadow":"0px 10px 30px"}},"assertion":"A visible browser contract fixture renders each preset's distinct article box-shadow while data-ly-layout remains synthwave."}}]}; +window.LAYOUT_STYLE_PERSONALITY_METADATA = {"schemaVersion":1,"generatedFrom":"manifest.json","selector":"data-ly-layout","independentSelectors":["data-ly-layout","data-ly-density","data-ui","data-theme","data-mode"],"personalities":[{"id":"minimal-saas","label":"Minimal SaaS","visualCompatibility":"native","recommendedVisualPresets":["minimal-saas"]},{"id":"bento","label":"Bento","visualCompatibility":"native","recommendedVisualPresets":["bento"]},{"id":"maximalist","label":"Maximalist","visualCompatibility":"native","recommendedVisualPresets":["maximalist"]},{"id":"bauhaus","label":"Bauhaus","visualCompatibility":"native","recommendedVisualPresets":["bauhaus"]},{"id":"tactile","label":"Tactile","visualCompatibility":"native","recommendedVisualPresets":["tactile"]},{"id":"neumorphism","label":"Neumorphism","visualCompatibility":"native","recommendedVisualPresets":["neumorphism"]},{"id":"retrofuturism","label":"Retrofuturism","visualCompatibility":"native","recommendedVisualPresets":["retrofuturism"]},{"id":"brutalism","label":"Brutalism","visualCompatibility":"native","recommendedVisualPresets":["brutalism"]},{"id":"cyberpunk","label":"Cyberpunk","visualCompatibility":"native","recommendedVisualPresets":["cyberpunk"]},{"id":"y2k","label":"Y2K","visualCompatibility":"native","recommendedVisualPresets":["y2k"]},{"id":"retro-glass","label":"Retro Glass","visualCompatibility":"native","recommendedVisualPresets":["retro-glass"]},{"id":"f-pattern","label":"F-pattern","visualCompatibility":"any","recommendedVisualPresets":[]},{"id":"z-pattern","label":"Z-pattern","visualCompatibility":"any","recommendedVisualPresets":[]},{"id":"split-screen","label":"Split Screen","visualCompatibility":"any","recommendedVisualPresets":[]},{"id":"mondrian","label":"Mondrian","visualCompatibility":"any","recommendedVisualPresets":[]},{"id":"synthwave","label":"Synthwave","visualCompatibility":"recommended","recommendedVisualPresets":["cyberpunk","retrofuturism"],"visualVerification":{"method":"rendered-computed-style","test":"test/demo-smoke.test.mjs","computedProperties":{"cyberpunk":{"boxShadow":"0px 0px 18px"},"retrofuturism":{"boxShadow":"0px 10px 30px"}},"assertion":"A visible browser contract fixture renders each preset's distinct article box-shadow while data-ly-layout remains synthwave."}}]}; diff --git a/dist/core.css b/dist/core.css index b215325..9c19e73 100644 --- a/dist/core.css +++ b/dist/core.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @import url("./foundation.css"); @import url("./wrappers.css"); diff --git a/dist/foundation.css b/dist/foundation.css index 368283c..96ab075 100644 --- a/dist/foundation.css +++ b/dist/foundation.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; /* Foundation owns only shared spatial tokens, containment, and defensive @@ -36,8 +36,8 @@ --ly-grid-gap: var(--ly-gap); --ly-stack-gap: var(--ly-space-4); --ly-cluster-gap: var(--ly-space-3); - --ly-section-padding-block: clamp(3rem, 7vh, 6rem); - --ly-section-padding-block-compact: clamp(1.5rem, 3.5vh, 3rem); + --ly-section-padding-block: clamp(2rem, 4vh, 4rem); + --ly-section-padding-block-compact: clamp(1rem, 2.5vh, 2rem); --ly-header-height: 4.5rem; --ly-sticky-position: sticky; --ly-cover-min: 100vh; @@ -172,8 +172,8 @@ --ly-grid-gap: var(--ly-gap); --ly-stack-gap: min(var(--ly-space-4), 3vh); --ly-cluster-gap: min(var(--ly-space-3), 2vh); - --ly-section-padding-block: clamp(1.5rem, 6vh, 3rem); - --ly-section-padding-block-compact: clamp(0.75rem, 3vh, 1.25rem); + --ly-section-padding-block: clamp(1.25rem, 4vh, 2rem); + --ly-section-padding-block-compact: clamp(0.75rem, 2vh, 1.25rem); --ly-header-height: 3.5rem; --ly-scroll-max: min(62vh, 34rem); } @@ -182,8 +182,8 @@ :where(.ly-root) { --ly-stack-gap: min(var(--ly-space-4), 3dvh); --ly-cluster-gap: min(var(--ly-space-3), 2dvh); - --ly-section-padding-block: clamp(1.5rem, 6dvh, 3rem); - --ly-section-padding-block-compact: clamp(0.75rem, 3dvh, 1.25rem); + --ly-section-padding-block: clamp(1.25rem, 4dvh, 2rem); + --ly-section-padding-block-compact: clamp(0.75rem, 2dvh, 1.25rem); --ly-scroll-max: min(62dvh, 34rem); } } @@ -194,8 +194,8 @@ --ly-cover-min: auto; --ly-shell-min: auto; --ly-sticky-position: static; - --ly-section-padding-block: var(--ly-space-5); - --ly-section-padding-block-compact: var(--ly-space-3); + --ly-section-padding-block: var(--ly-space-4); + --ly-section-padding-block-compact: var(--ly-space-2); --ly-scroll-max: max(12rem, calc(100vh - 8rem)); } @@ -206,3 +206,142 @@ } } } + +@layer ly.context { + :where( + .ly-root[data-ly-density="compact"], + .ly-root [data-ly-density="compact"] + ) { + --ly-gap: var(--ly-space-3); + --ly-grid-gap: var(--ly-space-3); + --ly-stack-gap: var(--ly-space-3); + --ly-cluster-gap: var(--ly-space-2); + --ly-section-padding-block: clamp(1rem, 2.5vh, 2rem); + --ly-section-padding-block-compact: clamp(0.5rem, 1.5vh, 1rem); + } + + :where( + .ly-root[data-ly-density="normal"], + .ly-root [data-ly-density="normal"] + ) { + --ly-gap: var(--ly-profile-gap); + --ly-grid-gap: var(--ly-gap); + --ly-stack-gap: var(--ly-space-4); + --ly-cluster-gap: var(--ly-space-3); + --ly-section-padding-block: clamp(2rem, 4vh, 4rem); + --ly-section-padding-block-compact: clamp(1rem, 2.5vh, 2rem); + } + + :where( + .ly-root[data-ly-density="spacious"], + .ly-root [data-ly-density="spacious"] + ) { + --ly-gap: max(var(--ly-profile-gap), var(--ly-space-6)); + --ly-grid-gap: var(--ly-gap); + --ly-stack-gap: var(--ly-space-5); + --ly-cluster-gap: var(--ly-space-4); + --ly-section-padding-block: clamp(3rem, 7vh, 6rem); + --ly-section-padding-block-compact: clamp(1.5rem, 3.5vh, 3rem); + } + + @media (max-height: 44rem) { + :where( + .ly-root[data-ly-density="compact"], + .ly-root [data-ly-density="compact"] + ) { + --ly-gap: var(--ly-space-3); + --ly-grid-gap: var(--ly-space-3); + --ly-stack-gap: var(--ly-space-3); + --ly-cluster-gap: var(--ly-space-2); + --ly-section-padding-block: clamp(0.75rem, 2vh, 1.25rem); + --ly-section-padding-block-compact: var(--ly-space-2); + } + + :where( + .ly-root[data-ly-density="normal"], + .ly-root [data-ly-density="normal"] + ) { + --ly-gap: min(var(--ly-profile-gap), var(--ly-space-4)); + --ly-grid-gap: var(--ly-gap); + --ly-stack-gap: var(--ly-space-4); + --ly-cluster-gap: var(--ly-space-3); + --ly-section-padding-block: clamp(1.25rem, 4vh, 2rem); + --ly-section-padding-block-compact: clamp(0.75rem, 2vh, 1.25rem); + } + + :where( + .ly-root[data-ly-density="spacious"], + .ly-root [data-ly-density="spacious"] + ) { + --ly-gap: var(--ly-space-5); + --ly-grid-gap: var(--ly-space-5); + --ly-stack-gap: var(--ly-space-4); + --ly-cluster-gap: var(--ly-space-3); + --ly-section-padding-block: clamp(1.5rem, 6vh, 3rem); + --ly-section-padding-block-compact: clamp(0.75rem, 3vh, 1.25rem); + } + + @supports (height: 100dvh) { + :where( + .ly-root[data-ly-density="compact"], + .ly-root [data-ly-density="compact"] + ) { + --ly-section-padding-block: clamp(0.75rem, 2dvh, 1.25rem); + } + + :where( + .ly-root[data-ly-density="normal"], + .ly-root [data-ly-density="normal"] + ) { + --ly-section-padding-block: clamp(1.25rem, 4dvh, 2rem); + --ly-section-padding-block-compact: clamp(0.75rem, 2dvh, 1.25rem); + } + + :where( + .ly-root[data-ly-density="spacious"], + .ly-root [data-ly-density="spacious"] + ) { + --ly-section-padding-block: clamp(1.5rem, 6dvh, 3rem); + --ly-section-padding-block-compact: clamp(0.75rem, 3dvh, 1.25rem); + } + } + } + + @media (max-height: 30rem) { + :where( + .ly-root[data-ly-density="compact"], + .ly-root [data-ly-density="compact"] + ) { + --ly-gap: var(--ly-space-2); + --ly-grid-gap: var(--ly-space-2); + --ly-stack-gap: var(--ly-space-2); + --ly-cluster-gap: var(--ly-space-1); + --ly-section-padding-block: var(--ly-space-3); + --ly-section-padding-block-compact: var(--ly-space-2); + } + + :where( + .ly-root[data-ly-density="normal"], + .ly-root [data-ly-density="normal"] + ) { + --ly-gap: var(--ly-space-3); + --ly-grid-gap: var(--ly-space-3); + --ly-stack-gap: var(--ly-space-3); + --ly-cluster-gap: var(--ly-space-2); + --ly-section-padding-block: var(--ly-space-4); + --ly-section-padding-block-compact: var(--ly-space-2); + } + + :where( + .ly-root[data-ly-density="spacious"], + .ly-root [data-ly-density="spacious"] + ) { + --ly-gap: var(--ly-space-4); + --ly-grid-gap: var(--ly-space-4); + --ly-stack-gap: var(--ly-space-4); + --ly-cluster-gap: var(--ly-space-3); + --ly-section-padding-block: var(--ly-space-5); + --ly-section-padding-block-compact: var(--ly-space-3); + } + } +} diff --git a/dist/layout-style-css.css b/dist/layout-style-css.css index 9a33fcf..3842f7d 100644 --- a/dist/layout-style-css.css +++ b/dist/layout-style-css.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; /* layout-style-css v3 bundle. Generated from focused styles/ modules. */ @@ -39,8 +39,8 @@ --ly-grid-gap: var(--ly-gap); --ly-stack-gap: var(--ly-space-4); --ly-cluster-gap: var(--ly-space-3); - --ly-section-padding-block: clamp(3rem, 7vh, 6rem); - --ly-section-padding-block-compact: clamp(1.5rem, 3.5vh, 3rem); + --ly-section-padding-block: clamp(2rem, 4vh, 4rem); + --ly-section-padding-block-compact: clamp(1rem, 2.5vh, 2rem); --ly-header-height: 4.5rem; --ly-sticky-position: sticky; --ly-cover-min: 100vh; @@ -175,8 +175,8 @@ --ly-grid-gap: var(--ly-gap); --ly-stack-gap: min(var(--ly-space-4), 3vh); --ly-cluster-gap: min(var(--ly-space-3), 2vh); - --ly-section-padding-block: clamp(1.5rem, 6vh, 3rem); - --ly-section-padding-block-compact: clamp(0.75rem, 3vh, 1.25rem); + --ly-section-padding-block: clamp(1.25rem, 4vh, 2rem); + --ly-section-padding-block-compact: clamp(0.75rem, 2vh, 1.25rem); --ly-header-height: 3.5rem; --ly-scroll-max: min(62vh, 34rem); } @@ -185,8 +185,8 @@ :where(.ly-root) { --ly-stack-gap: min(var(--ly-space-4), 3dvh); --ly-cluster-gap: min(var(--ly-space-3), 2dvh); - --ly-section-padding-block: clamp(1.5rem, 6dvh, 3rem); - --ly-section-padding-block-compact: clamp(0.75rem, 3dvh, 1.25rem); + --ly-section-padding-block: clamp(1.25rem, 4dvh, 2rem); + --ly-section-padding-block-compact: clamp(0.75rem, 2dvh, 1.25rem); --ly-scroll-max: min(62dvh, 34rem); } } @@ -197,8 +197,8 @@ --ly-cover-min: auto; --ly-shell-min: auto; --ly-sticky-position: static; - --ly-section-padding-block: var(--ly-space-5); - --ly-section-padding-block-compact: var(--ly-space-3); + --ly-section-padding-block: var(--ly-space-4); + --ly-section-padding-block-compact: var(--ly-space-2); --ly-scroll-max: max(12rem, calc(100vh - 8rem)); } @@ -210,6 +210,145 @@ } } +@layer ly.context { + :where( + .ly-root[data-ly-density="compact"], + .ly-root [data-ly-density="compact"] + ) { + --ly-gap: var(--ly-space-3); + --ly-grid-gap: var(--ly-space-3); + --ly-stack-gap: var(--ly-space-3); + --ly-cluster-gap: var(--ly-space-2); + --ly-section-padding-block: clamp(1rem, 2.5vh, 2rem); + --ly-section-padding-block-compact: clamp(0.5rem, 1.5vh, 1rem); + } + + :where( + .ly-root[data-ly-density="normal"], + .ly-root [data-ly-density="normal"] + ) { + --ly-gap: var(--ly-profile-gap); + --ly-grid-gap: var(--ly-gap); + --ly-stack-gap: var(--ly-space-4); + --ly-cluster-gap: var(--ly-space-3); + --ly-section-padding-block: clamp(2rem, 4vh, 4rem); + --ly-section-padding-block-compact: clamp(1rem, 2.5vh, 2rem); + } + + :where( + .ly-root[data-ly-density="spacious"], + .ly-root [data-ly-density="spacious"] + ) { + --ly-gap: max(var(--ly-profile-gap), var(--ly-space-6)); + --ly-grid-gap: var(--ly-gap); + --ly-stack-gap: var(--ly-space-5); + --ly-cluster-gap: var(--ly-space-4); + --ly-section-padding-block: clamp(3rem, 7vh, 6rem); + --ly-section-padding-block-compact: clamp(1.5rem, 3.5vh, 3rem); + } + + @media (max-height: 44rem) { + :where( + .ly-root[data-ly-density="compact"], + .ly-root [data-ly-density="compact"] + ) { + --ly-gap: var(--ly-space-3); + --ly-grid-gap: var(--ly-space-3); + --ly-stack-gap: var(--ly-space-3); + --ly-cluster-gap: var(--ly-space-2); + --ly-section-padding-block: clamp(0.75rem, 2vh, 1.25rem); + --ly-section-padding-block-compact: var(--ly-space-2); + } + + :where( + .ly-root[data-ly-density="normal"], + .ly-root [data-ly-density="normal"] + ) { + --ly-gap: min(var(--ly-profile-gap), var(--ly-space-4)); + --ly-grid-gap: var(--ly-gap); + --ly-stack-gap: var(--ly-space-4); + --ly-cluster-gap: var(--ly-space-3); + --ly-section-padding-block: clamp(1.25rem, 4vh, 2rem); + --ly-section-padding-block-compact: clamp(0.75rem, 2vh, 1.25rem); + } + + :where( + .ly-root[data-ly-density="spacious"], + .ly-root [data-ly-density="spacious"] + ) { + --ly-gap: var(--ly-space-5); + --ly-grid-gap: var(--ly-space-5); + --ly-stack-gap: var(--ly-space-4); + --ly-cluster-gap: var(--ly-space-3); + --ly-section-padding-block: clamp(1.5rem, 6vh, 3rem); + --ly-section-padding-block-compact: clamp(0.75rem, 3vh, 1.25rem); + } + + @supports (height: 100dvh) { + :where( + .ly-root[data-ly-density="compact"], + .ly-root [data-ly-density="compact"] + ) { + --ly-section-padding-block: clamp(0.75rem, 2dvh, 1.25rem); + } + + :where( + .ly-root[data-ly-density="normal"], + .ly-root [data-ly-density="normal"] + ) { + --ly-section-padding-block: clamp(1.25rem, 4dvh, 2rem); + --ly-section-padding-block-compact: clamp(0.75rem, 2dvh, 1.25rem); + } + + :where( + .ly-root[data-ly-density="spacious"], + .ly-root [data-ly-density="spacious"] + ) { + --ly-section-padding-block: clamp(1.5rem, 6dvh, 3rem); + --ly-section-padding-block-compact: clamp(0.75rem, 3dvh, 1.25rem); + } + } + } + + @media (max-height: 30rem) { + :where( + .ly-root[data-ly-density="compact"], + .ly-root [data-ly-density="compact"] + ) { + --ly-gap: var(--ly-space-2); + --ly-grid-gap: var(--ly-space-2); + --ly-stack-gap: var(--ly-space-2); + --ly-cluster-gap: var(--ly-space-1); + --ly-section-padding-block: var(--ly-space-3); + --ly-section-padding-block-compact: var(--ly-space-2); + } + + :where( + .ly-root[data-ly-density="normal"], + .ly-root [data-ly-density="normal"] + ) { + --ly-gap: var(--ly-space-3); + --ly-grid-gap: var(--ly-space-3); + --ly-stack-gap: var(--ly-space-3); + --ly-cluster-gap: var(--ly-space-2); + --ly-section-padding-block: var(--ly-space-4); + --ly-section-padding-block-compact: var(--ly-space-2); + } + + :where( + .ly-root[data-ly-density="spacious"], + .ly-root [data-ly-density="spacious"] + ) { + --ly-gap: var(--ly-space-4); + --ly-grid-gap: var(--ly-space-4); + --ly-stack-gap: var(--ly-space-4); + --ly-cluster-gap: var(--ly-space-3); + --ly-section-padding-block: var(--ly-space-5); + --ly-section-padding-block-compact: var(--ly-space-3); + } + } +} + /* wrappers.css */ /* Wrappers provide fluid measures and become the nearest responsive scope. */ diff --git a/dist/layout-style-css.min.css b/dist/layout-style-css.min.css index 93aee3c..6569f8e 100644 --- a/dist/layout-style-css.min.css +++ b/dist/layout-style-css.min.css @@ -1 +1 @@ -@layer ly.reset,ly.tokens,ly.wrappers,ly.primitives,ly.recipes,ly.utilities,ly.personalities;@layer ly.tokens{:where(.ly-root){--ly-space-0:0;--ly-space-1:0.25rem;--ly-space-2:0.5rem;--ly-space-3:0.75rem;--ly-space-4:1rem;--ly-space-5:1.5rem;--ly-space-6:2rem;--ly-space-7:3rem;--ly-space-8:4rem;--ly-space-9:6rem;--ly-wrapper-compact:40rem;--ly-wrapper-prose:68ch;--ly-wrapper-content:72rem;--ly-wrapper-wide:112rem;--ly-page-padding-inline:clamp(1rem,3vw,3rem);--ly-safe-area-inline:max( env(safe-area-inset-left,0px),env(safe-area-inset-right,0px) );--ly-safe-area-block-start:env(safe-area-inset-top,0px);--ly-safe-area-block-end:env(safe-area-inset-bottom,0px);--ly-wrapper-gutter:var(--ly-page-padding-inline);--ly-profile-gap:var(--ly-space-5);--ly-gap:var(--ly-profile-gap);--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(3rem,7vh,6rem);--ly-section-padding-block-compact:clamp(1.5rem,3.5vh,3rem);--ly-header-height:4.5rem;--ly-sticky-position:sticky;--ly-cover-min:100vh;--ly-shell-min:100vh;--ly-scroll-max:min(70vh,50rem);--ly-switcher-threshold:42rem;--ly-sidebar-size:18rem;--ly-sidebar-content-min:50%;--ly-grid-columns:12;--ly-grid-min:16rem;--ly-split-min:20rem;--ly-pane-min:18rem;--ly-pane-size:22rem;--ly-media-min:18rem;--ly-media-size:14rem;--ly-reel-item-min:16rem;--ly-reel-item-max:28rem;--ly-frame-ratio:16 / 9;--ly-split-primary:1fr;--ly-split-secondary:1fr;--ly-recipe-rail:18rem;--ly-recipe-aside:22rem;--ly-gallery-min:12rem;--ly-card-grid-min:16rem;--ly-app-shell-base-rows:auto auto minmax(0,1fr) auto auto;--ly-app-shell-medium-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-medium-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-medium-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-medium-areas:"header header" "nav nav" "main aside" "footer footer";--ly-dashboard-medium-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-wide-areas:"nav header header" "nav main aside" "nav footer footer";--ly-dashboard-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-docs-wide-areas:"header header" "nav main" "nav aside" "footer footer";--ly-docs-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-list-detail-wide-areas:"primary secondary" "actions actions";--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.7fr) minmax(0,1fr);--ly-split-hero-wide-areas:"content media" "actions media";--ly-split-hero-wide-columns:minmax(0,var(--ly-split-primary,1fr)) minmax(0,var(--ly-split-secondary,1fr));container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}@supports (width:1cqi){:where(.ly-root){--ly-page-padding-inline:clamp(1rem,3cqi,3rem)}}@supports (height:100dvh){:where(.ly-root){--ly-cover-min:100dvh;--ly-shell-min:100dvh;--ly-scroll-max:min(70dvh,50rem)}}}@layer ly.reset{:where(.ly-root),:where(.ly-root *){box-sizing:border-box}:where(.ly-root){margin:0}:where(.ly-root img),:where(.ly-root svg),:where(.ly-root video),:where(.ly-root canvas),:where(.ly-root iframe){max-inline-size:100%}}@layer ly.tokens{@media (max-height:44rem){:where(.ly-root){--ly-gap:min(var(--ly-profile-gap),var(--ly-space-4));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:min(var(--ly-space-4),3vh);--ly-cluster-gap:min(var(--ly-space-3),2vh);--ly-section-padding-block:clamp(1.5rem,6vh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3vh,1.25rem);--ly-header-height:3.5rem;--ly-scroll-max:min(62vh,34rem)}@supports (height:100dvh){:where(.ly-root){--ly-stack-gap:min(var(--ly-space-4),3dvh);--ly-cluster-gap:min(var(--ly-space-3),2dvh);--ly-section-padding-block:clamp(1.5rem,6dvh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3dvh,1.25rem);--ly-scroll-max:min(62dvh,34rem)}}}@media (max-height:30rem){:where(.ly-root){--ly-cover-min:auto;--ly-shell-min:auto;--ly-sticky-position:static;--ly-section-padding-block:var(--ly-space-5);--ly-section-padding-block-compact:var(--ly-space-3);--ly-scroll-max:max(12rem,calc(100vh - 8rem))}@supports (height:100dvh){:where(.ly-root){--ly-scroll-max:max(12rem,calc(100dvh - 8rem))}}}}@layer ly.wrappers{.ly-wrapper{--ly-wrapper-max:var(--ly-personality-wrapper-max,var(--ly-wrapper-content));--ly-wrapper-fluid-gutter:var(--ly-wrapper-gutter);--ly-wrapper-local-gutter:max( var(--ly-wrapper-fluid-gutter),var(--ly-safe-area-inline) );container-name:ly-scope;container-type:inline-size;inline-size:min(100%,calc(var(--ly-wrapper-max) + (var(--ly-wrapper-local-gutter) * 2)));max-inline-size:100%;margin-inline:auto;padding-inline:var(--ly-wrapper-local-gutter)}.ly-wrapper--compact{--ly-wrapper-max:var(--ly-wrapper-compact)}.ly-wrapper--prose{--ly-wrapper-max:var(--ly-wrapper-prose)}.ly-wrapper--content{--ly-wrapper-max:var(--ly-wrapper-content)}.ly-wrapper--wide{--ly-wrapper-max:var(--ly-wrapper-wide)}.ly-wrapper--full{--ly-wrapper-max:100%}.ly-wrapper--breakout{--ly-wrapper-max:100%;--ly-breakout-content-size:min( var(--ly-wrapper-content),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );--ly-breakout-feature-size:min( var(--ly-wrapper-wide),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );display:grid;grid-template-columns:[full-start] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [feature-start] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [content-start] minmax(0,var(--ly-breakout-content-size)) [content-end] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [feature-end] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [full-end];inline-size:100%;padding-inline:0}.ly-wrapper--breakout>*{grid-column:content;min-inline-size:0;max-inline-size:100%}.ly-wrapper--breakout>:where(.ly-lane--content,[data-ly-lane="content"]){grid-column:content}.ly-wrapper--breakout>:where(.ly-lane--feature,[data-ly-lane="feature"]){grid-column:feature}.ly-wrapper--breakout>:where(.ly-lane--full,[data-ly-lane="full"]){grid-column:full}}@layer ly.primitives{.ly-page{min-block-size:100vh;min-block-size:100dvh;padding-block-start:var(--ly-safe-area-block-start);padding-block-end:var(--ly-safe-area-block-end)}.ly-header,.ly-footer,.ly-main,.ly-section,.ly-surface{min-inline-size:0;min-block-size:0}.ly-header{min-block-size:var(--ly-header-height)}.ly-header--sticky{position:var(--ly-sticky-position,sticky);inset-block-start:0;z-index:var(--ly-z-header,20)}.ly-section{padding-block:var(--ly-section-padding-block)}.ly-section--compact{padding-block:var(--ly-section-padding-block-compact)}.ly-section--flush{padding-block:0}.ly-readable{max-inline-size:var(--ly-wrapper-prose)}.ly-stack{display:flex;flex-direction:column;gap:var(--ly-stack-gap)}.ly-cluster{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ly-cluster-gap)}.ly-center{inline-size:min(100%,var(--ly-center-max,var(--ly-wrapper-content)));margin-inline:auto}.ly-cover{display:flex;flex-direction:column;min-block-size:100vh;min-block-size:var(--ly-cover-min);padding-block:var(--ly-cover-padding,var(--ly-space-5))}.ly-cover>:where(.ly-cover__center,[data-ly-cover-center]){margin-block:auto}.ly-switcher{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-switcher>*{flex-basis:calc((var(--ly-switcher-threshold) - 100%) * 999);flex-grow:1;min-inline-size:0}.ly-sidebar{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-sidebar>:where(.ly-sidebar__side,[data-ly-sidebar="side"]){flex-basis:var(--ly-sidebar-size);flex-grow:1;min-inline-size:min(100%,var(--ly-sidebar-size))}.ly-sidebar>:where(.ly-sidebar__content,[data-ly-sidebar="content"]){flex-basis:0;flex-grow:999;min-inline-size:min(100%,var(--ly-sidebar-content-min))}.ly-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-grid-min)),1fr));gap:var(--ly-grid-gap)}.ly-grid--fixed{grid-template-columns:repeat(var(--ly-grid-columns),minmax(0,1fr))}.ly-split{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-split-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-split-align,center)}.ly-panes{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-pane-min)),1fr));gap:var(--ly-gap)}.ly-media{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-media-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-media-align,start)}.ly-media>:where(.ly-media__actions,[data-ly-media="actions"]){min-inline-size:min(100%,var(--ly-media-size))}.ly-grid>*,.ly-split>*,.ly-panes>*,.ly-media>*{min-inline-size:0;min-block-size:0}.ly-reel{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(min(100%,var(--ly-reel-item-min)),var(--ly-reel-item-max));gap:var(--ly-grid-gap);overflow-x:auto;overscroll-behavior-inline:contain;scroll-snap-type:inline proximity}.ly-reel>*{min-inline-size:0;scroll-snap-align:start}.ly-frame{aspect-ratio:var(--ly-frame-ratio);overflow:hidden}.ly-frame>*{inline-size:100%;block-size:100%;object-fit:cover}.ly-scroll{min-block-size:0;max-block-size:var(--ly-scroll-max);overflow-x:clip;overflow-y:auto;overscroll-behavior-block:contain}}@layer ly.recipes{:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] ){container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"] ){display:grid;grid-template-columns:minmax(0,1fr);gap:var(--ly-gap)}[data-ly-recipe="app-shell"]{min-block-size:100vh;min-block-size:var(--ly-shell-min);grid-template-areas:"header" "sidebar" "main" "aside" "footer";grid-template-rows:var(--ly-app-shell-base-rows)}[data-ly-recipe="dashboard"],[data-ly-recipe="docs"]{grid-template-areas:"header" "nav" "main" "aside" "footer"}[data-ly-recipe="list-detail"]{grid-template-areas:"primary" "secondary" "actions"}[data-ly-recipe="split-hero"]{grid-template-areas:"content" "media" "actions";align-items:var(--ly-split-hero-align,center)}[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"]{display:grid;gap:var(--ly-grid-gap)}[data-ly-recipe="gallery"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-gallery-min)),1fr))}[data-ly-recipe="card-grid"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-card-grid-min)),1fr))}:where( [data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )[data-ly-responsive="manual"]{grid-template-columns:minmax(0,1fr)}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )>*{min-inline-size:0;min-block-size:0}[data-ly-area="header"]{grid-area:header}[data-ly-area="nav"]{grid-area:nav}[data-ly-area="sidebar"]{grid-area:sidebar}[data-ly-area="main"]{grid-area:main}[data-ly-area="aside"]{grid-area:aside}[data-ly-area="footer"]{grid-area:footer}[data-ly-area="content"]{grid-area:content}[data-ly-area="media"]{grid-area:media}[data-ly-area="actions"]{grid-area:actions}[data-ly-area="primary"]{grid-area:primary}[data-ly-area="secondary"]{grid-area:secondary}@container ly-scope (min-width:42rem){[data-ly-recipe="split-hero"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-split-hero-wide-areas);grid-template-columns:var(--ly-split-hero-wide-columns)}}@container ly-scope (min-width:44rem){[data-ly-recipe="list-detail"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-list-detail-wide-areas);grid-template-columns:var(--ly-list-detail-wide-columns)}}@container ly-scope (min-width:48rem){[data-ly-recipe="docs"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-docs-wide-areas);grid-template-columns:var(--ly-docs-wide-columns)}}@container ly-scope (min-width:52rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-medium-areas);grid-template-columns:var(--ly-app-shell-medium-columns);grid-template-rows:var(--ly-app-shell-medium-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-medium-areas);grid-template-columns:var(--ly-dashboard-medium-columns)}}@container ly-scope (min-width:72rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-wide-areas);grid-template-columns:var(--ly-app-shell-wide-columns);grid-template-rows:var(--ly-app-shell-wide-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-wide-areas);grid-template-columns:var(--ly-dashboard-wide-columns)}}}@layer ly.utilities{.ly-cols-1{--ly-grid-columns:1}.ly-cols-2{--ly-grid-columns:2}.ly-cols-3{--ly-grid-columns:3}.ly-cols-4{--ly-grid-columns:4}.ly-cols-6{--ly-grid-columns:6}.ly-cols-12{--ly-grid-columns:12}.ly-span-1{grid-column:span 1}.ly-span-2{grid-column:span 2}.ly-span-3{grid-column:span 3}.ly-span-4{grid-column:span 4}.ly-span-full{grid-column:1 / -1}.ly-gap-0{--ly-gap:var(--ly-space-0);--ly-grid-gap:var(--ly-space-0);--ly-stack-gap:var(--ly-space-0);--ly-cluster-gap:var(--ly-space-0)}.ly-gap-2{--ly-gap:var(--ly-space-2);--ly-grid-gap:var(--ly-space-2);--ly-stack-gap:var(--ly-space-2);--ly-cluster-gap:var(--ly-space-2)}.ly-gap-4{--ly-gap:var(--ly-space-4);--ly-grid-gap:var(--ly-space-4);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-4)}.ly-gap-6{--ly-gap:var(--ly-space-6);--ly-grid-gap:var(--ly-space-6);--ly-stack-gap:var(--ly-space-6);--ly-cluster-gap:var(--ly-space-6)}.ly-gap-8{--ly-gap:var(--ly-space-8);--ly-grid-gap:var(--ly-space-8);--ly-stack-gap:var(--ly-space-8);--ly-cluster-gap:var(--ly-space-8)}.ly-pad-0{padding:var(--ly-space-0)}.ly-pad-2{padding:var(--ly-space-2)}.ly-pad-4{padding:var(--ly-space-4)}.ly-pad-6{padding:var(--ly-space-6)}.ly-pad-8{padding:var(--ly-space-8)}.ly-mx-auto{margin-inline:auto}.ly-w-full{inline-size:100%}.ly-h-full{block-size:100%}.ly-min-h-screen{min-block-size:100vh;min-block-size:100dvh}.ly-overflow-auto{overflow:auto}.ly-overflow-hidden{overflow:hidden}.ly-items-start{align-items:flex-start}.ly-items-center{align-items:center}.ly-items-end{align-items:flex-end}.ly-items-stretch{align-items:stretch}.ly-justify-start{justify-content:flex-start}.ly-justify-center{justify-content:center}.ly-justify-end{justify-content:flex-end}.ly-justify-between{justify-content:space-between}.ly-frame-1x1{--ly-frame-ratio:1 / 1}.ly-frame-3x2{--ly-frame-ratio:3 / 2}.ly-frame-4x3{--ly-frame-ratio:4 / 3}.ly-frame-16x9{--ly-frame-ratio:16 / 9}.ly-frame-21x9{--ly-frame-ratio:21 / 9}.ly-hidden{display:none !important}.ly-visually-hidden{position:absolute !important;inline-size:1px !important;block-size:1px !important;padding:0 !important;margin:-1px !important;overflow:hidden !important;clip:rect(0,0,0,0) !important;clip-path:inset(50%) !important;white-space:nowrap !important}}@layer ly.personalities.minimal-saas{:where(.ly-root[data-ly-layout="minimal-saas"]){--ly-personality-wrapper-max:88rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:16rem;--ly-recipe-rail:15rem;--ly-recipe-aside:16rem;--ly-split-primary:1.05fr;--ly-split-secondary:0.95fr}}@layer ly.personalities.bento{:where(.ly-root[data-ly-layout="bento"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-card-grid-min:14rem;--ly-gallery-min:11rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header" "sidebar main main" "aside main main" "footer footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.3fr) minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.maximalist{:where(.ly-root[data-ly-layout="maximalist"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-7);--ly-grid-min:10rem;--ly-card-grid-min:13rem;--ly-gallery-min:10rem;--ly-recipe-rail:15rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "main main sidebar aside" "main main footer footer";--ly-app-shell-wide-columns:minmax(0,1.4fr) minmax(0,0.6fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr) minmax(min(100%,var(--ly-recipe-aside)),0.38fr)}}@layer ly.personalities.bauhaus{:where(.ly-root[data-ly-layout="bauhaus"]){--ly-personality-wrapper-max:96rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "sidebar main main aside" "sidebar footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,0.7fr) minmax(0,1.3fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.tactile{:where(.ly-root[data-ly-layout="tactile"]){--ly-personality-wrapper-max:82rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:15rem;--ly-recipe-rail:17rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.5fr) minmax(0,0.9fr)}}@layer ly.personalities.neumorphism{:where(.ly-root[data-ly-layout="neumorphism"]){--ly-personality-wrapper-max:84rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:17rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header sidebar" "main sidebar" "aside sidebar" "footer sidebar";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-list-detail-wide-columns:minmax(0,1.1fr) minmax(min(100%,var(--ly-pane-size)),0.7fr)}}@layer ly.personalities.retrofuturism{:where(.ly-root[data-ly-layout="retrofuturism"]){--ly-personality-wrapper-max:106rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:14rem;--ly-recipe-aside:17rem;--ly-app-shell-wide-areas:"sidebar header aside" "sidebar main aside" "sidebar footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.brutalism{:where(.ly-root[data-ly-layout="brutalism"]){--ly-personality-wrapper-max:100rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(0,1.3fr) minmax(0,0.7fr) minmax(min(100%,var(--ly-recipe-rail)),0.34fr)}}@layer ly.personalities.cyberpunk{:where(.ly-root[data-ly-layout="cyberpunk"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-recipe-rail:13rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.2fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.32fr)}}@layer ly.personalities.y2k{:where(.ly-root[data-ly-layout="y2k"]){--ly-personality-wrapper-max:90rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:18rem;--ly-recipe-aside:16rem;--ly-app-shell-wide-areas:"header header sidebar" "aside main sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-aside)),0.34fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.28fr)}}@layer ly.personalities.retro-glass{:where(.ly-root[data-ly-layout="retro-glass"]){--ly-personality-wrapper-max:104rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:15rem;--ly-recipe-rail:18rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main main sidebar" "aside footer footer";--ly-app-shell-wide-columns:minmax(0,1.25fr) minmax(0,0.75fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr)}}@layer ly.personalities.f-pattern{:where(.ly-root[data-ly-layout="f-pattern"]){--ly-personality-wrapper-max:92rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.26fr) minmax(0,1.35fr) minmax(min(100%,var(--ly-recipe-aside)),0.65fr);--ly-split-hero-wide-columns:minmax(0,1.8fr) minmax(0,0.8fr)}}@layer ly.personalities.z-pattern{:where(.ly-root[data-ly-layout="z-pattern"]){--ly-personality-wrapper-max:108rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer aside sidebar";--ly-app-shell-wide-columns:minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-split-hero-wide-columns:minmax(0,1.5fr) minmax(0,0.5fr)}}@layer ly.personalities.split-screen{:where(.ly-root[data-ly-layout="split-screen"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:20rem;--ly-recipe-rail:24rem;--ly-recipe-aside:24rem;--ly-app-shell-wide-areas:"header header" "main sidebar" "aside sidebar" "footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-split-hero-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.mondrian{:where(.ly-root[data-ly-layout="mondrian"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:11rem;--ly-recipe-rail:13rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.22fr) minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr)}}@layer ly.personalities.synthwave{:where(.ly-root[data-ly-layout="synthwave"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header header" "sidebar main aside" "footer footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.34fr);--ly-reel-item-min:20rem}} +@layer ly.reset,ly.tokens,ly.wrappers,ly.primitives,ly.recipes,ly.utilities,ly.personalities,ly.context;@layer ly.tokens{:where(.ly-root){--ly-space-0:0;--ly-space-1:0.25rem;--ly-space-2:0.5rem;--ly-space-3:0.75rem;--ly-space-4:1rem;--ly-space-5:1.5rem;--ly-space-6:2rem;--ly-space-7:3rem;--ly-space-8:4rem;--ly-space-9:6rem;--ly-wrapper-compact:40rem;--ly-wrapper-prose:68ch;--ly-wrapper-content:72rem;--ly-wrapper-wide:112rem;--ly-page-padding-inline:clamp(1rem,3vw,3rem);--ly-safe-area-inline:max( env(safe-area-inset-left,0px),env(safe-area-inset-right,0px) );--ly-safe-area-block-start:env(safe-area-inset-top,0px);--ly-safe-area-block-end:env(safe-area-inset-bottom,0px);--ly-wrapper-gutter:var(--ly-page-padding-inline);--ly-profile-gap:var(--ly-space-5);--ly-gap:var(--ly-profile-gap);--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(2rem,4vh,4rem);--ly-section-padding-block-compact:clamp(1rem,2.5vh,2rem);--ly-header-height:4.5rem;--ly-sticky-position:sticky;--ly-cover-min:100vh;--ly-shell-min:100vh;--ly-scroll-max:min(70vh,50rem);--ly-switcher-threshold:42rem;--ly-sidebar-size:18rem;--ly-sidebar-content-min:50%;--ly-grid-columns:12;--ly-grid-min:16rem;--ly-split-min:20rem;--ly-pane-min:18rem;--ly-pane-size:22rem;--ly-media-min:18rem;--ly-media-size:14rem;--ly-reel-item-min:16rem;--ly-reel-item-max:28rem;--ly-frame-ratio:16 / 9;--ly-split-primary:1fr;--ly-split-secondary:1fr;--ly-recipe-rail:18rem;--ly-recipe-aside:22rem;--ly-gallery-min:12rem;--ly-card-grid-min:16rem;--ly-app-shell-base-rows:auto auto minmax(0,1fr) auto auto;--ly-app-shell-medium-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-medium-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-medium-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-medium-areas:"header header" "nav nav" "main aside" "footer footer";--ly-dashboard-medium-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-wide-areas:"nav header header" "nav main aside" "nav footer footer";--ly-dashboard-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-docs-wide-areas:"header header" "nav main" "nav aside" "footer footer";--ly-docs-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-list-detail-wide-areas:"primary secondary" "actions actions";--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.7fr) minmax(0,1fr);--ly-split-hero-wide-areas:"content media" "actions media";--ly-split-hero-wide-columns:minmax(0,var(--ly-split-primary,1fr)) minmax(0,var(--ly-split-secondary,1fr));container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}@supports (width:1cqi){:where(.ly-root){--ly-page-padding-inline:clamp(1rem,3cqi,3rem)}}@supports (height:100dvh){:where(.ly-root){--ly-cover-min:100dvh;--ly-shell-min:100dvh;--ly-scroll-max:min(70dvh,50rem)}}}@layer ly.reset{:where(.ly-root),:where(.ly-root *){box-sizing:border-box}:where(.ly-root){margin:0}:where(.ly-root img),:where(.ly-root svg),:where(.ly-root video),:where(.ly-root canvas),:where(.ly-root iframe){max-inline-size:100%}}@layer ly.tokens{@media (max-height:44rem){:where(.ly-root){--ly-gap:min(var(--ly-profile-gap),var(--ly-space-4));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:min(var(--ly-space-4),3vh);--ly-cluster-gap:min(var(--ly-space-3),2vh);--ly-section-padding-block:clamp(1.25rem,4vh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2vh,1.25rem);--ly-header-height:3.5rem;--ly-scroll-max:min(62vh,34rem)}@supports (height:100dvh){:where(.ly-root){--ly-stack-gap:min(var(--ly-space-4),3dvh);--ly-cluster-gap:min(var(--ly-space-3),2dvh);--ly-section-padding-block:clamp(1.25rem,4dvh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2dvh,1.25rem);--ly-scroll-max:min(62dvh,34rem)}}}@media (max-height:30rem){:where(.ly-root){--ly-cover-min:auto;--ly-shell-min:auto;--ly-sticky-position:static;--ly-section-padding-block:var(--ly-space-4);--ly-section-padding-block-compact:var(--ly-space-2);--ly-scroll-max:max(12rem,calc(100vh - 8rem))}@supports (height:100dvh){:where(.ly-root){--ly-scroll-max:max(12rem,calc(100dvh - 8rem))}}}}@layer ly.context{:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:clamp(1rem,2.5vh,2rem);--ly-section-padding-block-compact:clamp(0.5rem,1.5vh,1rem)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:var(--ly-profile-gap);--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(2rem,4vh,4rem);--ly-section-padding-block-compact:clamp(1rem,2.5vh,2rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:max(var(--ly-profile-gap),var(--ly-space-6));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-5);--ly-cluster-gap:var(--ly-space-4);--ly-section-padding-block:clamp(3rem,7vh,6rem);--ly-section-padding-block-compact:clamp(1.5rem,3.5vh,3rem)}@media (max-height:44rem){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:clamp(0.75rem,2vh,1.25rem);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:min(var(--ly-profile-gap),var(--ly-space-4));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(1.25rem,4vh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2vh,1.25rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:var(--ly-space-5);--ly-grid-gap:var(--ly-space-5);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(1.5rem,6vh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3vh,1.25rem)}@supports (height:100dvh){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-section-padding-block:clamp(0.75rem,2dvh,1.25rem)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-section-padding-block:clamp(1.25rem,4dvh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2dvh,1.25rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-section-padding-block:clamp(1.5rem,6dvh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3dvh,1.25rem)}}}@media (max-height:30rem){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-2);--ly-grid-gap:var(--ly-space-2);--ly-stack-gap:var(--ly-space-2);--ly-cluster-gap:var(--ly-space-1);--ly-section-padding-block:var(--ly-space-3);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:var(--ly-space-4);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:var(--ly-space-4);--ly-grid-gap:var(--ly-space-4);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:var(--ly-space-5);--ly-section-padding-block-compact:var(--ly-space-3)}}}@layer ly.wrappers{.ly-wrapper{--ly-wrapper-max:var(--ly-personality-wrapper-max,var(--ly-wrapper-content));--ly-wrapper-fluid-gutter:var(--ly-wrapper-gutter);--ly-wrapper-local-gutter:max( var(--ly-wrapper-fluid-gutter),var(--ly-safe-area-inline) );container-name:ly-scope;container-type:inline-size;inline-size:min(100%,calc(var(--ly-wrapper-max) + (var(--ly-wrapper-local-gutter) * 2)));max-inline-size:100%;margin-inline:auto;padding-inline:var(--ly-wrapper-local-gutter)}.ly-wrapper--compact{--ly-wrapper-max:var(--ly-wrapper-compact)}.ly-wrapper--prose{--ly-wrapper-max:var(--ly-wrapper-prose)}.ly-wrapper--content{--ly-wrapper-max:var(--ly-wrapper-content)}.ly-wrapper--wide{--ly-wrapper-max:var(--ly-wrapper-wide)}.ly-wrapper--full{--ly-wrapper-max:100%}.ly-wrapper--breakout{--ly-wrapper-max:100%;--ly-breakout-content-size:min( var(--ly-wrapper-content),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );--ly-breakout-feature-size:min( var(--ly-wrapper-wide),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );display:grid;grid-template-columns:[full-start] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [feature-start] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [content-start] minmax(0,var(--ly-breakout-content-size)) [content-end] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [feature-end] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [full-end];inline-size:100%;padding-inline:0}.ly-wrapper--breakout>*{grid-column:content;min-inline-size:0;max-inline-size:100%}.ly-wrapper--breakout>:where(.ly-lane--content,[data-ly-lane="content"]){grid-column:content}.ly-wrapper--breakout>:where(.ly-lane--feature,[data-ly-lane="feature"]){grid-column:feature}.ly-wrapper--breakout>:where(.ly-lane--full,[data-ly-lane="full"]){grid-column:full}}@layer ly.primitives{.ly-page{min-block-size:100vh;min-block-size:100dvh;padding-block-start:var(--ly-safe-area-block-start);padding-block-end:var(--ly-safe-area-block-end)}.ly-header,.ly-footer,.ly-main,.ly-section,.ly-surface{min-inline-size:0;min-block-size:0}.ly-header{min-block-size:var(--ly-header-height)}.ly-header--sticky{position:var(--ly-sticky-position,sticky);inset-block-start:0;z-index:var(--ly-z-header,20)}.ly-section{padding-block:var(--ly-section-padding-block)}.ly-section--compact{padding-block:var(--ly-section-padding-block-compact)}.ly-section--flush{padding-block:0}.ly-readable{max-inline-size:var(--ly-wrapper-prose)}.ly-stack{display:flex;flex-direction:column;gap:var(--ly-stack-gap)}.ly-cluster{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ly-cluster-gap)}.ly-center{inline-size:min(100%,var(--ly-center-max,var(--ly-wrapper-content)));margin-inline:auto}.ly-cover{display:flex;flex-direction:column;min-block-size:100vh;min-block-size:var(--ly-cover-min);padding-block:var(--ly-cover-padding,var(--ly-space-5))}.ly-cover>:where(.ly-cover__center,[data-ly-cover-center]){margin-block:auto}.ly-switcher{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-switcher>*{flex-basis:calc((var(--ly-switcher-threshold) - 100%) * 999);flex-grow:1;min-inline-size:0}.ly-sidebar{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-sidebar>:where(.ly-sidebar__side,[data-ly-sidebar="side"]){flex-basis:var(--ly-sidebar-size);flex-grow:1;min-inline-size:min(100%,var(--ly-sidebar-size))}.ly-sidebar>:where(.ly-sidebar__content,[data-ly-sidebar="content"]){flex-basis:0;flex-grow:999;min-inline-size:min(100%,var(--ly-sidebar-content-min))}.ly-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-grid-min)),1fr));gap:var(--ly-grid-gap)}.ly-grid--fixed{grid-template-columns:repeat(var(--ly-grid-columns),minmax(0,1fr))}.ly-split{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-split-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-split-align,center)}.ly-panes{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-pane-min)),1fr));gap:var(--ly-gap)}.ly-media{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-media-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-media-align,start)}.ly-media>:where(.ly-media__actions,[data-ly-media="actions"]){min-inline-size:min(100%,var(--ly-media-size))}.ly-grid>*,.ly-split>*,.ly-panes>*,.ly-media>*{min-inline-size:0;min-block-size:0}.ly-reel{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(min(100%,var(--ly-reel-item-min)),var(--ly-reel-item-max));gap:var(--ly-grid-gap);overflow-x:auto;overscroll-behavior-inline:contain;scroll-snap-type:inline proximity}.ly-reel>*{min-inline-size:0;scroll-snap-align:start}.ly-frame{aspect-ratio:var(--ly-frame-ratio);overflow:hidden}.ly-frame>*{inline-size:100%;block-size:100%;object-fit:cover}.ly-scroll{min-block-size:0;max-block-size:var(--ly-scroll-max);overflow-x:clip;overflow-y:auto;overscroll-behavior-block:contain}}@layer ly.recipes{:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] ){container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"] ){display:grid;grid-template-columns:minmax(0,1fr);gap:var(--ly-gap)}[data-ly-recipe="app-shell"]{min-block-size:100vh;min-block-size:var(--ly-shell-min);grid-template-areas:"header" "sidebar" "main" "aside" "footer";grid-template-rows:var(--ly-app-shell-base-rows)}[data-ly-recipe="dashboard"],[data-ly-recipe="docs"]{grid-template-areas:"header" "nav" "main" "aside" "footer"}[data-ly-recipe="list-detail"]{grid-template-areas:"primary" "secondary" "actions"}[data-ly-recipe="split-hero"]{grid-template-areas:"content" "media" "actions";align-items:var(--ly-split-hero-align,center)}[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"]{display:grid;gap:var(--ly-grid-gap)}[data-ly-recipe="gallery"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-gallery-min)),1fr))}[data-ly-recipe="card-grid"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-card-grid-min)),1fr))}:where( [data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )[data-ly-responsive="manual"]{grid-template-columns:minmax(0,1fr)}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )>*{min-inline-size:0;min-block-size:0}[data-ly-area="header"]{grid-area:header}[data-ly-area="nav"]{grid-area:nav}[data-ly-area="sidebar"]{grid-area:sidebar}[data-ly-area="main"]{grid-area:main}[data-ly-area="aside"]{grid-area:aside}[data-ly-area="footer"]{grid-area:footer}[data-ly-area="content"]{grid-area:content}[data-ly-area="media"]{grid-area:media}[data-ly-area="actions"]{grid-area:actions}[data-ly-area="primary"]{grid-area:primary}[data-ly-area="secondary"]{grid-area:secondary}@container ly-scope (min-width:42rem){[data-ly-recipe="split-hero"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-split-hero-wide-areas);grid-template-columns:var(--ly-split-hero-wide-columns)}}@container ly-scope (min-width:44rem){[data-ly-recipe="list-detail"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-list-detail-wide-areas);grid-template-columns:var(--ly-list-detail-wide-columns)}}@container ly-scope (min-width:48rem){[data-ly-recipe="docs"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-docs-wide-areas);grid-template-columns:var(--ly-docs-wide-columns)}}@container ly-scope (min-width:52rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-medium-areas);grid-template-columns:var(--ly-app-shell-medium-columns);grid-template-rows:var(--ly-app-shell-medium-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-medium-areas);grid-template-columns:var(--ly-dashboard-medium-columns)}}@container ly-scope (min-width:72rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-wide-areas);grid-template-columns:var(--ly-app-shell-wide-columns);grid-template-rows:var(--ly-app-shell-wide-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-wide-areas);grid-template-columns:var(--ly-dashboard-wide-columns)}}}@layer ly.utilities{.ly-cols-1{--ly-grid-columns:1}.ly-cols-2{--ly-grid-columns:2}.ly-cols-3{--ly-grid-columns:3}.ly-cols-4{--ly-grid-columns:4}.ly-cols-6{--ly-grid-columns:6}.ly-cols-12{--ly-grid-columns:12}.ly-span-1{grid-column:span 1}.ly-span-2{grid-column:span 2}.ly-span-3{grid-column:span 3}.ly-span-4{grid-column:span 4}.ly-span-full{grid-column:1 / -1}.ly-gap-0{--ly-gap:var(--ly-space-0);--ly-grid-gap:var(--ly-space-0);--ly-stack-gap:var(--ly-space-0);--ly-cluster-gap:var(--ly-space-0)}.ly-gap-2{--ly-gap:var(--ly-space-2);--ly-grid-gap:var(--ly-space-2);--ly-stack-gap:var(--ly-space-2);--ly-cluster-gap:var(--ly-space-2)}.ly-gap-4{--ly-gap:var(--ly-space-4);--ly-grid-gap:var(--ly-space-4);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-4)}.ly-gap-6{--ly-gap:var(--ly-space-6);--ly-grid-gap:var(--ly-space-6);--ly-stack-gap:var(--ly-space-6);--ly-cluster-gap:var(--ly-space-6)}.ly-gap-8{--ly-gap:var(--ly-space-8);--ly-grid-gap:var(--ly-space-8);--ly-stack-gap:var(--ly-space-8);--ly-cluster-gap:var(--ly-space-8)}.ly-pad-0{padding:var(--ly-space-0)}.ly-pad-2{padding:var(--ly-space-2)}.ly-pad-4{padding:var(--ly-space-4)}.ly-pad-6{padding:var(--ly-space-6)}.ly-pad-8{padding:var(--ly-space-8)}.ly-mx-auto{margin-inline:auto}.ly-w-full{inline-size:100%}.ly-h-full{block-size:100%}.ly-min-h-screen{min-block-size:100vh;min-block-size:100dvh}.ly-overflow-auto{overflow:auto}.ly-overflow-hidden{overflow:hidden}.ly-items-start{align-items:flex-start}.ly-items-center{align-items:center}.ly-items-end{align-items:flex-end}.ly-items-stretch{align-items:stretch}.ly-justify-start{justify-content:flex-start}.ly-justify-center{justify-content:center}.ly-justify-end{justify-content:flex-end}.ly-justify-between{justify-content:space-between}.ly-frame-1x1{--ly-frame-ratio:1 / 1}.ly-frame-3x2{--ly-frame-ratio:3 / 2}.ly-frame-4x3{--ly-frame-ratio:4 / 3}.ly-frame-16x9{--ly-frame-ratio:16 / 9}.ly-frame-21x9{--ly-frame-ratio:21 / 9}.ly-hidden{display:none !important}.ly-visually-hidden{position:absolute !important;inline-size:1px !important;block-size:1px !important;padding:0 !important;margin:-1px !important;overflow:hidden !important;clip:rect(0,0,0,0) !important;clip-path:inset(50%) !important;white-space:nowrap !important}}@layer ly.personalities.minimal-saas{:where(.ly-root[data-ly-layout="minimal-saas"]){--ly-personality-wrapper-max:88rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:16rem;--ly-recipe-rail:15rem;--ly-recipe-aside:16rem;--ly-split-primary:1.05fr;--ly-split-secondary:0.95fr}}@layer ly.personalities.bento{:where(.ly-root[data-ly-layout="bento"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-card-grid-min:14rem;--ly-gallery-min:11rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header" "sidebar main main" "aside main main" "footer footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.3fr) minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.maximalist{:where(.ly-root[data-ly-layout="maximalist"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-7);--ly-grid-min:10rem;--ly-card-grid-min:13rem;--ly-gallery-min:10rem;--ly-recipe-rail:15rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "main main sidebar aside" "main main footer footer";--ly-app-shell-wide-columns:minmax(0,1.4fr) minmax(0,0.6fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr) minmax(min(100%,var(--ly-recipe-aside)),0.38fr)}}@layer ly.personalities.bauhaus{:where(.ly-root[data-ly-layout="bauhaus"]){--ly-personality-wrapper-max:96rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "sidebar main main aside" "sidebar footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,0.7fr) minmax(0,1.3fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.tactile{:where(.ly-root[data-ly-layout="tactile"]){--ly-personality-wrapper-max:82rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:15rem;--ly-recipe-rail:17rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.5fr) minmax(0,0.9fr)}}@layer ly.personalities.neumorphism{:where(.ly-root[data-ly-layout="neumorphism"]){--ly-personality-wrapper-max:84rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:17rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header sidebar" "main sidebar" "aside sidebar" "footer sidebar";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-list-detail-wide-columns:minmax(0,1.1fr) minmax(min(100%,var(--ly-pane-size)),0.7fr)}}@layer ly.personalities.retrofuturism{:where(.ly-root[data-ly-layout="retrofuturism"]){--ly-personality-wrapper-max:106rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:14rem;--ly-recipe-aside:17rem;--ly-app-shell-wide-areas:"sidebar header aside" "sidebar main aside" "sidebar footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.brutalism{:where(.ly-root[data-ly-layout="brutalism"]){--ly-personality-wrapper-max:100rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(0,1.3fr) minmax(0,0.7fr) minmax(min(100%,var(--ly-recipe-rail)),0.34fr)}}@layer ly.personalities.cyberpunk{:where(.ly-root[data-ly-layout="cyberpunk"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-recipe-rail:13rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.2fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.32fr)}}@layer ly.personalities.y2k{:where(.ly-root[data-ly-layout="y2k"]){--ly-personality-wrapper-max:90rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:18rem;--ly-recipe-aside:16rem;--ly-app-shell-wide-areas:"header header sidebar" "aside main sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-aside)),0.34fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.28fr)}}@layer ly.personalities.retro-glass{:where(.ly-root[data-ly-layout="retro-glass"]){--ly-personality-wrapper-max:104rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:15rem;--ly-recipe-rail:18rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main main sidebar" "aside footer footer";--ly-app-shell-wide-columns:minmax(0,1.25fr) minmax(0,0.75fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr)}}@layer ly.personalities.f-pattern{:where(.ly-root[data-ly-layout="f-pattern"]){--ly-personality-wrapper-max:92rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.26fr) minmax(0,1.35fr) minmax(min(100%,var(--ly-recipe-aside)),0.65fr);--ly-split-hero-wide-columns:minmax(0,1.8fr) minmax(0,0.8fr)}}@layer ly.personalities.z-pattern{:where(.ly-root[data-ly-layout="z-pattern"]){--ly-personality-wrapper-max:108rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer aside sidebar";--ly-app-shell-wide-columns:minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-split-hero-wide-columns:minmax(0,1.5fr) minmax(0,0.5fr)}}@layer ly.personalities.split-screen{:where(.ly-root[data-ly-layout="split-screen"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:20rem;--ly-recipe-rail:24rem;--ly-recipe-aside:24rem;--ly-app-shell-wide-areas:"header header" "main sidebar" "aside sidebar" "footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-split-hero-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.mondrian{:where(.ly-root[data-ly-layout="mondrian"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:11rem;--ly-recipe-rail:13rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.22fr) minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr)}}@layer ly.personalities.synthwave{:where(.ly-root[data-ly-layout="synthwave"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header header" "sidebar main aside" "footer footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.34fr);--ly-reel-item-min:20rem}} diff --git a/dist/personalities.css b/dist/personalities.css index b98c221..4fbf79f 100644 --- a/dist/personalities.css +++ b/dist/personalities.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @import url("./personalities/minimal-saas.css"); @import url("./personalities/bento.css"); diff --git a/dist/personalities/bauhaus.css b/dist/personalities/bauhaus.css index 52ce8c2..09bb47b 100644 --- a/dist/personalities/bauhaus.css +++ b/dist/personalities/bauhaus.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.bauhaus { :where(.ly-root[data-ly-layout="bauhaus"]) { diff --git a/dist/personalities/bento.css b/dist/personalities/bento.css index b09b3d9..295fb81 100644 --- a/dist/personalities/bento.css +++ b/dist/personalities/bento.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.bento { :where(.ly-root[data-ly-layout="bento"]) { diff --git a/dist/personalities/brutalism.css b/dist/personalities/brutalism.css index 8815608..91dfd16 100644 --- a/dist/personalities/brutalism.css +++ b/dist/personalities/brutalism.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.brutalism { :where(.ly-root[data-ly-layout="brutalism"]) { diff --git a/dist/personalities/cyberpunk.css b/dist/personalities/cyberpunk.css index dca349f..cecd1cb 100644 --- a/dist/personalities/cyberpunk.css +++ b/dist/personalities/cyberpunk.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.cyberpunk { :where(.ly-root[data-ly-layout="cyberpunk"]) { diff --git a/dist/personalities/f-pattern.css b/dist/personalities/f-pattern.css index 904e0b7..5d0ef16 100644 --- a/dist/personalities/f-pattern.css +++ b/dist/personalities/f-pattern.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.f-pattern { :where(.ly-root[data-ly-layout="f-pattern"]) { diff --git a/dist/personalities/maximalist.css b/dist/personalities/maximalist.css index 79fc40d..82cdf0c 100644 --- a/dist/personalities/maximalist.css +++ b/dist/personalities/maximalist.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.maximalist { :where(.ly-root[data-ly-layout="maximalist"]) { diff --git a/dist/personalities/minimal-saas.css b/dist/personalities/minimal-saas.css index 9b2bb14..9a7f780 100644 --- a/dist/personalities/minimal-saas.css +++ b/dist/personalities/minimal-saas.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.minimal-saas { :where(.ly-root[data-ly-layout="minimal-saas"]) { diff --git a/dist/personalities/mondrian.css b/dist/personalities/mondrian.css index 0b1fc66..2e67153 100644 --- a/dist/personalities/mondrian.css +++ b/dist/personalities/mondrian.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.mondrian { :where(.ly-root[data-ly-layout="mondrian"]) { diff --git a/dist/personalities/neumorphism.css b/dist/personalities/neumorphism.css index 48daa23..f52a353 100644 --- a/dist/personalities/neumorphism.css +++ b/dist/personalities/neumorphism.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.neumorphism { :where(.ly-root[data-ly-layout="neumorphism"]) { diff --git a/dist/personalities/retro-glass.css b/dist/personalities/retro-glass.css index f0c959d..66c5031 100644 --- a/dist/personalities/retro-glass.css +++ b/dist/personalities/retro-glass.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.retro-glass { :where(.ly-root[data-ly-layout="retro-glass"]) { diff --git a/dist/personalities/retrofuturism.css b/dist/personalities/retrofuturism.css index 106d878..1ce1190 100644 --- a/dist/personalities/retrofuturism.css +++ b/dist/personalities/retrofuturism.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.retrofuturism { :where(.ly-root[data-ly-layout="retrofuturism"]) { diff --git a/dist/personalities/split-screen.css b/dist/personalities/split-screen.css index a828162..5b04b0e 100644 --- a/dist/personalities/split-screen.css +++ b/dist/personalities/split-screen.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.split-screen { :where(.ly-root[data-ly-layout="split-screen"]) { diff --git a/dist/personalities/synthwave.css b/dist/personalities/synthwave.css index 71f400a..7c2a4e0 100644 --- a/dist/personalities/synthwave.css +++ b/dist/personalities/synthwave.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.synthwave { :where(.ly-root[data-ly-layout="synthwave"]) { diff --git a/dist/personalities/tactile.css b/dist/personalities/tactile.css index f82dd90..e94d742 100644 --- a/dist/personalities/tactile.css +++ b/dist/personalities/tactile.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.tactile { :where(.ly-root[data-ly-layout="tactile"]) { diff --git a/dist/personalities/y2k.css b/dist/personalities/y2k.css index fbaa789..7e35ff2 100644 --- a/dist/personalities/y2k.css +++ b/dist/personalities/y2k.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.y2k { :where(.ly-root[data-ly-layout="y2k"]) { diff --git a/dist/personalities/z-pattern.css b/dist/personalities/z-pattern.css index 8d6c46e..34ac8f8 100644 --- a/dist/personalities/z-pattern.css +++ b/dist/personalities/z-pattern.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.z-pattern { :where(.ly-root[data-ly-layout="z-pattern"]) { diff --git a/dist/primitives.css b/dist/primitives.css index dd3262a..e23f185 100644 --- a/dist/primitives.css +++ b/dist/primitives.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; /* Composition primitives use intrinsic sizing so their content chooses reflow points. */ diff --git a/dist/recipes.css b/dist/recipes.css index bd8f16e..de91861 100644 --- a/dist/recipes.css +++ b/dist/recipes.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; /* Recipes preserve the narrow-screen DOM order. Automatic topology queries diff --git a/dist/utilities.css b/dist/utilities.css index 87330a2..a322ca5 100644 --- a/dist/utilities.css +++ b/dist/utilities.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; /* Optional structural utilities expose direct token overrides without breakpoint tiers. */ diff --git a/dist/wrappers.css b/dist/wrappers.css index b9b86d3..af0f564 100644 --- a/dist/wrappers.css +++ b/dist/wrappers.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; /* Wrappers provide fluid measures and become the nearest responsive scope. */ diff --git a/personalities.json b/personalities.json index 6e1d78a..2d02da3 100644 --- a/personalities.json +++ b/personalities.json @@ -4,6 +4,7 @@ "selector": "data-ly-layout", "independentSelectors": [ "data-ly-layout", + "data-ly-density", "data-ui", "data-theme", "data-mode" diff --git a/scripts/build.mjs b/scripts/build.mjs index 4230af1..3b0b8e8 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -7,7 +7,7 @@ const sourceDir = join(root, "styles"); const distDir = join(root, "dist"); const manifest = JSON.parse(await readFile(join(root, "manifest.json"), "utf8")); const cascadeLayerPrelude = - "@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities;"; + "@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context;"; const coreModuleFiles = [ "foundation.css", @@ -16,6 +16,13 @@ const coreModuleFiles = [ "recipes.css", "utilities.css" ]; +/** + * Builds generated personality metadata from the public package manifest. + * + * @param {Record} sourceManifest Parsed public manifest. + * @returns {{schemaVersion: number, generatedFrom: string, selector: string, independentSelectors: string[], personalities: object[]}} + * @throws {Error} When personality records are incomplete or inconsistent. + */ function buildPersonalityMetadata(sourceManifest) { const personalities = Array.isArray(sourceManifest.personalities) ? sourceManifest.personalities @@ -36,7 +43,13 @@ function buildPersonalityMetadata(sourceManifest) { schemaVersion: 1, generatedFrom: "manifest.json", selector: "data-ly-layout", - independentSelectors: ["data-ly-layout", "data-ui", "data-theme", "data-mode"], + independentSelectors: [ + "data-ly-layout", + "data-ly-density", + "data-ui", + "data-theme", + "data-mode" + ], personalities: pairings }; } diff --git a/styles/core.css b/styles/core.css index b215325..9c19e73 100644 --- a/styles/core.css +++ b/styles/core.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @import url("./foundation.css"); @import url("./wrappers.css"); diff --git a/styles/foundation.css b/styles/foundation.css index 368283c..96ab075 100644 --- a/styles/foundation.css +++ b/styles/foundation.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; /* Foundation owns only shared spatial tokens, containment, and defensive @@ -36,8 +36,8 @@ --ly-grid-gap: var(--ly-gap); --ly-stack-gap: var(--ly-space-4); --ly-cluster-gap: var(--ly-space-3); - --ly-section-padding-block: clamp(3rem, 7vh, 6rem); - --ly-section-padding-block-compact: clamp(1.5rem, 3.5vh, 3rem); + --ly-section-padding-block: clamp(2rem, 4vh, 4rem); + --ly-section-padding-block-compact: clamp(1rem, 2.5vh, 2rem); --ly-header-height: 4.5rem; --ly-sticky-position: sticky; --ly-cover-min: 100vh; @@ -172,8 +172,8 @@ --ly-grid-gap: var(--ly-gap); --ly-stack-gap: min(var(--ly-space-4), 3vh); --ly-cluster-gap: min(var(--ly-space-3), 2vh); - --ly-section-padding-block: clamp(1.5rem, 6vh, 3rem); - --ly-section-padding-block-compact: clamp(0.75rem, 3vh, 1.25rem); + --ly-section-padding-block: clamp(1.25rem, 4vh, 2rem); + --ly-section-padding-block-compact: clamp(0.75rem, 2vh, 1.25rem); --ly-header-height: 3.5rem; --ly-scroll-max: min(62vh, 34rem); } @@ -182,8 +182,8 @@ :where(.ly-root) { --ly-stack-gap: min(var(--ly-space-4), 3dvh); --ly-cluster-gap: min(var(--ly-space-3), 2dvh); - --ly-section-padding-block: clamp(1.5rem, 6dvh, 3rem); - --ly-section-padding-block-compact: clamp(0.75rem, 3dvh, 1.25rem); + --ly-section-padding-block: clamp(1.25rem, 4dvh, 2rem); + --ly-section-padding-block-compact: clamp(0.75rem, 2dvh, 1.25rem); --ly-scroll-max: min(62dvh, 34rem); } } @@ -194,8 +194,8 @@ --ly-cover-min: auto; --ly-shell-min: auto; --ly-sticky-position: static; - --ly-section-padding-block: var(--ly-space-5); - --ly-section-padding-block-compact: var(--ly-space-3); + --ly-section-padding-block: var(--ly-space-4); + --ly-section-padding-block-compact: var(--ly-space-2); --ly-scroll-max: max(12rem, calc(100vh - 8rem)); } @@ -206,3 +206,142 @@ } } } + +@layer ly.context { + :where( + .ly-root[data-ly-density="compact"], + .ly-root [data-ly-density="compact"] + ) { + --ly-gap: var(--ly-space-3); + --ly-grid-gap: var(--ly-space-3); + --ly-stack-gap: var(--ly-space-3); + --ly-cluster-gap: var(--ly-space-2); + --ly-section-padding-block: clamp(1rem, 2.5vh, 2rem); + --ly-section-padding-block-compact: clamp(0.5rem, 1.5vh, 1rem); + } + + :where( + .ly-root[data-ly-density="normal"], + .ly-root [data-ly-density="normal"] + ) { + --ly-gap: var(--ly-profile-gap); + --ly-grid-gap: var(--ly-gap); + --ly-stack-gap: var(--ly-space-4); + --ly-cluster-gap: var(--ly-space-3); + --ly-section-padding-block: clamp(2rem, 4vh, 4rem); + --ly-section-padding-block-compact: clamp(1rem, 2.5vh, 2rem); + } + + :where( + .ly-root[data-ly-density="spacious"], + .ly-root [data-ly-density="spacious"] + ) { + --ly-gap: max(var(--ly-profile-gap), var(--ly-space-6)); + --ly-grid-gap: var(--ly-gap); + --ly-stack-gap: var(--ly-space-5); + --ly-cluster-gap: var(--ly-space-4); + --ly-section-padding-block: clamp(3rem, 7vh, 6rem); + --ly-section-padding-block-compact: clamp(1.5rem, 3.5vh, 3rem); + } + + @media (max-height: 44rem) { + :where( + .ly-root[data-ly-density="compact"], + .ly-root [data-ly-density="compact"] + ) { + --ly-gap: var(--ly-space-3); + --ly-grid-gap: var(--ly-space-3); + --ly-stack-gap: var(--ly-space-3); + --ly-cluster-gap: var(--ly-space-2); + --ly-section-padding-block: clamp(0.75rem, 2vh, 1.25rem); + --ly-section-padding-block-compact: var(--ly-space-2); + } + + :where( + .ly-root[data-ly-density="normal"], + .ly-root [data-ly-density="normal"] + ) { + --ly-gap: min(var(--ly-profile-gap), var(--ly-space-4)); + --ly-grid-gap: var(--ly-gap); + --ly-stack-gap: var(--ly-space-4); + --ly-cluster-gap: var(--ly-space-3); + --ly-section-padding-block: clamp(1.25rem, 4vh, 2rem); + --ly-section-padding-block-compact: clamp(0.75rem, 2vh, 1.25rem); + } + + :where( + .ly-root[data-ly-density="spacious"], + .ly-root [data-ly-density="spacious"] + ) { + --ly-gap: var(--ly-space-5); + --ly-grid-gap: var(--ly-space-5); + --ly-stack-gap: var(--ly-space-4); + --ly-cluster-gap: var(--ly-space-3); + --ly-section-padding-block: clamp(1.5rem, 6vh, 3rem); + --ly-section-padding-block-compact: clamp(0.75rem, 3vh, 1.25rem); + } + + @supports (height: 100dvh) { + :where( + .ly-root[data-ly-density="compact"], + .ly-root [data-ly-density="compact"] + ) { + --ly-section-padding-block: clamp(0.75rem, 2dvh, 1.25rem); + } + + :where( + .ly-root[data-ly-density="normal"], + .ly-root [data-ly-density="normal"] + ) { + --ly-section-padding-block: clamp(1.25rem, 4dvh, 2rem); + --ly-section-padding-block-compact: clamp(0.75rem, 2dvh, 1.25rem); + } + + :where( + .ly-root[data-ly-density="spacious"], + .ly-root [data-ly-density="spacious"] + ) { + --ly-section-padding-block: clamp(1.5rem, 6dvh, 3rem); + --ly-section-padding-block-compact: clamp(0.75rem, 3dvh, 1.25rem); + } + } + } + + @media (max-height: 30rem) { + :where( + .ly-root[data-ly-density="compact"], + .ly-root [data-ly-density="compact"] + ) { + --ly-gap: var(--ly-space-2); + --ly-grid-gap: var(--ly-space-2); + --ly-stack-gap: var(--ly-space-2); + --ly-cluster-gap: var(--ly-space-1); + --ly-section-padding-block: var(--ly-space-3); + --ly-section-padding-block-compact: var(--ly-space-2); + } + + :where( + .ly-root[data-ly-density="normal"], + .ly-root [data-ly-density="normal"] + ) { + --ly-gap: var(--ly-space-3); + --ly-grid-gap: var(--ly-space-3); + --ly-stack-gap: var(--ly-space-3); + --ly-cluster-gap: var(--ly-space-2); + --ly-section-padding-block: var(--ly-space-4); + --ly-section-padding-block-compact: var(--ly-space-2); + } + + :where( + .ly-root[data-ly-density="spacious"], + .ly-root [data-ly-density="spacious"] + ) { + --ly-gap: var(--ly-space-4); + --ly-grid-gap: var(--ly-space-4); + --ly-stack-gap: var(--ly-space-4); + --ly-cluster-gap: var(--ly-space-3); + --ly-section-padding-block: var(--ly-space-5); + --ly-section-padding-block-compact: var(--ly-space-3); + } + } +} diff --git a/styles/personalities.css b/styles/personalities.css index b98c221..4fbf79f 100644 --- a/styles/personalities.css +++ b/styles/personalities.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @import url("./personalities/minimal-saas.css"); @import url("./personalities/bento.css"); diff --git a/styles/personalities/bauhaus.css b/styles/personalities/bauhaus.css index 52ce8c2..09bb47b 100644 --- a/styles/personalities/bauhaus.css +++ b/styles/personalities/bauhaus.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.bauhaus { :where(.ly-root[data-ly-layout="bauhaus"]) { diff --git a/styles/personalities/bento.css b/styles/personalities/bento.css index b09b3d9..295fb81 100644 --- a/styles/personalities/bento.css +++ b/styles/personalities/bento.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.bento { :where(.ly-root[data-ly-layout="bento"]) { diff --git a/styles/personalities/brutalism.css b/styles/personalities/brutalism.css index 8815608..91dfd16 100644 --- a/styles/personalities/brutalism.css +++ b/styles/personalities/brutalism.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.brutalism { :where(.ly-root[data-ly-layout="brutalism"]) { diff --git a/styles/personalities/cyberpunk.css b/styles/personalities/cyberpunk.css index dca349f..cecd1cb 100644 --- a/styles/personalities/cyberpunk.css +++ b/styles/personalities/cyberpunk.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.cyberpunk { :where(.ly-root[data-ly-layout="cyberpunk"]) { diff --git a/styles/personalities/f-pattern.css b/styles/personalities/f-pattern.css index 904e0b7..5d0ef16 100644 --- a/styles/personalities/f-pattern.css +++ b/styles/personalities/f-pattern.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.f-pattern { :where(.ly-root[data-ly-layout="f-pattern"]) { diff --git a/styles/personalities/maximalist.css b/styles/personalities/maximalist.css index 79fc40d..82cdf0c 100644 --- a/styles/personalities/maximalist.css +++ b/styles/personalities/maximalist.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.maximalist { :where(.ly-root[data-ly-layout="maximalist"]) { diff --git a/styles/personalities/minimal-saas.css b/styles/personalities/minimal-saas.css index 9b2bb14..9a7f780 100644 --- a/styles/personalities/minimal-saas.css +++ b/styles/personalities/minimal-saas.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.minimal-saas { :where(.ly-root[data-ly-layout="minimal-saas"]) { diff --git a/styles/personalities/mondrian.css b/styles/personalities/mondrian.css index 0b1fc66..2e67153 100644 --- a/styles/personalities/mondrian.css +++ b/styles/personalities/mondrian.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.mondrian { :where(.ly-root[data-ly-layout="mondrian"]) { diff --git a/styles/personalities/neumorphism.css b/styles/personalities/neumorphism.css index 48daa23..f52a353 100644 --- a/styles/personalities/neumorphism.css +++ b/styles/personalities/neumorphism.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.neumorphism { :where(.ly-root[data-ly-layout="neumorphism"]) { diff --git a/styles/personalities/retro-glass.css b/styles/personalities/retro-glass.css index f0c959d..66c5031 100644 --- a/styles/personalities/retro-glass.css +++ b/styles/personalities/retro-glass.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.retro-glass { :where(.ly-root[data-ly-layout="retro-glass"]) { diff --git a/styles/personalities/retrofuturism.css b/styles/personalities/retrofuturism.css index 106d878..1ce1190 100644 --- a/styles/personalities/retrofuturism.css +++ b/styles/personalities/retrofuturism.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.retrofuturism { :where(.ly-root[data-ly-layout="retrofuturism"]) { diff --git a/styles/personalities/split-screen.css b/styles/personalities/split-screen.css index a828162..5b04b0e 100644 --- a/styles/personalities/split-screen.css +++ b/styles/personalities/split-screen.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.split-screen { :where(.ly-root[data-ly-layout="split-screen"]) { diff --git a/styles/personalities/synthwave.css b/styles/personalities/synthwave.css index 71f400a..7c2a4e0 100644 --- a/styles/personalities/synthwave.css +++ b/styles/personalities/synthwave.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.synthwave { :where(.ly-root[data-ly-layout="synthwave"]) { diff --git a/styles/personalities/tactile.css b/styles/personalities/tactile.css index f82dd90..e94d742 100644 --- a/styles/personalities/tactile.css +++ b/styles/personalities/tactile.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.tactile { :where(.ly-root[data-ly-layout="tactile"]) { diff --git a/styles/personalities/y2k.css b/styles/personalities/y2k.css index fbaa789..7e35ff2 100644 --- a/styles/personalities/y2k.css +++ b/styles/personalities/y2k.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.y2k { :where(.ly-root[data-ly-layout="y2k"]) { diff --git a/styles/personalities/z-pattern.css b/styles/personalities/z-pattern.css index 8d6c46e..34ac8f8 100644 --- a/styles/personalities/z-pattern.css +++ b/styles/personalities/z-pattern.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; @layer ly.personalities.z-pattern { :where(.ly-root[data-ly-layout="z-pattern"]) { diff --git a/styles/primitives.css b/styles/primitives.css index dd3262a..e23f185 100644 --- a/styles/primitives.css +++ b/styles/primitives.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; /* Composition primitives use intrinsic sizing so their content chooses reflow points. */ diff --git a/styles/recipes.css b/styles/recipes.css index bd8f16e..de91861 100644 --- a/styles/recipes.css +++ b/styles/recipes.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; /* Recipes preserve the narrow-screen DOM order. Automatic topology queries diff --git a/styles/utilities.css b/styles/utilities.css index 87330a2..a322ca5 100644 --- a/styles/utilities.css +++ b/styles/utilities.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; /* Optional structural utilities expose direct token overrides without breakpoint tiers. */ diff --git a/styles/wrappers.css b/styles/wrappers.css index b9b86d3..af0f564 100644 --- a/styles/wrappers.css +++ b/styles/wrappers.css @@ -1,4 +1,4 @@ -@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities; +@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context; /* Wrappers provide fluid measures and become the nearest responsive scope. */ diff --git a/test/demo-smoke.test.mjs b/test/demo-smoke.test.mjs index b5db163..76e4f86 100644 --- a/test/demo-smoke.test.mjs +++ b/test/demo-smoke.test.mjs @@ -401,6 +401,14 @@ const installExternalFixtures = async (page) => { }); }; +/** + * Verifies demo identity, required controls, durable query state, and legacy + * density URL normalization. + * + * @param {import("@playwright/test").Page} page Active browser page. + * @param {string} baseUrl Demo server URL. + * @returns {Promise} + */ const verifyIdentityAndControls = async (page, baseUrl) => { await page.goto(`${baseUrl}?ecosystem=layout-only`, { waitUntil: "domcontentloaded" }); await page.waitForFunction(() => document.body.dataset.demoReady === "true"); @@ -444,6 +452,11 @@ const verifyIdentityAndControls = async (page, baseUrl) => { assert.equal(await page.locator("#recipeSelect").inputValue(), "docs"); assert.equal(await page.locator("#personalitySelect").inputValue(), "bauhaus"); assert.equal(await page.locator("[data-ly-recipe]").getAttribute("data-ly-responsive"), "manual"); + + await page.goto(`${baseUrl}?density=comfortable`, { waitUntil: "domcontentloaded" }); + await page.waitForFunction(() => document.body.dataset.demoReady === "true"); + assert.equal(await page.locator("#densitySelect").inputValue(), "normal"); + assert.equal(await page.locator("#previewRoot").getAttribute("data-ly-density"), "normal"); }; const verifyPersonalityOptionsUsePairingMetadata = async (page, baseUrl) => { @@ -895,6 +908,97 @@ const verifySectionAndGutterContracts = async (browser, baseUrl) => { } }; +/** + * Verifies root, sibling, and nested density contexts across viewport-height + * tiers without relying on demo-only spacing controls. + * + * @param {import("@playwright/test").Browser} browser Active browser instance. + * @param {string} baseUrl Demo server URL. + * @returns {Promise} + */ +const verifyDensityContexts = async (browser, baseUrl) => { + for (const height of [900, 600, 480]) { + const densityPage = await browser.newPage({ viewport: { width: 1440, height } }); + try { + await installExternalFixtures(densityPage); + await densityPage.goto(`${baseUrl}?ecosystem=layout-only`, { + waitUntil: "domcontentloaded" + }); + await densityPage.waitForFunction(() => document.body.dataset.demoReady === "true"); + + const result = await densityPage.evaluate(() => { + const root = document.createElement("div"); + root.className = "ly-root"; + + /** + * Creates one density-scoped section for computed-style comparison. + * + * @param {"compact" | "normal" | "spacious"} density Public density value. + * @returns {HTMLElement} Density-scoped section fixture. + */ + const createDensityFixture = (density) => { + const fixture = document.createElement("section"); + fixture.className = "ly-section"; + fixture.dataset.lyDensity = density; + fixture.style.display = "grid"; + fixture.style.gap = "var(--ly-gap)"; + fixture.append(document.createElement("span"), document.createElement("span")); + return fixture; + }; + + const compact = createDensityFixture("compact"); + const normal = createDensityFixture("normal"); + const spacious = createDensityFixture("spacious"); + const nestedNormal = createDensityFixture("normal"); + compact.append(nestedNormal); + root.append(compact, normal, spacious); + document.body.append(root); + + /** + * Reads a computed CSS length as a numeric pixel value. + * + * @param {HTMLElement} element Element whose style is inspected. + * @param {string} property Computed CSS property or custom property. + * @returns {number} Parsed numeric value. + */ + const computedNumber = (element, property) => + parseFloat(getComputedStyle(element).getPropertyValue(property)); + const values = { + compactGap: computedNumber(compact, "gap"), + normalGap: computedNumber(normal, "gap"), + spaciousGap: computedNumber(spacious, "gap"), + compactSectionPadding: computedNumber(compact, "padding-block-start"), + normalSectionPadding: computedNumber(normal, "padding-block-start"), + spaciousSectionPadding: computedNumber(spacious, "padding-block-start"), + nestedNormalGap: computedNumber(nestedNormal, "gap") + }; + root.remove(); + return values; + }); + + assert( + result.compactGap < result.normalGap && result.normalGap < result.spaciousGap, + `Density gaps must remain ordered at ${height}px: ${JSON.stringify(result)}` + ); + assert( + result.compactSectionPadding < result.normalSectionPadding, + `Compact density must remain tighter than normal at ${height}px.` + ); + assert( + result.normalSectionPadding < result.spaciousSectionPadding, + `Normal density must remain tighter than spacious at ${height}px.` + ); + assert.equal( + result.nestedNormalGap, + result.normalGap, + `Nested normal density must reset its compact ancestor at ${height}px.` + ); + } finally { + await densityPage.close(); + } + } +}; + const verifyDefaultFontHeightTiers = async (baseUrl) => { if (browserName !== "chromium") return; @@ -1242,6 +1346,7 @@ try { await verifyManualAndNearestContainer(page, server.baseUrl); await verifyHeightBehavior(page, server.baseUrl); await verifySectionAndGutterContracts(browser, server.baseUrl); + await verifyDensityContexts(browser, server.baseUrl); await verifyDefaultFontHeightTiers(server.baseUrl); await verifyDeviceMatrix(page, server.baseUrl); await verifyPersonalityMatrix(page, server.baseUrl); diff --git a/test/layout-css-contract.test.mjs b/test/layout-css-contract.test.mjs index 43e3014..50b7d2c 100644 --- a/test/layout-css-contract.test.mjs +++ b/test/layout-css-contract.test.mjs @@ -11,7 +11,7 @@ const packageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8")) const manifest = JSON.parse(readFileSync(join(root, "manifest.json"), "utf8")); const layerPrelude = - "@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities;"; + "@layer ly.reset, ly.tokens, ly.wrappers, ly.primitives, ly.recipes, ly.utilities, ly.personalities, ly.context;"; const focusedFiles = [ "foundation.css", "wrappers.css", @@ -246,6 +246,20 @@ assert( "Foundation must expose short and shallow viewport tiers" ); assert(!foundation.includes("(orientation:"), "v3 must respond to space rather than orientation labels"); +for (const density of ["compact", "normal", "spacious"]) { + assert( + foundation.includes(`[data-ly-density="${density}"]`), + `Foundation must implement the ${density} density context.` + ); +} +assert( + foundation.includes("@layer ly.context"), + "Explicit density contexts must live after personality defaults." +); +assert( + /--ly-section-padding-block:\s*clamp\(2rem,\s*4vh,\s*4rem\)/.test(foundation), + "Normal 3.1 sections need the conservative default rhythm." +); for (const variant of ["compact", "prose", "content", "wide", "full", "breakout"]) { assert(wrappers.includes(`.ly-wrapper--${variant}`), `Missing wrapper variant: ${variant}`); diff --git a/test/manifest-contract.test.mjs b/test/manifest-contract.test.mjs index d400266..a439ea9 100644 --- a/test/manifest-contract.test.mjs +++ b/test/manifest-contract.test.mjs @@ -106,7 +106,7 @@ test("public personality pairings inventory every exported layout profile withou assert.equal(personalityMetadata.selector, "data-ly-layout"); assert.deepEqual( personalityMetadata.independentSelectors, - ["data-ly-layout", "data-ui", "data-theme", "data-mode"] + ["data-ly-layout", "data-ly-density", "data-ui", "data-theme", "data-mode"] ); assert.deepEqual(personalityMetadata.personalities, manifest.personalityPairings); assert.deepEqual(personalityMetadata.personalities.map(({ id }) => id), personalities); From 9997fa1bb40a736726e2e4b367a379ab342b5ca5 Mon Sep 17 00:00:00 2001 From: Foscat Date: Tue, 25 Aug 2026 17:40:03 -0500 Subject: [PATCH 2/6] feat: add workspace wrapper measure --- demo/demo.js | 11 +++++- demo/index.html | 1 + dist/foundation.css | 1 + dist/layout-style-css.css | 5 +++ dist/layout-style-css.min.css | 2 +- dist/wrappers.css | 4 ++ manifest.json | 5 ++- styles/foundation.css | 1 + styles/wrappers.css | 4 ++ test/demo-smoke.test.mjs | 66 ++++++++++++++++++++++++++++++- test/layout-css-contract.test.mjs | 9 ++++- test/manifest-contract.test.mjs | 7 +++- 12 files changed, 108 insertions(+), 8 deletions(-) diff --git a/demo/demo.js b/demo/demo.js index 4c42133..4bab8a3 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -161,7 +161,16 @@ const ALLOWLISTS = Object.freeze({ "desktop-landscape", "desktop-portrait" ]), - wrapper: Object.freeze(["default", "compact", "prose", "content", "wide", "full", "breakout"]), + wrapper: Object.freeze([ + "default", + "compact", + "prose", + "content", + "workspace", + "wide", + "full", + "breakout" + ]), recipe: Object.freeze([ "app-shell", "dashboard", diff --git a/demo/index.html b/demo/index.html index 4a36660..1d27133 100644 --- a/demo/index.html +++ b/demo/index.html @@ -143,6 +143,7 @@

Layout controls

+ diff --git a/dist/foundation.css b/dist/foundation.css index 96ab075..2722155 100644 --- a/dist/foundation.css +++ b/dist/foundation.css @@ -21,6 +21,7 @@ --ly-wrapper-compact: 40rem; --ly-wrapper-prose: 68ch; --ly-wrapper-content: 72rem; + --ly-wrapper-workspace: 96rem; --ly-wrapper-wide: 112rem; --ly-page-padding-inline: clamp(1rem, 3vw, 3rem); --ly-safe-area-inline: max( diff --git a/dist/layout-style-css.css b/dist/layout-style-css.css index 3842f7d..741ec9e 100644 --- a/dist/layout-style-css.css +++ b/dist/layout-style-css.css @@ -24,6 +24,7 @@ --ly-wrapper-compact: 40rem; --ly-wrapper-prose: 68ch; --ly-wrapper-content: 72rem; + --ly-wrapper-workspace: 96rem; --ly-wrapper-wide: 112rem; --ly-page-padding-inline: clamp(1rem, 3vw, 3rem); --ly-safe-area-inline: max( @@ -381,6 +382,10 @@ --ly-wrapper-max: var(--ly-wrapper-content); } + .ly-wrapper--workspace { + --ly-wrapper-max: var(--ly-wrapper-workspace); + } + .ly-wrapper--wide { --ly-wrapper-max: var(--ly-wrapper-wide); } diff --git a/dist/layout-style-css.min.css b/dist/layout-style-css.min.css index 6569f8e..d07c60e 100644 --- a/dist/layout-style-css.min.css +++ b/dist/layout-style-css.min.css @@ -1 +1 @@ -@layer ly.reset,ly.tokens,ly.wrappers,ly.primitives,ly.recipes,ly.utilities,ly.personalities,ly.context;@layer ly.tokens{:where(.ly-root){--ly-space-0:0;--ly-space-1:0.25rem;--ly-space-2:0.5rem;--ly-space-3:0.75rem;--ly-space-4:1rem;--ly-space-5:1.5rem;--ly-space-6:2rem;--ly-space-7:3rem;--ly-space-8:4rem;--ly-space-9:6rem;--ly-wrapper-compact:40rem;--ly-wrapper-prose:68ch;--ly-wrapper-content:72rem;--ly-wrapper-wide:112rem;--ly-page-padding-inline:clamp(1rem,3vw,3rem);--ly-safe-area-inline:max( env(safe-area-inset-left,0px),env(safe-area-inset-right,0px) );--ly-safe-area-block-start:env(safe-area-inset-top,0px);--ly-safe-area-block-end:env(safe-area-inset-bottom,0px);--ly-wrapper-gutter:var(--ly-page-padding-inline);--ly-profile-gap:var(--ly-space-5);--ly-gap:var(--ly-profile-gap);--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(2rem,4vh,4rem);--ly-section-padding-block-compact:clamp(1rem,2.5vh,2rem);--ly-header-height:4.5rem;--ly-sticky-position:sticky;--ly-cover-min:100vh;--ly-shell-min:100vh;--ly-scroll-max:min(70vh,50rem);--ly-switcher-threshold:42rem;--ly-sidebar-size:18rem;--ly-sidebar-content-min:50%;--ly-grid-columns:12;--ly-grid-min:16rem;--ly-split-min:20rem;--ly-pane-min:18rem;--ly-pane-size:22rem;--ly-media-min:18rem;--ly-media-size:14rem;--ly-reel-item-min:16rem;--ly-reel-item-max:28rem;--ly-frame-ratio:16 / 9;--ly-split-primary:1fr;--ly-split-secondary:1fr;--ly-recipe-rail:18rem;--ly-recipe-aside:22rem;--ly-gallery-min:12rem;--ly-card-grid-min:16rem;--ly-app-shell-base-rows:auto auto minmax(0,1fr) auto auto;--ly-app-shell-medium-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-medium-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-medium-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-medium-areas:"header header" "nav nav" "main aside" "footer footer";--ly-dashboard-medium-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-wide-areas:"nav header header" "nav main aside" "nav footer footer";--ly-dashboard-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-docs-wide-areas:"header header" "nav main" "nav aside" "footer footer";--ly-docs-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-list-detail-wide-areas:"primary secondary" "actions actions";--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.7fr) minmax(0,1fr);--ly-split-hero-wide-areas:"content media" "actions media";--ly-split-hero-wide-columns:minmax(0,var(--ly-split-primary,1fr)) minmax(0,var(--ly-split-secondary,1fr));container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}@supports (width:1cqi){:where(.ly-root){--ly-page-padding-inline:clamp(1rem,3cqi,3rem)}}@supports (height:100dvh){:where(.ly-root){--ly-cover-min:100dvh;--ly-shell-min:100dvh;--ly-scroll-max:min(70dvh,50rem)}}}@layer ly.reset{:where(.ly-root),:where(.ly-root *){box-sizing:border-box}:where(.ly-root){margin:0}:where(.ly-root img),:where(.ly-root svg),:where(.ly-root video),:where(.ly-root canvas),:where(.ly-root iframe){max-inline-size:100%}}@layer ly.tokens{@media (max-height:44rem){:where(.ly-root){--ly-gap:min(var(--ly-profile-gap),var(--ly-space-4));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:min(var(--ly-space-4),3vh);--ly-cluster-gap:min(var(--ly-space-3),2vh);--ly-section-padding-block:clamp(1.25rem,4vh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2vh,1.25rem);--ly-header-height:3.5rem;--ly-scroll-max:min(62vh,34rem)}@supports (height:100dvh){:where(.ly-root){--ly-stack-gap:min(var(--ly-space-4),3dvh);--ly-cluster-gap:min(var(--ly-space-3),2dvh);--ly-section-padding-block:clamp(1.25rem,4dvh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2dvh,1.25rem);--ly-scroll-max:min(62dvh,34rem)}}}@media (max-height:30rem){:where(.ly-root){--ly-cover-min:auto;--ly-shell-min:auto;--ly-sticky-position:static;--ly-section-padding-block:var(--ly-space-4);--ly-section-padding-block-compact:var(--ly-space-2);--ly-scroll-max:max(12rem,calc(100vh - 8rem))}@supports (height:100dvh){:where(.ly-root){--ly-scroll-max:max(12rem,calc(100dvh - 8rem))}}}}@layer ly.context{:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:clamp(1rem,2.5vh,2rem);--ly-section-padding-block-compact:clamp(0.5rem,1.5vh,1rem)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:var(--ly-profile-gap);--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(2rem,4vh,4rem);--ly-section-padding-block-compact:clamp(1rem,2.5vh,2rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:max(var(--ly-profile-gap),var(--ly-space-6));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-5);--ly-cluster-gap:var(--ly-space-4);--ly-section-padding-block:clamp(3rem,7vh,6rem);--ly-section-padding-block-compact:clamp(1.5rem,3.5vh,3rem)}@media (max-height:44rem){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:clamp(0.75rem,2vh,1.25rem);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:min(var(--ly-profile-gap),var(--ly-space-4));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(1.25rem,4vh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2vh,1.25rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:var(--ly-space-5);--ly-grid-gap:var(--ly-space-5);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(1.5rem,6vh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3vh,1.25rem)}@supports (height:100dvh){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-section-padding-block:clamp(0.75rem,2dvh,1.25rem)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-section-padding-block:clamp(1.25rem,4dvh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2dvh,1.25rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-section-padding-block:clamp(1.5rem,6dvh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3dvh,1.25rem)}}}@media (max-height:30rem){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-2);--ly-grid-gap:var(--ly-space-2);--ly-stack-gap:var(--ly-space-2);--ly-cluster-gap:var(--ly-space-1);--ly-section-padding-block:var(--ly-space-3);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:var(--ly-space-4);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:var(--ly-space-4);--ly-grid-gap:var(--ly-space-4);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:var(--ly-space-5);--ly-section-padding-block-compact:var(--ly-space-3)}}}@layer ly.wrappers{.ly-wrapper{--ly-wrapper-max:var(--ly-personality-wrapper-max,var(--ly-wrapper-content));--ly-wrapper-fluid-gutter:var(--ly-wrapper-gutter);--ly-wrapper-local-gutter:max( var(--ly-wrapper-fluid-gutter),var(--ly-safe-area-inline) );container-name:ly-scope;container-type:inline-size;inline-size:min(100%,calc(var(--ly-wrapper-max) + (var(--ly-wrapper-local-gutter) * 2)));max-inline-size:100%;margin-inline:auto;padding-inline:var(--ly-wrapper-local-gutter)}.ly-wrapper--compact{--ly-wrapper-max:var(--ly-wrapper-compact)}.ly-wrapper--prose{--ly-wrapper-max:var(--ly-wrapper-prose)}.ly-wrapper--content{--ly-wrapper-max:var(--ly-wrapper-content)}.ly-wrapper--wide{--ly-wrapper-max:var(--ly-wrapper-wide)}.ly-wrapper--full{--ly-wrapper-max:100%}.ly-wrapper--breakout{--ly-wrapper-max:100%;--ly-breakout-content-size:min( var(--ly-wrapper-content),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );--ly-breakout-feature-size:min( var(--ly-wrapper-wide),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );display:grid;grid-template-columns:[full-start] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [feature-start] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [content-start] minmax(0,var(--ly-breakout-content-size)) [content-end] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [feature-end] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [full-end];inline-size:100%;padding-inline:0}.ly-wrapper--breakout>*{grid-column:content;min-inline-size:0;max-inline-size:100%}.ly-wrapper--breakout>:where(.ly-lane--content,[data-ly-lane="content"]){grid-column:content}.ly-wrapper--breakout>:where(.ly-lane--feature,[data-ly-lane="feature"]){grid-column:feature}.ly-wrapper--breakout>:where(.ly-lane--full,[data-ly-lane="full"]){grid-column:full}}@layer ly.primitives{.ly-page{min-block-size:100vh;min-block-size:100dvh;padding-block-start:var(--ly-safe-area-block-start);padding-block-end:var(--ly-safe-area-block-end)}.ly-header,.ly-footer,.ly-main,.ly-section,.ly-surface{min-inline-size:0;min-block-size:0}.ly-header{min-block-size:var(--ly-header-height)}.ly-header--sticky{position:var(--ly-sticky-position,sticky);inset-block-start:0;z-index:var(--ly-z-header,20)}.ly-section{padding-block:var(--ly-section-padding-block)}.ly-section--compact{padding-block:var(--ly-section-padding-block-compact)}.ly-section--flush{padding-block:0}.ly-readable{max-inline-size:var(--ly-wrapper-prose)}.ly-stack{display:flex;flex-direction:column;gap:var(--ly-stack-gap)}.ly-cluster{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ly-cluster-gap)}.ly-center{inline-size:min(100%,var(--ly-center-max,var(--ly-wrapper-content)));margin-inline:auto}.ly-cover{display:flex;flex-direction:column;min-block-size:100vh;min-block-size:var(--ly-cover-min);padding-block:var(--ly-cover-padding,var(--ly-space-5))}.ly-cover>:where(.ly-cover__center,[data-ly-cover-center]){margin-block:auto}.ly-switcher{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-switcher>*{flex-basis:calc((var(--ly-switcher-threshold) - 100%) * 999);flex-grow:1;min-inline-size:0}.ly-sidebar{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-sidebar>:where(.ly-sidebar__side,[data-ly-sidebar="side"]){flex-basis:var(--ly-sidebar-size);flex-grow:1;min-inline-size:min(100%,var(--ly-sidebar-size))}.ly-sidebar>:where(.ly-sidebar__content,[data-ly-sidebar="content"]){flex-basis:0;flex-grow:999;min-inline-size:min(100%,var(--ly-sidebar-content-min))}.ly-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-grid-min)),1fr));gap:var(--ly-grid-gap)}.ly-grid--fixed{grid-template-columns:repeat(var(--ly-grid-columns),minmax(0,1fr))}.ly-split{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-split-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-split-align,center)}.ly-panes{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-pane-min)),1fr));gap:var(--ly-gap)}.ly-media{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-media-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-media-align,start)}.ly-media>:where(.ly-media__actions,[data-ly-media="actions"]){min-inline-size:min(100%,var(--ly-media-size))}.ly-grid>*,.ly-split>*,.ly-panes>*,.ly-media>*{min-inline-size:0;min-block-size:0}.ly-reel{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(min(100%,var(--ly-reel-item-min)),var(--ly-reel-item-max));gap:var(--ly-grid-gap);overflow-x:auto;overscroll-behavior-inline:contain;scroll-snap-type:inline proximity}.ly-reel>*{min-inline-size:0;scroll-snap-align:start}.ly-frame{aspect-ratio:var(--ly-frame-ratio);overflow:hidden}.ly-frame>*{inline-size:100%;block-size:100%;object-fit:cover}.ly-scroll{min-block-size:0;max-block-size:var(--ly-scroll-max);overflow-x:clip;overflow-y:auto;overscroll-behavior-block:contain}}@layer ly.recipes{:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] ){container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"] ){display:grid;grid-template-columns:minmax(0,1fr);gap:var(--ly-gap)}[data-ly-recipe="app-shell"]{min-block-size:100vh;min-block-size:var(--ly-shell-min);grid-template-areas:"header" "sidebar" "main" "aside" "footer";grid-template-rows:var(--ly-app-shell-base-rows)}[data-ly-recipe="dashboard"],[data-ly-recipe="docs"]{grid-template-areas:"header" "nav" "main" "aside" "footer"}[data-ly-recipe="list-detail"]{grid-template-areas:"primary" "secondary" "actions"}[data-ly-recipe="split-hero"]{grid-template-areas:"content" "media" "actions";align-items:var(--ly-split-hero-align,center)}[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"]{display:grid;gap:var(--ly-grid-gap)}[data-ly-recipe="gallery"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-gallery-min)),1fr))}[data-ly-recipe="card-grid"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-card-grid-min)),1fr))}:where( [data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )[data-ly-responsive="manual"]{grid-template-columns:minmax(0,1fr)}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )>*{min-inline-size:0;min-block-size:0}[data-ly-area="header"]{grid-area:header}[data-ly-area="nav"]{grid-area:nav}[data-ly-area="sidebar"]{grid-area:sidebar}[data-ly-area="main"]{grid-area:main}[data-ly-area="aside"]{grid-area:aside}[data-ly-area="footer"]{grid-area:footer}[data-ly-area="content"]{grid-area:content}[data-ly-area="media"]{grid-area:media}[data-ly-area="actions"]{grid-area:actions}[data-ly-area="primary"]{grid-area:primary}[data-ly-area="secondary"]{grid-area:secondary}@container ly-scope (min-width:42rem){[data-ly-recipe="split-hero"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-split-hero-wide-areas);grid-template-columns:var(--ly-split-hero-wide-columns)}}@container ly-scope (min-width:44rem){[data-ly-recipe="list-detail"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-list-detail-wide-areas);grid-template-columns:var(--ly-list-detail-wide-columns)}}@container ly-scope (min-width:48rem){[data-ly-recipe="docs"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-docs-wide-areas);grid-template-columns:var(--ly-docs-wide-columns)}}@container ly-scope (min-width:52rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-medium-areas);grid-template-columns:var(--ly-app-shell-medium-columns);grid-template-rows:var(--ly-app-shell-medium-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-medium-areas);grid-template-columns:var(--ly-dashboard-medium-columns)}}@container ly-scope (min-width:72rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-wide-areas);grid-template-columns:var(--ly-app-shell-wide-columns);grid-template-rows:var(--ly-app-shell-wide-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-wide-areas);grid-template-columns:var(--ly-dashboard-wide-columns)}}}@layer ly.utilities{.ly-cols-1{--ly-grid-columns:1}.ly-cols-2{--ly-grid-columns:2}.ly-cols-3{--ly-grid-columns:3}.ly-cols-4{--ly-grid-columns:4}.ly-cols-6{--ly-grid-columns:6}.ly-cols-12{--ly-grid-columns:12}.ly-span-1{grid-column:span 1}.ly-span-2{grid-column:span 2}.ly-span-3{grid-column:span 3}.ly-span-4{grid-column:span 4}.ly-span-full{grid-column:1 / -1}.ly-gap-0{--ly-gap:var(--ly-space-0);--ly-grid-gap:var(--ly-space-0);--ly-stack-gap:var(--ly-space-0);--ly-cluster-gap:var(--ly-space-0)}.ly-gap-2{--ly-gap:var(--ly-space-2);--ly-grid-gap:var(--ly-space-2);--ly-stack-gap:var(--ly-space-2);--ly-cluster-gap:var(--ly-space-2)}.ly-gap-4{--ly-gap:var(--ly-space-4);--ly-grid-gap:var(--ly-space-4);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-4)}.ly-gap-6{--ly-gap:var(--ly-space-6);--ly-grid-gap:var(--ly-space-6);--ly-stack-gap:var(--ly-space-6);--ly-cluster-gap:var(--ly-space-6)}.ly-gap-8{--ly-gap:var(--ly-space-8);--ly-grid-gap:var(--ly-space-8);--ly-stack-gap:var(--ly-space-8);--ly-cluster-gap:var(--ly-space-8)}.ly-pad-0{padding:var(--ly-space-0)}.ly-pad-2{padding:var(--ly-space-2)}.ly-pad-4{padding:var(--ly-space-4)}.ly-pad-6{padding:var(--ly-space-6)}.ly-pad-8{padding:var(--ly-space-8)}.ly-mx-auto{margin-inline:auto}.ly-w-full{inline-size:100%}.ly-h-full{block-size:100%}.ly-min-h-screen{min-block-size:100vh;min-block-size:100dvh}.ly-overflow-auto{overflow:auto}.ly-overflow-hidden{overflow:hidden}.ly-items-start{align-items:flex-start}.ly-items-center{align-items:center}.ly-items-end{align-items:flex-end}.ly-items-stretch{align-items:stretch}.ly-justify-start{justify-content:flex-start}.ly-justify-center{justify-content:center}.ly-justify-end{justify-content:flex-end}.ly-justify-between{justify-content:space-between}.ly-frame-1x1{--ly-frame-ratio:1 / 1}.ly-frame-3x2{--ly-frame-ratio:3 / 2}.ly-frame-4x3{--ly-frame-ratio:4 / 3}.ly-frame-16x9{--ly-frame-ratio:16 / 9}.ly-frame-21x9{--ly-frame-ratio:21 / 9}.ly-hidden{display:none !important}.ly-visually-hidden{position:absolute !important;inline-size:1px !important;block-size:1px !important;padding:0 !important;margin:-1px !important;overflow:hidden !important;clip:rect(0,0,0,0) !important;clip-path:inset(50%) !important;white-space:nowrap !important}}@layer ly.personalities.minimal-saas{:where(.ly-root[data-ly-layout="minimal-saas"]){--ly-personality-wrapper-max:88rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:16rem;--ly-recipe-rail:15rem;--ly-recipe-aside:16rem;--ly-split-primary:1.05fr;--ly-split-secondary:0.95fr}}@layer ly.personalities.bento{:where(.ly-root[data-ly-layout="bento"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-card-grid-min:14rem;--ly-gallery-min:11rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header" "sidebar main main" "aside main main" "footer footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.3fr) minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.maximalist{:where(.ly-root[data-ly-layout="maximalist"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-7);--ly-grid-min:10rem;--ly-card-grid-min:13rem;--ly-gallery-min:10rem;--ly-recipe-rail:15rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "main main sidebar aside" "main main footer footer";--ly-app-shell-wide-columns:minmax(0,1.4fr) minmax(0,0.6fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr) minmax(min(100%,var(--ly-recipe-aside)),0.38fr)}}@layer ly.personalities.bauhaus{:where(.ly-root[data-ly-layout="bauhaus"]){--ly-personality-wrapper-max:96rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "sidebar main main aside" "sidebar footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,0.7fr) minmax(0,1.3fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.tactile{:where(.ly-root[data-ly-layout="tactile"]){--ly-personality-wrapper-max:82rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:15rem;--ly-recipe-rail:17rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.5fr) minmax(0,0.9fr)}}@layer ly.personalities.neumorphism{:where(.ly-root[data-ly-layout="neumorphism"]){--ly-personality-wrapper-max:84rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:17rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header sidebar" "main sidebar" "aside sidebar" "footer sidebar";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-list-detail-wide-columns:minmax(0,1.1fr) minmax(min(100%,var(--ly-pane-size)),0.7fr)}}@layer ly.personalities.retrofuturism{:where(.ly-root[data-ly-layout="retrofuturism"]){--ly-personality-wrapper-max:106rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:14rem;--ly-recipe-aside:17rem;--ly-app-shell-wide-areas:"sidebar header aside" "sidebar main aside" "sidebar footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.brutalism{:where(.ly-root[data-ly-layout="brutalism"]){--ly-personality-wrapper-max:100rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(0,1.3fr) minmax(0,0.7fr) minmax(min(100%,var(--ly-recipe-rail)),0.34fr)}}@layer ly.personalities.cyberpunk{:where(.ly-root[data-ly-layout="cyberpunk"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-recipe-rail:13rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.2fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.32fr)}}@layer ly.personalities.y2k{:where(.ly-root[data-ly-layout="y2k"]){--ly-personality-wrapper-max:90rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:18rem;--ly-recipe-aside:16rem;--ly-app-shell-wide-areas:"header header sidebar" "aside main sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-aside)),0.34fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.28fr)}}@layer ly.personalities.retro-glass{:where(.ly-root[data-ly-layout="retro-glass"]){--ly-personality-wrapper-max:104rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:15rem;--ly-recipe-rail:18rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main main sidebar" "aside footer footer";--ly-app-shell-wide-columns:minmax(0,1.25fr) minmax(0,0.75fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr)}}@layer ly.personalities.f-pattern{:where(.ly-root[data-ly-layout="f-pattern"]){--ly-personality-wrapper-max:92rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.26fr) minmax(0,1.35fr) minmax(min(100%,var(--ly-recipe-aside)),0.65fr);--ly-split-hero-wide-columns:minmax(0,1.8fr) minmax(0,0.8fr)}}@layer ly.personalities.z-pattern{:where(.ly-root[data-ly-layout="z-pattern"]){--ly-personality-wrapper-max:108rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer aside sidebar";--ly-app-shell-wide-columns:minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-split-hero-wide-columns:minmax(0,1.5fr) minmax(0,0.5fr)}}@layer ly.personalities.split-screen{:where(.ly-root[data-ly-layout="split-screen"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:20rem;--ly-recipe-rail:24rem;--ly-recipe-aside:24rem;--ly-app-shell-wide-areas:"header header" "main sidebar" "aside sidebar" "footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-split-hero-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.mondrian{:where(.ly-root[data-ly-layout="mondrian"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:11rem;--ly-recipe-rail:13rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.22fr) minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr)}}@layer ly.personalities.synthwave{:where(.ly-root[data-ly-layout="synthwave"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header header" "sidebar main aside" "footer footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.34fr);--ly-reel-item-min:20rem}} +@layer ly.reset,ly.tokens,ly.wrappers,ly.primitives,ly.recipes,ly.utilities,ly.personalities,ly.context;@layer ly.tokens{:where(.ly-root){--ly-space-0:0;--ly-space-1:0.25rem;--ly-space-2:0.5rem;--ly-space-3:0.75rem;--ly-space-4:1rem;--ly-space-5:1.5rem;--ly-space-6:2rem;--ly-space-7:3rem;--ly-space-8:4rem;--ly-space-9:6rem;--ly-wrapper-compact:40rem;--ly-wrapper-prose:68ch;--ly-wrapper-content:72rem;--ly-wrapper-workspace:96rem;--ly-wrapper-wide:112rem;--ly-page-padding-inline:clamp(1rem,3vw,3rem);--ly-safe-area-inline:max( env(safe-area-inset-left,0px),env(safe-area-inset-right,0px) );--ly-safe-area-block-start:env(safe-area-inset-top,0px);--ly-safe-area-block-end:env(safe-area-inset-bottom,0px);--ly-wrapper-gutter:var(--ly-page-padding-inline);--ly-profile-gap:var(--ly-space-5);--ly-gap:var(--ly-profile-gap);--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(2rem,4vh,4rem);--ly-section-padding-block-compact:clamp(1rem,2.5vh,2rem);--ly-header-height:4.5rem;--ly-sticky-position:sticky;--ly-cover-min:100vh;--ly-shell-min:100vh;--ly-scroll-max:min(70vh,50rem);--ly-switcher-threshold:42rem;--ly-sidebar-size:18rem;--ly-sidebar-content-min:50%;--ly-grid-columns:12;--ly-grid-min:16rem;--ly-split-min:20rem;--ly-pane-min:18rem;--ly-pane-size:22rem;--ly-media-min:18rem;--ly-media-size:14rem;--ly-reel-item-min:16rem;--ly-reel-item-max:28rem;--ly-frame-ratio:16 / 9;--ly-split-primary:1fr;--ly-split-secondary:1fr;--ly-recipe-rail:18rem;--ly-recipe-aside:22rem;--ly-gallery-min:12rem;--ly-card-grid-min:16rem;--ly-app-shell-base-rows:auto auto minmax(0,1fr) auto auto;--ly-app-shell-medium-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-medium-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-medium-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-medium-areas:"header header" "nav nav" "main aside" "footer footer";--ly-dashboard-medium-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-wide-areas:"nav header header" "nav main aside" "nav footer footer";--ly-dashboard-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-docs-wide-areas:"header header" "nav main" "nav aside" "footer footer";--ly-docs-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-list-detail-wide-areas:"primary secondary" "actions actions";--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.7fr) minmax(0,1fr);--ly-split-hero-wide-areas:"content media" "actions media";--ly-split-hero-wide-columns:minmax(0,var(--ly-split-primary,1fr)) minmax(0,var(--ly-split-secondary,1fr));container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}@supports (width:1cqi){:where(.ly-root){--ly-page-padding-inline:clamp(1rem,3cqi,3rem)}}@supports (height:100dvh){:where(.ly-root){--ly-cover-min:100dvh;--ly-shell-min:100dvh;--ly-scroll-max:min(70dvh,50rem)}}}@layer ly.reset{:where(.ly-root),:where(.ly-root *){box-sizing:border-box}:where(.ly-root){margin:0}:where(.ly-root img),:where(.ly-root svg),:where(.ly-root video),:where(.ly-root canvas),:where(.ly-root iframe){max-inline-size:100%}}@layer ly.tokens{@media (max-height:44rem){:where(.ly-root){--ly-gap:min(var(--ly-profile-gap),var(--ly-space-4));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:min(var(--ly-space-4),3vh);--ly-cluster-gap:min(var(--ly-space-3),2vh);--ly-section-padding-block:clamp(1.25rem,4vh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2vh,1.25rem);--ly-header-height:3.5rem;--ly-scroll-max:min(62vh,34rem)}@supports (height:100dvh){:where(.ly-root){--ly-stack-gap:min(var(--ly-space-4),3dvh);--ly-cluster-gap:min(var(--ly-space-3),2dvh);--ly-section-padding-block:clamp(1.25rem,4dvh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2dvh,1.25rem);--ly-scroll-max:min(62dvh,34rem)}}}@media (max-height:30rem){:where(.ly-root){--ly-cover-min:auto;--ly-shell-min:auto;--ly-sticky-position:static;--ly-section-padding-block:var(--ly-space-4);--ly-section-padding-block-compact:var(--ly-space-2);--ly-scroll-max:max(12rem,calc(100vh - 8rem))}@supports (height:100dvh){:where(.ly-root){--ly-scroll-max:max(12rem,calc(100dvh - 8rem))}}}}@layer ly.context{:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:clamp(1rem,2.5vh,2rem);--ly-section-padding-block-compact:clamp(0.5rem,1.5vh,1rem)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:var(--ly-profile-gap);--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(2rem,4vh,4rem);--ly-section-padding-block-compact:clamp(1rem,2.5vh,2rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:max(var(--ly-profile-gap),var(--ly-space-6));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-5);--ly-cluster-gap:var(--ly-space-4);--ly-section-padding-block:clamp(3rem,7vh,6rem);--ly-section-padding-block-compact:clamp(1.5rem,3.5vh,3rem)}@media (max-height:44rem){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:clamp(0.75rem,2vh,1.25rem);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:min(var(--ly-profile-gap),var(--ly-space-4));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(1.25rem,4vh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2vh,1.25rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:var(--ly-space-5);--ly-grid-gap:var(--ly-space-5);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(1.5rem,6vh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3vh,1.25rem)}@supports (height:100dvh){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-section-padding-block:clamp(0.75rem,2dvh,1.25rem)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-section-padding-block:clamp(1.25rem,4dvh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2dvh,1.25rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-section-padding-block:clamp(1.5rem,6dvh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3dvh,1.25rem)}}}@media (max-height:30rem){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-2);--ly-grid-gap:var(--ly-space-2);--ly-stack-gap:var(--ly-space-2);--ly-cluster-gap:var(--ly-space-1);--ly-section-padding-block:var(--ly-space-3);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:var(--ly-space-4);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:var(--ly-space-4);--ly-grid-gap:var(--ly-space-4);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:var(--ly-space-5);--ly-section-padding-block-compact:var(--ly-space-3)}}}@layer ly.wrappers{.ly-wrapper{--ly-wrapper-max:var(--ly-personality-wrapper-max,var(--ly-wrapper-content));--ly-wrapper-fluid-gutter:var(--ly-wrapper-gutter);--ly-wrapper-local-gutter:max( var(--ly-wrapper-fluid-gutter),var(--ly-safe-area-inline) );container-name:ly-scope;container-type:inline-size;inline-size:min(100%,calc(var(--ly-wrapper-max) + (var(--ly-wrapper-local-gutter) * 2)));max-inline-size:100%;margin-inline:auto;padding-inline:var(--ly-wrapper-local-gutter)}.ly-wrapper--compact{--ly-wrapper-max:var(--ly-wrapper-compact)}.ly-wrapper--prose{--ly-wrapper-max:var(--ly-wrapper-prose)}.ly-wrapper--content{--ly-wrapper-max:var(--ly-wrapper-content)}.ly-wrapper--workspace{--ly-wrapper-max:var(--ly-wrapper-workspace)}.ly-wrapper--wide{--ly-wrapper-max:var(--ly-wrapper-wide)}.ly-wrapper--full{--ly-wrapper-max:100%}.ly-wrapper--breakout{--ly-wrapper-max:100%;--ly-breakout-content-size:min( var(--ly-wrapper-content),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );--ly-breakout-feature-size:min( var(--ly-wrapper-wide),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );display:grid;grid-template-columns:[full-start] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [feature-start] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [content-start] minmax(0,var(--ly-breakout-content-size)) [content-end] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [feature-end] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [full-end];inline-size:100%;padding-inline:0}.ly-wrapper--breakout>*{grid-column:content;min-inline-size:0;max-inline-size:100%}.ly-wrapper--breakout>:where(.ly-lane--content,[data-ly-lane="content"]){grid-column:content}.ly-wrapper--breakout>:where(.ly-lane--feature,[data-ly-lane="feature"]){grid-column:feature}.ly-wrapper--breakout>:where(.ly-lane--full,[data-ly-lane="full"]){grid-column:full}}@layer ly.primitives{.ly-page{min-block-size:100vh;min-block-size:100dvh;padding-block-start:var(--ly-safe-area-block-start);padding-block-end:var(--ly-safe-area-block-end)}.ly-header,.ly-footer,.ly-main,.ly-section,.ly-surface{min-inline-size:0;min-block-size:0}.ly-header{min-block-size:var(--ly-header-height)}.ly-header--sticky{position:var(--ly-sticky-position,sticky);inset-block-start:0;z-index:var(--ly-z-header,20)}.ly-section{padding-block:var(--ly-section-padding-block)}.ly-section--compact{padding-block:var(--ly-section-padding-block-compact)}.ly-section--flush{padding-block:0}.ly-readable{max-inline-size:var(--ly-wrapper-prose)}.ly-stack{display:flex;flex-direction:column;gap:var(--ly-stack-gap)}.ly-cluster{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ly-cluster-gap)}.ly-center{inline-size:min(100%,var(--ly-center-max,var(--ly-wrapper-content)));margin-inline:auto}.ly-cover{display:flex;flex-direction:column;min-block-size:100vh;min-block-size:var(--ly-cover-min);padding-block:var(--ly-cover-padding,var(--ly-space-5))}.ly-cover>:where(.ly-cover__center,[data-ly-cover-center]){margin-block:auto}.ly-switcher{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-switcher>*{flex-basis:calc((var(--ly-switcher-threshold) - 100%) * 999);flex-grow:1;min-inline-size:0}.ly-sidebar{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-sidebar>:where(.ly-sidebar__side,[data-ly-sidebar="side"]){flex-basis:var(--ly-sidebar-size);flex-grow:1;min-inline-size:min(100%,var(--ly-sidebar-size))}.ly-sidebar>:where(.ly-sidebar__content,[data-ly-sidebar="content"]){flex-basis:0;flex-grow:999;min-inline-size:min(100%,var(--ly-sidebar-content-min))}.ly-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-grid-min)),1fr));gap:var(--ly-grid-gap)}.ly-grid--fixed{grid-template-columns:repeat(var(--ly-grid-columns),minmax(0,1fr))}.ly-split{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-split-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-split-align,center)}.ly-panes{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-pane-min)),1fr));gap:var(--ly-gap)}.ly-media{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-media-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-media-align,start)}.ly-media>:where(.ly-media__actions,[data-ly-media="actions"]){min-inline-size:min(100%,var(--ly-media-size))}.ly-grid>*,.ly-split>*,.ly-panes>*,.ly-media>*{min-inline-size:0;min-block-size:0}.ly-reel{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(min(100%,var(--ly-reel-item-min)),var(--ly-reel-item-max));gap:var(--ly-grid-gap);overflow-x:auto;overscroll-behavior-inline:contain;scroll-snap-type:inline proximity}.ly-reel>*{min-inline-size:0;scroll-snap-align:start}.ly-frame{aspect-ratio:var(--ly-frame-ratio);overflow:hidden}.ly-frame>*{inline-size:100%;block-size:100%;object-fit:cover}.ly-scroll{min-block-size:0;max-block-size:var(--ly-scroll-max);overflow-x:clip;overflow-y:auto;overscroll-behavior-block:contain}}@layer ly.recipes{:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] ){container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"] ){display:grid;grid-template-columns:minmax(0,1fr);gap:var(--ly-gap)}[data-ly-recipe="app-shell"]{min-block-size:100vh;min-block-size:var(--ly-shell-min);grid-template-areas:"header" "sidebar" "main" "aside" "footer";grid-template-rows:var(--ly-app-shell-base-rows)}[data-ly-recipe="dashboard"],[data-ly-recipe="docs"]{grid-template-areas:"header" "nav" "main" "aside" "footer"}[data-ly-recipe="list-detail"]{grid-template-areas:"primary" "secondary" "actions"}[data-ly-recipe="split-hero"]{grid-template-areas:"content" "media" "actions";align-items:var(--ly-split-hero-align,center)}[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"]{display:grid;gap:var(--ly-grid-gap)}[data-ly-recipe="gallery"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-gallery-min)),1fr))}[data-ly-recipe="card-grid"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-card-grid-min)),1fr))}:where( [data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )[data-ly-responsive="manual"]{grid-template-columns:minmax(0,1fr)}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )>*{min-inline-size:0;min-block-size:0}[data-ly-area="header"]{grid-area:header}[data-ly-area="nav"]{grid-area:nav}[data-ly-area="sidebar"]{grid-area:sidebar}[data-ly-area="main"]{grid-area:main}[data-ly-area="aside"]{grid-area:aside}[data-ly-area="footer"]{grid-area:footer}[data-ly-area="content"]{grid-area:content}[data-ly-area="media"]{grid-area:media}[data-ly-area="actions"]{grid-area:actions}[data-ly-area="primary"]{grid-area:primary}[data-ly-area="secondary"]{grid-area:secondary}@container ly-scope (min-width:42rem){[data-ly-recipe="split-hero"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-split-hero-wide-areas);grid-template-columns:var(--ly-split-hero-wide-columns)}}@container ly-scope (min-width:44rem){[data-ly-recipe="list-detail"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-list-detail-wide-areas);grid-template-columns:var(--ly-list-detail-wide-columns)}}@container ly-scope (min-width:48rem){[data-ly-recipe="docs"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-docs-wide-areas);grid-template-columns:var(--ly-docs-wide-columns)}}@container ly-scope (min-width:52rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-medium-areas);grid-template-columns:var(--ly-app-shell-medium-columns);grid-template-rows:var(--ly-app-shell-medium-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-medium-areas);grid-template-columns:var(--ly-dashboard-medium-columns)}}@container ly-scope (min-width:72rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-wide-areas);grid-template-columns:var(--ly-app-shell-wide-columns);grid-template-rows:var(--ly-app-shell-wide-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-wide-areas);grid-template-columns:var(--ly-dashboard-wide-columns)}}}@layer ly.utilities{.ly-cols-1{--ly-grid-columns:1}.ly-cols-2{--ly-grid-columns:2}.ly-cols-3{--ly-grid-columns:3}.ly-cols-4{--ly-grid-columns:4}.ly-cols-6{--ly-grid-columns:6}.ly-cols-12{--ly-grid-columns:12}.ly-span-1{grid-column:span 1}.ly-span-2{grid-column:span 2}.ly-span-3{grid-column:span 3}.ly-span-4{grid-column:span 4}.ly-span-full{grid-column:1 / -1}.ly-gap-0{--ly-gap:var(--ly-space-0);--ly-grid-gap:var(--ly-space-0);--ly-stack-gap:var(--ly-space-0);--ly-cluster-gap:var(--ly-space-0)}.ly-gap-2{--ly-gap:var(--ly-space-2);--ly-grid-gap:var(--ly-space-2);--ly-stack-gap:var(--ly-space-2);--ly-cluster-gap:var(--ly-space-2)}.ly-gap-4{--ly-gap:var(--ly-space-4);--ly-grid-gap:var(--ly-space-4);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-4)}.ly-gap-6{--ly-gap:var(--ly-space-6);--ly-grid-gap:var(--ly-space-6);--ly-stack-gap:var(--ly-space-6);--ly-cluster-gap:var(--ly-space-6)}.ly-gap-8{--ly-gap:var(--ly-space-8);--ly-grid-gap:var(--ly-space-8);--ly-stack-gap:var(--ly-space-8);--ly-cluster-gap:var(--ly-space-8)}.ly-pad-0{padding:var(--ly-space-0)}.ly-pad-2{padding:var(--ly-space-2)}.ly-pad-4{padding:var(--ly-space-4)}.ly-pad-6{padding:var(--ly-space-6)}.ly-pad-8{padding:var(--ly-space-8)}.ly-mx-auto{margin-inline:auto}.ly-w-full{inline-size:100%}.ly-h-full{block-size:100%}.ly-min-h-screen{min-block-size:100vh;min-block-size:100dvh}.ly-overflow-auto{overflow:auto}.ly-overflow-hidden{overflow:hidden}.ly-items-start{align-items:flex-start}.ly-items-center{align-items:center}.ly-items-end{align-items:flex-end}.ly-items-stretch{align-items:stretch}.ly-justify-start{justify-content:flex-start}.ly-justify-center{justify-content:center}.ly-justify-end{justify-content:flex-end}.ly-justify-between{justify-content:space-between}.ly-frame-1x1{--ly-frame-ratio:1 / 1}.ly-frame-3x2{--ly-frame-ratio:3 / 2}.ly-frame-4x3{--ly-frame-ratio:4 / 3}.ly-frame-16x9{--ly-frame-ratio:16 / 9}.ly-frame-21x9{--ly-frame-ratio:21 / 9}.ly-hidden{display:none !important}.ly-visually-hidden{position:absolute !important;inline-size:1px !important;block-size:1px !important;padding:0 !important;margin:-1px !important;overflow:hidden !important;clip:rect(0,0,0,0) !important;clip-path:inset(50%) !important;white-space:nowrap !important}}@layer ly.personalities.minimal-saas{:where(.ly-root[data-ly-layout="minimal-saas"]){--ly-personality-wrapper-max:88rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:16rem;--ly-recipe-rail:15rem;--ly-recipe-aside:16rem;--ly-split-primary:1.05fr;--ly-split-secondary:0.95fr}}@layer ly.personalities.bento{:where(.ly-root[data-ly-layout="bento"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-card-grid-min:14rem;--ly-gallery-min:11rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header" "sidebar main main" "aside main main" "footer footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.3fr) minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.maximalist{:where(.ly-root[data-ly-layout="maximalist"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-7);--ly-grid-min:10rem;--ly-card-grid-min:13rem;--ly-gallery-min:10rem;--ly-recipe-rail:15rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "main main sidebar aside" "main main footer footer";--ly-app-shell-wide-columns:minmax(0,1.4fr) minmax(0,0.6fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr) minmax(min(100%,var(--ly-recipe-aside)),0.38fr)}}@layer ly.personalities.bauhaus{:where(.ly-root[data-ly-layout="bauhaus"]){--ly-personality-wrapper-max:96rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "sidebar main main aside" "sidebar footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,0.7fr) minmax(0,1.3fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.tactile{:where(.ly-root[data-ly-layout="tactile"]){--ly-personality-wrapper-max:82rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:15rem;--ly-recipe-rail:17rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.5fr) minmax(0,0.9fr)}}@layer ly.personalities.neumorphism{:where(.ly-root[data-ly-layout="neumorphism"]){--ly-personality-wrapper-max:84rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:17rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header sidebar" "main sidebar" "aside sidebar" "footer sidebar";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-list-detail-wide-columns:minmax(0,1.1fr) minmax(min(100%,var(--ly-pane-size)),0.7fr)}}@layer ly.personalities.retrofuturism{:where(.ly-root[data-ly-layout="retrofuturism"]){--ly-personality-wrapper-max:106rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:14rem;--ly-recipe-aside:17rem;--ly-app-shell-wide-areas:"sidebar header aside" "sidebar main aside" "sidebar footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.brutalism{:where(.ly-root[data-ly-layout="brutalism"]){--ly-personality-wrapper-max:100rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(0,1.3fr) minmax(0,0.7fr) minmax(min(100%,var(--ly-recipe-rail)),0.34fr)}}@layer ly.personalities.cyberpunk{:where(.ly-root[data-ly-layout="cyberpunk"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-recipe-rail:13rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.2fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.32fr)}}@layer ly.personalities.y2k{:where(.ly-root[data-ly-layout="y2k"]){--ly-personality-wrapper-max:90rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:18rem;--ly-recipe-aside:16rem;--ly-app-shell-wide-areas:"header header sidebar" "aside main sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-aside)),0.34fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.28fr)}}@layer ly.personalities.retro-glass{:where(.ly-root[data-ly-layout="retro-glass"]){--ly-personality-wrapper-max:104rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:15rem;--ly-recipe-rail:18rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main main sidebar" "aside footer footer";--ly-app-shell-wide-columns:minmax(0,1.25fr) minmax(0,0.75fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr)}}@layer ly.personalities.f-pattern{:where(.ly-root[data-ly-layout="f-pattern"]){--ly-personality-wrapper-max:92rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.26fr) minmax(0,1.35fr) minmax(min(100%,var(--ly-recipe-aside)),0.65fr);--ly-split-hero-wide-columns:minmax(0,1.8fr) minmax(0,0.8fr)}}@layer ly.personalities.z-pattern{:where(.ly-root[data-ly-layout="z-pattern"]){--ly-personality-wrapper-max:108rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer aside sidebar";--ly-app-shell-wide-columns:minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-split-hero-wide-columns:minmax(0,1.5fr) minmax(0,0.5fr)}}@layer ly.personalities.split-screen{:where(.ly-root[data-ly-layout="split-screen"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:20rem;--ly-recipe-rail:24rem;--ly-recipe-aside:24rem;--ly-app-shell-wide-areas:"header header" "main sidebar" "aside sidebar" "footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-split-hero-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.mondrian{:where(.ly-root[data-ly-layout="mondrian"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:11rem;--ly-recipe-rail:13rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.22fr) minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr)}}@layer ly.personalities.synthwave{:where(.ly-root[data-ly-layout="synthwave"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header header" "sidebar main aside" "footer footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.34fr);--ly-reel-item-min:20rem}} diff --git a/dist/wrappers.css b/dist/wrappers.css index af0f564..e5018ae 100644 --- a/dist/wrappers.css +++ b/dist/wrappers.css @@ -31,6 +31,10 @@ --ly-wrapper-max: var(--ly-wrapper-content); } + .ly-wrapper--workspace { + --ly-wrapper-max: var(--ly-wrapper-workspace); + } + .ly-wrapper--wide { --ly-wrapper-max: var(--ly-wrapper-wide); } diff --git a/manifest.json b/manifest.json index 5141c4e..d2032f5 100644 --- a/manifest.json +++ b/manifest.json @@ -23,7 +23,7 @@ "stable": [".ly-root", ".ly-wrapper"], "deprecated": [] }, - "wrappers": ["compact", "prose", "content", "wide", "full", "breakout"], + "wrappers": ["compact", "prose", "content", "workspace", "wide", "full", "breakout"], "primitives": ["page", "header", "footer", "main", "section", "surface", "readable", "stack", "cluster", "center", "cover", "switcher", "sidebar", "grid", "split", "panes", "media", "reel", "frame", "scroll"], "recipes": ["app-shell", "dashboard", "docs", "list-detail", "split-hero", "gallery", "card-grid"], "areas": ["header", "nav", "sidebar", "main", "aside", "footer", "content", "media", "actions", "primary", "secondary"], @@ -72,7 +72,8 @@ "geometry": [ "--ly-space-0", "--ly-space-1", "--ly-space-2", "--ly-space-3", "--ly-space-4", "--ly-space-5", "--ly-space-6", "--ly-space-7", "--ly-space-8", "--ly-space-9", - "--ly-wrapper-compact", "--ly-wrapper-prose", "--ly-wrapper-content", "--ly-wrapper-wide", + "--ly-wrapper-compact", "--ly-wrapper-prose", "--ly-wrapper-content", "--ly-wrapper-workspace", + "--ly-wrapper-wide", "--ly-page-padding-inline", "--ly-safe-area-inline", "--ly-safe-area-block-start", "--ly-safe-area-block-end", "--ly-wrapper-gutter", "--ly-wrapper-max", "--ly-profile-gap", "--ly-gap", "--ly-grid-gap", "--ly-stack-gap", "--ly-cluster-gap", diff --git a/styles/foundation.css b/styles/foundation.css index 96ab075..2722155 100644 --- a/styles/foundation.css +++ b/styles/foundation.css @@ -21,6 +21,7 @@ --ly-wrapper-compact: 40rem; --ly-wrapper-prose: 68ch; --ly-wrapper-content: 72rem; + --ly-wrapper-workspace: 96rem; --ly-wrapper-wide: 112rem; --ly-page-padding-inline: clamp(1rem, 3vw, 3rem); --ly-safe-area-inline: max( diff --git a/styles/wrappers.css b/styles/wrappers.css index af0f564..e5018ae 100644 --- a/styles/wrappers.css +++ b/styles/wrappers.css @@ -31,6 +31,10 @@ --ly-wrapper-max: var(--ly-wrapper-content); } + .ly-wrapper--workspace { + --ly-wrapper-max: var(--ly-wrapper-workspace); + } + .ly-wrapper--wide { --ly-wrapper-max: var(--ly-wrapper-wide); } diff --git a/test/demo-smoke.test.mjs b/test/demo-smoke.test.mjs index 76e4f86..162a5dc 100644 --- a/test/demo-smoke.test.mjs +++ b/test/demo-smoke.test.mjs @@ -46,7 +46,16 @@ const recipes = [ "card-grid" ]; const personalities = personalityMetadata.personalities.map(({ id }) => id); -const wrappers = ["default", "compact", "prose", "content", "wide", "full", "breakout"]; +const wrappers = [ + "default", + "compact", + "prose", + "content", + "workspace", + "wide", + "full", + "breakout" +]; const devices = { "phone-portrait": { width: 360, height: 800 }, "phone-landscape": { width: 800, height: 360 }, @@ -1119,6 +1128,60 @@ const verifyMinimumWidth = async (page, baseUrl) => { } }; +/** + * Verifies that the workspace Wrapper exposes a distinct task-oriented content + * measure between the conventional content and wide variants. + * + * @param {import("@playwright/test").Page} page Active browser page. + * @param {string} baseUrl Demo server URL. + * @returns {Promise} + */ +const verifyWorkspaceMeasure = async (page, baseUrl) => { + await page.setViewportSize({ width: 1920, height: 1080 }); + await page.goto(`${baseUrl}?ecosystem=layout-only`, { waitUntil: "domcontentloaded" }); + await page.waitForFunction(() => document.body.dataset.demoReady === "true"); + + const widths = await page.evaluate(() => { + const root = document.createElement("div"); + root.className = "ly-root"; + + /** + * Creates a Wrapper whose inner child exposes its usable content measure. + * + * @param {"content" | "workspace" | "wide"} variant Wrapper variant. + * @returns {{wrapper: HTMLElement, inner: HTMLElement}} Wrapper fixture. + */ + const createWrapperFixture = (variant) => { + const wrapper = document.createElement("section"); + wrapper.className = `ly-wrapper ly-wrapper--${variant}`; + const inner = document.createElement("div"); + inner.textContent = `${variant} measure`; + wrapper.append(inner); + return { wrapper, inner }; + }; + + const content = createWrapperFixture("content"); + const workspace = createWrapperFixture("workspace"); + const wide = createWrapperFixture("wide"); + root.append(content.wrapper, workspace.wrapper, wide.wrapper); + document.body.append(root); + const result = { + contentWidth: content.inner.getBoundingClientRect().width, + workspaceWidth: workspace.inner.getBoundingClientRect().width, + wideWidth: wide.inner.getBoundingClientRect().width + }; + root.remove(); + return result; + }); + + assert(widths.workspaceWidth > widths.contentWidth, "Workspace must use more width than content."); + assert(widths.workspaceWidth < widths.wideWidth, "Workspace must remain narrower than wide."); + assert( + widths.workspaceWidth >= 1536, + `Workspace content measure must reach 96rem at a wide viewport: ${JSON.stringify(widths)}` + ); +}; + const verifyBreakoutGeometry = async (page, baseUrl) => { await page.setViewportSize({ width: 1920, height: 1080 }); await page.goto(`${baseUrl}?ecosystem=layout-only`, { waitUntil: "domcontentloaded" }); @@ -1351,6 +1414,7 @@ try { await verifyDeviceMatrix(page, server.baseUrl); await verifyPersonalityMatrix(page, server.baseUrl); await verifyMinimumWidth(page, server.baseUrl); + await verifyWorkspaceMeasure(page, server.baseUrl); await verifyBreakoutGeometry(page, server.baseUrl); await verifyProfileAndUtilityIsolation(page, server.baseUrl); await verifyPrimitiveOverflow(page, server.baseUrl); diff --git a/test/layout-css-contract.test.mjs b/test/layout-css-contract.test.mjs index 50b7d2c..50c1127 100644 --- a/test/layout-css-contract.test.mjs +++ b/test/layout-css-contract.test.mjs @@ -261,9 +261,16 @@ assert( "Normal 3.1 sections need the conservative default rhythm." ); -for (const variant of ["compact", "prose", "content", "wide", "full", "breakout"]) { +for (const variant of ["compact", "prose", "content", "workspace", "wide", "full", "breakout"]) { assert(wrappers.includes(`.ly-wrapper--${variant}`), `Missing wrapper variant: ${variant}`); } +assert( + /--ly-wrapper-workspace:\s*96rem/.test(foundation) && + /\.ly-wrapper--workspace\s*\{[^}]*--ly-wrapper-max:\s*var\(--ly-wrapper-workspace\)/s.test( + wrappers + ), + "Workspace Wrapper must expose and consume the 96rem application measure." +); assert( wrappers.includes("container-name: ly-scope;") && wrappers.includes("container-type: inline-size;"), diff --git a/test/manifest-contract.test.mjs b/test/manifest-contract.test.mjs index a439ea9..701713c 100644 --- a/test/manifest-contract.test.mjs +++ b/test/manifest-contract.test.mjs @@ -48,7 +48,8 @@ const personalities = manifest.personalities; const geometryTokens = [ "--ly-space-0", "--ly-space-1", "--ly-space-2", "--ly-space-3", "--ly-space-4", "--ly-space-5", "--ly-space-6", "--ly-space-7", "--ly-space-8", "--ly-space-9", - "--ly-wrapper-compact", "--ly-wrapper-prose", "--ly-wrapper-content", "--ly-wrapper-wide", + "--ly-wrapper-compact", "--ly-wrapper-prose", "--ly-wrapper-content", "--ly-wrapper-workspace", + "--ly-wrapper-wide", "--ly-page-padding-inline", "--ly-safe-area-inline", "--ly-safe-area-block-start", "--ly-safe-area-block-end", "--ly-wrapper-gutter", "--ly-wrapper-max", "--ly-profile-gap", "--ly-gap", "--ly-grid-gap", "--ly-stack-gap", "--ly-cluster-gap", @@ -162,7 +163,9 @@ test("build regenerates public pairing metadata from manifest records", () => { test("ecosystem manifest describes real structural selectors, thresholds, and tokens", () => { assert.deepEqual(manifest.selectors.stable, [".ly-root", ".ly-wrapper"]); assert.deepEqual(manifest.selectors.deprecated, []); - assert.deepEqual(manifest.wrappers, ["compact", "prose", "content", "wide", "full", "breakout"]); + assert.deepEqual(manifest.wrappers, [ + "compact", "prose", "content", "workspace", "wide", "full", "breakout" + ]); assert.deepEqual(manifest.primitives, [ "page", "header", "footer", "main", "section", "surface", "readable", "stack", "cluster", "center", "cover", "switcher", "sidebar", "grid", "split", "panes", "media", "reel", "frame", "scroll" From 223e94c3f10460a8a7b8bd5729314d563593cfae Mon Sep 17 00:00:00 2001 From: Foscat Date: Tue, 25 Aug 2026 17:41:14 -0500 Subject: [PATCH 3/6] feat: localize layout gap utilities --- dist/layout-style-css.css | 44 +++++++-------------------- dist/layout-style-css.min.css | 2 +- dist/utilities.css | 44 +++++++-------------------- styles/utilities.css | 44 +++++++-------------------- test/demo-smoke.test.mjs | 50 +++++++++++++++++++++++-------- test/layout-css-contract.test.mjs | 18 +++++++++-- 6 files changed, 83 insertions(+), 119 deletions(-) diff --git a/dist/layout-style-css.css b/dist/layout-style-css.css index 741ec9e..bbeb05c 100644 --- a/dist/layout-style-css.css +++ b/dist/layout-style-css.css @@ -838,40 +838,16 @@ .ly-span-4 { grid-column: span 4; } .ly-span-full { grid-column: 1 / -1; } - .ly-gap-0 { - --ly-gap: var(--ly-space-0); - --ly-grid-gap: var(--ly-space-0); - --ly-stack-gap: var(--ly-space-0); - --ly-cluster-gap: var(--ly-space-0); - } - - .ly-gap-2 { - --ly-gap: var(--ly-space-2); - --ly-grid-gap: var(--ly-space-2); - --ly-stack-gap: var(--ly-space-2); - --ly-cluster-gap: var(--ly-space-2); - } - - .ly-gap-4 { - --ly-gap: var(--ly-space-4); - --ly-grid-gap: var(--ly-space-4); - --ly-stack-gap: var(--ly-space-4); - --ly-cluster-gap: var(--ly-space-4); - } - - .ly-gap-6 { - --ly-gap: var(--ly-space-6); - --ly-grid-gap: var(--ly-space-6); - --ly-stack-gap: var(--ly-space-6); - --ly-cluster-gap: var(--ly-space-6); - } - - .ly-gap-8 { - --ly-gap: var(--ly-space-8); - --ly-grid-gap: var(--ly-space-8); - --ly-stack-gap: var(--ly-space-8); - --ly-cluster-gap: var(--ly-space-8); - } + .ly-gap-0 { gap: var(--ly-space-0); } + .ly-gap-1 { gap: var(--ly-space-1); } + .ly-gap-2 { gap: var(--ly-space-2); } + .ly-gap-3 { gap: var(--ly-space-3); } + .ly-gap-4 { gap: var(--ly-space-4); } + .ly-gap-5 { gap: var(--ly-space-5); } + .ly-gap-6 { gap: var(--ly-space-6); } + .ly-gap-7 { gap: var(--ly-space-7); } + .ly-gap-8 { gap: var(--ly-space-8); } + .ly-gap-9 { gap: var(--ly-space-9); } .ly-pad-0 { padding: var(--ly-space-0); } .ly-pad-2 { padding: var(--ly-space-2); } diff --git a/dist/layout-style-css.min.css b/dist/layout-style-css.min.css index d07c60e..e128c0d 100644 --- a/dist/layout-style-css.min.css +++ b/dist/layout-style-css.min.css @@ -1 +1 @@ -@layer ly.reset,ly.tokens,ly.wrappers,ly.primitives,ly.recipes,ly.utilities,ly.personalities,ly.context;@layer ly.tokens{:where(.ly-root){--ly-space-0:0;--ly-space-1:0.25rem;--ly-space-2:0.5rem;--ly-space-3:0.75rem;--ly-space-4:1rem;--ly-space-5:1.5rem;--ly-space-6:2rem;--ly-space-7:3rem;--ly-space-8:4rem;--ly-space-9:6rem;--ly-wrapper-compact:40rem;--ly-wrapper-prose:68ch;--ly-wrapper-content:72rem;--ly-wrapper-workspace:96rem;--ly-wrapper-wide:112rem;--ly-page-padding-inline:clamp(1rem,3vw,3rem);--ly-safe-area-inline:max( env(safe-area-inset-left,0px),env(safe-area-inset-right,0px) );--ly-safe-area-block-start:env(safe-area-inset-top,0px);--ly-safe-area-block-end:env(safe-area-inset-bottom,0px);--ly-wrapper-gutter:var(--ly-page-padding-inline);--ly-profile-gap:var(--ly-space-5);--ly-gap:var(--ly-profile-gap);--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(2rem,4vh,4rem);--ly-section-padding-block-compact:clamp(1rem,2.5vh,2rem);--ly-header-height:4.5rem;--ly-sticky-position:sticky;--ly-cover-min:100vh;--ly-shell-min:100vh;--ly-scroll-max:min(70vh,50rem);--ly-switcher-threshold:42rem;--ly-sidebar-size:18rem;--ly-sidebar-content-min:50%;--ly-grid-columns:12;--ly-grid-min:16rem;--ly-split-min:20rem;--ly-pane-min:18rem;--ly-pane-size:22rem;--ly-media-min:18rem;--ly-media-size:14rem;--ly-reel-item-min:16rem;--ly-reel-item-max:28rem;--ly-frame-ratio:16 / 9;--ly-split-primary:1fr;--ly-split-secondary:1fr;--ly-recipe-rail:18rem;--ly-recipe-aside:22rem;--ly-gallery-min:12rem;--ly-card-grid-min:16rem;--ly-app-shell-base-rows:auto auto minmax(0,1fr) auto auto;--ly-app-shell-medium-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-medium-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-medium-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-medium-areas:"header header" "nav nav" "main aside" "footer footer";--ly-dashboard-medium-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-wide-areas:"nav header header" "nav main aside" "nav footer footer";--ly-dashboard-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-docs-wide-areas:"header header" "nav main" "nav aside" "footer footer";--ly-docs-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-list-detail-wide-areas:"primary secondary" "actions actions";--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.7fr) minmax(0,1fr);--ly-split-hero-wide-areas:"content media" "actions media";--ly-split-hero-wide-columns:minmax(0,var(--ly-split-primary,1fr)) minmax(0,var(--ly-split-secondary,1fr));container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}@supports (width:1cqi){:where(.ly-root){--ly-page-padding-inline:clamp(1rem,3cqi,3rem)}}@supports (height:100dvh){:where(.ly-root){--ly-cover-min:100dvh;--ly-shell-min:100dvh;--ly-scroll-max:min(70dvh,50rem)}}}@layer ly.reset{:where(.ly-root),:where(.ly-root *){box-sizing:border-box}:where(.ly-root){margin:0}:where(.ly-root img),:where(.ly-root svg),:where(.ly-root video),:where(.ly-root canvas),:where(.ly-root iframe){max-inline-size:100%}}@layer ly.tokens{@media (max-height:44rem){:where(.ly-root){--ly-gap:min(var(--ly-profile-gap),var(--ly-space-4));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:min(var(--ly-space-4),3vh);--ly-cluster-gap:min(var(--ly-space-3),2vh);--ly-section-padding-block:clamp(1.25rem,4vh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2vh,1.25rem);--ly-header-height:3.5rem;--ly-scroll-max:min(62vh,34rem)}@supports (height:100dvh){:where(.ly-root){--ly-stack-gap:min(var(--ly-space-4),3dvh);--ly-cluster-gap:min(var(--ly-space-3),2dvh);--ly-section-padding-block:clamp(1.25rem,4dvh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2dvh,1.25rem);--ly-scroll-max:min(62dvh,34rem)}}}@media (max-height:30rem){:where(.ly-root){--ly-cover-min:auto;--ly-shell-min:auto;--ly-sticky-position:static;--ly-section-padding-block:var(--ly-space-4);--ly-section-padding-block-compact:var(--ly-space-2);--ly-scroll-max:max(12rem,calc(100vh - 8rem))}@supports (height:100dvh){:where(.ly-root){--ly-scroll-max:max(12rem,calc(100dvh - 8rem))}}}}@layer ly.context{:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:clamp(1rem,2.5vh,2rem);--ly-section-padding-block-compact:clamp(0.5rem,1.5vh,1rem)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:var(--ly-profile-gap);--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(2rem,4vh,4rem);--ly-section-padding-block-compact:clamp(1rem,2.5vh,2rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:max(var(--ly-profile-gap),var(--ly-space-6));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-5);--ly-cluster-gap:var(--ly-space-4);--ly-section-padding-block:clamp(3rem,7vh,6rem);--ly-section-padding-block-compact:clamp(1.5rem,3.5vh,3rem)}@media (max-height:44rem){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:clamp(0.75rem,2vh,1.25rem);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:min(var(--ly-profile-gap),var(--ly-space-4));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(1.25rem,4vh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2vh,1.25rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:var(--ly-space-5);--ly-grid-gap:var(--ly-space-5);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(1.5rem,6vh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3vh,1.25rem)}@supports (height:100dvh){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-section-padding-block:clamp(0.75rem,2dvh,1.25rem)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-section-padding-block:clamp(1.25rem,4dvh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2dvh,1.25rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-section-padding-block:clamp(1.5rem,6dvh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3dvh,1.25rem)}}}@media (max-height:30rem){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-2);--ly-grid-gap:var(--ly-space-2);--ly-stack-gap:var(--ly-space-2);--ly-cluster-gap:var(--ly-space-1);--ly-section-padding-block:var(--ly-space-3);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:var(--ly-space-4);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:var(--ly-space-4);--ly-grid-gap:var(--ly-space-4);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:var(--ly-space-5);--ly-section-padding-block-compact:var(--ly-space-3)}}}@layer ly.wrappers{.ly-wrapper{--ly-wrapper-max:var(--ly-personality-wrapper-max,var(--ly-wrapper-content));--ly-wrapper-fluid-gutter:var(--ly-wrapper-gutter);--ly-wrapper-local-gutter:max( var(--ly-wrapper-fluid-gutter),var(--ly-safe-area-inline) );container-name:ly-scope;container-type:inline-size;inline-size:min(100%,calc(var(--ly-wrapper-max) + (var(--ly-wrapper-local-gutter) * 2)));max-inline-size:100%;margin-inline:auto;padding-inline:var(--ly-wrapper-local-gutter)}.ly-wrapper--compact{--ly-wrapper-max:var(--ly-wrapper-compact)}.ly-wrapper--prose{--ly-wrapper-max:var(--ly-wrapper-prose)}.ly-wrapper--content{--ly-wrapper-max:var(--ly-wrapper-content)}.ly-wrapper--workspace{--ly-wrapper-max:var(--ly-wrapper-workspace)}.ly-wrapper--wide{--ly-wrapper-max:var(--ly-wrapper-wide)}.ly-wrapper--full{--ly-wrapper-max:100%}.ly-wrapper--breakout{--ly-wrapper-max:100%;--ly-breakout-content-size:min( var(--ly-wrapper-content),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );--ly-breakout-feature-size:min( var(--ly-wrapper-wide),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );display:grid;grid-template-columns:[full-start] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [feature-start] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [content-start] minmax(0,var(--ly-breakout-content-size)) [content-end] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [feature-end] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [full-end];inline-size:100%;padding-inline:0}.ly-wrapper--breakout>*{grid-column:content;min-inline-size:0;max-inline-size:100%}.ly-wrapper--breakout>:where(.ly-lane--content,[data-ly-lane="content"]){grid-column:content}.ly-wrapper--breakout>:where(.ly-lane--feature,[data-ly-lane="feature"]){grid-column:feature}.ly-wrapper--breakout>:where(.ly-lane--full,[data-ly-lane="full"]){grid-column:full}}@layer ly.primitives{.ly-page{min-block-size:100vh;min-block-size:100dvh;padding-block-start:var(--ly-safe-area-block-start);padding-block-end:var(--ly-safe-area-block-end)}.ly-header,.ly-footer,.ly-main,.ly-section,.ly-surface{min-inline-size:0;min-block-size:0}.ly-header{min-block-size:var(--ly-header-height)}.ly-header--sticky{position:var(--ly-sticky-position,sticky);inset-block-start:0;z-index:var(--ly-z-header,20)}.ly-section{padding-block:var(--ly-section-padding-block)}.ly-section--compact{padding-block:var(--ly-section-padding-block-compact)}.ly-section--flush{padding-block:0}.ly-readable{max-inline-size:var(--ly-wrapper-prose)}.ly-stack{display:flex;flex-direction:column;gap:var(--ly-stack-gap)}.ly-cluster{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ly-cluster-gap)}.ly-center{inline-size:min(100%,var(--ly-center-max,var(--ly-wrapper-content)));margin-inline:auto}.ly-cover{display:flex;flex-direction:column;min-block-size:100vh;min-block-size:var(--ly-cover-min);padding-block:var(--ly-cover-padding,var(--ly-space-5))}.ly-cover>:where(.ly-cover__center,[data-ly-cover-center]){margin-block:auto}.ly-switcher{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-switcher>*{flex-basis:calc((var(--ly-switcher-threshold) - 100%) * 999);flex-grow:1;min-inline-size:0}.ly-sidebar{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-sidebar>:where(.ly-sidebar__side,[data-ly-sidebar="side"]){flex-basis:var(--ly-sidebar-size);flex-grow:1;min-inline-size:min(100%,var(--ly-sidebar-size))}.ly-sidebar>:where(.ly-sidebar__content,[data-ly-sidebar="content"]){flex-basis:0;flex-grow:999;min-inline-size:min(100%,var(--ly-sidebar-content-min))}.ly-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-grid-min)),1fr));gap:var(--ly-grid-gap)}.ly-grid--fixed{grid-template-columns:repeat(var(--ly-grid-columns),minmax(0,1fr))}.ly-split{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-split-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-split-align,center)}.ly-panes{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-pane-min)),1fr));gap:var(--ly-gap)}.ly-media{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-media-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-media-align,start)}.ly-media>:where(.ly-media__actions,[data-ly-media="actions"]){min-inline-size:min(100%,var(--ly-media-size))}.ly-grid>*,.ly-split>*,.ly-panes>*,.ly-media>*{min-inline-size:0;min-block-size:0}.ly-reel{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(min(100%,var(--ly-reel-item-min)),var(--ly-reel-item-max));gap:var(--ly-grid-gap);overflow-x:auto;overscroll-behavior-inline:contain;scroll-snap-type:inline proximity}.ly-reel>*{min-inline-size:0;scroll-snap-align:start}.ly-frame{aspect-ratio:var(--ly-frame-ratio);overflow:hidden}.ly-frame>*{inline-size:100%;block-size:100%;object-fit:cover}.ly-scroll{min-block-size:0;max-block-size:var(--ly-scroll-max);overflow-x:clip;overflow-y:auto;overscroll-behavior-block:contain}}@layer ly.recipes{:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] ){container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"] ){display:grid;grid-template-columns:minmax(0,1fr);gap:var(--ly-gap)}[data-ly-recipe="app-shell"]{min-block-size:100vh;min-block-size:var(--ly-shell-min);grid-template-areas:"header" "sidebar" "main" "aside" "footer";grid-template-rows:var(--ly-app-shell-base-rows)}[data-ly-recipe="dashboard"],[data-ly-recipe="docs"]{grid-template-areas:"header" "nav" "main" "aside" "footer"}[data-ly-recipe="list-detail"]{grid-template-areas:"primary" "secondary" "actions"}[data-ly-recipe="split-hero"]{grid-template-areas:"content" "media" "actions";align-items:var(--ly-split-hero-align,center)}[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"]{display:grid;gap:var(--ly-grid-gap)}[data-ly-recipe="gallery"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-gallery-min)),1fr))}[data-ly-recipe="card-grid"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-card-grid-min)),1fr))}:where( [data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )[data-ly-responsive="manual"]{grid-template-columns:minmax(0,1fr)}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )>*{min-inline-size:0;min-block-size:0}[data-ly-area="header"]{grid-area:header}[data-ly-area="nav"]{grid-area:nav}[data-ly-area="sidebar"]{grid-area:sidebar}[data-ly-area="main"]{grid-area:main}[data-ly-area="aside"]{grid-area:aside}[data-ly-area="footer"]{grid-area:footer}[data-ly-area="content"]{grid-area:content}[data-ly-area="media"]{grid-area:media}[data-ly-area="actions"]{grid-area:actions}[data-ly-area="primary"]{grid-area:primary}[data-ly-area="secondary"]{grid-area:secondary}@container ly-scope (min-width:42rem){[data-ly-recipe="split-hero"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-split-hero-wide-areas);grid-template-columns:var(--ly-split-hero-wide-columns)}}@container ly-scope (min-width:44rem){[data-ly-recipe="list-detail"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-list-detail-wide-areas);grid-template-columns:var(--ly-list-detail-wide-columns)}}@container ly-scope (min-width:48rem){[data-ly-recipe="docs"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-docs-wide-areas);grid-template-columns:var(--ly-docs-wide-columns)}}@container ly-scope (min-width:52rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-medium-areas);grid-template-columns:var(--ly-app-shell-medium-columns);grid-template-rows:var(--ly-app-shell-medium-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-medium-areas);grid-template-columns:var(--ly-dashboard-medium-columns)}}@container ly-scope (min-width:72rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-wide-areas);grid-template-columns:var(--ly-app-shell-wide-columns);grid-template-rows:var(--ly-app-shell-wide-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-wide-areas);grid-template-columns:var(--ly-dashboard-wide-columns)}}}@layer ly.utilities{.ly-cols-1{--ly-grid-columns:1}.ly-cols-2{--ly-grid-columns:2}.ly-cols-3{--ly-grid-columns:3}.ly-cols-4{--ly-grid-columns:4}.ly-cols-6{--ly-grid-columns:6}.ly-cols-12{--ly-grid-columns:12}.ly-span-1{grid-column:span 1}.ly-span-2{grid-column:span 2}.ly-span-3{grid-column:span 3}.ly-span-4{grid-column:span 4}.ly-span-full{grid-column:1 / -1}.ly-gap-0{--ly-gap:var(--ly-space-0);--ly-grid-gap:var(--ly-space-0);--ly-stack-gap:var(--ly-space-0);--ly-cluster-gap:var(--ly-space-0)}.ly-gap-2{--ly-gap:var(--ly-space-2);--ly-grid-gap:var(--ly-space-2);--ly-stack-gap:var(--ly-space-2);--ly-cluster-gap:var(--ly-space-2)}.ly-gap-4{--ly-gap:var(--ly-space-4);--ly-grid-gap:var(--ly-space-4);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-4)}.ly-gap-6{--ly-gap:var(--ly-space-6);--ly-grid-gap:var(--ly-space-6);--ly-stack-gap:var(--ly-space-6);--ly-cluster-gap:var(--ly-space-6)}.ly-gap-8{--ly-gap:var(--ly-space-8);--ly-grid-gap:var(--ly-space-8);--ly-stack-gap:var(--ly-space-8);--ly-cluster-gap:var(--ly-space-8)}.ly-pad-0{padding:var(--ly-space-0)}.ly-pad-2{padding:var(--ly-space-2)}.ly-pad-4{padding:var(--ly-space-4)}.ly-pad-6{padding:var(--ly-space-6)}.ly-pad-8{padding:var(--ly-space-8)}.ly-mx-auto{margin-inline:auto}.ly-w-full{inline-size:100%}.ly-h-full{block-size:100%}.ly-min-h-screen{min-block-size:100vh;min-block-size:100dvh}.ly-overflow-auto{overflow:auto}.ly-overflow-hidden{overflow:hidden}.ly-items-start{align-items:flex-start}.ly-items-center{align-items:center}.ly-items-end{align-items:flex-end}.ly-items-stretch{align-items:stretch}.ly-justify-start{justify-content:flex-start}.ly-justify-center{justify-content:center}.ly-justify-end{justify-content:flex-end}.ly-justify-between{justify-content:space-between}.ly-frame-1x1{--ly-frame-ratio:1 / 1}.ly-frame-3x2{--ly-frame-ratio:3 / 2}.ly-frame-4x3{--ly-frame-ratio:4 / 3}.ly-frame-16x9{--ly-frame-ratio:16 / 9}.ly-frame-21x9{--ly-frame-ratio:21 / 9}.ly-hidden{display:none !important}.ly-visually-hidden{position:absolute !important;inline-size:1px !important;block-size:1px !important;padding:0 !important;margin:-1px !important;overflow:hidden !important;clip:rect(0,0,0,0) !important;clip-path:inset(50%) !important;white-space:nowrap !important}}@layer ly.personalities.minimal-saas{:where(.ly-root[data-ly-layout="minimal-saas"]){--ly-personality-wrapper-max:88rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:16rem;--ly-recipe-rail:15rem;--ly-recipe-aside:16rem;--ly-split-primary:1.05fr;--ly-split-secondary:0.95fr}}@layer ly.personalities.bento{:where(.ly-root[data-ly-layout="bento"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-card-grid-min:14rem;--ly-gallery-min:11rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header" "sidebar main main" "aside main main" "footer footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.3fr) minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.maximalist{:where(.ly-root[data-ly-layout="maximalist"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-7);--ly-grid-min:10rem;--ly-card-grid-min:13rem;--ly-gallery-min:10rem;--ly-recipe-rail:15rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "main main sidebar aside" "main main footer footer";--ly-app-shell-wide-columns:minmax(0,1.4fr) minmax(0,0.6fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr) minmax(min(100%,var(--ly-recipe-aside)),0.38fr)}}@layer ly.personalities.bauhaus{:where(.ly-root[data-ly-layout="bauhaus"]){--ly-personality-wrapper-max:96rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "sidebar main main aside" "sidebar footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,0.7fr) minmax(0,1.3fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.tactile{:where(.ly-root[data-ly-layout="tactile"]){--ly-personality-wrapper-max:82rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:15rem;--ly-recipe-rail:17rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.5fr) minmax(0,0.9fr)}}@layer ly.personalities.neumorphism{:where(.ly-root[data-ly-layout="neumorphism"]){--ly-personality-wrapper-max:84rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:17rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header sidebar" "main sidebar" "aside sidebar" "footer sidebar";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-list-detail-wide-columns:minmax(0,1.1fr) minmax(min(100%,var(--ly-pane-size)),0.7fr)}}@layer ly.personalities.retrofuturism{:where(.ly-root[data-ly-layout="retrofuturism"]){--ly-personality-wrapper-max:106rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:14rem;--ly-recipe-aside:17rem;--ly-app-shell-wide-areas:"sidebar header aside" "sidebar main aside" "sidebar footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.brutalism{:where(.ly-root[data-ly-layout="brutalism"]){--ly-personality-wrapper-max:100rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(0,1.3fr) minmax(0,0.7fr) minmax(min(100%,var(--ly-recipe-rail)),0.34fr)}}@layer ly.personalities.cyberpunk{:where(.ly-root[data-ly-layout="cyberpunk"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-recipe-rail:13rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.2fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.32fr)}}@layer ly.personalities.y2k{:where(.ly-root[data-ly-layout="y2k"]){--ly-personality-wrapper-max:90rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:18rem;--ly-recipe-aside:16rem;--ly-app-shell-wide-areas:"header header sidebar" "aside main sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-aside)),0.34fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.28fr)}}@layer ly.personalities.retro-glass{:where(.ly-root[data-ly-layout="retro-glass"]){--ly-personality-wrapper-max:104rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:15rem;--ly-recipe-rail:18rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main main sidebar" "aside footer footer";--ly-app-shell-wide-columns:minmax(0,1.25fr) minmax(0,0.75fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr)}}@layer ly.personalities.f-pattern{:where(.ly-root[data-ly-layout="f-pattern"]){--ly-personality-wrapper-max:92rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.26fr) minmax(0,1.35fr) minmax(min(100%,var(--ly-recipe-aside)),0.65fr);--ly-split-hero-wide-columns:minmax(0,1.8fr) minmax(0,0.8fr)}}@layer ly.personalities.z-pattern{:where(.ly-root[data-ly-layout="z-pattern"]){--ly-personality-wrapper-max:108rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer aside sidebar";--ly-app-shell-wide-columns:minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-split-hero-wide-columns:minmax(0,1.5fr) minmax(0,0.5fr)}}@layer ly.personalities.split-screen{:where(.ly-root[data-ly-layout="split-screen"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:20rem;--ly-recipe-rail:24rem;--ly-recipe-aside:24rem;--ly-app-shell-wide-areas:"header header" "main sidebar" "aside sidebar" "footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-split-hero-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.mondrian{:where(.ly-root[data-ly-layout="mondrian"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:11rem;--ly-recipe-rail:13rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.22fr) minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr)}}@layer ly.personalities.synthwave{:where(.ly-root[data-ly-layout="synthwave"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header header" "sidebar main aside" "footer footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.34fr);--ly-reel-item-min:20rem}} +@layer ly.reset,ly.tokens,ly.wrappers,ly.primitives,ly.recipes,ly.utilities,ly.personalities,ly.context;@layer ly.tokens{:where(.ly-root){--ly-space-0:0;--ly-space-1:0.25rem;--ly-space-2:0.5rem;--ly-space-3:0.75rem;--ly-space-4:1rem;--ly-space-5:1.5rem;--ly-space-6:2rem;--ly-space-7:3rem;--ly-space-8:4rem;--ly-space-9:6rem;--ly-wrapper-compact:40rem;--ly-wrapper-prose:68ch;--ly-wrapper-content:72rem;--ly-wrapper-workspace:96rem;--ly-wrapper-wide:112rem;--ly-page-padding-inline:clamp(1rem,3vw,3rem);--ly-safe-area-inline:max( env(safe-area-inset-left,0px),env(safe-area-inset-right,0px) );--ly-safe-area-block-start:env(safe-area-inset-top,0px);--ly-safe-area-block-end:env(safe-area-inset-bottom,0px);--ly-wrapper-gutter:var(--ly-page-padding-inline);--ly-profile-gap:var(--ly-space-5);--ly-gap:var(--ly-profile-gap);--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(2rem,4vh,4rem);--ly-section-padding-block-compact:clamp(1rem,2.5vh,2rem);--ly-header-height:4.5rem;--ly-sticky-position:sticky;--ly-cover-min:100vh;--ly-shell-min:100vh;--ly-scroll-max:min(70vh,50rem);--ly-switcher-threshold:42rem;--ly-sidebar-size:18rem;--ly-sidebar-content-min:50%;--ly-grid-columns:12;--ly-grid-min:16rem;--ly-split-min:20rem;--ly-pane-min:18rem;--ly-pane-size:22rem;--ly-media-min:18rem;--ly-media-size:14rem;--ly-reel-item-min:16rem;--ly-reel-item-max:28rem;--ly-frame-ratio:16 / 9;--ly-split-primary:1fr;--ly-split-secondary:1fr;--ly-recipe-rail:18rem;--ly-recipe-aside:22rem;--ly-gallery-min:12rem;--ly-card-grid-min:16rem;--ly-app-shell-base-rows:auto auto minmax(0,1fr) auto auto;--ly-app-shell-medium-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-medium-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-medium-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-medium-areas:"header header" "nav nav" "main aside" "footer footer";--ly-dashboard-medium-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-wide-areas:"nav header header" "nav main aside" "nav footer footer";--ly-dashboard-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-docs-wide-areas:"header header" "nav main" "nav aside" "footer footer";--ly-docs-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-list-detail-wide-areas:"primary secondary" "actions actions";--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.7fr) minmax(0,1fr);--ly-split-hero-wide-areas:"content media" "actions media";--ly-split-hero-wide-columns:minmax(0,var(--ly-split-primary,1fr)) minmax(0,var(--ly-split-secondary,1fr));container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}@supports (width:1cqi){:where(.ly-root){--ly-page-padding-inline:clamp(1rem,3cqi,3rem)}}@supports (height:100dvh){:where(.ly-root){--ly-cover-min:100dvh;--ly-shell-min:100dvh;--ly-scroll-max:min(70dvh,50rem)}}}@layer ly.reset{:where(.ly-root),:where(.ly-root *){box-sizing:border-box}:where(.ly-root){margin:0}:where(.ly-root img),:where(.ly-root svg),:where(.ly-root video),:where(.ly-root canvas),:where(.ly-root iframe){max-inline-size:100%}}@layer ly.tokens{@media (max-height:44rem){:where(.ly-root){--ly-gap:min(var(--ly-profile-gap),var(--ly-space-4));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:min(var(--ly-space-4),3vh);--ly-cluster-gap:min(var(--ly-space-3),2vh);--ly-section-padding-block:clamp(1.25rem,4vh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2vh,1.25rem);--ly-header-height:3.5rem;--ly-scroll-max:min(62vh,34rem)}@supports (height:100dvh){:where(.ly-root){--ly-stack-gap:min(var(--ly-space-4),3dvh);--ly-cluster-gap:min(var(--ly-space-3),2dvh);--ly-section-padding-block:clamp(1.25rem,4dvh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2dvh,1.25rem);--ly-scroll-max:min(62dvh,34rem)}}}@media (max-height:30rem){:where(.ly-root){--ly-cover-min:auto;--ly-shell-min:auto;--ly-sticky-position:static;--ly-section-padding-block:var(--ly-space-4);--ly-section-padding-block-compact:var(--ly-space-2);--ly-scroll-max:max(12rem,calc(100vh - 8rem))}@supports (height:100dvh){:where(.ly-root){--ly-scroll-max:max(12rem,calc(100dvh - 8rem))}}}}@layer ly.context{:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:clamp(1rem,2.5vh,2rem);--ly-section-padding-block-compact:clamp(0.5rem,1.5vh,1rem)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:var(--ly-profile-gap);--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(2rem,4vh,4rem);--ly-section-padding-block-compact:clamp(1rem,2.5vh,2rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:max(var(--ly-profile-gap),var(--ly-space-6));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-5);--ly-cluster-gap:var(--ly-space-4);--ly-section-padding-block:clamp(3rem,7vh,6rem);--ly-section-padding-block-compact:clamp(1.5rem,3.5vh,3rem)}@media (max-height:44rem){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:clamp(0.75rem,2vh,1.25rem);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:min(var(--ly-profile-gap),var(--ly-space-4));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(1.25rem,4vh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2vh,1.25rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:var(--ly-space-5);--ly-grid-gap:var(--ly-space-5);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(1.5rem,6vh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3vh,1.25rem)}@supports (height:100dvh){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-section-padding-block:clamp(0.75rem,2dvh,1.25rem)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-section-padding-block:clamp(1.25rem,4dvh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2dvh,1.25rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-section-padding-block:clamp(1.5rem,6dvh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3dvh,1.25rem)}}}@media (max-height:30rem){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-2);--ly-grid-gap:var(--ly-space-2);--ly-stack-gap:var(--ly-space-2);--ly-cluster-gap:var(--ly-space-1);--ly-section-padding-block:var(--ly-space-3);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:var(--ly-space-4);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:var(--ly-space-4);--ly-grid-gap:var(--ly-space-4);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:var(--ly-space-5);--ly-section-padding-block-compact:var(--ly-space-3)}}}@layer ly.wrappers{.ly-wrapper{--ly-wrapper-max:var(--ly-personality-wrapper-max,var(--ly-wrapper-content));--ly-wrapper-fluid-gutter:var(--ly-wrapper-gutter);--ly-wrapper-local-gutter:max( var(--ly-wrapper-fluid-gutter),var(--ly-safe-area-inline) );container-name:ly-scope;container-type:inline-size;inline-size:min(100%,calc(var(--ly-wrapper-max) + (var(--ly-wrapper-local-gutter) * 2)));max-inline-size:100%;margin-inline:auto;padding-inline:var(--ly-wrapper-local-gutter)}.ly-wrapper--compact{--ly-wrapper-max:var(--ly-wrapper-compact)}.ly-wrapper--prose{--ly-wrapper-max:var(--ly-wrapper-prose)}.ly-wrapper--content{--ly-wrapper-max:var(--ly-wrapper-content)}.ly-wrapper--workspace{--ly-wrapper-max:var(--ly-wrapper-workspace)}.ly-wrapper--wide{--ly-wrapper-max:var(--ly-wrapper-wide)}.ly-wrapper--full{--ly-wrapper-max:100%}.ly-wrapper--breakout{--ly-wrapper-max:100%;--ly-breakout-content-size:min( var(--ly-wrapper-content),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );--ly-breakout-feature-size:min( var(--ly-wrapper-wide),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );display:grid;grid-template-columns:[full-start] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [feature-start] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [content-start] minmax(0,var(--ly-breakout-content-size)) [content-end] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [feature-end] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [full-end];inline-size:100%;padding-inline:0}.ly-wrapper--breakout>*{grid-column:content;min-inline-size:0;max-inline-size:100%}.ly-wrapper--breakout>:where(.ly-lane--content,[data-ly-lane="content"]){grid-column:content}.ly-wrapper--breakout>:where(.ly-lane--feature,[data-ly-lane="feature"]){grid-column:feature}.ly-wrapper--breakout>:where(.ly-lane--full,[data-ly-lane="full"]){grid-column:full}}@layer ly.primitives{.ly-page{min-block-size:100vh;min-block-size:100dvh;padding-block-start:var(--ly-safe-area-block-start);padding-block-end:var(--ly-safe-area-block-end)}.ly-header,.ly-footer,.ly-main,.ly-section,.ly-surface{min-inline-size:0;min-block-size:0}.ly-header{min-block-size:var(--ly-header-height)}.ly-header--sticky{position:var(--ly-sticky-position,sticky);inset-block-start:0;z-index:var(--ly-z-header,20)}.ly-section{padding-block:var(--ly-section-padding-block)}.ly-section--compact{padding-block:var(--ly-section-padding-block-compact)}.ly-section--flush{padding-block:0}.ly-readable{max-inline-size:var(--ly-wrapper-prose)}.ly-stack{display:flex;flex-direction:column;gap:var(--ly-stack-gap)}.ly-cluster{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ly-cluster-gap)}.ly-center{inline-size:min(100%,var(--ly-center-max,var(--ly-wrapper-content)));margin-inline:auto}.ly-cover{display:flex;flex-direction:column;min-block-size:100vh;min-block-size:var(--ly-cover-min);padding-block:var(--ly-cover-padding,var(--ly-space-5))}.ly-cover>:where(.ly-cover__center,[data-ly-cover-center]){margin-block:auto}.ly-switcher{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-switcher>*{flex-basis:calc((var(--ly-switcher-threshold) - 100%) * 999);flex-grow:1;min-inline-size:0}.ly-sidebar{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-sidebar>:where(.ly-sidebar__side,[data-ly-sidebar="side"]){flex-basis:var(--ly-sidebar-size);flex-grow:1;min-inline-size:min(100%,var(--ly-sidebar-size))}.ly-sidebar>:where(.ly-sidebar__content,[data-ly-sidebar="content"]){flex-basis:0;flex-grow:999;min-inline-size:min(100%,var(--ly-sidebar-content-min))}.ly-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-grid-min)),1fr));gap:var(--ly-grid-gap)}.ly-grid--fixed{grid-template-columns:repeat(var(--ly-grid-columns),minmax(0,1fr))}.ly-split{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-split-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-split-align,center)}.ly-panes{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-pane-min)),1fr));gap:var(--ly-gap)}.ly-media{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-media-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-media-align,start)}.ly-media>:where(.ly-media__actions,[data-ly-media="actions"]){min-inline-size:min(100%,var(--ly-media-size))}.ly-grid>*,.ly-split>*,.ly-panes>*,.ly-media>*{min-inline-size:0;min-block-size:0}.ly-reel{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(min(100%,var(--ly-reel-item-min)),var(--ly-reel-item-max));gap:var(--ly-grid-gap);overflow-x:auto;overscroll-behavior-inline:contain;scroll-snap-type:inline proximity}.ly-reel>*{min-inline-size:0;scroll-snap-align:start}.ly-frame{aspect-ratio:var(--ly-frame-ratio);overflow:hidden}.ly-frame>*{inline-size:100%;block-size:100%;object-fit:cover}.ly-scroll{min-block-size:0;max-block-size:var(--ly-scroll-max);overflow-x:clip;overflow-y:auto;overscroll-behavior-block:contain}}@layer ly.recipes{:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] ){container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"] ){display:grid;grid-template-columns:minmax(0,1fr);gap:var(--ly-gap)}[data-ly-recipe="app-shell"]{min-block-size:100vh;min-block-size:var(--ly-shell-min);grid-template-areas:"header" "sidebar" "main" "aside" "footer";grid-template-rows:var(--ly-app-shell-base-rows)}[data-ly-recipe="dashboard"],[data-ly-recipe="docs"]{grid-template-areas:"header" "nav" "main" "aside" "footer"}[data-ly-recipe="list-detail"]{grid-template-areas:"primary" "secondary" "actions"}[data-ly-recipe="split-hero"]{grid-template-areas:"content" "media" "actions";align-items:var(--ly-split-hero-align,center)}[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"]{display:grid;gap:var(--ly-grid-gap)}[data-ly-recipe="gallery"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-gallery-min)),1fr))}[data-ly-recipe="card-grid"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-card-grid-min)),1fr))}:where( [data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )[data-ly-responsive="manual"]{grid-template-columns:minmax(0,1fr)}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )>*{min-inline-size:0;min-block-size:0}[data-ly-area="header"]{grid-area:header}[data-ly-area="nav"]{grid-area:nav}[data-ly-area="sidebar"]{grid-area:sidebar}[data-ly-area="main"]{grid-area:main}[data-ly-area="aside"]{grid-area:aside}[data-ly-area="footer"]{grid-area:footer}[data-ly-area="content"]{grid-area:content}[data-ly-area="media"]{grid-area:media}[data-ly-area="actions"]{grid-area:actions}[data-ly-area="primary"]{grid-area:primary}[data-ly-area="secondary"]{grid-area:secondary}@container ly-scope (min-width:42rem){[data-ly-recipe="split-hero"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-split-hero-wide-areas);grid-template-columns:var(--ly-split-hero-wide-columns)}}@container ly-scope (min-width:44rem){[data-ly-recipe="list-detail"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-list-detail-wide-areas);grid-template-columns:var(--ly-list-detail-wide-columns)}}@container ly-scope (min-width:48rem){[data-ly-recipe="docs"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-docs-wide-areas);grid-template-columns:var(--ly-docs-wide-columns)}}@container ly-scope (min-width:52rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-medium-areas);grid-template-columns:var(--ly-app-shell-medium-columns);grid-template-rows:var(--ly-app-shell-medium-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-medium-areas);grid-template-columns:var(--ly-dashboard-medium-columns)}}@container ly-scope (min-width:72rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-wide-areas);grid-template-columns:var(--ly-app-shell-wide-columns);grid-template-rows:var(--ly-app-shell-wide-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-wide-areas);grid-template-columns:var(--ly-dashboard-wide-columns)}}}@layer ly.utilities{.ly-cols-1{--ly-grid-columns:1}.ly-cols-2{--ly-grid-columns:2}.ly-cols-3{--ly-grid-columns:3}.ly-cols-4{--ly-grid-columns:4}.ly-cols-6{--ly-grid-columns:6}.ly-cols-12{--ly-grid-columns:12}.ly-span-1{grid-column:span 1}.ly-span-2{grid-column:span 2}.ly-span-3{grid-column:span 3}.ly-span-4{grid-column:span 4}.ly-span-full{grid-column:1 / -1}.ly-gap-0{gap:var(--ly-space-0)}.ly-gap-1{gap:var(--ly-space-1)}.ly-gap-2{gap:var(--ly-space-2)}.ly-gap-3{gap:var(--ly-space-3)}.ly-gap-4{gap:var(--ly-space-4)}.ly-gap-5{gap:var(--ly-space-5)}.ly-gap-6{gap:var(--ly-space-6)}.ly-gap-7{gap:var(--ly-space-7)}.ly-gap-8{gap:var(--ly-space-8)}.ly-gap-9{gap:var(--ly-space-9)}.ly-pad-0{padding:var(--ly-space-0)}.ly-pad-2{padding:var(--ly-space-2)}.ly-pad-4{padding:var(--ly-space-4)}.ly-pad-6{padding:var(--ly-space-6)}.ly-pad-8{padding:var(--ly-space-8)}.ly-mx-auto{margin-inline:auto}.ly-w-full{inline-size:100%}.ly-h-full{block-size:100%}.ly-min-h-screen{min-block-size:100vh;min-block-size:100dvh}.ly-overflow-auto{overflow:auto}.ly-overflow-hidden{overflow:hidden}.ly-items-start{align-items:flex-start}.ly-items-center{align-items:center}.ly-items-end{align-items:flex-end}.ly-items-stretch{align-items:stretch}.ly-justify-start{justify-content:flex-start}.ly-justify-center{justify-content:center}.ly-justify-end{justify-content:flex-end}.ly-justify-between{justify-content:space-between}.ly-frame-1x1{--ly-frame-ratio:1 / 1}.ly-frame-3x2{--ly-frame-ratio:3 / 2}.ly-frame-4x3{--ly-frame-ratio:4 / 3}.ly-frame-16x9{--ly-frame-ratio:16 / 9}.ly-frame-21x9{--ly-frame-ratio:21 / 9}.ly-hidden{display:none !important}.ly-visually-hidden{position:absolute !important;inline-size:1px !important;block-size:1px !important;padding:0 !important;margin:-1px !important;overflow:hidden !important;clip:rect(0,0,0,0) !important;clip-path:inset(50%) !important;white-space:nowrap !important}}@layer ly.personalities.minimal-saas{:where(.ly-root[data-ly-layout="minimal-saas"]){--ly-personality-wrapper-max:88rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:16rem;--ly-recipe-rail:15rem;--ly-recipe-aside:16rem;--ly-split-primary:1.05fr;--ly-split-secondary:0.95fr}}@layer ly.personalities.bento{:where(.ly-root[data-ly-layout="bento"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-card-grid-min:14rem;--ly-gallery-min:11rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header" "sidebar main main" "aside main main" "footer footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.3fr) minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.maximalist{:where(.ly-root[data-ly-layout="maximalist"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-7);--ly-grid-min:10rem;--ly-card-grid-min:13rem;--ly-gallery-min:10rem;--ly-recipe-rail:15rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "main main sidebar aside" "main main footer footer";--ly-app-shell-wide-columns:minmax(0,1.4fr) minmax(0,0.6fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr) minmax(min(100%,var(--ly-recipe-aside)),0.38fr)}}@layer ly.personalities.bauhaus{:where(.ly-root[data-ly-layout="bauhaus"]){--ly-personality-wrapper-max:96rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "sidebar main main aside" "sidebar footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,0.7fr) minmax(0,1.3fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.tactile{:where(.ly-root[data-ly-layout="tactile"]){--ly-personality-wrapper-max:82rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:15rem;--ly-recipe-rail:17rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.5fr) minmax(0,0.9fr)}}@layer ly.personalities.neumorphism{:where(.ly-root[data-ly-layout="neumorphism"]){--ly-personality-wrapper-max:84rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:17rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header sidebar" "main sidebar" "aside sidebar" "footer sidebar";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-list-detail-wide-columns:minmax(0,1.1fr) minmax(min(100%,var(--ly-pane-size)),0.7fr)}}@layer ly.personalities.retrofuturism{:where(.ly-root[data-ly-layout="retrofuturism"]){--ly-personality-wrapper-max:106rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:14rem;--ly-recipe-aside:17rem;--ly-app-shell-wide-areas:"sidebar header aside" "sidebar main aside" "sidebar footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.brutalism{:where(.ly-root[data-ly-layout="brutalism"]){--ly-personality-wrapper-max:100rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(0,1.3fr) minmax(0,0.7fr) minmax(min(100%,var(--ly-recipe-rail)),0.34fr)}}@layer ly.personalities.cyberpunk{:where(.ly-root[data-ly-layout="cyberpunk"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-recipe-rail:13rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.2fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.32fr)}}@layer ly.personalities.y2k{:where(.ly-root[data-ly-layout="y2k"]){--ly-personality-wrapper-max:90rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:18rem;--ly-recipe-aside:16rem;--ly-app-shell-wide-areas:"header header sidebar" "aside main sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-aside)),0.34fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.28fr)}}@layer ly.personalities.retro-glass{:where(.ly-root[data-ly-layout="retro-glass"]){--ly-personality-wrapper-max:104rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:15rem;--ly-recipe-rail:18rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main main sidebar" "aside footer footer";--ly-app-shell-wide-columns:minmax(0,1.25fr) minmax(0,0.75fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr)}}@layer ly.personalities.f-pattern{:where(.ly-root[data-ly-layout="f-pattern"]){--ly-personality-wrapper-max:92rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.26fr) minmax(0,1.35fr) minmax(min(100%,var(--ly-recipe-aside)),0.65fr);--ly-split-hero-wide-columns:minmax(0,1.8fr) minmax(0,0.8fr)}}@layer ly.personalities.z-pattern{:where(.ly-root[data-ly-layout="z-pattern"]){--ly-personality-wrapper-max:108rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer aside sidebar";--ly-app-shell-wide-columns:minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-split-hero-wide-columns:minmax(0,1.5fr) minmax(0,0.5fr)}}@layer ly.personalities.split-screen{:where(.ly-root[data-ly-layout="split-screen"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:20rem;--ly-recipe-rail:24rem;--ly-recipe-aside:24rem;--ly-app-shell-wide-areas:"header header" "main sidebar" "aside sidebar" "footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-split-hero-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.mondrian{:where(.ly-root[data-ly-layout="mondrian"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:11rem;--ly-recipe-rail:13rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.22fr) minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr)}}@layer ly.personalities.synthwave{:where(.ly-root[data-ly-layout="synthwave"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header header" "sidebar main aside" "footer footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.34fr);--ly-reel-item-min:20rem}} diff --git a/dist/utilities.css b/dist/utilities.css index a322ca5..959dae5 100644 --- a/dist/utilities.css +++ b/dist/utilities.css @@ -16,40 +16,16 @@ .ly-span-4 { grid-column: span 4; } .ly-span-full { grid-column: 1 / -1; } - .ly-gap-0 { - --ly-gap: var(--ly-space-0); - --ly-grid-gap: var(--ly-space-0); - --ly-stack-gap: var(--ly-space-0); - --ly-cluster-gap: var(--ly-space-0); - } - - .ly-gap-2 { - --ly-gap: var(--ly-space-2); - --ly-grid-gap: var(--ly-space-2); - --ly-stack-gap: var(--ly-space-2); - --ly-cluster-gap: var(--ly-space-2); - } - - .ly-gap-4 { - --ly-gap: var(--ly-space-4); - --ly-grid-gap: var(--ly-space-4); - --ly-stack-gap: var(--ly-space-4); - --ly-cluster-gap: var(--ly-space-4); - } - - .ly-gap-6 { - --ly-gap: var(--ly-space-6); - --ly-grid-gap: var(--ly-space-6); - --ly-stack-gap: var(--ly-space-6); - --ly-cluster-gap: var(--ly-space-6); - } - - .ly-gap-8 { - --ly-gap: var(--ly-space-8); - --ly-grid-gap: var(--ly-space-8); - --ly-stack-gap: var(--ly-space-8); - --ly-cluster-gap: var(--ly-space-8); - } + .ly-gap-0 { gap: var(--ly-space-0); } + .ly-gap-1 { gap: var(--ly-space-1); } + .ly-gap-2 { gap: var(--ly-space-2); } + .ly-gap-3 { gap: var(--ly-space-3); } + .ly-gap-4 { gap: var(--ly-space-4); } + .ly-gap-5 { gap: var(--ly-space-5); } + .ly-gap-6 { gap: var(--ly-space-6); } + .ly-gap-7 { gap: var(--ly-space-7); } + .ly-gap-8 { gap: var(--ly-space-8); } + .ly-gap-9 { gap: var(--ly-space-9); } .ly-pad-0 { padding: var(--ly-space-0); } .ly-pad-2 { padding: var(--ly-space-2); } diff --git a/styles/utilities.css b/styles/utilities.css index a322ca5..959dae5 100644 --- a/styles/utilities.css +++ b/styles/utilities.css @@ -16,40 +16,16 @@ .ly-span-4 { grid-column: span 4; } .ly-span-full { grid-column: 1 / -1; } - .ly-gap-0 { - --ly-gap: var(--ly-space-0); - --ly-grid-gap: var(--ly-space-0); - --ly-stack-gap: var(--ly-space-0); - --ly-cluster-gap: var(--ly-space-0); - } - - .ly-gap-2 { - --ly-gap: var(--ly-space-2); - --ly-grid-gap: var(--ly-space-2); - --ly-stack-gap: var(--ly-space-2); - --ly-cluster-gap: var(--ly-space-2); - } - - .ly-gap-4 { - --ly-gap: var(--ly-space-4); - --ly-grid-gap: var(--ly-space-4); - --ly-stack-gap: var(--ly-space-4); - --ly-cluster-gap: var(--ly-space-4); - } - - .ly-gap-6 { - --ly-gap: var(--ly-space-6); - --ly-grid-gap: var(--ly-space-6); - --ly-stack-gap: var(--ly-space-6); - --ly-cluster-gap: var(--ly-space-6); - } - - .ly-gap-8 { - --ly-gap: var(--ly-space-8); - --ly-grid-gap: var(--ly-space-8); - --ly-stack-gap: var(--ly-space-8); - --ly-cluster-gap: var(--ly-space-8); - } + .ly-gap-0 { gap: var(--ly-space-0); } + .ly-gap-1 { gap: var(--ly-space-1); } + .ly-gap-2 { gap: var(--ly-space-2); } + .ly-gap-3 { gap: var(--ly-space-3); } + .ly-gap-4 { gap: var(--ly-space-4); } + .ly-gap-5 { gap: var(--ly-space-5); } + .ly-gap-6 { gap: var(--ly-space-6); } + .ly-gap-7 { gap: var(--ly-space-7); } + .ly-gap-8 { gap: var(--ly-space-8); } + .ly-gap-9 { gap: var(--ly-space-9); } .ly-pad-0 { padding: var(--ly-space-0); } .ly-pad-2 { padding: var(--ly-space-2); } diff --git a/test/demo-smoke.test.mjs b/test/demo-smoke.test.mjs index 162a5dc..ec4e34d 100644 --- a/test/demo-smoke.test.mjs +++ b/test/demo-smoke.test.mjs @@ -1217,6 +1217,14 @@ const verifyBreakoutGeometry = async (page, baseUrl) => { ); }; +/** + * Verifies nested personality-token isolation and proves gap utilities affect + * only the element carrying the utility class. + * + * @param {import("@playwright/test").Page} page Active browser page. + * @param {string} baseUrl Demo server URL. + * @returns {Promise} + */ const verifyProfileAndUtilityIsolation = async (page, baseUrl) => { await page.setViewportSize({ width: 1280, height: 800 }); await page.goto(`${baseUrl}?ecosystem=layout-only&wrapper=full&personality=minimal-saas`, { @@ -1228,6 +1236,7 @@ const verifyProfileAndUtilityIsolation = async (page, baseUrl) => { const innerRoot = document.createElement("section"); innerRoot.className = "ly-root"; innerRoot.dataset.lyLayout = "bauhaus"; + innerRoot.dataset.lyDensity = "normal"; innerRoot.style.inlineSize = "50rem"; innerRoot.style.maxInlineSize = "100%"; @@ -1240,15 +1249,22 @@ const verifyProfileAndUtilityIsolation = async (page, baseUrl) => { splitHero.append(region); } - const stack = document.createElement("div"); - stack.className = "ly-stack ly-gap-0"; - stack.append(document.createElement("span"), document.createElement("span")); - - const cluster = document.createElement("div"); - cluster.className = "ly-cluster ly-gap-8"; - cluster.append(document.createElement("span"), document.createElement("span")); - - innerRoot.append(splitHero, stack, cluster); + const outerGap = document.createElement("div"); + outerGap.className = "ly-stack ly-gap-8"; + const innerDefaultGap = document.createElement("div"); + innerDefaultGap.className = "ly-stack"; + innerDefaultGap.append(document.createElement("span"), document.createElement("span")); + const innerLocalGap = document.createElement("div"); + innerLocalGap.className = "ly-stack ly-gap-5"; + innerLocalGap.append(document.createElement("span"), document.createElement("span")); + outerGap.append(innerDefaultGap, innerLocalGap); + + const cardGrid = document.createElement("div"); + cardGrid.dataset.lyRecipe = "card-grid"; + cardGrid.className = "ly-gap-7"; + cardGrid.append(document.createElement("article"), document.createElement("article")); + + innerRoot.append(splitHero, outerGap, cardGrid); document.querySelector("#layoutLab").append(innerRoot); const splitStyle = getComputedStyle(splitHero); @@ -1258,16 +1274,24 @@ const verifyProfileAndUtilityIsolation = async (page, baseUrl) => { primary: splitStyle.getPropertyValue("--ly-split-primary").trim(), secondary: splitStyle.getPropertyValue("--ly-split-secondary").trim(), splitDifference: Math.abs(contentWidth - mediaWidth), - stackGap: getComputedStyle(stack).rowGap, - clusterGap: getComputedStyle(cluster).columnGap + outerGap: getComputedStyle(outerGap).rowGap, + innerDefaultGap: getComputedStyle(innerDefaultGap).rowGap, + innerLocalGap: getComputedStyle(innerLocalGap).rowGap, + cardGridGap: getComputedStyle(cardGrid).gap }; }); assert.equal(result.primary, "1fr", "The outer personality leaked its primary split ratio."); assert.equal(result.secondary, "1fr", "The outer personality leaked its secondary split ratio."); assert(result.splitDifference <= 2, "A nested neutral split did not render equal tracks."); - assert.equal(result.stackGap, "0px", "The zero-gap utility did not affect a stack."); - assert.equal(result.clusterGap, "64px", "The gap utility did not affect a cluster."); + assert.equal(result.outerGap, "64px", "The outer Stack did not receive its local gap."); + assert.equal( + result.innerDefaultGap, + "16px", + "A nested default Stack inherited its ancestor's local gap utility." + ); + assert.equal(result.innerLocalGap, "24px", "The nested local gap did not override its own Stack."); + assert.equal(result.cardGridGap, "48px", "The local gap utility did not affect a Card Grid."); }; const verifyPrimitiveOverflow = async (page, baseUrl) => { diff --git a/test/layout-css-contract.test.mjs b/test/layout-css-contract.test.mjs index 50c1127..d5b79e1 100644 --- a/test/layout-css-contract.test.mjs +++ b/test/layout-css-contract.test.mjs @@ -386,10 +386,22 @@ assert(!/\bly-(?:md|lg)-/.test(utilities), "Fixed responsive utility families mu assert(!/\bly-order-/.test(utilities), "Visual order utilities must be removed"); assert(!utilities.includes(".ly-bleed"), "The scrollbar-unsafe viewport bleed utility must be removed"); assert(!utilities.includes("100vw"), "Utilities must not use scrollbar-unsafe viewport widths"); -for (const gap of ["0", "2", "4", "6", "8"]) { +for (let gap = 0; gap <= 9; gap += 1) { const rule = utilities.match(new RegExp(`\\.ly-gap-${gap}\\s*\\{([^}]*)\\}`))?.[1] ?? ""; - for (const token of ["--ly-gap", "--ly-grid-gap", "--ly-stack-gap", "--ly-cluster-gap"]) { - assert(rule.includes(`${token}:`), `.ly-gap-${gap} must set ${token}.`); + assert( + rule.includes(`gap: var(--ly-space-${gap})`), + `.ly-gap-${gap} must map directly to --ly-space-${gap}.` + ); + for (const inheritedToken of [ + "--ly-gap", + "--ly-grid-gap", + "--ly-stack-gap", + "--ly-cluster-gap" + ]) { + assert( + !rule.includes(`${inheritedToken}:`), + `.ly-gap-${gap} must not redefine ${inheritedToken}.` + ); } } assert(!/(?:^|[;{}\n\r])\s*order\s*:/.test(authoredCss), "Layout source must never set visual order"); From b239017187bfe07b4350c0f40424e171aa294a54 Mon Sep 17 00:00:00 2001 From: Foscat Date: Tue, 25 Aug 2026 17:42:34 -0500 Subject: [PATCH 4/6] feat: make scroll constraints explicit --- demo/demo.css | 4 +- demo/demo.js | 8 +++- dist/foundation.css | 13 +++--- dist/layout-style-css.css | 22 +++++++--- dist/layout-style-css.min.css | 2 +- dist/primitives.css | 9 +++- manifest.json | 3 +- styles/foundation.css | 13 +++--- styles/primitives.css | 9 +++- test/demo-smoke.test.mjs | 73 ++++++++++++++++++++++++++++++- test/layout-css-contract.test.mjs | 20 ++++++++- test/manifest-contract.test.mjs | 3 +- 12 files changed, 150 insertions(+), 29 deletions(-) diff --git a/demo/demo.css b/demo/demo.css index dcc9e42..c6a753f 100644 --- a/demo/demo.css +++ b/demo/demo.css @@ -367,7 +367,7 @@ body[data-ecosystem="all-three"] .demo-status { --ly-grid-gap: var(--ly-gap); --ly-section-padding-block: clamp(1.5rem, 6%, 3rem); --ly-header-height: 3.5rem; - --ly-scroll-max: min(62%, 34rem); + --ly-scroll-viewport-max: min(62%, 34rem); } .demo-preview-root[data-demo-height-tier="shallow"] { @@ -375,7 +375,7 @@ body[data-ecosystem="all-three"] .demo-status { --ly-shell-min: auto; --ly-sticky-position: static; --ly-section-padding-block: 1.5rem; - --ly-scroll-max: max(12rem, calc(100% - 8rem)); + --ly-scroll-viewport-max: max(12rem, calc(100% - 8rem)); } .demo-preview-root[data-demo-height-tier="shallow"] .demo-recipe { diff --git a/demo/demo.js b/demo/demo.js index 4bab8a3..ca80265 100644 --- a/demo/demo.js +++ b/demo/demo.js @@ -411,6 +411,12 @@ function createAction(sequence) { }); } +/** + * Creates one semantic recipe region and its representative preview content. + * + * @param {string} area Canonical recipe area name. + * @returns {HTMLElement} Populated preview region. + */ function createRegion(area) { const semanticTags = { header: "header", @@ -439,7 +445,7 @@ function createRegion(area) { if (area === "secondary") { const scroll = createElement("div", { - className: "ly-scroll demo-list-scroll", + className: "ly-scroll ly-scroll--bounded demo-list-scroll", attributes: { tabindex: "0", "aria-label": "Scrollable detail activity" } }); diff --git a/dist/foundation.css b/dist/foundation.css index 2722155..88468e9 100644 --- a/dist/foundation.css +++ b/dist/foundation.css @@ -43,7 +43,8 @@ --ly-sticky-position: sticky; --ly-cover-min: 100vh; --ly-shell-min: 100vh; - --ly-scroll-max: min(70vh, 50rem); + --ly-scroll-max: 50rem; + --ly-scroll-viewport-max: min(70vh, 50rem); --ly-switcher-threshold: 42rem; --ly-sidebar-size: 18rem; @@ -142,7 +143,7 @@ :where(.ly-root) { --ly-cover-min: 100dvh; --ly-shell-min: 100dvh; - --ly-scroll-max: min(70dvh, 50rem); + --ly-scroll-viewport-max: min(70dvh, 50rem); } } } @@ -176,7 +177,7 @@ --ly-section-padding-block: clamp(1.25rem, 4vh, 2rem); --ly-section-padding-block-compact: clamp(0.75rem, 2vh, 1.25rem); --ly-header-height: 3.5rem; - --ly-scroll-max: min(62vh, 34rem); + --ly-scroll-viewport-max: min(62vh, 34rem); } @supports (height: 100dvh) { @@ -185,7 +186,7 @@ --ly-cluster-gap: min(var(--ly-space-3), 2dvh); --ly-section-padding-block: clamp(1.25rem, 4dvh, 2rem); --ly-section-padding-block-compact: clamp(0.75rem, 2dvh, 1.25rem); - --ly-scroll-max: min(62dvh, 34rem); + --ly-scroll-viewport-max: min(62dvh, 34rem); } } } @@ -197,12 +198,12 @@ --ly-sticky-position: static; --ly-section-padding-block: var(--ly-space-4); --ly-section-padding-block-compact: var(--ly-space-2); - --ly-scroll-max: max(12rem, calc(100vh - 8rem)); + --ly-scroll-viewport-max: max(12rem, calc(100vh - 8rem)); } @supports (height: 100dvh) { :where(.ly-root) { - --ly-scroll-max: max(12rem, calc(100dvh - 8rem)); + --ly-scroll-viewport-max: max(12rem, calc(100dvh - 8rem)); } } } diff --git a/dist/layout-style-css.css b/dist/layout-style-css.css index bbeb05c..6709c75 100644 --- a/dist/layout-style-css.css +++ b/dist/layout-style-css.css @@ -46,7 +46,8 @@ --ly-sticky-position: sticky; --ly-cover-min: 100vh; --ly-shell-min: 100vh; - --ly-scroll-max: min(70vh, 50rem); + --ly-scroll-max: 50rem; + --ly-scroll-viewport-max: min(70vh, 50rem); --ly-switcher-threshold: 42rem; --ly-sidebar-size: 18rem; @@ -145,7 +146,7 @@ :where(.ly-root) { --ly-cover-min: 100dvh; --ly-shell-min: 100dvh; - --ly-scroll-max: min(70dvh, 50rem); + --ly-scroll-viewport-max: min(70dvh, 50rem); } } } @@ -179,7 +180,7 @@ --ly-section-padding-block: clamp(1.25rem, 4vh, 2rem); --ly-section-padding-block-compact: clamp(0.75rem, 2vh, 1.25rem); --ly-header-height: 3.5rem; - --ly-scroll-max: min(62vh, 34rem); + --ly-scroll-viewport-max: min(62vh, 34rem); } @supports (height: 100dvh) { @@ -188,7 +189,7 @@ --ly-cluster-gap: min(var(--ly-space-3), 2dvh); --ly-section-padding-block: clamp(1.25rem, 4dvh, 2rem); --ly-section-padding-block-compact: clamp(0.75rem, 2dvh, 1.25rem); - --ly-scroll-max: min(62dvh, 34rem); + --ly-scroll-viewport-max: min(62dvh, 34rem); } } } @@ -200,12 +201,12 @@ --ly-sticky-position: static; --ly-section-padding-block: var(--ly-space-4); --ly-section-padding-block-compact: var(--ly-space-2); - --ly-scroll-max: max(12rem, calc(100vh - 8rem)); + --ly-scroll-viewport-max: max(12rem, calc(100vh - 8rem)); } @supports (height: 100dvh) { :where(.ly-root) { - --ly-scroll-max: max(12rem, calc(100dvh - 8rem)); + --ly-scroll-viewport-max: max(12rem, calc(100dvh - 8rem)); } } } @@ -617,11 +618,18 @@ .ly-scroll { min-block-size: 0; - max-block-size: var(--ly-scroll-max); overflow-x: clip; overflow-y: auto; overscroll-behavior-block: contain; } + + .ly-scroll--bounded { + max-block-size: var(--ly-scroll-max); + } + + .ly-scroll--viewport { + max-block-size: var(--ly-scroll-viewport-max); + } } /* recipes.css */ diff --git a/dist/layout-style-css.min.css b/dist/layout-style-css.min.css index e128c0d..4f09f14 100644 --- a/dist/layout-style-css.min.css +++ b/dist/layout-style-css.min.css @@ -1 +1 @@ -@layer ly.reset,ly.tokens,ly.wrappers,ly.primitives,ly.recipes,ly.utilities,ly.personalities,ly.context;@layer ly.tokens{:where(.ly-root){--ly-space-0:0;--ly-space-1:0.25rem;--ly-space-2:0.5rem;--ly-space-3:0.75rem;--ly-space-4:1rem;--ly-space-5:1.5rem;--ly-space-6:2rem;--ly-space-7:3rem;--ly-space-8:4rem;--ly-space-9:6rem;--ly-wrapper-compact:40rem;--ly-wrapper-prose:68ch;--ly-wrapper-content:72rem;--ly-wrapper-workspace:96rem;--ly-wrapper-wide:112rem;--ly-page-padding-inline:clamp(1rem,3vw,3rem);--ly-safe-area-inline:max( env(safe-area-inset-left,0px),env(safe-area-inset-right,0px) );--ly-safe-area-block-start:env(safe-area-inset-top,0px);--ly-safe-area-block-end:env(safe-area-inset-bottom,0px);--ly-wrapper-gutter:var(--ly-page-padding-inline);--ly-profile-gap:var(--ly-space-5);--ly-gap:var(--ly-profile-gap);--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(2rem,4vh,4rem);--ly-section-padding-block-compact:clamp(1rem,2.5vh,2rem);--ly-header-height:4.5rem;--ly-sticky-position:sticky;--ly-cover-min:100vh;--ly-shell-min:100vh;--ly-scroll-max:min(70vh,50rem);--ly-switcher-threshold:42rem;--ly-sidebar-size:18rem;--ly-sidebar-content-min:50%;--ly-grid-columns:12;--ly-grid-min:16rem;--ly-split-min:20rem;--ly-pane-min:18rem;--ly-pane-size:22rem;--ly-media-min:18rem;--ly-media-size:14rem;--ly-reel-item-min:16rem;--ly-reel-item-max:28rem;--ly-frame-ratio:16 / 9;--ly-split-primary:1fr;--ly-split-secondary:1fr;--ly-recipe-rail:18rem;--ly-recipe-aside:22rem;--ly-gallery-min:12rem;--ly-card-grid-min:16rem;--ly-app-shell-base-rows:auto auto minmax(0,1fr) auto auto;--ly-app-shell-medium-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-medium-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-medium-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-medium-areas:"header header" "nav nav" "main aside" "footer footer";--ly-dashboard-medium-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-wide-areas:"nav header header" "nav main aside" "nav footer footer";--ly-dashboard-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-docs-wide-areas:"header header" "nav main" "nav aside" "footer footer";--ly-docs-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-list-detail-wide-areas:"primary secondary" "actions actions";--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.7fr) minmax(0,1fr);--ly-split-hero-wide-areas:"content media" "actions media";--ly-split-hero-wide-columns:minmax(0,var(--ly-split-primary,1fr)) minmax(0,var(--ly-split-secondary,1fr));container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}@supports (width:1cqi){:where(.ly-root){--ly-page-padding-inline:clamp(1rem,3cqi,3rem)}}@supports (height:100dvh){:where(.ly-root){--ly-cover-min:100dvh;--ly-shell-min:100dvh;--ly-scroll-max:min(70dvh,50rem)}}}@layer ly.reset{:where(.ly-root),:where(.ly-root *){box-sizing:border-box}:where(.ly-root){margin:0}:where(.ly-root img),:where(.ly-root svg),:where(.ly-root video),:where(.ly-root canvas),:where(.ly-root iframe){max-inline-size:100%}}@layer ly.tokens{@media (max-height:44rem){:where(.ly-root){--ly-gap:min(var(--ly-profile-gap),var(--ly-space-4));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:min(var(--ly-space-4),3vh);--ly-cluster-gap:min(var(--ly-space-3),2vh);--ly-section-padding-block:clamp(1.25rem,4vh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2vh,1.25rem);--ly-header-height:3.5rem;--ly-scroll-max:min(62vh,34rem)}@supports (height:100dvh){:where(.ly-root){--ly-stack-gap:min(var(--ly-space-4),3dvh);--ly-cluster-gap:min(var(--ly-space-3),2dvh);--ly-section-padding-block:clamp(1.25rem,4dvh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2dvh,1.25rem);--ly-scroll-max:min(62dvh,34rem)}}}@media (max-height:30rem){:where(.ly-root){--ly-cover-min:auto;--ly-shell-min:auto;--ly-sticky-position:static;--ly-section-padding-block:var(--ly-space-4);--ly-section-padding-block-compact:var(--ly-space-2);--ly-scroll-max:max(12rem,calc(100vh - 8rem))}@supports (height:100dvh){:where(.ly-root){--ly-scroll-max:max(12rem,calc(100dvh - 8rem))}}}}@layer ly.context{:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:clamp(1rem,2.5vh,2rem);--ly-section-padding-block-compact:clamp(0.5rem,1.5vh,1rem)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:var(--ly-profile-gap);--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(2rem,4vh,4rem);--ly-section-padding-block-compact:clamp(1rem,2.5vh,2rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:max(var(--ly-profile-gap),var(--ly-space-6));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-5);--ly-cluster-gap:var(--ly-space-4);--ly-section-padding-block:clamp(3rem,7vh,6rem);--ly-section-padding-block-compact:clamp(1.5rem,3.5vh,3rem)}@media (max-height:44rem){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:clamp(0.75rem,2vh,1.25rem);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:min(var(--ly-profile-gap),var(--ly-space-4));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(1.25rem,4vh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2vh,1.25rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:var(--ly-space-5);--ly-grid-gap:var(--ly-space-5);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(1.5rem,6vh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3vh,1.25rem)}@supports (height:100dvh){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-section-padding-block:clamp(0.75rem,2dvh,1.25rem)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-section-padding-block:clamp(1.25rem,4dvh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2dvh,1.25rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-section-padding-block:clamp(1.5rem,6dvh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3dvh,1.25rem)}}}@media (max-height:30rem){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-2);--ly-grid-gap:var(--ly-space-2);--ly-stack-gap:var(--ly-space-2);--ly-cluster-gap:var(--ly-space-1);--ly-section-padding-block:var(--ly-space-3);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:var(--ly-space-4);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:var(--ly-space-4);--ly-grid-gap:var(--ly-space-4);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:var(--ly-space-5);--ly-section-padding-block-compact:var(--ly-space-3)}}}@layer ly.wrappers{.ly-wrapper{--ly-wrapper-max:var(--ly-personality-wrapper-max,var(--ly-wrapper-content));--ly-wrapper-fluid-gutter:var(--ly-wrapper-gutter);--ly-wrapper-local-gutter:max( var(--ly-wrapper-fluid-gutter),var(--ly-safe-area-inline) );container-name:ly-scope;container-type:inline-size;inline-size:min(100%,calc(var(--ly-wrapper-max) + (var(--ly-wrapper-local-gutter) * 2)));max-inline-size:100%;margin-inline:auto;padding-inline:var(--ly-wrapper-local-gutter)}.ly-wrapper--compact{--ly-wrapper-max:var(--ly-wrapper-compact)}.ly-wrapper--prose{--ly-wrapper-max:var(--ly-wrapper-prose)}.ly-wrapper--content{--ly-wrapper-max:var(--ly-wrapper-content)}.ly-wrapper--workspace{--ly-wrapper-max:var(--ly-wrapper-workspace)}.ly-wrapper--wide{--ly-wrapper-max:var(--ly-wrapper-wide)}.ly-wrapper--full{--ly-wrapper-max:100%}.ly-wrapper--breakout{--ly-wrapper-max:100%;--ly-breakout-content-size:min( var(--ly-wrapper-content),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );--ly-breakout-feature-size:min( var(--ly-wrapper-wide),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );display:grid;grid-template-columns:[full-start] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [feature-start] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [content-start] minmax(0,var(--ly-breakout-content-size)) [content-end] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [feature-end] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [full-end];inline-size:100%;padding-inline:0}.ly-wrapper--breakout>*{grid-column:content;min-inline-size:0;max-inline-size:100%}.ly-wrapper--breakout>:where(.ly-lane--content,[data-ly-lane="content"]){grid-column:content}.ly-wrapper--breakout>:where(.ly-lane--feature,[data-ly-lane="feature"]){grid-column:feature}.ly-wrapper--breakout>:where(.ly-lane--full,[data-ly-lane="full"]){grid-column:full}}@layer ly.primitives{.ly-page{min-block-size:100vh;min-block-size:100dvh;padding-block-start:var(--ly-safe-area-block-start);padding-block-end:var(--ly-safe-area-block-end)}.ly-header,.ly-footer,.ly-main,.ly-section,.ly-surface{min-inline-size:0;min-block-size:0}.ly-header{min-block-size:var(--ly-header-height)}.ly-header--sticky{position:var(--ly-sticky-position,sticky);inset-block-start:0;z-index:var(--ly-z-header,20)}.ly-section{padding-block:var(--ly-section-padding-block)}.ly-section--compact{padding-block:var(--ly-section-padding-block-compact)}.ly-section--flush{padding-block:0}.ly-readable{max-inline-size:var(--ly-wrapper-prose)}.ly-stack{display:flex;flex-direction:column;gap:var(--ly-stack-gap)}.ly-cluster{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ly-cluster-gap)}.ly-center{inline-size:min(100%,var(--ly-center-max,var(--ly-wrapper-content)));margin-inline:auto}.ly-cover{display:flex;flex-direction:column;min-block-size:100vh;min-block-size:var(--ly-cover-min);padding-block:var(--ly-cover-padding,var(--ly-space-5))}.ly-cover>:where(.ly-cover__center,[data-ly-cover-center]){margin-block:auto}.ly-switcher{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-switcher>*{flex-basis:calc((var(--ly-switcher-threshold) - 100%) * 999);flex-grow:1;min-inline-size:0}.ly-sidebar{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-sidebar>:where(.ly-sidebar__side,[data-ly-sidebar="side"]){flex-basis:var(--ly-sidebar-size);flex-grow:1;min-inline-size:min(100%,var(--ly-sidebar-size))}.ly-sidebar>:where(.ly-sidebar__content,[data-ly-sidebar="content"]){flex-basis:0;flex-grow:999;min-inline-size:min(100%,var(--ly-sidebar-content-min))}.ly-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-grid-min)),1fr));gap:var(--ly-grid-gap)}.ly-grid--fixed{grid-template-columns:repeat(var(--ly-grid-columns),minmax(0,1fr))}.ly-split{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-split-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-split-align,center)}.ly-panes{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-pane-min)),1fr));gap:var(--ly-gap)}.ly-media{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-media-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-media-align,start)}.ly-media>:where(.ly-media__actions,[data-ly-media="actions"]){min-inline-size:min(100%,var(--ly-media-size))}.ly-grid>*,.ly-split>*,.ly-panes>*,.ly-media>*{min-inline-size:0;min-block-size:0}.ly-reel{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(min(100%,var(--ly-reel-item-min)),var(--ly-reel-item-max));gap:var(--ly-grid-gap);overflow-x:auto;overscroll-behavior-inline:contain;scroll-snap-type:inline proximity}.ly-reel>*{min-inline-size:0;scroll-snap-align:start}.ly-frame{aspect-ratio:var(--ly-frame-ratio);overflow:hidden}.ly-frame>*{inline-size:100%;block-size:100%;object-fit:cover}.ly-scroll{min-block-size:0;max-block-size:var(--ly-scroll-max);overflow-x:clip;overflow-y:auto;overscroll-behavior-block:contain}}@layer ly.recipes{:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] ){container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"] ){display:grid;grid-template-columns:minmax(0,1fr);gap:var(--ly-gap)}[data-ly-recipe="app-shell"]{min-block-size:100vh;min-block-size:var(--ly-shell-min);grid-template-areas:"header" "sidebar" "main" "aside" "footer";grid-template-rows:var(--ly-app-shell-base-rows)}[data-ly-recipe="dashboard"],[data-ly-recipe="docs"]{grid-template-areas:"header" "nav" "main" "aside" "footer"}[data-ly-recipe="list-detail"]{grid-template-areas:"primary" "secondary" "actions"}[data-ly-recipe="split-hero"]{grid-template-areas:"content" "media" "actions";align-items:var(--ly-split-hero-align,center)}[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"]{display:grid;gap:var(--ly-grid-gap)}[data-ly-recipe="gallery"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-gallery-min)),1fr))}[data-ly-recipe="card-grid"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-card-grid-min)),1fr))}:where( [data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )[data-ly-responsive="manual"]{grid-template-columns:minmax(0,1fr)}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )>*{min-inline-size:0;min-block-size:0}[data-ly-area="header"]{grid-area:header}[data-ly-area="nav"]{grid-area:nav}[data-ly-area="sidebar"]{grid-area:sidebar}[data-ly-area="main"]{grid-area:main}[data-ly-area="aside"]{grid-area:aside}[data-ly-area="footer"]{grid-area:footer}[data-ly-area="content"]{grid-area:content}[data-ly-area="media"]{grid-area:media}[data-ly-area="actions"]{grid-area:actions}[data-ly-area="primary"]{grid-area:primary}[data-ly-area="secondary"]{grid-area:secondary}@container ly-scope (min-width:42rem){[data-ly-recipe="split-hero"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-split-hero-wide-areas);grid-template-columns:var(--ly-split-hero-wide-columns)}}@container ly-scope (min-width:44rem){[data-ly-recipe="list-detail"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-list-detail-wide-areas);grid-template-columns:var(--ly-list-detail-wide-columns)}}@container ly-scope (min-width:48rem){[data-ly-recipe="docs"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-docs-wide-areas);grid-template-columns:var(--ly-docs-wide-columns)}}@container ly-scope (min-width:52rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-medium-areas);grid-template-columns:var(--ly-app-shell-medium-columns);grid-template-rows:var(--ly-app-shell-medium-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-medium-areas);grid-template-columns:var(--ly-dashboard-medium-columns)}}@container ly-scope (min-width:72rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-wide-areas);grid-template-columns:var(--ly-app-shell-wide-columns);grid-template-rows:var(--ly-app-shell-wide-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-wide-areas);grid-template-columns:var(--ly-dashboard-wide-columns)}}}@layer ly.utilities{.ly-cols-1{--ly-grid-columns:1}.ly-cols-2{--ly-grid-columns:2}.ly-cols-3{--ly-grid-columns:3}.ly-cols-4{--ly-grid-columns:4}.ly-cols-6{--ly-grid-columns:6}.ly-cols-12{--ly-grid-columns:12}.ly-span-1{grid-column:span 1}.ly-span-2{grid-column:span 2}.ly-span-3{grid-column:span 3}.ly-span-4{grid-column:span 4}.ly-span-full{grid-column:1 / -1}.ly-gap-0{gap:var(--ly-space-0)}.ly-gap-1{gap:var(--ly-space-1)}.ly-gap-2{gap:var(--ly-space-2)}.ly-gap-3{gap:var(--ly-space-3)}.ly-gap-4{gap:var(--ly-space-4)}.ly-gap-5{gap:var(--ly-space-5)}.ly-gap-6{gap:var(--ly-space-6)}.ly-gap-7{gap:var(--ly-space-7)}.ly-gap-8{gap:var(--ly-space-8)}.ly-gap-9{gap:var(--ly-space-9)}.ly-pad-0{padding:var(--ly-space-0)}.ly-pad-2{padding:var(--ly-space-2)}.ly-pad-4{padding:var(--ly-space-4)}.ly-pad-6{padding:var(--ly-space-6)}.ly-pad-8{padding:var(--ly-space-8)}.ly-mx-auto{margin-inline:auto}.ly-w-full{inline-size:100%}.ly-h-full{block-size:100%}.ly-min-h-screen{min-block-size:100vh;min-block-size:100dvh}.ly-overflow-auto{overflow:auto}.ly-overflow-hidden{overflow:hidden}.ly-items-start{align-items:flex-start}.ly-items-center{align-items:center}.ly-items-end{align-items:flex-end}.ly-items-stretch{align-items:stretch}.ly-justify-start{justify-content:flex-start}.ly-justify-center{justify-content:center}.ly-justify-end{justify-content:flex-end}.ly-justify-between{justify-content:space-between}.ly-frame-1x1{--ly-frame-ratio:1 / 1}.ly-frame-3x2{--ly-frame-ratio:3 / 2}.ly-frame-4x3{--ly-frame-ratio:4 / 3}.ly-frame-16x9{--ly-frame-ratio:16 / 9}.ly-frame-21x9{--ly-frame-ratio:21 / 9}.ly-hidden{display:none !important}.ly-visually-hidden{position:absolute !important;inline-size:1px !important;block-size:1px !important;padding:0 !important;margin:-1px !important;overflow:hidden !important;clip:rect(0,0,0,0) !important;clip-path:inset(50%) !important;white-space:nowrap !important}}@layer ly.personalities.minimal-saas{:where(.ly-root[data-ly-layout="minimal-saas"]){--ly-personality-wrapper-max:88rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:16rem;--ly-recipe-rail:15rem;--ly-recipe-aside:16rem;--ly-split-primary:1.05fr;--ly-split-secondary:0.95fr}}@layer ly.personalities.bento{:where(.ly-root[data-ly-layout="bento"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-card-grid-min:14rem;--ly-gallery-min:11rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header" "sidebar main main" "aside main main" "footer footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.3fr) minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.maximalist{:where(.ly-root[data-ly-layout="maximalist"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-7);--ly-grid-min:10rem;--ly-card-grid-min:13rem;--ly-gallery-min:10rem;--ly-recipe-rail:15rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "main main sidebar aside" "main main footer footer";--ly-app-shell-wide-columns:minmax(0,1.4fr) minmax(0,0.6fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr) minmax(min(100%,var(--ly-recipe-aside)),0.38fr)}}@layer ly.personalities.bauhaus{:where(.ly-root[data-ly-layout="bauhaus"]){--ly-personality-wrapper-max:96rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "sidebar main main aside" "sidebar footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,0.7fr) minmax(0,1.3fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.tactile{:where(.ly-root[data-ly-layout="tactile"]){--ly-personality-wrapper-max:82rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:15rem;--ly-recipe-rail:17rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.5fr) minmax(0,0.9fr)}}@layer ly.personalities.neumorphism{:where(.ly-root[data-ly-layout="neumorphism"]){--ly-personality-wrapper-max:84rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:17rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header sidebar" "main sidebar" "aside sidebar" "footer sidebar";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-list-detail-wide-columns:minmax(0,1.1fr) minmax(min(100%,var(--ly-pane-size)),0.7fr)}}@layer ly.personalities.retrofuturism{:where(.ly-root[data-ly-layout="retrofuturism"]){--ly-personality-wrapper-max:106rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:14rem;--ly-recipe-aside:17rem;--ly-app-shell-wide-areas:"sidebar header aside" "sidebar main aside" "sidebar footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.brutalism{:where(.ly-root[data-ly-layout="brutalism"]){--ly-personality-wrapper-max:100rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(0,1.3fr) minmax(0,0.7fr) minmax(min(100%,var(--ly-recipe-rail)),0.34fr)}}@layer ly.personalities.cyberpunk{:where(.ly-root[data-ly-layout="cyberpunk"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-recipe-rail:13rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.2fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.32fr)}}@layer ly.personalities.y2k{:where(.ly-root[data-ly-layout="y2k"]){--ly-personality-wrapper-max:90rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:18rem;--ly-recipe-aside:16rem;--ly-app-shell-wide-areas:"header header sidebar" "aside main sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-aside)),0.34fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.28fr)}}@layer ly.personalities.retro-glass{:where(.ly-root[data-ly-layout="retro-glass"]){--ly-personality-wrapper-max:104rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:15rem;--ly-recipe-rail:18rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main main sidebar" "aside footer footer";--ly-app-shell-wide-columns:minmax(0,1.25fr) minmax(0,0.75fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr)}}@layer ly.personalities.f-pattern{:where(.ly-root[data-ly-layout="f-pattern"]){--ly-personality-wrapper-max:92rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.26fr) minmax(0,1.35fr) minmax(min(100%,var(--ly-recipe-aside)),0.65fr);--ly-split-hero-wide-columns:minmax(0,1.8fr) minmax(0,0.8fr)}}@layer ly.personalities.z-pattern{:where(.ly-root[data-ly-layout="z-pattern"]){--ly-personality-wrapper-max:108rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer aside sidebar";--ly-app-shell-wide-columns:minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-split-hero-wide-columns:minmax(0,1.5fr) minmax(0,0.5fr)}}@layer ly.personalities.split-screen{:where(.ly-root[data-ly-layout="split-screen"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:20rem;--ly-recipe-rail:24rem;--ly-recipe-aside:24rem;--ly-app-shell-wide-areas:"header header" "main sidebar" "aside sidebar" "footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-split-hero-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.mondrian{:where(.ly-root[data-ly-layout="mondrian"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:11rem;--ly-recipe-rail:13rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.22fr) minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr)}}@layer ly.personalities.synthwave{:where(.ly-root[data-ly-layout="synthwave"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header header" "sidebar main aside" "footer footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.34fr);--ly-reel-item-min:20rem}} +@layer ly.reset,ly.tokens,ly.wrappers,ly.primitives,ly.recipes,ly.utilities,ly.personalities,ly.context;@layer ly.tokens{:where(.ly-root){--ly-space-0:0;--ly-space-1:0.25rem;--ly-space-2:0.5rem;--ly-space-3:0.75rem;--ly-space-4:1rem;--ly-space-5:1.5rem;--ly-space-6:2rem;--ly-space-7:3rem;--ly-space-8:4rem;--ly-space-9:6rem;--ly-wrapper-compact:40rem;--ly-wrapper-prose:68ch;--ly-wrapper-content:72rem;--ly-wrapper-workspace:96rem;--ly-wrapper-wide:112rem;--ly-page-padding-inline:clamp(1rem,3vw,3rem);--ly-safe-area-inline:max( env(safe-area-inset-left,0px),env(safe-area-inset-right,0px) );--ly-safe-area-block-start:env(safe-area-inset-top,0px);--ly-safe-area-block-end:env(safe-area-inset-bottom,0px);--ly-wrapper-gutter:var(--ly-page-padding-inline);--ly-profile-gap:var(--ly-space-5);--ly-gap:var(--ly-profile-gap);--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(2rem,4vh,4rem);--ly-section-padding-block-compact:clamp(1rem,2.5vh,2rem);--ly-header-height:4.5rem;--ly-sticky-position:sticky;--ly-cover-min:100vh;--ly-shell-min:100vh;--ly-scroll-max:50rem;--ly-scroll-viewport-max:min(70vh,50rem);--ly-switcher-threshold:42rem;--ly-sidebar-size:18rem;--ly-sidebar-content-min:50%;--ly-grid-columns:12;--ly-grid-min:16rem;--ly-split-min:20rem;--ly-pane-min:18rem;--ly-pane-size:22rem;--ly-media-min:18rem;--ly-media-size:14rem;--ly-reel-item-min:16rem;--ly-reel-item-max:28rem;--ly-frame-ratio:16 / 9;--ly-split-primary:1fr;--ly-split-secondary:1fr;--ly-recipe-rail:18rem;--ly-recipe-aside:22rem;--ly-gallery-min:12rem;--ly-card-grid-min:16rem;--ly-app-shell-base-rows:auto auto minmax(0,1fr) auto auto;--ly-app-shell-medium-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-medium-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-medium-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-medium-areas:"header header" "nav nav" "main aside" "footer footer";--ly-dashboard-medium-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-dashboard-wide-areas:"nav header header" "nav main aside" "nav footer footer";--ly-dashboard-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.35fr);--ly-docs-wide-areas:"header header" "nav main" "nav aside" "footer footer";--ly-docs-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),auto) minmax(0,1fr);--ly-list-detail-wide-areas:"primary secondary" "actions actions";--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.7fr) minmax(0,1fr);--ly-split-hero-wide-areas:"content media" "actions media";--ly-split-hero-wide-columns:minmax(0,var(--ly-split-primary,1fr)) minmax(0,var(--ly-split-secondary,1fr));container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}@supports (width:1cqi){:where(.ly-root){--ly-page-padding-inline:clamp(1rem,3cqi,3rem)}}@supports (height:100dvh){:where(.ly-root){--ly-cover-min:100dvh;--ly-shell-min:100dvh;--ly-scroll-viewport-max:min(70dvh,50rem)}}}@layer ly.reset{:where(.ly-root),:where(.ly-root *){box-sizing:border-box}:where(.ly-root){margin:0}:where(.ly-root img),:where(.ly-root svg),:where(.ly-root video),:where(.ly-root canvas),:where(.ly-root iframe){max-inline-size:100%}}@layer ly.tokens{@media (max-height:44rem){:where(.ly-root){--ly-gap:min(var(--ly-profile-gap),var(--ly-space-4));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:min(var(--ly-space-4),3vh);--ly-cluster-gap:min(var(--ly-space-3),2vh);--ly-section-padding-block:clamp(1.25rem,4vh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2vh,1.25rem);--ly-header-height:3.5rem;--ly-scroll-viewport-max:min(62vh,34rem)}@supports (height:100dvh){:where(.ly-root){--ly-stack-gap:min(var(--ly-space-4),3dvh);--ly-cluster-gap:min(var(--ly-space-3),2dvh);--ly-section-padding-block:clamp(1.25rem,4dvh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2dvh,1.25rem);--ly-scroll-viewport-max:min(62dvh,34rem)}}}@media (max-height:30rem){:where(.ly-root){--ly-cover-min:auto;--ly-shell-min:auto;--ly-sticky-position:static;--ly-section-padding-block:var(--ly-space-4);--ly-section-padding-block-compact:var(--ly-space-2);--ly-scroll-viewport-max:max(12rem,calc(100vh - 8rem))}@supports (height:100dvh){:where(.ly-root){--ly-scroll-viewport-max:max(12rem,calc(100dvh - 8rem))}}}}@layer ly.context{:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:clamp(1rem,2.5vh,2rem);--ly-section-padding-block-compact:clamp(0.5rem,1.5vh,1rem)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:var(--ly-profile-gap);--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(2rem,4vh,4rem);--ly-section-padding-block-compact:clamp(1rem,2.5vh,2rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:max(var(--ly-profile-gap),var(--ly-space-6));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-5);--ly-cluster-gap:var(--ly-space-4);--ly-section-padding-block:clamp(3rem,7vh,6rem);--ly-section-padding-block-compact:clamp(1.5rem,3.5vh,3rem)}@media (max-height:44rem){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:clamp(0.75rem,2vh,1.25rem);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:min(var(--ly-profile-gap),var(--ly-space-4));--ly-grid-gap:var(--ly-gap);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(1.25rem,4vh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2vh,1.25rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:var(--ly-space-5);--ly-grid-gap:var(--ly-space-5);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:clamp(1.5rem,6vh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3vh,1.25rem)}@supports (height:100dvh){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-section-padding-block:clamp(0.75rem,2dvh,1.25rem)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-section-padding-block:clamp(1.25rem,4dvh,2rem);--ly-section-padding-block-compact:clamp(0.75rem,2dvh,1.25rem)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-section-padding-block:clamp(1.5rem,6dvh,3rem);--ly-section-padding-block-compact:clamp(0.75rem,3dvh,1.25rem)}}}@media (max-height:30rem){:where( .ly-root[data-ly-density="compact"],.ly-root [data-ly-density="compact"] ){--ly-gap:var(--ly-space-2);--ly-grid-gap:var(--ly-space-2);--ly-stack-gap:var(--ly-space-2);--ly-cluster-gap:var(--ly-space-1);--ly-section-padding-block:var(--ly-space-3);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="normal"],.ly-root [data-ly-density="normal"] ){--ly-gap:var(--ly-space-3);--ly-grid-gap:var(--ly-space-3);--ly-stack-gap:var(--ly-space-3);--ly-cluster-gap:var(--ly-space-2);--ly-section-padding-block:var(--ly-space-4);--ly-section-padding-block-compact:var(--ly-space-2)}:where( .ly-root[data-ly-density="spacious"],.ly-root [data-ly-density="spacious"] ){--ly-gap:var(--ly-space-4);--ly-grid-gap:var(--ly-space-4);--ly-stack-gap:var(--ly-space-4);--ly-cluster-gap:var(--ly-space-3);--ly-section-padding-block:var(--ly-space-5);--ly-section-padding-block-compact:var(--ly-space-3)}}}@layer ly.wrappers{.ly-wrapper{--ly-wrapper-max:var(--ly-personality-wrapper-max,var(--ly-wrapper-content));--ly-wrapper-fluid-gutter:var(--ly-wrapper-gutter);--ly-wrapper-local-gutter:max( var(--ly-wrapper-fluid-gutter),var(--ly-safe-area-inline) );container-name:ly-scope;container-type:inline-size;inline-size:min(100%,calc(var(--ly-wrapper-max) + (var(--ly-wrapper-local-gutter) * 2)));max-inline-size:100%;margin-inline:auto;padding-inline:var(--ly-wrapper-local-gutter)}.ly-wrapper--compact{--ly-wrapper-max:var(--ly-wrapper-compact)}.ly-wrapper--prose{--ly-wrapper-max:var(--ly-wrapper-prose)}.ly-wrapper--content{--ly-wrapper-max:var(--ly-wrapper-content)}.ly-wrapper--workspace{--ly-wrapper-max:var(--ly-wrapper-workspace)}.ly-wrapper--wide{--ly-wrapper-max:var(--ly-wrapper-wide)}.ly-wrapper--full{--ly-wrapper-max:100%}.ly-wrapper--breakout{--ly-wrapper-max:100%;--ly-breakout-content-size:min( var(--ly-wrapper-content),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );--ly-breakout-feature-size:min( var(--ly-wrapper-wide),calc(100% - (var(--ly-wrapper-local-gutter) * 2)) );display:grid;grid-template-columns:[full-start] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [feature-start] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [content-start] minmax(0,var(--ly-breakout-content-size)) [content-end] minmax( 0,calc((var(--ly-breakout-feature-size) - var(--ly-breakout-content-size)) / 2) ) [feature-end] minmax(min(100%,var(--ly-wrapper-local-gutter)),1fr) [full-end];inline-size:100%;padding-inline:0}.ly-wrapper--breakout>*{grid-column:content;min-inline-size:0;max-inline-size:100%}.ly-wrapper--breakout>:where(.ly-lane--content,[data-ly-lane="content"]){grid-column:content}.ly-wrapper--breakout>:where(.ly-lane--feature,[data-ly-lane="feature"]){grid-column:feature}.ly-wrapper--breakout>:where(.ly-lane--full,[data-ly-lane="full"]){grid-column:full}}@layer ly.primitives{.ly-page{min-block-size:100vh;min-block-size:100dvh;padding-block-start:var(--ly-safe-area-block-start);padding-block-end:var(--ly-safe-area-block-end)}.ly-header,.ly-footer,.ly-main,.ly-section,.ly-surface{min-inline-size:0;min-block-size:0}.ly-header{min-block-size:var(--ly-header-height)}.ly-header--sticky{position:var(--ly-sticky-position,sticky);inset-block-start:0;z-index:var(--ly-z-header,20)}.ly-section{padding-block:var(--ly-section-padding-block)}.ly-section--compact{padding-block:var(--ly-section-padding-block-compact)}.ly-section--flush{padding-block:0}.ly-readable{max-inline-size:var(--ly-wrapper-prose)}.ly-stack{display:flex;flex-direction:column;gap:var(--ly-stack-gap)}.ly-cluster{display:flex;flex-wrap:wrap;align-items:center;gap:var(--ly-cluster-gap)}.ly-center{inline-size:min(100%,var(--ly-center-max,var(--ly-wrapper-content)));margin-inline:auto}.ly-cover{display:flex;flex-direction:column;min-block-size:100vh;min-block-size:var(--ly-cover-min);padding-block:var(--ly-cover-padding,var(--ly-space-5))}.ly-cover>:where(.ly-cover__center,[data-ly-cover-center]){margin-block:auto}.ly-switcher{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-switcher>*{flex-basis:calc((var(--ly-switcher-threshold) - 100%) * 999);flex-grow:1;min-inline-size:0}.ly-sidebar{display:flex;flex-wrap:wrap;gap:var(--ly-gap)}.ly-sidebar>:where(.ly-sidebar__side,[data-ly-sidebar="side"]){flex-basis:var(--ly-sidebar-size);flex-grow:1;min-inline-size:min(100%,var(--ly-sidebar-size))}.ly-sidebar>:where(.ly-sidebar__content,[data-ly-sidebar="content"]){flex-basis:0;flex-grow:999;min-inline-size:min(100%,var(--ly-sidebar-content-min))}.ly-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-grid-min)),1fr));gap:var(--ly-grid-gap)}.ly-grid--fixed{grid-template-columns:repeat(var(--ly-grid-columns),minmax(0,1fr))}.ly-split{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-split-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-split-align,center)}.ly-panes{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-pane-min)),1fr));gap:var(--ly-gap)}.ly-media{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-media-min)),1fr));gap:var(--ly-gap);align-items:var(--ly-media-align,start)}.ly-media>:where(.ly-media__actions,[data-ly-media="actions"]){min-inline-size:min(100%,var(--ly-media-size))}.ly-grid>*,.ly-split>*,.ly-panes>*,.ly-media>*{min-inline-size:0;min-block-size:0}.ly-reel{display:grid;grid-auto-flow:column;grid-auto-columns:minmax(min(100%,var(--ly-reel-item-min)),var(--ly-reel-item-max));gap:var(--ly-grid-gap);overflow-x:auto;overscroll-behavior-inline:contain;scroll-snap-type:inline proximity}.ly-reel>*{min-inline-size:0;scroll-snap-align:start}.ly-frame{aspect-ratio:var(--ly-frame-ratio);overflow:hidden}.ly-frame>*{inline-size:100%;block-size:100%;object-fit:cover}.ly-scroll{min-block-size:0;overflow-x:clip;overflow-y:auto;overscroll-behavior-block:contain}.ly-scroll--bounded{max-block-size:var(--ly-scroll-max)}.ly-scroll--viewport{max-block-size:var(--ly-scroll-viewport-max)}}@layer ly.recipes{:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] ){container-name:ly-scope;container-type:inline-size;min-inline-size:0;min-block-size:0}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"] ){display:grid;grid-template-columns:minmax(0,1fr);gap:var(--ly-gap)}[data-ly-recipe="app-shell"]{min-block-size:100vh;min-block-size:var(--ly-shell-min);grid-template-areas:"header" "sidebar" "main" "aside" "footer";grid-template-rows:var(--ly-app-shell-base-rows)}[data-ly-recipe="dashboard"],[data-ly-recipe="docs"]{grid-template-areas:"header" "nav" "main" "aside" "footer"}[data-ly-recipe="list-detail"]{grid-template-areas:"primary" "secondary" "actions"}[data-ly-recipe="split-hero"]{grid-template-areas:"content" "media" "actions";align-items:var(--ly-split-hero-align,center)}[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"]{display:grid;gap:var(--ly-grid-gap)}[data-ly-recipe="gallery"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-gallery-min)),1fr))}[data-ly-recipe="card-grid"]{grid-template-columns:repeat(auto-fit,minmax(min(100%,var(--ly-card-grid-min)),1fr))}:where( [data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )[data-ly-responsive="manual"]{grid-template-columns:minmax(0,1fr)}:where( [data-ly-recipe="app-shell"],[data-ly-recipe="dashboard"],[data-ly-recipe="docs"],[data-ly-recipe="list-detail"],[data-ly-recipe="split-hero"],[data-ly-recipe="gallery"],[data-ly-recipe="card-grid"] )>*{min-inline-size:0;min-block-size:0}[data-ly-area="header"]{grid-area:header}[data-ly-area="nav"]{grid-area:nav}[data-ly-area="sidebar"]{grid-area:sidebar}[data-ly-area="main"]{grid-area:main}[data-ly-area="aside"]{grid-area:aside}[data-ly-area="footer"]{grid-area:footer}[data-ly-area="content"]{grid-area:content}[data-ly-area="media"]{grid-area:media}[data-ly-area="actions"]{grid-area:actions}[data-ly-area="primary"]{grid-area:primary}[data-ly-area="secondary"]{grid-area:secondary}@container ly-scope (min-width:42rem){[data-ly-recipe="split-hero"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-split-hero-wide-areas);grid-template-columns:var(--ly-split-hero-wide-columns)}}@container ly-scope (min-width:44rem){[data-ly-recipe="list-detail"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-list-detail-wide-areas);grid-template-columns:var(--ly-list-detail-wide-columns)}}@container ly-scope (min-width:48rem){[data-ly-recipe="docs"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-docs-wide-areas);grid-template-columns:var(--ly-docs-wide-columns)}}@container ly-scope (min-width:52rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-medium-areas);grid-template-columns:var(--ly-app-shell-medium-columns);grid-template-rows:var(--ly-app-shell-medium-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-medium-areas);grid-template-columns:var(--ly-dashboard-medium-columns)}}@container ly-scope (min-width:72rem){[data-ly-recipe="app-shell"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-app-shell-wide-areas);grid-template-columns:var(--ly-app-shell-wide-columns);grid-template-rows:var(--ly-app-shell-wide-rows)}[data-ly-recipe="dashboard"]:not([data-ly-responsive="manual"]){grid-template-areas:var(--ly-dashboard-wide-areas);grid-template-columns:var(--ly-dashboard-wide-columns)}}}@layer ly.utilities{.ly-cols-1{--ly-grid-columns:1}.ly-cols-2{--ly-grid-columns:2}.ly-cols-3{--ly-grid-columns:3}.ly-cols-4{--ly-grid-columns:4}.ly-cols-6{--ly-grid-columns:6}.ly-cols-12{--ly-grid-columns:12}.ly-span-1{grid-column:span 1}.ly-span-2{grid-column:span 2}.ly-span-3{grid-column:span 3}.ly-span-4{grid-column:span 4}.ly-span-full{grid-column:1 / -1}.ly-gap-0{gap:var(--ly-space-0)}.ly-gap-1{gap:var(--ly-space-1)}.ly-gap-2{gap:var(--ly-space-2)}.ly-gap-3{gap:var(--ly-space-3)}.ly-gap-4{gap:var(--ly-space-4)}.ly-gap-5{gap:var(--ly-space-5)}.ly-gap-6{gap:var(--ly-space-6)}.ly-gap-7{gap:var(--ly-space-7)}.ly-gap-8{gap:var(--ly-space-8)}.ly-gap-9{gap:var(--ly-space-9)}.ly-pad-0{padding:var(--ly-space-0)}.ly-pad-2{padding:var(--ly-space-2)}.ly-pad-4{padding:var(--ly-space-4)}.ly-pad-6{padding:var(--ly-space-6)}.ly-pad-8{padding:var(--ly-space-8)}.ly-mx-auto{margin-inline:auto}.ly-w-full{inline-size:100%}.ly-h-full{block-size:100%}.ly-min-h-screen{min-block-size:100vh;min-block-size:100dvh}.ly-overflow-auto{overflow:auto}.ly-overflow-hidden{overflow:hidden}.ly-items-start{align-items:flex-start}.ly-items-center{align-items:center}.ly-items-end{align-items:flex-end}.ly-items-stretch{align-items:stretch}.ly-justify-start{justify-content:flex-start}.ly-justify-center{justify-content:center}.ly-justify-end{justify-content:flex-end}.ly-justify-between{justify-content:space-between}.ly-frame-1x1{--ly-frame-ratio:1 / 1}.ly-frame-3x2{--ly-frame-ratio:3 / 2}.ly-frame-4x3{--ly-frame-ratio:4 / 3}.ly-frame-16x9{--ly-frame-ratio:16 / 9}.ly-frame-21x9{--ly-frame-ratio:21 / 9}.ly-hidden{display:none !important}.ly-visually-hidden{position:absolute !important;inline-size:1px !important;block-size:1px !important;padding:0 !important;margin:-1px !important;overflow:hidden !important;clip:rect(0,0,0,0) !important;clip-path:inset(50%) !important;white-space:nowrap !important}}@layer ly.personalities.minimal-saas{:where(.ly-root[data-ly-layout="minimal-saas"]){--ly-personality-wrapper-max:88rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:16rem;--ly-recipe-rail:15rem;--ly-recipe-aside:16rem;--ly-split-primary:1.05fr;--ly-split-secondary:0.95fr}}@layer ly.personalities.bento{:where(.ly-root[data-ly-layout="bento"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-card-grid-min:14rem;--ly-gallery-min:11rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header" "sidebar main main" "aside main main" "footer footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.3fr) minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.maximalist{:where(.ly-root[data-ly-layout="maximalist"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-7);--ly-grid-min:10rem;--ly-card-grid-min:13rem;--ly-gallery-min:10rem;--ly-recipe-rail:15rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "main main sidebar aside" "main main footer footer";--ly-app-shell-wide-columns:minmax(0,1.4fr) minmax(0,0.6fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr) minmax(min(100%,var(--ly-recipe-aside)),0.38fr)}}@layer ly.personalities.bauhaus{:where(.ly-root[data-ly-layout="bauhaus"]){--ly-personality-wrapper-max:96rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:14rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header header header" "sidebar main main aside" "sidebar footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,0.7fr) minmax(0,1.3fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.tactile{:where(.ly-root[data-ly-layout="tactile"]){--ly-personality-wrapper-max:82rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:15rem;--ly-recipe-rail:17rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header" "sidebar main" "aside main" "footer footer";--ly-app-shell-wide-rows:auto auto minmax(0,1fr) auto;--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(min(100%,var(--ly-pane-size)),0.5fr) minmax(0,0.9fr)}}@layer ly.personalities.neumorphism{:where(.ly-root[data-ly-layout="neumorphism"]){--ly-personality-wrapper-max:84rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:17rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header sidebar" "main sidebar" "aside sidebar" "footer sidebar";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-list-detail-wide-columns:minmax(0,1.1fr) minmax(min(100%,var(--ly-pane-size)),0.7fr)}}@layer ly.personalities.retrofuturism{:where(.ly-root[data-ly-layout="retrofuturism"]){--ly-personality-wrapper-max:106rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:14rem;--ly-recipe-aside:17rem;--ly-app-shell-wide-areas:"sidebar header aside" "sidebar main aside" "sidebar footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.24fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.3fr)}}@layer ly.personalities.brutalism{:where(.ly-root[data-ly-layout="brutalism"]){--ly-personality-wrapper-max:100rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(0,1.3fr) minmax(0,0.7fr) minmax(min(100%,var(--ly-recipe-rail)),0.34fr)}}@layer ly.personalities.cyberpunk{:where(.ly-root[data-ly-layout="cyberpunk"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:12rem;--ly-recipe-rail:13rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.2fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.32fr)}}@layer ly.personalities.y2k{:where(.ly-root[data-ly-layout="y2k"]){--ly-personality-wrapper-max:90rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:13rem;--ly-recipe-rail:18rem;--ly-recipe-aside:16rem;--ly-app-shell-wide-areas:"header header sidebar" "aside main sidebar" "footer footer sidebar";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-aside)),0.34fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-rail)),0.28fr)}}@layer ly.personalities.retro-glass{:where(.ly-root[data-ly-layout="retro-glass"]){--ly-personality-wrapper-max:104rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:15rem;--ly-recipe-rail:18rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"header header sidebar" "main main sidebar" "aside footer footer";--ly-app-shell-wide-columns:minmax(0,1.25fr) minmax(0,0.75fr) minmax(min(100%,var(--ly-recipe-rail)),0.32fr)}}@layer ly.personalities.f-pattern{:where(.ly-root[data-ly-layout="f-pattern"]){--ly-personality-wrapper-max:92rem;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "sidebar footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.26fr) minmax(0,1.35fr) minmax(min(100%,var(--ly-recipe-aside)),0.65fr);--ly-split-hero-wide-columns:minmax(0,1.8fr) minmax(0,0.8fr)}}@layer ly.personalities.z-pattern{:where(.ly-root[data-ly-layout="z-pattern"]){--ly-personality-wrapper-max:108rem;--ly-profile-gap:var(--ly-space-5);--ly-grid-min:14rem;--ly-recipe-rail:17rem;--ly-recipe-aside:19rem;--ly-app-shell-wide-areas:"header header sidebar" "main aside sidebar" "footer aside sidebar";--ly-app-shell-wide-columns:minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr) minmax(min(100%,var(--ly-recipe-rail)),0.3fr);--ly-split-hero-wide-columns:minmax(0,1.5fr) minmax(0,0.5fr)}}@layer ly.personalities.split-screen{:where(.ly-root[data-ly-layout="split-screen"]){--ly-personality-wrapper-max:100%;--ly-profile-gap:var(--ly-space-4);--ly-grid-min:20rem;--ly-recipe-rail:24rem;--ly-recipe-aside:24rem;--ly-app-shell-wide-areas:"header header" "main sidebar" "aside sidebar" "footer footer";--ly-app-shell-wide-rows:auto minmax(0,1fr) auto auto;--ly-app-shell-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-split-hero-wide-columns:minmax(0,1fr) minmax(0,1fr);--ly-list-detail-wide-columns:minmax(0,1fr) minmax(0,1fr)}}@layer ly.personalities.mondrian{:where(.ly-root[data-ly-layout="mondrian"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-3);--ly-grid-min:11rem;--ly-recipe-rail:13rem;--ly-recipe-aside:18rem;--ly-app-shell-wide-areas:"sidebar header header" "sidebar main aside" "footer footer aside";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.22fr) minmax(0,1.45fr) minmax(min(100%,var(--ly-recipe-aside)),0.55fr)}}@layer ly.personalities.synthwave{:where(.ly-root[data-ly-layout="synthwave"]){--ly-personality-wrapper-max:112rem;--ly-profile-gap:var(--ly-space-6);--ly-grid-min:14rem;--ly-recipe-rail:16rem;--ly-recipe-aside:20rem;--ly-app-shell-wide-areas:"header header header" "sidebar main aside" "footer footer footer";--ly-app-shell-wide-columns:minmax(min(100%,var(--ly-recipe-rail)),0.28fr) minmax(0,1fr) minmax(min(100%,var(--ly-recipe-aside)),0.34fr);--ly-reel-item-min:20rem}} diff --git a/dist/primitives.css b/dist/primitives.css index e23f185..1af7140 100644 --- a/dist/primitives.css +++ b/dist/primitives.css @@ -175,9 +175,16 @@ .ly-scroll { min-block-size: 0; - max-block-size: var(--ly-scroll-max); overflow-x: clip; overflow-y: auto; overscroll-behavior-block: contain; } + + .ly-scroll--bounded { + max-block-size: var(--ly-scroll-max); + } + + .ly-scroll--viewport { + max-block-size: var(--ly-scroll-viewport-max); + } } diff --git a/manifest.json b/manifest.json index d2032f5..e55b1b2 100644 --- a/manifest.json +++ b/manifest.json @@ -79,7 +79,8 @@ "--ly-gap", "--ly-grid-gap", "--ly-stack-gap", "--ly-cluster-gap", "--ly-section-padding-block", "--ly-section-padding-block-compact", "--ly-header-height", "--ly-sticky-position", "--ly-cover-min", - "--ly-shell-min", "--ly-scroll-max", "--ly-switcher-threshold", "--ly-sidebar-size", + "--ly-shell-min", "--ly-scroll-max", "--ly-scroll-viewport-max", "--ly-switcher-threshold", + "--ly-sidebar-size", "--ly-sidebar-content-min", "--ly-grid-columns", "--ly-grid-min", "--ly-split-min", "--ly-pane-min", "--ly-pane-size", "--ly-media-min", "--ly-media-size", "--ly-reel-item-min", "--ly-reel-item-max", "--ly-frame-ratio", "--ly-split-primary", "--ly-split-secondary", diff --git a/styles/foundation.css b/styles/foundation.css index 2722155..88468e9 100644 --- a/styles/foundation.css +++ b/styles/foundation.css @@ -43,7 +43,8 @@ --ly-sticky-position: sticky; --ly-cover-min: 100vh; --ly-shell-min: 100vh; - --ly-scroll-max: min(70vh, 50rem); + --ly-scroll-max: 50rem; + --ly-scroll-viewport-max: min(70vh, 50rem); --ly-switcher-threshold: 42rem; --ly-sidebar-size: 18rem; @@ -142,7 +143,7 @@ :where(.ly-root) { --ly-cover-min: 100dvh; --ly-shell-min: 100dvh; - --ly-scroll-max: min(70dvh, 50rem); + --ly-scroll-viewport-max: min(70dvh, 50rem); } } } @@ -176,7 +177,7 @@ --ly-section-padding-block: clamp(1.25rem, 4vh, 2rem); --ly-section-padding-block-compact: clamp(0.75rem, 2vh, 1.25rem); --ly-header-height: 3.5rem; - --ly-scroll-max: min(62vh, 34rem); + --ly-scroll-viewport-max: min(62vh, 34rem); } @supports (height: 100dvh) { @@ -185,7 +186,7 @@ --ly-cluster-gap: min(var(--ly-space-3), 2dvh); --ly-section-padding-block: clamp(1.25rem, 4dvh, 2rem); --ly-section-padding-block-compact: clamp(0.75rem, 2dvh, 1.25rem); - --ly-scroll-max: min(62dvh, 34rem); + --ly-scroll-viewport-max: min(62dvh, 34rem); } } } @@ -197,12 +198,12 @@ --ly-sticky-position: static; --ly-section-padding-block: var(--ly-space-4); --ly-section-padding-block-compact: var(--ly-space-2); - --ly-scroll-max: max(12rem, calc(100vh - 8rem)); + --ly-scroll-viewport-max: max(12rem, calc(100vh - 8rem)); } @supports (height: 100dvh) { :where(.ly-root) { - --ly-scroll-max: max(12rem, calc(100dvh - 8rem)); + --ly-scroll-viewport-max: max(12rem, calc(100dvh - 8rem)); } } } diff --git a/styles/primitives.css b/styles/primitives.css index e23f185..1af7140 100644 --- a/styles/primitives.css +++ b/styles/primitives.css @@ -175,9 +175,16 @@ .ly-scroll { min-block-size: 0; - max-block-size: var(--ly-scroll-max); overflow-x: clip; overflow-y: auto; overscroll-behavior-block: contain; } + + .ly-scroll--bounded { + max-block-size: var(--ly-scroll-max); + } + + .ly-scroll--viewport { + max-block-size: var(--ly-scroll-viewport-max); + } } diff --git a/test/demo-smoke.test.mjs b/test/demo-smoke.test.mjs index ec4e34d..79aa29e 100644 --- a/test/demo-smoke.test.mjs +++ b/test/demo-smoke.test.mjs @@ -1294,6 +1294,14 @@ const verifyProfileAndUtilityIsolation = async (page, baseUrl) => { assert.equal(result.cardGridGap, "48px", "The local gap utility did not affect a Card Grid."); }; +/** + * Verifies intrinsic primitive overflow behavior and the three explicit Scroll + * sizing modes at narrow and wide viewport allocations. + * + * @param {import("@playwright/test").Page} page Active browser page. + * @param {string} baseUrl Demo server URL. + * @returns {Promise} + */ const verifyPrimitiveOverflow = async (page, baseUrl) => { const primitives = [ "stack", @@ -1316,13 +1324,76 @@ const verifyPrimitiveOverflow = async (page, baseUrl) => { }); await page.waitForFunction(() => document.body.dataset.demoReady === "true"); + const scrollModes = await page.evaluate(() => { + const wrapper = document.querySelector("#previewWrapper"); + wrapper.style.setProperty("--ly-scroll-max", "20rem"); + wrapper.style.setProperty("--ly-scroll-viewport-max", "12rem"); + + /** + * Creates one long-content Scroll fixture. + * + * @param {string} className Scroll class list. + * @param {string} id Stable fixture identifier. + * @returns {HTMLElement} Populated Scroll fixture. + */ + const createScrollFixture = (className, id) => { + const fixture = document.createElement("div"); + fixture.className = className; + fixture.id = id; + for (let index = 0; index < 30; index += 1) { + const item = document.createElement("p"); + item.textContent = `Scrollable activity ${index + 1}`; + fixture.append(item); + } + return fixture; + }; + + const containedParent = document.createElement("div"); + containedParent.style.display = "grid"; + containedParent.style.gridTemplateRows = "8rem"; + const contained = createScrollFixture("ly-scroll", "scroll-contained"); + const bounded = createScrollFixture("ly-scroll ly-scroll--bounded", "scroll-bounded"); + const viewport = createScrollFixture("ly-scroll ly-scroll--viewport", "scroll-viewport"); + containedParent.append(contained); + wrapper.replaceChildren(containedParent, bounded, viewport); + + /** + * Captures the computed constraint and rendered overflow for a Scroll. + * + * @param {HTMLElement} element Scroll fixture. + * @returns {{maxBlockSize: string, scrollHeight: number, clientHeight: number}} Scroll metrics. + */ + const scrollMetrics = (element) => ({ + maxBlockSize: getComputedStyle(element).maxBlockSize, + scrollHeight: element.scrollHeight, + clientHeight: element.clientHeight + }); + + return { + contained: scrollMetrics(contained), + bounded: scrollMetrics(bounded), + viewport: scrollMetrics(viewport) + }; + }); + + assert.equal(scrollModes.contained.maxBlockSize, "none"); + assert.equal(scrollModes.bounded.maxBlockSize, "320px"); + assert.equal(scrollModes.viewport.maxBlockSize, "192px"); + for (const [mode, metrics] of Object.entries(scrollModes)) { + assert( + metrics.scrollHeight > metrics.clientHeight, + `${mode} Scroll did not overflow inside its intended constraint.` + ); + } + for (const width of [320, 1440]) { await page.setViewportSize({ width, height: 800 }); for (const primitive of primitives) { const result = await page.evaluate((primitiveName) => { const wrapper = document.querySelector("#previewWrapper"); const fixture = document.createElement("section"); - fixture.className = `ly-${primitiveName}`; + fixture.className = + primitiveName === "scroll" ? "ly-scroll ly-scroll--bounded" : `ly-${primitiveName}`; fixture.style.setProperty("--ly-scroll-max", "8rem"); fixture.style.setProperty("--ly-cover-min", "20rem"); diff --git a/test/layout-css-contract.test.mjs b/test/layout-css-contract.test.mjs index d5b79e1..cdad7b6 100644 --- a/test/layout-css-contract.test.mjs +++ b/test/layout-css-contract.test.mjs @@ -321,12 +321,30 @@ assert(!primitives.includes("@container"), "Intrinsic primitives must not depend assert( /--ly-cover-min:\s*100vh/.test(foundation) && /--ly-shell-min:\s*100vh/.test(foundation) && - /--ly-scroll-max:\s*min\(70vh,\s*50rem\)/.test(foundation) && /@supports\s*\(height:\s*100dvh\)[\s\S]*--ly-cover-min:\s*100dvh[\s\S]*--ly-shell-min:\s*100dvh/.test( foundation ), "Dynamic viewport tokens must enhance valid vh defaults through feature detection" ); +assert( + /--ly-scroll-max:\s*50rem/.test(foundation) && + /--ly-scroll-viewport-max:\s*min\(70vh,\s*50rem\)/.test(foundation), + "Bounded and viewport-relative Scroll need separate public maxima." +); +assert( + /\.ly-scroll\s*\{[^}]*overflow-y:\s*auto/s.test(primitives) && + !/\.ly-scroll\s*\{[^}]*max-block-size:/s.test(primitives), + "Base Scroll must not impose a height cap." +); +assert( + /\.ly-scroll--bounded\s*\{[^}]*max-block-size:\s*var\(--ly-scroll-max\)/s.test( + primitives + ) && + /\.ly-scroll--viewport\s*\{[^}]*max-block-size:\s*var\(--ly-scroll-viewport-max\)/s.test( + primitives + ), + "Scroll modifiers must consume distinct maxima." +); assert( /--ly-split-primary:\s*1fr/.test(foundation) && /--ly-split-secondary:\s*1fr/.test(foundation), diff --git a/test/manifest-contract.test.mjs b/test/manifest-contract.test.mjs index 701713c..d73ad43 100644 --- a/test/manifest-contract.test.mjs +++ b/test/manifest-contract.test.mjs @@ -55,7 +55,8 @@ const geometryTokens = [ "--ly-gap", "--ly-grid-gap", "--ly-stack-gap", "--ly-cluster-gap", "--ly-section-padding-block", "--ly-section-padding-block-compact", "--ly-header-height", "--ly-sticky-position", "--ly-cover-min", - "--ly-shell-min", "--ly-scroll-max", "--ly-switcher-threshold", "--ly-sidebar-size", + "--ly-shell-min", "--ly-scroll-max", "--ly-scroll-viewport-max", "--ly-switcher-threshold", + "--ly-sidebar-size", "--ly-sidebar-content-min", "--ly-grid-columns", "--ly-grid-min", "--ly-split-min", "--ly-pane-min", "--ly-pane-size", "--ly-media-min", "--ly-media-size", "--ly-reel-item-min", "--ly-reel-item-max", "--ly-frame-ratio", "--ly-split-primary", "--ly-split-secondary", From 61f2331a79f7f014e902ae3212367aece2389919 Mon Sep 17 00:00:00 2001 From: Foscat Date: Tue, 25 Aug 2026 17:43:18 -0500 Subject: [PATCH 5/6] feat: publish layout contract metadata --- manifest.json | 12 ++++- test/manifest-contract.test.mjs | 96 ++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index e55b1b2..b8a0dd6 100644 --- a/manifest.json +++ b/manifest.json @@ -66,7 +66,14 @@ }, "thresholds": { "containerMinWidths": ["42rem", "44rem", "48rem", "52rem", "72rem"], - "viewportMaxHeights": ["44rem", "30rem"] + "viewportMaxHeights": ["44rem", "30rem"], + "recipes": { + "splitHero": { "wide": "42rem" }, + "listDetail": { "wide": "44rem" }, + "docs": { "wide": "48rem" }, + "appShell": { "medium": "52rem", "wide": "72rem" }, + "dashboard": { "medium": "52rem", "wide": "72rem" } + } }, "tokens": { "geometry": [ @@ -92,7 +99,8 @@ "--ly-split-hero-wide-areas", "--ly-split-hero-wide-columns", "--ly-center-max", "--ly-cover-padding", "--ly-z-header", "--ly-split-align", "--ly-media-align", "--ly-split-hero-align" - ] + ], + "extensionOnly": [] }, "companions": { "ui-style-kit-css": ">=2.1.0 <3.0.0", diff --git a/test/manifest-contract.test.mjs b/test/manifest-contract.test.mjs index d73ad43..c53e039 100644 --- a/test/manifest-contract.test.mjs +++ b/test/manifest-contract.test.mjs @@ -4,6 +4,7 @@ import { readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { fileURLToPath } from "node:url"; import test from "node:test"; +import { generate, parse, walk } from "css-tree"; const root = fileURLToPath(new URL("..", import.meta.url)); const packageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8")); @@ -77,6 +78,60 @@ const internalLayoutTokens = [ "--ly-wrapper-local-gutter" ]; +/** + * Returns the authored source for one container-query threshold block. + * + * @param {string} css Recipe stylesheet source. + * @param {string} threshold Minimum inline-size value. + * @returns {string} Matching query block source. + */ +function containerQuerySource(css, threshold) { + const start = css.indexOf(`@container ly-scope (min-width: ${threshold})`); + assert(start >= 0, `Missing ${threshold} container query.`); + const next = css.indexOf("@container ly-scope", start + 1); + return css.slice(start, next === -1 ? css.length : next); +} + +/** + * Finds custom properties that contribute transitively to non-custom CSS + * declarations through var() references. + * + * @param {string} css Authored CSS source. + * @returns {Set} Runtime-consumed custom-property names. + */ +function runtimeConsumedCustomProperties(css) { + const dependencies = new Map(); + const directConsumers = new Set(); + const ast = parse(css); + + walk(ast, { + visit: "Declaration", + enter(node) { + const value = generate(node.value); + const references = [...value.matchAll(/var\(\s*(--ly-[a-z0-9-]+)/g)].map( + ([, token]) => token + ); + if (node.property.startsWith("--")) { + const existing = dependencies.get(node.property) ?? new Set(); + references.forEach((token) => existing.add(token)); + dependencies.set(node.property, existing); + } else { + references.forEach((token) => directConsumers.add(token)); + } + } + }); + + const consumed = new Set(); + const pending = [...directConsumers]; + while (pending.length > 0) { + const token = pending.pop(); + if (consumed.has(token)) continue; + consumed.add(token); + for (const dependency of dependencies.get(token) ?? []) pending.push(dependency); + } + return consumed; +} + test("ecosystem manifest publishes the structural API and package export", () => { assert.equal(manifest.schemaVersion, 1); assert.equal(manifest.name, packageJson.name); @@ -181,9 +236,29 @@ test("ecosystem manifest describes real structural selectors, thresholds, and to assert.deepEqual(manifest.containers, { names: ["ly-scope"], type: "inline-size" }); assert.deepEqual(manifest.thresholds, { containerMinWidths: ["42rem", "44rem", "48rem", "52rem", "72rem"], - viewportMaxHeights: ["44rem", "30rem"] + viewportMaxHeights: ["44rem", "30rem"], + recipes: { + splitHero: { wide: "42rem" }, + listDetail: { wide: "44rem" }, + docs: { wide: "48rem" }, + appShell: { medium: "52rem", wide: "72rem" }, + dashboard: { medium: "52rem", wide: "72rem" } + } }); + const recipesCss = readFileSync(join(root, "styles", "recipes.css"), "utf8"); + for (const [recipe, tiers] of Object.entries(manifest.thresholds.recipes)) { + const selectorName = recipe.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`); + for (const threshold of Object.values(tiers)) { + assert( + containerQuerySource(recipesCss, threshold).includes( + `[data-ly-recipe="${selectorName}"]` + ), + `${recipe} must own its documented ${threshold} threshold.` + ); + } + } assert.deepEqual(manifest.tokens.geometry, geometryTokens); + assert.deepEqual(manifest.tokens.extensionOnly, []); assert.deepEqual(manifest.companions, { "ui-style-kit-css": ">=2.1.0 <3.0.0", "interactive-surface-css": ">=1.5.0 <2.0.0" @@ -229,4 +304,23 @@ test("geometry manifest inventories documented and implemented public controls b for (const token of internalLayoutTokens) { assert(!manifest.tokens.geometry.includes(token), `${token} must remain implementation-only`); } + + const consumedTokens = runtimeConsumedCustomProperties(coreSources); + const extensionOnlyTokens = new Set(manifest.tokens.extensionOnly); + for (const token of manifest.tokens.geometry) { + assert( + consumedTokens.has(token) || extensionOnlyTokens.has(token), + `${token} must be runtime-consumed or explicitly extension-only.` + ); + } + for (const token of extensionOnlyTokens) { + assert( + manifest.tokens.geometry.includes(token), + `${token} must remain a public geometry token.` + ); + assert( + !consumedTokens.has(token), + `${token} no longer needs an extension-only classification.` + ); + } }); From 38db533799f10e7978166c4009edede50249bb36 Mon Sep 17 00:00:00 2001 From: Foscat Date: Tue, 25 Aug 2026 17:50:08 -0500 Subject: [PATCH 6/6] chore: prepare layout style 3.1.0 --- .github/workflows/npm-publish.yml | 2 +- CHANGELOG.md | 20 +++++++++ README.md | 33 +++++++++++---- demo/index.html | 12 +++--- docs/wiki/Getting-Started.md | 13 ++++-- docs/wiki/Home.md | 4 +- docs/wiki/Installation-And-CDN.md | 11 ++--- docs/wiki/Layout-Primitives.md | 15 +++++-- docs/wiki/Layout-Recipes.md | 2 + docs/wiki/Layout-Styles.md | 6 ++- docs/wiki/Migrating-To-3.1.md | 62 ++++++++++++++++++++++++++++ docs/wiki/Release-And-Publishing.md | 10 ++--- docs/wiki/_Sidebar.md | 1 + ecosystem-release-fixture.json | 2 +- manifest.json | 2 +- package-lock.json | 4 +- package.json | 2 +- scripts/release-fixture-contract.mjs | 24 ++++++++--- test/demo-smoke.test.mjs | 12 +++--- test/layout-css-contract.test.mjs | 3 +- test/manifest-contract.test.mjs | 2 +- test/package-contract.test.mjs | 11 ++--- test/pages-artifact.test.mjs | 6 +-- test/release-docs-contract.test.mjs | 38 +++++++++++++---- test/release-preflight.test.mjs | 15 ++++++- 25 files changed, 244 insertions(+), 68 deletions(-) create mode 100644 docs/wiki/Migrating-To-3.1.md diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index cbb1f42..7846905 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -6,7 +6,7 @@ on: workflow_dispatch: inputs: release_tag: - description: "Release tag to publish, for example v3.0.2" + description: "Release tag to publish, for example v3.1.0" required: true type: string diff --git a/CHANGELOG.md b/CHANGELOG.md index e195de8..5b47cde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,26 @@ All notable changes to `layout-style-css` are documented here. This project follows semantic versioning. +## [3.1.0] - 2026-08-25 + +### Added + +- Added compact, normal, and spacious density contexts that can be applied at the root or to a nested layout subtree. +- Added the `96rem` workspace Wrapper, all ten local gap utilities, explicit bounded and viewport-relative Scroll modifiers, and recipe-owned threshold metadata. + +### Changed + +- Made normal section spacing more conservative while retaining the former marketing rhythm through spacious density. +- Made gap utilities local to the element carrying the class and made base Scroll rely on its containing layout for height constraints. + +### Migration + +- Use spacious density for the former section rhythm, explicit inherited gap tokens for a shared spacing context, and `ly-scroll--viewport` for the former viewport-relative Scroll behavior. + +### Tests + +- Added density, workspace utilization, nested-gap isolation, Scroll-mode, public-token liveness, threshold metadata, cross-height, generated-output, package, and Pages contracts. + ## [3.0.2] - 2026-08-25 ### Fixed diff --git a/README.md b/README.md index 67297f4..cc3f4b7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ Zero-configuration, dependency-free structural CSS that responds to the width and height a layout actually receives. -`layout-style-css@3.0.2` is the current v3 patch release and preserves the clean-break structural contract introduced in 3.0.0. It provides intrinsic wrappers, composition primitives, seven semantic recipes, and sixteen spatial personalities. Layout owns structure; UI Style Kit owns paint; Interactive Surface owns interaction styling. +`layout-style-css@3.1.0` is the current additive v3 release. It provides intrinsic wrappers, composition primitives, seven semantic recipes, explicit local density controls, and sixteen spatial personalities. Layout owns structure; UI Style Kit owns paint; Interactive Surface owns interaction styling. ## Requirements @@ -13,7 +13,7 @@ Zero-configuration, dependency-free structural CSS that responds to the width an ## Install ```bash -npm install layout-style-css@3.0.2 +npm install layout-style-css@3.1.0 ``` Most applications need one import: @@ -44,6 +44,17 @@ Use `.ly-root` as the responsive boundary and keep the mobile DOM order authorit `.ly-root`, wrappers, and recipe roots establish the named `ly-scope` inline-size container. A recipe works directly in the root; when it is nested in a wrapper, it responds to that nearest wrapper. +## Density Contexts + +Normal density is the zero-configuration default. Use `data-ly-density="compact"`, `data-ly-density="normal"`, or `data-ly-density="spacious"` on `.ly-root` or any nested layout subtree. A nested density explicitly resets inherited gaps and section rhythm, so task controls can remain compact inside a spacious marketing page. + +```html +
+
Dense application controls
+
Campaign presentation
+
+``` + ## Wrappers Wrappers are optional measure and nesting controls. Every wrapper uses logical properties, fluid container-relative gutters, safe-area compensation, and shrink-safe sizing. @@ -54,6 +65,7 @@ Wrappers are optional measure and nesting controls. Every wrapper uses logical p | `.ly-wrapper--compact` | `40rem` compact measure | | `.ly-wrapper--prose` | `68ch` reading measure | | `.ly-wrapper--content` | `72rem` content measure | +| `.ly-wrapper--workspace` | `96rem` task-oriented application measure | | `.ly-wrapper--wide` | `112rem` wide measure | | `.ly-wrapper--full` | Full available inline size | | `.ly-wrapper--breakout` | Clamped content, feature, and full lanes | @@ -79,7 +91,9 @@ The core includes: - `.ly-panes` and `.ly-media` - `.ly-reel`, `.ly-frame`, and `.ly-scroll` -Grid and flex primitives wrap intrinsically whenever track wrapping can replace a query. Page, cover, and bounded-scroll sizing use `vh` fallbacks followed by `100dvh`-aware behavior. Only `.ly-reel` deliberately scrolls horizontally; only `.ly-scroll` deliberately creates a bounded vertical scroll region. +Grid and flex primitives wrap intrinsically whenever track wrapping can replace a query. Base `.ly-scroll` owns overflow but no height cap. Add `.ly-scroll--bounded` for the stable `--ly-scroll-max` cap or `.ly-scroll--viewport` for height-aware `--ly-scroll-viewport-max` behavior. Only `.ly-reel` deliberately scrolls horizontally; only the Scroll family deliberately creates vertical scrolling. + +Gap utilities `.ly-gap-0` through `.ly-gap-9` assign `gap` only on the element carrying the class. They do not alter inherited primitive gap variables. Override `--ly-gap`, `--ly-grid-gap`, `--ly-stack-gap`, or `--ly-cluster-gap` when a shared spacing context is intentional. ## Automatic Recipe Engine @@ -95,6 +109,8 @@ Recipes use attributes only. The stacked semantic source order is always safe, a | `data-ly-recipe="gallery"` | Intrinsic tracks; no topology breakpoint | | `data-ly-recipe="card-grid"` | Intrinsic tracks; no topology breakpoint | +The same ownership is available in `manifest.json` through `thresholds.recipes`, alongside the generic container-width and viewport-height arrays. Release checks verify every mapping against authored queries and require every public geometry token to reach a runtime CSS declaration. + App Shell base, medium, and wide topologies own matching row definitions. This keeps the primary workspace flexible while header and footer tracks remain intrinsic, including personality-specific four-row topologies. Canonical regions use `data-ly-area`, including `header`, `sidebar`, `nav`, `main`, `aside`, `footer`, `content`, `media`, `actions`, `primary`, and `secondary`. @@ -162,7 +178,7 @@ Each personality is a token/topology profile consumed by the shared recipe engin | `layout-style-css/personalities.json` | Public layout-to-visual pairing recommendations | | `layout-style-css/package.json` | Package metadata | -The cascade order is `ly.reset`, `ly.tokens`, `ly.wrappers`, `ly.primitives`, `ly.recipes`, `ly.utilities`, and `ly.personalities`. +The cascade order is `ly.reset`, `ly.tokens`, `ly.wrappers`, `ly.primitives`, `ly.recipes`, `ly.utilities`, `ly.personalities`, and `ly.context`. Unlayered application CSS remains stronger than every library layer. ## Ecosystem Imports @@ -177,18 +193,18 @@ import "layout-style-css"; This order lets UI Style Kit establish paint and theme roles, Interactive Surface add interaction states, and Layout apply structure. -`data-ly-layout`, `data-ui`, `data-theme`, and `data-mode` are independently selectable. See [Layout Styles](docs/wiki/Layout-Styles.md#visual-pairing-guidance) for the full recommendation matrix; pairings are never dependencies. +`data-ly-layout`, `data-ly-density`, `data-ui`, `data-theme`, and `data-mode` are independently selectable. See [Layout Styles](docs/wiki/Layout-Styles.md#visual-pairing-guidance) for the full recommendation matrix; pairings are never dependencies. ## CDN ```html - - + + ``` ## Clean-Break Migration -v3 has no compatibility bundle. It removes `legacy.css`, the deprecated UI Style Kit structural bridge, v1/v2 aliases, responsive `ly-md-*` and `ly-lg-*` utilities, and all visual-order utilities. See [Migrating To 3.0](docs/wiki/Migrating-To-3.0.md) for exact mappings. +v3 has no compatibility bundle. It removes `legacy.css`, the deprecated UI Style Kit structural bridge, v1/v2 aliases, responsive `ly-md-*` and `ly-lg-*` utilities, and all visual-order utilities. See [Migrating To 3.0](docs/wiki/Migrating-To-3.0.md) for the major-version mappings and [Migrating To 3.1](docs/wiki/Migrating-To-3.1.md) for density, local gap, and Scroll behavior changes. ## Demo And Verification @@ -214,6 +230,7 @@ npm run release:verify - [Layout Recipes](docs/wiki/Layout-Recipes.md) - [Layout Styles](docs/wiki/Layout-Styles.md) - [Migrating To 3.0](docs/wiki/Migrating-To-3.0.md) +- [Migrating To 3.1](docs/wiki/Migrating-To-3.1.md) - [Demo And GitHub Pages](docs/wiki/Demo-And-GitHub-Pages.md) - [Release And Publishing](docs/wiki/Release-And-Publishing.md) - [Security And Support](docs/wiki/Security-And-Support.md) diff --git a/demo/index.html b/demo/index.html index 1d27133..9518cab 100644 --- a/demo/index.html +++ b/demo/index.html @@ -6,7 +6,7 @@ - + @@ -71,15 +71,15 @@ - - + + - - + + Skip to the layout lab diff --git a/docs/wiki/Getting-Started.md b/docs/wiki/Getting-Started.md index 7385b18..9b40bb5 100644 --- a/docs/wiki/Getting-Started.md +++ b/docs/wiki/Getting-Started.md @@ -1,9 +1,9 @@ # Getting Started -Install `layout-style-css@3.0.2` and import the root bundle: +Install `layout-style-css@3.1.0` and import the root bundle: ```bash -npm install layout-style-css@3.0.2 +npm install layout-style-css@3.1.0 ``` ```js @@ -12,7 +12,7 @@ import "layout-style-css"; ## First Automatic Layout -The zero-configuration path needs only `.ly-root`, a recipe attribute, and semantic areas: +The zero-configuration path uses normal density and needs only `.ly-root`, a recipe attribute, and semantic areas: ```html @@ -39,7 +39,11 @@ Use wrappers when a composition needs a named measure or local responsive scope: ``` -Available measures are `.ly-wrapper--compact`, `.ly-wrapper--prose`, `.ly-wrapper--content`, `.ly-wrapper--wide`, `.ly-wrapper--full`, and `.ly-wrapper--breakout`. +Available measures are `.ly-wrapper--compact`, `.ly-wrapper--prose`, `.ly-wrapper--content`, `.ly-wrapper--workspace`, `.ly-wrapper--wide`, `.ly-wrapper--full`, and `.ly-wrapper--breakout`. Workspace is the `96rem` task-oriented application measure; content is the conventional `72rem` measure. + +## Optional Density + +Set `data-ly-density="compact"`, `data-ly-density="normal"`, or `data-ly-density="spacious"` on the root or a nested subtree. Nested contexts intentionally reset inherited spacing. ## Manual Topology @@ -61,3 +65,4 @@ Keep mobile DOM order authoritative. The package never changes reading or focus - [Layout Recipes](Layout-Recipes.md) - [Layout Styles](Layout-Styles.md) - [Migrating To 3.0](Migrating-To-3.0.md) +- [Migrating To 3.1](Migrating-To-3.1.md) diff --git a/docs/wiki/Home.md b/docs/wiki/Home.md index 8601e07..e4c483e 100644 --- a/docs/wiki/Home.md +++ b/docs/wiki/Home.md @@ -1,11 +1,12 @@ # Layout Style CSS v3 -`layout-style-css@3.0.2` is a dependency-free, CSS-only structural system for layouts that adapt to available width and height. +`layout-style-css@3.1.0` is a dependency-free, CSS-only structural system for layouts that adapt to available width and height. ## Contract - `.ly-root` is a usable layout and named `ly-scope` container. - Wrappers are optional measure and nesting controls. +- Normal density is the zero-configuration default; compact and spacious contexts can be nested locally. - Seven `data-ly-recipe` values enhance a semantic stacked fallback. - `data-ly-responsive="manual"` transfers topology ownership to application CSS. - Sixteen `data-ly-layout` profiles tune one shared responsive engine. @@ -22,6 +23,7 @@ Layout owns structure. UI Style Kit owns paint. Interactive Surface owns interac - [Layout Recipes](Layout-Recipes.md) - [Layout Styles](Layout-Styles.md) - [Migrating To 3.0](Migrating-To-3.0.md) +- [Migrating To 3.1](Migrating-To-3.1.md) - [Demo And GitHub Pages](Demo-And-GitHub-Pages.md) - [Release And Publishing](Release-And-Publishing.md) - [Security And Support](Security-And-Support.md) diff --git a/docs/wiki/Installation-And-CDN.md b/docs/wiki/Installation-And-CDN.md index c85b186..f07f92f 100644 --- a/docs/wiki/Installation-And-CDN.md +++ b/docs/wiki/Installation-And-CDN.md @@ -9,7 +9,7 @@ ## Package ```bash -npm install layout-style-css@3.0.2 +npm install layout-style-css@3.1.0 ``` ```js @@ -36,11 +36,11 @@ Package and bundler defaults (`main`, `style`, and the root export) use the read ## CDN ```html - + ``` ```html - + ``` The CDN paths include `/dist/` because CDN clients address files in the published tarball, while package import maps use the public exports. @@ -69,9 +69,10 @@ The module order is: 5. `ly.recipes` 6. `ly.utilities` 7. `ly.personalities` +8. `ly.context` -Application styles can override public custom properties without reordering the package modules. +The final context layer lets explicit density profiles override personality defaults. Unlayered application styles remain stronger than every package layer and can override public custom properties without reordering modules. ## Clean-Break Note -v3 does not export a compatibility bundle, legacy aliases, the deprecated structural bridge, extensionless aliases, or responsive/order utility families. See [Migrating To 3.0](Migrating-To-3.0.md). +v3 does not export a compatibility bundle, legacy aliases, the deprecated structural bridge, extensionless aliases, or responsive/order utility families. See [Migrating To 3.0](Migrating-To-3.0.md) for the major migration and [Migrating To 3.1](Migrating-To-3.1.md) for current behavioral refinements. diff --git a/docs/wiki/Layout-Primitives.md b/docs/wiki/Layout-Primitives.md index 2e328af..4a9fe33 100644 --- a/docs/wiki/Layout-Primitives.md +++ b/docs/wiki/Layout-Primitives.md @@ -13,6 +13,7 @@ Wrappers are optional local responsive scopes: - `.ly-wrapper--compact` - `.ly-wrapper--prose` - `.ly-wrapper--content` +- `.ly-wrapper--workspace` - `.ly-wrapper--wide` - `.ly-wrapper--full` - `.ly-wrapper--breakout` @@ -21,6 +22,8 @@ Breakout children select clamped lanes with `data-ly-lane="content"`, `data-ly-l `--ly-page-padding-inline` supplies the default `--ly-wrapper-gutter` value. `--ly-wrapper-gutter` controls rendered Wrapper padding at the scope where it is overridden, while the Wrapper applies safe-area compensation once at its outer edge. +Use content for conventional page content and workspace for task-oriented application layouts that need the `96rem` measure. + ## Flow - `.ly-stack` creates vertical flow. @@ -41,13 +44,15 @@ Breakout children select clamped lanes with `data-ly-lane="content"`, `data-ly-l - `.ly-frame` keeps a configurable aspect ratio. - `.ly-reel` is the deliberate horizontal-flow primitive. -- `.ly-scroll` is the deliberate bounded vertical-scroll primitive. +- `.ly-scroll` owns vertical overflow without imposing a height cap. +- `.ly-scroll--bounded` applies the stable `--ly-scroll-max` cap. +- `.ly-scroll--viewport` applies the height-aware `--ly-scroll-viewport-max` cap. -In normal use, only `.ly-reel` introduces intentional horizontal scrolling, and only `.ly-scroll` introduces intentional vertical scrolling. Other wrappers, primitives, and recipes clamp to their available inline size. +In normal use, only `.ly-reel` introduces intentional horizontal scrolling, and only `.ly-scroll` and its modifiers introduce intentional vertical scrolling. Other wrappers, primitives, and recipes clamp to their available inline size. ## Height Behavior -Page, cover, and bounded scroll behaviors use `vh` fallbacks followed by dynamic viewport units such as `100dvh`. +Page, cover, and viewport-relative Scroll behaviors use `vh` fallbacks followed by dynamic viewport units such as `100dvh`. Bounded Scroll remains application-controlled across viewport tiers. At `44rem` viewport height or less, gaps and scroll maxima tighten. At `30rem` or less, forced cover/shell minimums and recipe-owned sticky positioning are removed. Required regions remain in normal document flow. @@ -64,3 +69,7 @@ Advanced consumers can override stable custom properties for: - shell, cover, and bounded-scroll sizing These tokens tune behavior without creating a second breakpoint system. + +## Local Gap Utilities + +`.ly-gap-0` through `.ly-gap-9` set `gap` only on the element carrying the class. Nested primitives keep their own default gaps. For an intentionally inherited spacing context, set `--ly-gap`, `--ly-grid-gap`, `--ly-stack-gap`, or `--ly-cluster-gap` on the shared ancestor instead. diff --git a/docs/wiki/Layout-Recipes.md b/docs/wiki/Layout-Recipes.md index 9152bf2..f56d55f 100644 --- a/docs/wiki/Layout-Recipes.md +++ b/docs/wiki/Layout-Recipes.md @@ -16,6 +16,8 @@ Recipes are attribute-only semantic layouts. The mobile DOM order is authoritati Recipe roots also establish `ly-scope`, so child compositions can respond without extra setup. +`manifest.json` publishes the same ownership under `thresholds.recipes`, in addition to the generic `containerMinWidths` and `viewportMaxHeights` arrays. The release gate checks those mappings against authored queries and verifies public-token liveness. + App Shell base, medium, and wide topologies own matching row definitions. Header and footer tracks remain intrinsic while the main workspace receives the flexible row, including personality-specific four-row topologies. ## Areas diff --git a/docs/wiki/Layout-Styles.md b/docs/wiki/Layout-Styles.md index ac2a90b..e7a4850 100644 --- a/docs/wiki/Layout-Styles.md +++ b/docs/wiki/Layout-Styles.md @@ -25,6 +25,10 @@ A profile must remain visibly distinct through at least two spatial characterist When a profile supplies an App Shell area template, its matching row definition must contain the same number of rows and keep the main workspace on the flexible track. +## Density Contexts + +Normal density is the zero-configuration default. `data-ly-density="compact"`, `data-ly-density="normal"`, and `data-ly-density="spacious"` override personality spacing at the root or on a nested layout subtree. A nested density context intentionally resets inherited gap and section tokens while leaving the selected personality unchanged. + ## Ownership Layout owns structure. UI Style Kit owns paint. Interactive Surface owns interaction styling. @@ -33,7 +37,7 @@ The personality name does not promise colors, typography, borders, shadows, or i ## Visual Pairing Guidance -[`layout-style-css/personalities.json`](../../personalities.json) is the public, machine-readable pairing source used by the demo. Its entries are recommendations, never dependencies: `data-ly-layout`, `data-ui`, `data-theme`, and `data-mode` are independently selectable on the same document. +[`layout-style-css/personalities.json`](../../personalities.json) is the public, machine-readable pairing source used by the demo. Its entries are recommendations, never dependencies: `data-ly-layout`, `data-ly-density`, `data-ui`, `data-theme`, and `data-mode` are independently selectable on the same document. | Layout personality | Visual pairing guidance | | --- | --- | diff --git a/docs/wiki/Migrating-To-3.1.md b/docs/wiki/Migrating-To-3.1.md new file mode 100644 index 0000000..4689c94 --- /dev/null +++ b/docs/wiki/Migrating-To-3.1.md @@ -0,0 +1,62 @@ +# Migrating To 3.1 + +Layout Style CSS 3.1 is additive: no package export, recipe, area, personality, or recipe threshold was removed or renamed. The refinements make density, gap locality, Wrapper intent, and Scroll constraints explicit. + +## Section Rhythm And Density + +Normal density is now the conservative zero-configuration rhythm. The former larger marketing rhythm is available through spacious density: + +```html + +
+
Campaign overview
+
+``` + +Use compact density for task-heavy application regions and normal for conventional content. Density can be set at the root or reset on a nested subtree: + +```html + +
+
Inventory workspace
+
+``` + +The public values are `data-ly-density="compact"`, `data-ly-density="normal"`, and `data-ly-density="spacious"`. A nested value intentionally resets inherited gap and section tokens. + +## Gap Locality + +`.ly-gap-0` through `.ly-gap-9` now set `gap` only on the element carrying the class. Descendant Stacks, Clusters, Grids, and recipes keep their defaults. Move shared spacing intent to the public inherited variables: + +```html + +
+
Primary
Secondary
+
+``` + +Use a local utility when only one composition changes, for example `class="ly-grid ly-gap-9"`. + +## Scroll Constraints + +Base `.ly-scroll` now owns overflow without choosing a maximum height. Add the modifier that matches the container contract: + +```html + +
Viewport-relative activity log
+ + +
+ Application-controlled activity log +
+``` + +`ly-scroll--viewport` consumes `--ly-scroll-viewport-max` and adapts at the `44rem` and `30rem` viewport-height tiers. `ly-scroll--bounded` consumes `--ly-scroll-max` and remains stable unless the application overrides it. + +## Structured Threshold Metadata + +The generic threshold arrays remain available. `manifest.json` now also publishes recipe ownership under `thresholds.recipes`, so tooling can distinguish the medium and wide tiers for App Shell and Dashboard from each recipe's single wide tier. Release validation requires every public geometry token to be consumed by runtime CSS. + +## Compatibility + +All 13 package exports and the `42rem`, `44rem`, `48rem`, `52rem`, and `72rem` recipe thresholds remain unchanged. Existing markup without a density attribute uses normal density. Legacy demo links containing `?density=comfortable` normalize to `normal`. diff --git a/docs/wiki/Release-And-Publishing.md b/docs/wiki/Release-And-Publishing.md index a8a2521..bd70919 100644 --- a/docs/wiki/Release-And-Publishing.md +++ b/docs/wiki/Release-And-Publishing.md @@ -1,6 +1,6 @@ # Release And Publishing -This guide describes the release gate for `layout-style-css@3.0.2`. Running verification does not publish, tag, push, create a GitHub Release, or change the npm registry. +This guide describes the release gate for `layout-style-css@3.1.0`. Running verification does not publish, tag, push, create a GitHub Release, or change the npm registry. ## Local Candidate Gate @@ -23,7 +23,7 @@ The package's `prepublishOnly` script runs `npm run release:verify`, so a direct The immutable cross-repository pins require this exact remote sequence: -1. Push a stable UI bootstrap ref containing `33e3c834aec62b85650dd14aea9d7d388abef191`. +1. Push a stable UI bootstrap ref containing `a44dd41bf3611aba0a20bddfd00eb37938ca884f`. 2. Push and merge Interactive Surface CSS and Layout Style CSS with merge commits so their reviewed commit SHAs remain reachable. 3. Update and verify the final UI companion pins against those merged companion commits. 4. Push the final UI branch, rerun its ecosystem preflight, and merge UI with a merge commit. @@ -35,8 +35,8 @@ The bootstrap SHA is deliberately stable: companion workflows use it to load the Confirm: -- package version is `3.0.2` -- intended tag would be `v3.0.2` +- package version is `3.1.0` +- intended tag would be `v3.1.0` - runtime and peer dependencies are empty - exact exports and tarball files match the v3 contract - generated CSS matches authored sources @@ -50,7 +50,7 @@ Only after explicit approval: 1. Commit the verified candidate. 2. Push the intended branch. 3. Merge through the repository's normal review policy. -4. Create the exact `v3.0.2` tag from the protected release commit. +4. Create the exact `v3.1.0` tag from the protected release commit. 5. Publish the GitHub Release. 6. Let the protected npm workflow verify the tag and publish with provenance. 7. Confirm the GitHub Release, tag, and npm registry state independently. diff --git a/docs/wiki/_Sidebar.md b/docs/wiki/_Sidebar.md index 7af6a33..07815df 100644 --- a/docs/wiki/_Sidebar.md +++ b/docs/wiki/_Sidebar.md @@ -6,6 +6,7 @@ - [Layout Styles](Layout-Styles) - [Companion Compatibility](UI-Style-Kit-Compatibility) - [Migrating To 3.0](Migrating-To-3.0) +- [Migrating To 3.1](Migrating-To-3.1) - [Demo And GitHub Pages](Demo-And-GitHub-Pages) - [Release And Publishing](Release-And-Publishing) - [Security And Support](Security-And-Support) diff --git a/ecosystem-release-fixture.json b/ecosystem-release-fixture.json index da5aba3..b58b691 100644 --- a/ecosystem-release-fixture.json +++ b/ecosystem-release-fixture.json @@ -1,4 +1,4 @@ { "repository": "Foscat/ui-style-kit-css", - "revision": "33e3c834aec62b85650dd14aea9d7d388abef191" + "revision": "a44dd41bf3611aba0a20bddfd00eb37938ca884f" } diff --git a/manifest.json b/manifest.json index b8a0dd6..02549b2 100644 --- a/manifest.json +++ b/manifest.json @@ -5,7 +5,7 @@ "breakingChange": "increment-schemaVersion-before-removing-or-renaming-fields" }, "name": "layout-style-css", - "version": "3.0.2", + "version": "3.1.0", "entrypoints": { "default": "./dist/layout-style-css.css", "minified": "./dist/layout-style-css.min.css", diff --git a/package-lock.json b/package-lock.json index 783746e..9bb0887 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "layout-style-css", - "version": "3.0.2", + "version": "3.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "layout-style-css", - "version": "3.0.2", + "version": "3.1.0", "license": "MIT", "devDependencies": { "@playwright/test": "1.61.1", diff --git a/package.json b/package.json index 0c6ee2f..cb8b217 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "layout-style-css", - "version": "3.0.2", + "version": "3.1.0", "description": "Dependency-free structural CSS for responsive wrappers, composition primitives, recipes, utilities, and layout personalities.", "type": "module", "license": "MIT", diff --git a/scripts/release-fixture-contract.mjs b/scripts/release-fixture-contract.mjs index 12464cb..024df43 100644 --- a/scripts/release-fixture-contract.mjs +++ b/scripts/release-fixture-contract.mjs @@ -125,6 +125,24 @@ export function validateRepositoryWorkflows(repositoryRoot) { validateWorkflowSources(workflows); } +/** + * Resolves the reviewed Interactive Surface checkout used by local ecosystem + * verification while retaining the sibling-repository default used in CI. + * + * @param {string} repositoryRoot Layout Style repository root. + * @param {NodeJS.ProcessEnv | Record} [environment] Environment values. + * @returns {string} Absolute Interactive Surface repository path. + */ +export function resolveInteractiveRoot( + repositoryRoot, + environment = process.env, +) { + return path.resolve( + environment.CSS_ECOSYSTEM_INTERACTIVE_ROOT ?? + path.join(repositoryRoot, "..", "Interactive-Surface-CSS"), + ); +} + async function runCli(args) { const descriptor = readFixtureDescriptor(rootDir); if (args.includes("--write-github-outputs")) { @@ -152,11 +170,7 @@ async function runCli(args) { const packageName = JSON.parse( fs.readFileSync(path.join(rootDir, "package.json"), "utf8"), ).name; - const siblingInteractive = path.resolve( - rootDir, - "..", - "Interactive-Surface-CSS", - ); + const siblingInteractive = resolveInteractiveRoot(rootDir); const commandArgs = [ preflightModule, "--fixture-root", diff --git a/test/demo-smoke.test.mjs b/test/demo-smoke.test.mjs index 79aa29e..5be6219 100644 --- a/test/demo-smoke.test.mjs +++ b/test/demo-smoke.test.mjs @@ -139,7 +139,7 @@ const topologyEdges = [ const assertStaticDemoContract = () => { assert.match(demoHtml, /Layout Style CSS v3/); - assert.match(demoHtml, /content="3\.0\.2"/); + assert.match(demoHtml, /content="3\.1\.0"/); assert.match(demoHtml, /id="deviceSelect"/); assert.match(demoHtml, /id="containerSelect"/); assert.match(demoHtml, /id="heightSelect"/); @@ -147,17 +147,17 @@ const assertStaticDemoContract = () => { assert.match(demoHtml, /id="topologyReadout"/); assert.match( demoHtml, - /href="\.\.\/dist\/layout-style-css\.css\?v=3\.0\.2"/, + /href="\.\.\/dist\/layout-style-css\.css\?v=3\.1\.0"/, "The demo should cache-bust its v3 layout bundle." ); assert.match( demoHtml, - /href="\.\/demo\.css\?v=3\.0\.2"/, + /href="\.\/demo\.css\?v=3\.1\.0"/, "The demo should cache-bust its v3 presentation styles." ); assert.match( demoHtml, - /src="\.\/demo\.js\?v=3\.0\.2"/, + /src="\.\/demo\.js\?v=3\.1\.0"/, "The demo should cache-bust its v3 controller." ); assert.doesNotMatch(demoHtml, /integrations\/ui-style-kit\.css/); @@ -469,7 +469,7 @@ const verifyIdentityAndControls = async (page, baseUrl) => { }; const verifyPersonalityOptionsUsePairingMetadata = async (page, baseUrl) => { - const metadataUrl = new URL("../personalities.json?v=3.0.2", baseUrl).toString(); + const metadataUrl = new URL("../personalities.json?v=3.1.0", baseUrl).toString(); const pairingFixture = { schemaVersion: 1, personalities: [ @@ -569,7 +569,7 @@ const verifySynthwaveVisualRecommendations = async (page, baseUrl) => { * @returns {Promise} */ const verifyPersonalityMetadataFailureRecovery = async (page, baseUrl) => { - const metadataUrl = new URL("../personalities.json?v=3.0.2", baseUrl).toString(); + const metadataUrl = new URL("../personalities.json?v=3.1.0", baseUrl).toString(); const recoveryContext = await page.context().browser().newContext(); try { const recoveryPage = await recoveryContext.newPage(); diff --git a/test/layout-css-contract.test.mjs b/test/layout-css-contract.test.mjs index cdad7b6..01c6553 100644 --- a/test/layout-css-contract.test.mjs +++ b/test/layout-css-contract.test.mjs @@ -106,7 +106,7 @@ function minifyCss(css) { .trim(); } -assert.equal(packageJson.version, "3.0.2", "The patch branch must expose version 3.0.2"); +assert.equal(packageJson.version, "3.1.0", "The minor branch must expose version 3.1.0"); assert.equal(packageJson.engines?.node, ">=20", "Development must retain the Node 20 floor"); assert.deepEqual(packageJson.exports, expectedExports, "Package exports must match the clean v3 API"); assert.deepEqual( @@ -143,6 +143,7 @@ const expectedTarballFiles = [ "docs/wiki/Layout-Styles.md", "docs/wiki/Migrating-To-2.0.md", "docs/wiki/Migrating-To-3.0.md", + "docs/wiki/Migrating-To-3.1.md", "docs/wiki/Release-And-Publishing.md", "docs/wiki/Security-And-Support.md", "docs/wiki/UI-Style-Kit-Compatibility.md", diff --git a/test/manifest-contract.test.mjs b/test/manifest-contract.test.mjs index c53e039..8976a5c 100644 --- a/test/manifest-contract.test.mjs +++ b/test/manifest-contract.test.mjs @@ -135,7 +135,7 @@ function runtimeConsumedCustomProperties(css) { test("ecosystem manifest publishes the structural API and package export", () => { assert.equal(manifest.schemaVersion, 1); assert.equal(manifest.name, packageJson.name); - assert.equal(manifest.version, "3.0.2"); + assert.equal(manifest.version, "3.1.0"); assert.equal(manifest.version, packageJson.version); assert.equal(manifest.schemaPolicy.compatibility, "additive-within-major"); assert.equal( diff --git a/test/package-contract.test.mjs b/test/package-contract.test.mjs index 834eda6..b754535 100644 --- a/test/package-contract.test.mjs +++ b/test/package-contract.test.mjs @@ -59,6 +59,7 @@ const requiredPackagedDocuments = [ "docs/wiki/Layout-Styles.md", "docs/wiki/Migrating-To-2.0.md", "docs/wiki/Migrating-To-3.0.md", + "docs/wiki/Migrating-To-3.1.md", "docs/wiki/Release-And-Publishing.md", "docs/wiki/Security-And-Support.md", "docs/wiki/UI-Style-Kit-Compatibility.md", @@ -129,11 +130,11 @@ test("package defaults and homepage expose the intended distribution contract", } }); -test("release metadata stays synchronized for the 3.0.2 patch", () => { - assert.equal(manifest.version, "3.0.2"); - assert.equal(packageLock.version, "3.0.2"); - assert.equal(packageLock.packages[""].version, "3.0.2"); - assert.equal(ecosystemManifest.version, "3.0.2"); +test("release metadata stays synchronized for the 3.1.0 minor", () => { + assert.equal(manifest.version, "3.1.0"); + assert.equal(packageLock.version, "3.1.0"); + assert.equal(packageLock.packages[""].version, "3.1.0"); + assert.equal(ecosystemManifest.version, "3.1.0"); assert.equal(Object.keys(manifest.exports).length, 13); }); diff --git a/test/pages-artifact.test.mjs b/test/pages-artifact.test.mjs index 67c1ca0..ea9f277 100644 --- a/test/pages-artifact.test.mjs +++ b/test/pages-artifact.test.mjs @@ -81,7 +81,7 @@ for (const { attribute, path } of [ const contents = readFileSync(join(outputDir, ...path.split("/"))); const fingerprint = createHash("sha256").update(contents).digest("hex").slice(0, 12); assert( - index.includes(`${attribute}="./${path}?v=3.0.2-${fingerprint}"`), + index.includes(`${attribute}="./${path}?v=3.1.0-${fingerprint}"`), `Pages should fingerprint ${path} from its deployed contents.` ); } @@ -101,7 +101,7 @@ assert( "Pages demo should preserve the source demo canonical URL" ); assert( - index.includes('"version": "3.0.2"') && index.includes("Layout Style CSS v3"), + index.includes('"version": "3.1.0"') && index.includes("Layout Style CSS v3"), "Pages metadata should identify the v3 intrinsic responsive lab" ); assert( @@ -124,7 +124,7 @@ assert( sitemap.includes(`${expectedPagesBaseUrl}`), "The sitemap must use the case-sensitive Pages canonical URL." ); -assert(sitemap.includes("2026-08-25"), "Pages sitemap should carry 3.0.2 metadata"); +assert(sitemap.includes("2026-08-25"), "Pages sitemap should carry 3.1.0 metadata"); const pagesWorkflow = readFileSync(pagesWorkflowPath, "utf8"); const pagesPreflightStep = pagesWorkflow.indexOf("- name: Verify Pages configuration"); diff --git a/test/release-docs-contract.test.mjs b/test/release-docs-contract.test.mjs index dcfe471..d5ebc23 100644 --- a/test/release-docs-contract.test.mjs +++ b/test/release-docs-contract.test.mjs @@ -8,14 +8,17 @@ const normalizeLineEndings = (value) => value.replace(/\r\n?/g, "\n"); const read = (...parts) => normalizeLineEndings(readFileSync(join(root, ...parts), "utf8")); const packageJson = JSON.parse(read("package.json")); const migrationPath = join(root, "docs", "wiki", "Migrating-To-3.0.md"); +const minorMigrationPath = join(root, "docs", "wiki", "Migrating-To-3.1.md"); const v2SurfacePath = join(root, "test", "fixtures", "v2-public-selectors.json"); assert(existsSync(migrationPath), "The v3 package must ship a complete v2-to-v3 migration guide."); +assert(existsSync(minorMigrationPath), "The v3.1 package must ship its focused migration guide."); assert(existsSync(v2SurfacePath), "Migration coverage requires a checked-in v2 public selector fixture."); const readme = read("README.md"); const changelog = read("CHANGELOG.md"); const migration = read("docs", "wiki", "Migrating-To-3.0.md"); +const minorMigration = read("docs", "wiki", "Migrating-To-3.1.md"); const installation = read("docs", "wiki", "Installation-And-CDN.md"); const compatibility = read("docs", "wiki", "UI-Style-Kit-Compatibility.md"); const primitivesGuide = read("docs", "wiki", "Layout-Primitives.md"); @@ -50,6 +53,7 @@ for (const selector of removedV2Selectors) { const docsCorpus = [ readme, migration, + minorMigration, installation, compatibility, primitivesGuide, @@ -68,6 +72,7 @@ const currentGuidanceCorpus = [ "Layout-Primitives.md", "Layout-Recipes.md", "Layout-Styles.md", + "Migrating-To-3.1.md", "Release-And-Publishing.md", "Security-And-Support.md", "UI-Style-Kit-Compatibility.md" @@ -85,7 +90,7 @@ assert( ); for (const requiredText of [ - "3.0.2", + "3.1.0", "Node.js 20", "dependency-free", "zero-configuration", @@ -109,6 +114,22 @@ for (const requiredText of [ assert(docsCorpus.includes(requiredText), `V3 documentation must explain ${requiredText}.`); } +for (const minorContract of [ + 'data-ly-density="compact"', + 'data-ly-density="normal"', + 'data-ly-density="spacious"', + "ly-wrapper--workspace", + "ly-gap-9", + "ly-scroll--bounded", + "ly-scroll--viewport", + "thresholds.recipes" +]) { + assert( + currentGuidanceCorpus.includes(minorContract), + `Current 3.1 documentation must explain ${minorContract}.` + ); +} + for (const exportPath of [ "layout-style-css", "layout-style-css/min.css", @@ -169,7 +190,7 @@ for (const recipe of [ ); } -for (const wrapper of ["compact", "prose", "content", "wide", "full", "breakout"]) { +for (const wrapper of ["compact", "prose", "content", "workspace", "wide", "full", "breakout"]) { assert(docsCorpus.includes(`ly-wrapper--${wrapper}`), `Documentation must cover ${wrapper}.`); } for (const lane of ["content", "feature", "full"]) { @@ -267,9 +288,11 @@ assert.equal( 1, "Changelog needs one dated 3.0.2 section." ); +const minorReleaseHeadings = changelog.match(/^## \[3\.1\.0\] - 2026-08-25$/gm) ?? []; +assert.equal(minorReleaseHeadings.length, 1, "Changelog needs one dated 3.1.0 section."); assert( - release.includes("layout-style-css@3.0.2") && - release.includes("v3.0.2") && + release.includes("layout-style-css@3.1.0") && + release.includes("v3.1.0") && release.includes("does not publish"), "Release documentation must distinguish verification from publication." ); @@ -277,9 +300,9 @@ assert.deepEqual( releaseFixture, { repository: "Foscat/ui-style-kit-css", - revision: "33e3c834aec62b85650dd14aea9d7d388abef191" + revision: "a44dd41bf3611aba0a20bddfd00eb37938ca884f" }, - "Release automation must pin the reviewed UI Layout 3.0.2 bootstrap commit." + "Release automation must pin the reviewed UI Layout 3.1.0 bootstrap commit." ); assert( release.includes(releaseFixture.revision), @@ -287,6 +310,7 @@ assert( ); assert(support.includes("`3.x` | Yes"), "Support table must identify the supported v3 line."); assert(sidebar.includes("Migrating To 3.0"), "Wiki navigation must link the v3 migration guide."); +assert(sidebar.includes("Migrating To 3.1"), "Wiki navigation must link the v3.1 migration guide."); // The manifest contract rebuilds dist, so built-artifact ownership tests must not overlap it. assert( @@ -327,7 +351,7 @@ for (const browser of ["chromium", "firefox", "webkit"]) { const publishWorkflow = read(".github", "workflows", "npm-publish.yml"); const demoSmoke = read("test", "demo-smoke.test.mjs"); -assert(publishWorkflow.includes("for example v3.0.2")); +assert(publishWorkflow.includes("for example v3.1.0")); assert(publishWorkflow.includes("playwright install --with-deps chromium firefox webkit")); assert( !/^\s*run:\s+npm\s+run\s+release:verify\s*$/m.test(publishWorkflow) && diff --git a/test/release-preflight.test.mjs b/test/release-preflight.test.mjs index 0849242..d160a16 100644 --- a/test/release-preflight.test.mjs +++ b/test/release-preflight.test.mjs @@ -11,7 +11,7 @@ const rootDir = path.resolve( "..", ); const reviewedUiFixtureRevision = - "33e3c834aec62b85650dd14aea9d7d388abef191"; + "a44dd41bf3611aba0a20bddfd00eb37938ca884f"; let releaseContract; try { releaseContract = await import("../scripts/release-fixture-contract.mjs"); @@ -46,6 +46,19 @@ test("pins an immutable reviewed UI release fixture and writes exact checkout ou } }); +test("local release verification accepts an explicit reviewed Interactive fixture", () => { + assert.equal( + releaseContract.resolveInteractiveRoot("C:/workspace/Layout-Style-CSS", { + CSS_ECOSYSTEM_INTERACTIVE_ROOT: "C:/fixtures/Interactive-Surface-CSS" + }), + path.resolve("C:/fixtures/Interactive-Surface-CSS") + ); + assert.equal( + releaseContract.resolveInteractiveRoot("C:/workspace/Layout-Style-CSS", {}), + path.resolve("C:/workspace/Interactive-Surface-CSS") + ); +}); + test("pull requests execute read-only preflight and npm publish stays downstream", () => { assert.ok( releaseContract,