diff --git a/core/layout.css b/core/layout.css index 4e9c2b7d..be8879a8 100644 --- a/core/layout.css +++ b/core/layout.css @@ -103,6 +103,22 @@ .sf-stack--end { align-items: flex-end; } .sf-stack--stretch { align-items: stretch; } + /* -- Gap ---------------------------------------------------- + Injects gap into any existing flex or grid container without + imposing a display type or axis. Unlike .sf-stack (flex + column) or .sf-cluster (flex-wrap row), .sf-gap is layout- + agnostic — use it on elements that already have their own + display context. + Override: style="--sf-gap-size: 2rem" + ---------------------------------------------------------- */ + .sf-gap { gap: var(--sf-gap-size); } + .sf-gap--xs { gap: var(--sf-space-xs); } + .sf-gap--s { gap: var(--sf-space-s); } + .sf-gap--m { gap: var(--sf-space-m); } + .sf-gap--l { gap: var(--sf-space-l); } + .sf-gap--xl { gap: var(--sf-space-xl); } + .sf-gap--2xl { gap: var(--sf-space-2xl); } + /* -- Box ----------------------------------------------------- Isolated unit with padding and an optional border. Outline instead of border — doesn't disturb the box model when toggled. @@ -446,6 +462,23 @@ .sf-grid-3-1 { grid-template-columns: 3fr 1fr; } } + /* -- Equal columns ------------------------------------------ + Fixed N-column grid. Column count NEVER collapses — unlike + .sf-grid-N which uses container queries to stack at small + sizes. Use when the layout must remain N columns at all + widths; the caller is responsible for overflow handling. + Override: style="--sf-equal-cols: 3" + ---------------------------------------------------------- */ + .sf-equal { + display: grid; + grid-template-columns: repeat(var(--sf-equal-cols), 1fr); + gap: var(--sf-equal-gap); + } + .sf-equal--2 { --sf-equal-cols: 2; } + .sf-equal--3 { --sf-equal-cols: 3; } + .sf-equal--4 { --sf-equal-cols: 4; } + .sf-equal--6 { --sf-equal-cols: 6; } + /* -- Content grid (breakout pattern) ------------------------- Full-width grid that lets items break out to breakout or full width. Children default to the content column. diff --git a/core/tokens.layout.css b/core/tokens.layout.css index dc7ce3f0..4912d782 100644 --- a/core/tokens.layout.css +++ b/core/tokens.layout.css @@ -36,6 +36,11 @@ ---------------------------------------------------------- */ --sf-stack-gap: var(--sf-space-content); + /* ---------------------------------------------------------- + Gap + ---------------------------------------------------------- */ + --sf-gap-size: var(--sf-space-gap); + /* ---------------------------------------------------------- Box ---------------------------------------------------------- */ @@ -82,6 +87,12 @@ --sf-grid-min-xl: 24rem; --sf-grid-min-2xl: 28rem; + /* ---------------------------------------------------------- + Equal columns (fixed, non-responsive) + ---------------------------------------------------------- */ + --sf-equal-cols: 2; + --sf-equal-gap: var(--sf-space-gap); + /* ---------------------------------------------------------- Cover ---------------------------------------------------------- */ diff --git a/dist/slashed.essential.css b/dist/slashed.essential.css index 3f29d4ca..50a335b2 100644 --- a/dist/slashed.essential.css +++ b/dist/slashed.essential.css @@ -1056,6 +1056,11 @@ ---------------------------------------------------------- */ --sf-stack-gap: var(--sf-space-content); + /* ---------------------------------------------------------- + Gap + ---------------------------------------------------------- */ + --sf-gap-size: var(--sf-space-gap); + /* ---------------------------------------------------------- Box ---------------------------------------------------------- */ @@ -1102,6 +1107,12 @@ --sf-grid-min-xl: 24rem; --sf-grid-min-2xl: 28rem; + /* ---------------------------------------------------------- + Equal columns (fixed, non-responsive) + ---------------------------------------------------------- */ + --sf-equal-cols: 2; + --sf-equal-gap: var(--sf-space-gap); + /* ---------------------------------------------------------- Cover ---------------------------------------------------------- */ @@ -1770,6 +1781,22 @@ .sf-stack--end { align-items: flex-end; } .sf-stack--stretch { align-items: stretch; } + /* -- Gap ---------------------------------------------------- + Injects gap into any existing flex or grid container without + imposing a display type or axis. Unlike .sf-stack (flex + column) or .sf-cluster (flex-wrap row), .sf-gap is layout- + agnostic — use it on elements that already have their own + display context. + Override: style="--sf-gap-size: 2rem" + ---------------------------------------------------------- */ + .sf-gap { gap: var(--sf-gap-size); } + .sf-gap--xs { gap: var(--sf-space-xs); } + .sf-gap--s { gap: var(--sf-space-s); } + .sf-gap--m { gap: var(--sf-space-m); } + .sf-gap--l { gap: var(--sf-space-l); } + .sf-gap--xl { gap: var(--sf-space-xl); } + .sf-gap--2xl { gap: var(--sf-space-2xl); } + /* -- Box ----------------------------------------------------- Isolated unit with padding and an optional border. Outline instead of border — doesn't disturb the box model when toggled. @@ -2113,6 +2140,23 @@ .sf-grid-3-1 { grid-template-columns: 3fr 1fr; } } + /* -- Equal columns ------------------------------------------ + Fixed N-column grid. Column count NEVER collapses — unlike + .sf-grid-N which uses container queries to stack at small + sizes. Use when the layout must remain N columns at all + widths; the caller is responsible for overflow handling. + Override: style="--sf-equal-cols: 3" + ---------------------------------------------------------- */ + .sf-equal { + display: grid; + grid-template-columns: repeat(var(--sf-equal-cols), 1fr); + gap: var(--sf-equal-gap); + } + .sf-equal--2 { --sf-equal-cols: 2; } + .sf-equal--3 { --sf-equal-cols: 3; } + .sf-equal--4 { --sf-equal-cols: 4; } + .sf-equal--6 { --sf-equal-cols: 6; } + /* -- Content grid (breakout pattern) ------------------------- Full-width grid that lets items break out to breakout or full width. Children default to the content column. diff --git a/dist/slashed.essential.flat.css b/dist/slashed.essential.flat.css index 54614b31..29c29c27 100644 --- a/dist/slashed.essential.flat.css +++ b/dist/slashed.essential.flat.css @@ -1035,6 +1035,11 @@ ---------------------------------------------------------- */ --sf-stack-gap: var(--sf-space-content); + /* ---------------------------------------------------------- + Gap + ---------------------------------------------------------- */ + --sf-gap-size: var(--sf-space-gap); + /* ---------------------------------------------------------- Box ---------------------------------------------------------- */ @@ -1081,6 +1086,12 @@ --sf-grid-min-xl: 24rem; --sf-grid-min-2xl: 28rem; + /* ---------------------------------------------------------- + Equal columns (fixed, non-responsive) + ---------------------------------------------------------- */ + --sf-equal-cols: 2; + --sf-equal-gap: var(--sf-space-gap); + /* ---------------------------------------------------------- Cover ---------------------------------------------------------- */ @@ -1736,6 +1747,22 @@ td, th { .sf-stack--end { align-items: flex-end; } .sf-stack--stretch { align-items: stretch; } +/* -- Gap ---------------------------------------------------- + Injects gap into any existing flex or grid container without + imposing a display type or axis. Unlike .sf-stack (flex + column) or .sf-cluster (flex-wrap row), .sf-gap is layout- + agnostic — use it on elements that already have their own + display context. + Override: style="--sf-gap-size: 2rem" + ---------------------------------------------------------- */ +.sf-gap { gap: var(--sf-gap-size); } +.sf-gap--xs { gap: var(--sf-space-xs); } +.sf-gap--s { gap: var(--sf-space-s); } +.sf-gap--m { gap: var(--sf-space-m); } +.sf-gap--l { gap: var(--sf-space-l); } +.sf-gap--xl { gap: var(--sf-space-xl); } +.sf-gap--2xl { gap: var(--sf-space-2xl); } + /* -- Box ----------------------------------------------------- Isolated unit with padding and an optional border. Outline instead of border — doesn't disturb the box model when toggled. @@ -2079,6 +2106,23 @@ td, th { .sf-grid-3-1 { grid-template-columns: 3fr 1fr; } } +/* -- Equal columns ------------------------------------------ + Fixed N-column grid. Column count NEVER collapses — unlike + .sf-grid-N which uses container queries to stack at small + sizes. Use when the layout must remain N columns at all + widths; the caller is responsible for overflow handling. + Override: style="--sf-equal-cols: 3" + ---------------------------------------------------------- */ +.sf-equal { + display: grid; + grid-template-columns: repeat(var(--sf-equal-cols), 1fr); + gap: var(--sf-equal-gap); +} +.sf-equal--2 { --sf-equal-cols: 2; } +.sf-equal--3 { --sf-equal-cols: 3; } +.sf-equal--4 { --sf-equal-cols: 4; } +.sf-equal--6 { --sf-equal-cols: 6; } + /* -- Content grid (breakout pattern) ------------------------- Full-width grid that lets items break out to breakout or full width. Children default to the content column. diff --git a/dist/slashed.essential.flat.min.css b/dist/slashed.essential.flat.min.css index 5df1ce8d..b9b84821 100644 --- a/dist/slashed.essential.flat.min.css +++ b/dist/slashed.essential.flat.min.css @@ -1,2 +1,2 @@ -@property --sf-color-primary-light{syntax:"";inherits:true;initial-value:oklch(45% .2 264)}@property --sf-color-secondary-light{syntax:"";inherits:true;initial-value:oklch(25% .03 260)}@property --sf-color-tertiary-light{syntax:"";inherits:true;initial-value:oklch(48% .14 310)}@property --sf-color-action-light{syntax:"";inherits:true;initial-value:oklch(60% .16 210)}@property --sf-color-neutral-light{syntax:"";inherits:true;initial-value:oklch(45% .02 260)}@property --sf-color-base-light{syntax:"";inherits:true;initial-value:oklch(98% .005 260)}@property --sf-color-success-light{syntax:"";inherits:true;initial-value:oklch(48% .17 150)}@property --sf-color-warning-light{syntax:"";inherits:true;initial-value:oklch(75% .17 80)}@property --sf-color-error-light{syntax:"";inherits:true;initial-value:oklch(62% .2 35)}@property --sf-color-info-light{syntax:"";inherits:true;initial-value:oklch(48% .15 240)}@property --sf-color-danger-light{syntax:"";inherits:true;initial-value:oklch(48% .24 12)}@property --sf-is-dark{syntax:"";inherits:true;initial-value:0}@property --sf-is-active{syntax:"";inherits:true;initial-value:0}@property --sf-is-current{syntax:"";inherits:true;initial-value:0}@property --sf-is-pressed{syntax:"";inherits:true;initial-value:0}@property --sf-is-open{syntax:"";inherits:true;initial-value:0}:root{--sf-color-scheme:light dark;--sf-lumlocker:.65;--sf-color-primary:light-dark(var(--sf-color-primary-light),var(--sf-color-primary-dark,oklch(from var(--sf-color-primary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-secondary:light-dark(var(--sf-color-secondary-light),var(--sf-color-secondary-dark,oklch(from var(--sf-color-secondary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-tertiary:light-dark(var(--sf-color-tertiary-light),var(--sf-color-tertiary-dark,oklch(from var(--sf-color-tertiary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-action:light-dark(var(--sf-color-action-light),var(--sf-color-action-dark,oklch(from var(--sf-color-action-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-neutral:light-dark(var(--sf-color-neutral-light),var(--sf-color-neutral-dark,oklch(from var(--sf-color-neutral-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-base:light-dark(var(--sf-color-base-light),var(--sf-color-base-dark,oklch(from var(--sf-color-base-light) clamp(.16, calc(1.18 - l), .24) calc(c * .5) h)));--sf-color-success:light-dark(var(--sf-color-success-light),var(--sf-color-success-dark,oklch(from var(--sf-color-success-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-warning:light-dark(var(--sf-color-warning-light),var(--sf-color-warning-dark,oklch(from var(--sf-color-warning-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-error:light-dark(var(--sf-color-error-light),var(--sf-color-error-dark,oklch(from var(--sf-color-error-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-info:light-dark(var(--sf-color-info-light),var(--sf-color-info-dark,oklch(from var(--sf-color-info-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-danger:light-dark(var(--sf-color-danger-light),var(--sf-color-danger-dark,oklch(from var(--sf-color-danger-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-bg:oklch(from var(--sf-color-base) calc(l + .02) c h);--sf-color-surface:var(--sf-color-base);--sf-color-well:oklch(from var(--sf-color-base) calc(l - .02) c h);--sf-color-raised:oklch(from var(--sf-color-base) calc(l + .04) c h);--sf-color-overlay:oklch(from var(--sf-color-base) l c h / .9);--sf-color-inverse:oklch(from var(--sf-color-base) calc(1 - l) c h);--sf-color-text:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.05, calc(l - .4 - var(--sf-contrast-bias)), .35) c h),oklch(from var(--sf-color-neutral) clamp(.7, calc(l + .25 + var(--sf-contrast-bias)), 1) c h));--sf-color-text--secondary:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.15, calc(l - .25 - var(--sf-contrast-bias)), .45) c h),oklch(from var(--sf-color-neutral) clamp(.55, calc(l + .1 + var(--sf-contrast-bias)), .9) c h));--sf-color-text--muted:var(--sf-color-neutral);--sf-color-text--placeholder:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.45, calc(l + .15), .75) c h),oklch(from var(--sf-color-neutral) clamp(.35, calc(l - .1), .65) c h));--sf-color-text--disabled:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.55, calc(l + .25), .82) c h),oklch(from var(--sf-color-neutral) clamp(.25, calc(l - .2), .55) c h));--sf-color-text--inverse:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.85, calc(l + .4), .98) c h),oklch(from var(--sf-color-neutral) clamp(.05, calc(l - .4), .35) c h));--sf-color-heading:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.05, calc(l - .4 - var(--sf-contrast-bias)), .35) c h),oklch(from var(--sf-color-neutral) clamp(.7, calc(l + .25 + var(--sf-contrast-bias)), 1) c h));--sf-color-code-text:oklch(from var(--sf-color-code-bg) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-primary:oklch(from var(--sf-color-primary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-secondary:oklch(from var(--sf-color-secondary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-tertiary:oklch(from var(--sf-color-tertiary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-action:oklch(from var(--sf-color-action) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-neutral:oklch(from var(--sf-color-neutral) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-base:var(--sf-color-text);--sf-color-text--on-inverse:var(--sf-color-text--inverse);--sf-color-text--on-success:oklch(from var(--sf-color-success) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-warning:oklch(from var(--sf-color-warning) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-error:oklch(from var(--sf-color-error) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-info:oklch(from var(--sf-color-info) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-danger:oklch(from var(--sf-color-danger) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-border:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.7, calc(l + .35), .95) .005 h),oklch(from var(--sf-color-neutral) clamp(.25, calc(l - .3), .55) .005 h));--sf-color-border--subtle:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.75, calc(l + .4), .97) .005 h),oklch(from var(--sf-color-neutral) clamp(.2, calc(l - .38), .45) .005 h));--sf-color-border--strong:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.55, calc(l + .1), .85) .02 h),oklch(from var(--sf-color-neutral) clamp(.38, calc(l - .1), .65) .02 h));--sf-color-border--focus:var(--sf-color-action);--sf-color-border--disabled:oklch(from var(--sf-color-border--subtle) l 0 h / .5);--sf-color-border--translucent:oklch(from var(--sf-color-neutral) l c h / .15);--sf-color-link:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .07, .48), 1) c h),oklch(from var(--sf-color-action) clamp(.68, l, 1) c h));--sf-color-link--hover:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .15, .4), 1) c h),oklch(from var(--sf-color-action) clamp(0, max(l + .1, .68), 1) c h));--sf-color-link--active:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .21, .34), 1) c h),oklch(from var(--sf-color-action) clamp(0, max(l + .15, .74), 1) c h));--sf-color-link--visited:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .07, .48), 1) c calc(h + 60)),oklch(from var(--sf-color-action) clamp(.68, l, 1) c calc(h + 60)));--sf-color-link--underline:oklch(from var(--sf-color-action) l c h / .3);--sf-color-link--disabled:var(--sf-color-text--disabled);--sf-color-bg--hover:oklch(from var(--sf-color-neutral) l c h / .08);--sf-color-bg--active:oklch(from var(--sf-color-neutral) l c h / .12);--sf-color-bg--selected:oklch(from var(--sf-color-action) l c h / .1);--sf-color-bg--focus:oklch(from var(--sf-color-action) l c h / .06);--sf-color-bg--disabled:var(--sf-color-well);--sf-color-code-bg:var(--sf-color-well);--sf-color-selection-bg:oklch(from var(--sf-color-action) l c h / .22);--sf-color-selection-text:inherit;--sf-color-mark-bg:oklch(from var(--sf-color-warning) l c h / .25);--sf-color-mark-text:inherit;--sf-color-dim:oklch(0% 0 0/.5);--sf-scrollbar-thumb:var(--sf-color-neutral);--sf-scrollbar-track:transparent;--sf-color-success-subtle:oklch(from var(--sf-color-success) l c h / .12);--sf-color-success-strong:light-dark(oklch(from var(--sf-color-success-light) calc(l - .15) c h),oklch(from var(--sf-color-success) clamp(.7, calc(l + .15), 1) c h));--sf-color-success-muted:oklch(from var(--sf-color-success) l c h / .3);--sf-color-warning-subtle:oklch(from var(--sf-color-warning) l c h / .12);--sf-color-warning-strong:light-dark(oklch(from var(--sf-color-warning-light) calc(l - .25) c h),oklch(from var(--sf-color-warning) clamp(.7, calc(l + .05), 1) c h));--sf-color-warning-muted:oklch(from var(--sf-color-warning) l c h / .3);--sf-color-error-subtle:oklch(from var(--sf-color-error) l c h / .1);--sf-color-error-strong:light-dark(oklch(from var(--sf-color-error-light) calc(l - .1) c h),oklch(from var(--sf-color-error) clamp(.7, calc(l + .15), 1) c h));--sf-color-error-muted:oklch(from var(--sf-color-error) l c h / .3);--sf-color-info-subtle:oklch(from var(--sf-color-info) l c h / .1);--sf-color-info-strong:light-dark(oklch(from var(--sf-color-info-light) calc(l - .1) c h),oklch(from var(--sf-color-info) clamp(.7, calc(l + .15), 1) c h));--sf-color-info-muted:oklch(from var(--sf-color-info) l c h / .3);--sf-color-danger-subtle:oklch(from var(--sf-color-danger) l c h / .1);--sf-color-danger-strong:light-dark(oklch(from var(--sf-color-danger-light) calc(l - .1) c h),oklch(from var(--sf-color-danger) clamp(.7, calc(l + .15), 1) c h));--sf-color-danger-muted:oklch(from var(--sf-color-danger) l c h / .3);--sf-gradient-primary:linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) calc(l - .08) c h));--sf-gradient-secondary:linear-gradient(135deg, var(--sf-color-secondary), oklch(from var(--sf-color-secondary) calc(l - .08) c h));--sf-gradient-tertiary:linear-gradient(135deg, var(--sf-color-tertiary), oklch(from var(--sf-color-tertiary) calc(l - .08) c h));--sf-gradient-brand:linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) l c calc(h + 30)));--sf-gradient-surface:linear-gradient(180deg, var(--sf-color-surface), var(--sf-color-bg));--sf-gradient-fade--r:linear-gradient(to right, transparent, var(--sf-color-bg));--sf-gradient-fade--l:linear-gradient(to left, transparent, var(--sf-color-bg));--sf-gradient-fade--t:linear-gradient(to top, transparent, var(--sf-color-bg));--sf-gradient-fade--b:linear-gradient(to bottom, transparent, var(--sf-color-bg));--sf-shadow-strength:calc(.08 + var(--sf-is-dark) * .17);--sf-shadow-glow-color:var(--sf-color-primary);--sf-shadow-glow:0 0 15px 2px oklch(from var(--sf-shadow-glow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-opacity-disabled:.45;--sf-state-pending-opacity:.7;--sf-focus-ring-color:var(--sf-color-action);--sf-focus-ring-shadow:0 0 0 var(--sf-focus-ring-offset) var(--sf-color-bg), 0 0 0 calc(var(--sf-focus-ring-offset) + var(--sf-focus-ring-width)) var(--sf-focus-ring-color);--sf-caret-color:var(--sf-color-action);--sf-space-scale:1;--sf-text-scale:1;--sf-text-display-scale:1;--sf-radius-scale:1;--sf-motion-scale:1;--sf-font-body:system-ui, -apple-system, sans-serif;--sf-font-heading:var(--sf-font-body);--sf-font-display:var(--sf-font-heading);--sf-font-mono:ui-monospace, monospace;--sf-font-humanist:"Seravek", "Gill Sans Nova", "Ubuntu", "Calibri", "DejaVu Sans", source-sans-pro, sans-serif;--sf-font-geometric:"Avenir", "Montserrat", "Corbel", "URW Gothic", source-sans-pro, sans-serif;--sf-font-slab:"Rockwell", "Rockwell Nova", "Roboto Slab", "DejaVu Serif", "Sitka Small", serif;--sf-font-features:normal;--sf-font-variation:normal;--sf-optical-sizing:auto;--sf-font-weight-thin:100;--sf-font-weight-extralight:200;--sf-font-weight-light:300;--sf-font-weight-normal:400;--sf-font-weight-medium:500;--sf-font-weight-semibold:600;--sf-font-weight-bold:700;--sf-font-weight-extrabold:800;--sf-font-weight-black:900;--sf-font-weight-body:var(--sf-font-weight-normal);--sf-font-weight-heading:var(--sf-font-weight-semibold);--sf-font-weight-display:var(--sf-font-weight-bold);--sf-current-font-weight:var(--sf-font-weight-bold);--sf-text-2xs:clamp(.51rem, calc(.000296296 * (100vw - 22.5rem) + .51rem), .53rem);--sf-text-xs:clamp(.64rem, calc(.000888889 * (100vw - 22.5rem) + .64rem), .7rem);--sf-text-s:clamp(.8rem, calc(.00207407 * (100vw - 22.5rem) + .8rem), .94rem);--sf-text-m:clamp(1rem, calc(.0037037 * (100vw - 22.5rem) + 1rem), 1.25rem);--sf-text-l:clamp(1.25rem, calc(.00622222 * (100vw - 22.5rem) + 1.25rem), 1.67rem);--sf-text-xl:clamp(1.56rem, calc(.00977778 * (100vw - 22.5rem) + 1.56rem), 2.22rem);--sf-text-2xl:clamp(1.95rem, calc(.014963 * (100vw - 22.5rem) + 1.95rem), 2.96rem);--sf-text-3xl:clamp(2.44rem, calc(.0223704 * (100vw - 22.5rem) + 2.44rem), 3.95rem);--sf-text-4xl:clamp(3.05rem, calc(.0327407 * (100vw - 22.5rem) + 3.05rem), 5.26rem);--sf-text-display-s:clamp(2.4rem, calc(.00888889 * (100vw - 22.5rem) + 2.4rem), 3rem);--sf-text-display-m:clamp(3rem, calc(.0148148 * (100vw - 22.5rem) + 3rem), 4rem);--sf-text-display-l:clamp(3.75rem, calc(.0234074 * (100vw - 22.5rem) + 3.75rem), 5.33rem);--sf-leading-tight:1.1;--sf-leading-snug:1.3;--sf-leading-normal:1.5;--sf-leading-relaxed:1.625;--sf-tracking-tight:-.025em;--sf-tracking-normal:0;--sf-tracking-wide:.025em;--sf-tracking-wider:.05em;--sf-tracking-widest:.1em;--sf-icon-xs:.875em;--sf-icon-s:1em;--sf-icon-m:1.5em;--sf-icon-l:2em;--sf-icon-xl:3em;--sf-space-none:0;--sf-space-px:1px;--sf-space-gutter:var(--sf-space-l);--sf-space-2xs:calc(clamp(.51rem, calc(.00488889 * (100vw - 22.5rem) + .51rem), .84rem) * var(--sf-space-scale));--sf-space-xs:calc(clamp(.64rem, calc(.00725926 * (100vw - 22.5rem) + .64rem), 1.13rem) * var(--sf-space-scale));--sf-space-s:calc(clamp(.8rem, calc(.0103704 * (100vw - 22.5rem) + .8rem), 1.5rem) * var(--sf-space-scale));--sf-space-m:calc(clamp(1rem, calc(.0148148 * (100vw - 22.5rem) + 1rem), 2rem) * var(--sf-space-scale));--sf-space-l:calc(clamp(1.25rem, calc(.021037 * (100vw - 22.5rem) + 1.25rem), 2.67rem) * var(--sf-space-scale));--sf-space-xl:calc(clamp(1.56rem, calc(.0294815 * (100vw - 22.5rem) + 1.56rem), 3.55rem) * var(--sf-space-scale));--sf-space-2xl:calc(clamp(1.95rem, calc(.0413333 * (100vw - 22.5rem) + 1.95rem), 4.74rem) * var(--sf-space-scale));--sf-space-3xl:calc(clamp(2.44rem, calc(.0573333 * (100vw - 22.5rem) + 2.44rem), 6.31rem) * var(--sf-space-scale));--sf-space-4xl:calc(clamp(3.05rem, calc(.0795556 * (100vw - 22.5rem) + 3.05rem), 8.42rem) * var(--sf-space-scale));--sf-size-xs:1.5rem;--sf-size-s:2rem;--sf-size-m:2.5rem;--sf-size-l:2.75rem;--sf-size-xl:3.5rem;--sf-container-narrow:38rem;--sf-container-prose:65ch;--sf-container-default:75rem;--sf-container-wide:90rem;--sf-container-full:100%;--sf-ratio-square:1;--sf-ratio-video:16 / 9;--sf-ratio-cinema:21 / 9;--sf-ratio-4-3:4 / 3;--sf-ratio-3-2:3 / 2;--sf-ratio-portrait:3 / 4;--sf-ratio-golden:1.618 / 1;--sf-border-width-hairline:.5px;--sf-border-width-1:1px;--sf-border-width-2:2px;--sf-border-width-3:3px;--sf-border-width-4:4px;--sf-border-style:solid;--sf-border-style-strong:solid;--sf-border-style-soft:dashed;--sf-border-style-dotted:dotted;--sf-divider-width:var(--sf-border-width-1);--sf-divider-style:solid;--sf-divider-color:var(--sf-color-border);--sf-radius-none:0;--sf-radius-xs:calc(2px * var(--sf-radius-scale));--sf-radius-s:calc(4px * var(--sf-radius-scale));--sf-radius-m:calc(8px * var(--sf-radius-scale));--sf-radius-l:calc(12px * var(--sf-radius-scale));--sf-radius-xl:calc(16px * var(--sf-radius-scale));--sf-radius-2xl:calc(24px * var(--sf-radius-scale));--sf-radius-3xl:calc(32px * var(--sf-radius-scale));--sf-radius-4xl:calc(48px * var(--sf-radius-scale));--sf-radius-full:9999px;--sf-radius-pill:var(--sf-radius-full);--sf-radius-outer:calc(var(--sf-radius-m) + var(--sf-component-pad));--sf-shadow-color:oklch(from var(--sf-color-neutral) .15 c h);--sf-shadow-none:none;--sf-shadow-xs:0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7));--sf-shadow-s:0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 2px 6px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, var(--sf-shadow-strength), .7));--sf-shadow-m:0 1px 3px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-shadow-l:0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 8px 24px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), .7)), 0 16px 48px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-shadow-xl:0 2px 8px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 12px 36px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3.5), .7)), 0 24px 72px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7));--sf-shadow-2xl:0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .6), .7)), 0 20px 60px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 4), .7)), 0 40px 100px -8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 5), .7));--sf-shadow-inner:inset 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-text-shadow-none:none;--sf-text-shadow-s:0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), .7));--sf-text-shadow-m:0 2px 4px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-text-shadow-l:0 4px 8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7));--sf-drop-shadow-s:drop-shadow(0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), .7)));--sf-drop-shadow-m:drop-shadow(0 4px 6px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7)));--sf-drop-shadow-l:drop-shadow(0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7)));--sf-blur-xs:4px;--sf-blur-s:8px;--sf-blur-m:16px;--sf-blur-l:32px;--sf-blur-xl:48px;--sf-perspective-near:500px;--sf-perspective-normal:1000px;--sf-perspective-far:2000px;--sf-opacity-0:0;--sf-opacity-10:.1;--sf-opacity-25:.25;--sf-opacity-50:.5;--sf-opacity-75:.75;--sf-opacity-100:1;--sf-duration-none:0s;--sf-duration-instant:calc(.1s * var(--sf-motion-scale));--sf-duration-fast:calc(.15s * var(--sf-motion-scale));--sf-duration-normal:calc(.25s * var(--sf-motion-scale));--sf-duration-slow:calc(.4s * var(--sf-motion-scale));--sf-duration-slower:calc(.6s * var(--sf-motion-scale));--sf-ease-linear:linear;--sf-ease-out:cubic-bezier(.25, 0, .15, 1);--sf-ease-in:cubic-bezier(.5, 0, .75, .25);--sf-ease-in-out:cubic-bezier(.4, 0, .2, 1);--sf-ease-spring:linear(0, .5, 1.1, .95, 1.02, 1);--sf-ease-elastic:linear(0, .3, 1.2, .9, 1.05, 1);--sf-ease-bounce:linear(0, .35 18%, 1 32%, .86 42%, 1.02 56%, .98 72%, 1);--sf-ease-overshoot:linear(0, .6 30%, 1.08 55%, .98 75%, 1);--sf-animation-fade-in:sf-fade-in var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-fade-out:sf-fade-out var(--sf-duration-normal) var(--sf-ease-in) both;--sf-animation-slide-in-up:sf-slide-in-up var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-down:sf-slide-in-down var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-left:sf-slide-in-left var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-right:sf-slide-in-right var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-scale-up:sf-scale-up var(--sf-duration-normal) var(--sf-ease-overshoot) both;--sf-animation-scale-down:sf-scale-down var(--sf-duration-normal) var(--sf-ease-in) both;--sf-animation-color-pulse:sf-color-pulse var(--sf-duration-slow) var(--sf-ease-in-out) infinite;--sf-animation-ping:sf-ping var(--sf-duration-slow) var(--sf-ease-out) infinite;--sf-animation-blink:sf-blink calc(1s * var(--sf-motion-scale)) steps(1, end) infinite;--sf-animation-float:sf-float calc(3s * var(--sf-motion-scale)) var(--sf-ease-in-out) infinite;--sf-animation-spin:sf-spin var(--sf-duration-slower) linear infinite;--sf-animation-shimmer:sf-shimmer calc(1.5s * var(--sf-motion-scale)) ease-in-out infinite;--sf-animation-delay-1:calc(75ms * var(--sf-motion-scale));--sf-animation-delay-2:calc(.15s * var(--sf-motion-scale));--sf-animation-delay-3:calc(.225s * var(--sf-motion-scale));--sf-animation-delay-4:calc(.3s * var(--sf-motion-scale));--sf-animation-delay-5:calc(.375s * var(--sf-motion-scale));--sf-scroll-timeline-range-start:0%;--sf-scroll-timeline-range-end:100%;--sf-mask-scrim-start:var(--sf-space-l);--sf-mask-scrim-end:var(--sf-space-l);--sf-z-below:-1;--sf-z-base:0;--sf-z-raised:1;--sf-z-low:10;--sf-z-mid:100;--sf-z-high:500;--sf-z-top:900;--sf-z-max:9999;--sf-header-height:8rem;--sf-sticky-offset:var(--sf-header-height);--sf-focus-ring-width:2px;--sf-focus-ring-offset:2px;--sf-focus-ring-style:solid;--sf-touch-target:var(--sf-size-l);--sf-contrast-bias:0;--sf-contrast-threshold:.6;--sf-safe-top:env(safe-area-inset-top,0px);--sf-safe-bottom:env(safe-area-inset-bottom,0px);--sf-safe-left:env(safe-area-inset-left,0px);--sf-safe-right:env(safe-area-inset-right,0px);--sf-print-page-margin:2cm;--sf-print-page-size:a4;--sf-print-base-size:11pt;--sf-stroke-thin:1px;--sf-stroke-regular:1.5px;--sf-stroke-bold:2px;--sf-stroke-heavy:3px;--sf-body-font-size:var(--sf-text-m);--sf-body-line-height:var(--sf-leading-normal);--sf-body-font-weight:var(--sf-font-weight-body);--sf-body-font-family:var(--sf-font-body);--sf-body-color:var(--sf-color-text);--sf-body-text-wrap:pretty;--sf-body-strong-weight:var(--sf-font-weight-bold);--sf-body-em-style:italic;--sf-code-font-size:.875em;--sf-heading-font-family:var(--sf-font-heading);--sf-heading-color:var(--sf-color-heading);--sf-heading-text-wrap:balance;--sf-h1-size:var(--sf-text-4xl);--sf-h1-line-height:var(--sf-leading-tight);--sf-h1-font-weight:var(--sf-font-weight-heading);--sf-h1-letter-spacing:var(--sf-tracking-tight);--sf-h2-size:var(--sf-text-3xl);--sf-h2-line-height:var(--sf-leading-tight);--sf-h2-font-weight:var(--sf-font-weight-heading);--sf-h2-letter-spacing:var(--sf-tracking-tight);--sf-h3-size:var(--sf-text-2xl);--sf-h3-line-height:var(--sf-leading-snug);--sf-h3-font-weight:var(--sf-font-weight-heading);--sf-h3-letter-spacing:var(--sf-tracking-normal);--sf-h4-size:var(--sf-text-xl);--sf-h4-line-height:var(--sf-leading-snug);--sf-h4-font-weight:var(--sf-font-weight-heading);--sf-h4-letter-spacing:var(--sf-tracking-normal);--sf-h5-size:var(--sf-text-l);--sf-h5-line-height:var(--sf-leading-normal);--sf-h5-font-weight:var(--sf-font-weight-heading);--sf-h5-letter-spacing:var(--sf-tracking-normal);--sf-h6-size:var(--sf-text-m);--sf-h6-line-height:var(--sf-leading-normal);--sf-h6-font-weight:var(--sf-font-weight-heading);--sf-h6-letter-spacing:var(--sf-tracking-wide);--sf-gap:var(--sf-space-m);--sf-content-gap:var(--sf-space-s);--sf-component-pad:var(--sf-space-m);--sf-field-block:var(--sf-space-l);--sf-field-required-marker:" *";--sf-link-external-marker:" ↗";--sf-section-pad:var(--sf-section-pad--m);--sf-section-pad--xs:var(--sf-space-xl);--sf-section-pad--s:var(--sf-space-2xl);--sf-section-pad--m:var(--sf-space-3xl);--sf-section-pad--l:var(--sf-space-4xl);--sf-section-pad--xl:calc(var(--sf-space-4xl) * 1.5);--sf-section-pad--2xl:calc(var(--sf-space-4xl) * 2);--sf-transition-all:all var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-colors:color var(--sf-duration-normal) var(--sf-ease-out), background-color var(--sf-duration-normal) var(--sf-ease-out), border-color var(--sf-duration-normal) var(--sf-ease-out), text-decoration-color var(--sf-duration-normal) var(--sf-ease-out), fill var(--sf-duration-normal) var(--sf-ease-out), stroke var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-transform:transform var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-opacity:opacity var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-shadow:box-shadow var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-fast:all var(--sf-duration-fast) var(--sf-ease-out);--sf-transition-slow:all var(--sf-duration-slow) var(--sf-ease-in-out);--sf-transition-enter:all var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-exit:all var(--sf-duration-fast) var(--sf-ease-in);--sf-space-gap:var(--sf-gap);--sf-space-content:var(--sf-content-gap);--sf-stack-gap:var(--sf-space-content);--sf-box-padding:var(--sf-space-m);--sf-box-border-width:0;--sf-box-border-color:var(--sf-color-border);--sf-center-max:var(--sf-container-default);--sf-center-gutter:var(--sf-space-gutter);--sf-cluster-gap:var(--sf-space-gap);--sf-cluster-align:center;--sf-cluster-justify:flex-start;--sf-sidebar-gap:var(--sf-space-gap);--sf-sidebar-min-width:50%;--sf-sidebar-width:18rem;--sf-switcher-threshold:30rem;--sf-switcher-gap:var(--sf-space-gap);--sf-grid-min:16rem;--sf-grid-gap:var(--sf-space-gap);--sf-grid-min-xs:10rem;--sf-grid-min-s:13rem;--sf-grid-min-m:16rem;--sf-grid-min-l:20rem;--sf-grid-min-xl:24rem;--sf-grid-min-2xl:28rem;--sf-cover-min-height:100dvh;--sf-cover-padding:var(--sf-section-pad);--sf-frame-ratio:16 / 9;--sf-icon-box-pad:.5em;--sf-icon-box-radius:var(--sf-radius-s);--sf-icon-box-bg:var(--sf-color-well);--sf-icon-box-border:var(--sf-border-width-1) solid var(--sf-color-border);--sf-reel-item-width:max-content;--sf-reel-gap:var(--sf-space-gap);--sf-reel-height:auto;--sf-imposter-margin:var(--sf-space-m);--sf-bento-cols-default:3;--sf-bento-row-default:10rem;--sf-bento-row-compact:6rem;--sf-bento-row-tall:16rem;--sf-bento-gap:var(--sf-space-gap);--sf-breakout-width:var(--sf-container-wide);--sf-content-width:var(--sf-container-default);--sf-prose-paragraph:var(--sf-space-content);--sf-alternate-gap:var(--sf-space-content);--sf-alternate-inner-gap:var(--sf-space-gap);--sf-flow-space:var(--sf-space-content);--sf-line-clamp:3;--sf-truncate-suffix:"…";--sf-aspect:16 / 9;--sf-scroll-shadow-size:2rem}*,:before,:after{box-sizing:border-box;margin:0;padding:0}html{text-size-adjust:100%;scroll-padding-top:var(--sf-header-height,3.5rem);scrollbar-gutter:stable;color-scheme:var(--sf-color-scheme,light dark);hanging-punctuation:first last}@supports (interpolate-size:allow-keywords){html{interpolate-size:allow-keywords}}body{min-height:100dvh;min-inline-size:320px}menu,ul,ol{list-style:none}img,picture,video,canvas,svg{max-width:100%;height:auto;display:block}iframe,embed,object{max-width:100%;height:unset;border:none;display:block}button,input,select,textarea{font:inherit;color:inherit;letter-spacing:inherit;color-scheme:inherit;line-height:inherit}button{text-transform:none}search{display:block}fieldset{border:none;min-width:0;padding:0}legend{padding:0}a,button,input,select,textarea,summary{touch-action:manipulation}summary{cursor:pointer;list-style:none}textarea{resize:vertical}button{cursor:pointer}table{border-collapse:collapse;border-spacing:0}[hidden]{display:none!important}dialog,[popover]{color:inherit;background:0 0;border:none;padding:0}[inert]{cursor:default}body{font-family:var(--sf-body-font-family,var(--sf-font-body));font-size:var(--sf-body-font-size,var(--sf-text-m));line-height:var(--sf-body-line-height,var(--sf-leading-normal));font-weight:var(--sf-body-font-weight,var(--sf-font-weight-body));color:var(--sf-body-color,var(--sf-color-text));background-color:var(--sf-color-bg,#fff);font-synthesis:none}h1,h2,h3,h4,h5,h6{font-family:var(--sf-heading-font-family,var(--sf-font-heading));line-height:var(--sf-leading-tight);color:var(--sf-heading-color,var(--sf-color-heading));text-wrap:var(--sf-heading-text-wrap);overflow-wrap:break-word;text-rendering:optimizelegibility;scroll-margin-top:calc(var(--sf-header-height,3.5rem) + var(--sf-space-m))}h1{font-size:var(--sf-h1-size);line-height:var(--sf-h1-line-height);font-weight:var(--sf-h1-font-weight);letter-spacing:var(--sf-h1-letter-spacing)}h2{font-size:var(--sf-h2-size);line-height:var(--sf-h2-line-height);font-weight:var(--sf-h2-font-weight);letter-spacing:var(--sf-h2-letter-spacing)}h3{font-size:var(--sf-h3-size);line-height:var(--sf-h3-line-height);font-weight:var(--sf-h3-font-weight);letter-spacing:var(--sf-h3-letter-spacing)}h4{font-size:var(--sf-h4-size);line-height:var(--sf-h4-line-height);font-weight:var(--sf-h4-font-weight);letter-spacing:var(--sf-h4-letter-spacing)}h5{font-size:var(--sf-h5-size);line-height:var(--sf-h5-line-height);font-weight:var(--sf-h5-font-weight);letter-spacing:var(--sf-h5-letter-spacing)}h6{font-size:var(--sf-h6-size);line-height:var(--sf-h6-line-height);font-weight:var(--sf-h6-font-weight);letter-spacing:var(--sf-h6-letter-spacing)}p{text-wrap:var(--sf-body-text-wrap);overflow-wrap:break-word;orphans:2;widows:2}b,strong{font-weight:var(--sf-body-strong-weight)}em{font-style:var(--sf-body-em-style)}small{font-size:var(--sf-text-s,80%)}mark{background-color:var(--sf-color-mark-bg,yellow);color:var(--sf-color-mark-text,black);border-radius:var(--sf-radius-xs);padding-inline:.2em}abbr[title]{cursor:help;text-decoration:underline dotted}kbd,samp{font-family:var(--sf-font-mono);font-size:inherit}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sup{top:-.5em}sub{bottom:-.25em}a:link{color:var(--sf-color-link);text-decoration-color:var(--sf-color-link--underline);text-underline-offset:.15em;text-underline-position:from-font;transition:color var(--sf-duration-fast) var(--sf-ease-out), text-decoration-color var(--sf-duration-fast) var(--sf-ease-out)}a:hover{color:var(--sf-color-link--hover);text-decoration-color:currentColor}a:visited{color:var(--sf-color-link--visited)}a:active{color:var(--sf-color-link--active)}a:not([class]){text-decoration-skip-ink:auto}a,code{overflow-wrap:break-word}code{font-family:var(--sf-font-mono);font-size:var(--sf-code-font-size);border-radius:var(--sf-radius-xs);background-color:var(--sf-color-code-bg);color:var(--sf-color-code-text,inherit);padding:.125em .3em}pre{font-family:var(--sf-font-mono);font-size:var(--sf-text-s);padding:var(--sf-space-m);border-radius:var(--sf-radius-m);background:var(--sf-color-code-block-bg,var(--sf-color-code-bg));color:var(--sf-color-code-block-text,inherit);tab-size:2;hyphens:none;overflow-x:auto}pre code{color:inherit;font-size:inherit;background:0 0;border:0;border-radius:0;padding:0}svg{fill:currentColor}hr{border:none;border-block-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color)}:target{scroll-margin-top:calc(var(--sf-header-height,3.5rem) + var(--sf-space-m))}::selection{background:var(--sf-color-selection-bg);color:var(--sf-color-selection-text);text-shadow:none}th,caption{text-align:start}address,cite,dfn,var{font-style:normal}input,textarea{caret-color:var(--sf-caret-color)}input,progress{accent-color:var(--sf-color-action)}::backdrop{background-color:var(--sf-color-dim)}optgroup{font:inherit}::file-selector-button{font:inherit}::placeholder{opacity:1;color:var(--sf-color-text--placeholder,currentcolor)}blockquote{border-inline-start:var(--sf-border-width-3) solid var(--sf-color-border);padding-inline-start:var(--sf-space-m);font-style:italic}dt{font-weight:var(--sf-font-weight-heading)}dd{margin-inline-start:var(--sf-space-m)}table{text-align:start;width:100%}th{font-weight:var(--sf-font-weight-heading)}td,th{padding:var(--sf-space-2xs) var(--sf-space-xs);border-block-end:var(--sf-border-width-hairline) solid var(--sf-color-border--subtle)}@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--sf-is-dark:1}}[data-theme=light]{color-scheme:light;--sf-is-dark:0}[data-theme=dark]{color-scheme:dark;--sf-is-dark:1}:root[data-lumlocker]{--sf-color-primary:light-dark(oklch(from var(--sf-color-primary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-primary-dark,var(--sf-color-primary-light)) var(--sf-lumlocker) c h));--sf-color-secondary:light-dark(oklch(from var(--sf-color-secondary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-secondary-dark,var(--sf-color-secondary-light)) var(--sf-lumlocker) c h));--sf-color-tertiary:light-dark(oklch(from var(--sf-color-tertiary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-tertiary-dark,var(--sf-color-tertiary-light)) var(--sf-lumlocker) c h));--sf-color-action:light-dark(oklch(from var(--sf-color-action-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-action-dark,var(--sf-color-action-light)) var(--sf-lumlocker) c h));--sf-color-neutral:light-dark(oklch(from var(--sf-color-neutral-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-neutral-dark,var(--sf-color-neutral-light)) var(--sf-lumlocker) c h))}.sf-section{padding-block:var(--sf-section-pad)}.sf-section--xs{padding-block:var(--sf-section-pad--xs)}.sf-section--s{padding-block:var(--sf-section-pad--s)}.sf-section--m{padding-block:var(--sf-section-pad--m)}.sf-section--l{padding-block:var(--sf-section-pad--l)}.sf-section--xl{padding-block:var(--sf-section-pad--xl)}.sf-section--2xl{padding-block:var(--sf-section-pad--2xl)}.sf-section-group>.sf-section+.sf-section{padding-block-start:0}.sf-section--collapse:has(+.sf-section--collapse){padding-block-end:calc(var(--sf-section-pad) / 2)}.sf-section--collapse+.sf-section--collapse{padding-block-start:calc(var(--sf-section-pad) / 2)}.sf-divider{border:none;border-block-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);margin-block:var(--sf-space-m)}.sf-divider--vertical{border-block-start:none;border-inline-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);margin-block:0;margin-inline:var(--sf-space-m);align-self:stretch}.sf-container{width:100%;max-width:var(--sf-container-default);padding-inline:var(--sf-space-gutter);margin-inline:auto;container:sf-layout/inline-size}.sf-container--narrow{max-width:var(--sf-container-narrow)}.sf-container--prose{max-width:var(--sf-container-prose)}.sf-container--wide{max-width:var(--sf-container-wide)}.sf-container--full{max-width:var(--sf-container-full)}.sf-stack{gap:var(--sf-stack-gap);flex-direction:column;display:flex}.sf-stack--xs{gap:var(--sf-space-xs)}.sf-stack--s{gap:var(--sf-space-s)}.sf-stack--m{gap:var(--sf-space-m)}.sf-stack--l{gap:var(--sf-space-l)}.sf-stack--xl{gap:var(--sf-space-xl)}.sf-stack--2xl{gap:var(--sf-space-2xl)}.sf-stack--center{align-items:center}.sf-stack--end{align-items:flex-end}.sf-stack--stretch{align-items:stretch}.sf-box{padding:var(--sf-box-padding);outline:var(--sf-box-border-width) solid var(--sf-box-border-color)}.sf-center{box-sizing:content-box;max-inline-size:var(--sf-center-max);padding-inline:var(--sf-center-gutter);margin-inline:auto}.sf-center--intrinsic{flex-direction:column;align-items:center;display:flex}.sf-cluster{gap:var(--sf-cluster-gap);align-items:var(--sf-cluster-align);justify-content:var(--sf-cluster-justify);flex-wrap:wrap;display:flex}.sf-cluster--xs{gap:var(--sf-space-xs)}.sf-cluster--s{gap:var(--sf-space-s)}.sf-cluster--m{gap:var(--sf-space-m)}.sf-cluster--l{gap:var(--sf-space-l)}.sf-cluster--xl{gap:var(--sf-space-xl)}.sf-cluster--2xl{gap:var(--sf-space-2xl)}.sf-cluster--no-wrap{flex-wrap:nowrap}.sf-cluster--center{justify-content:center}.sf-cluster--end{justify-content:flex-end}.sf-cluster--between{justify-content:space-between}.sf-sidebar{gap:var(--sf-sidebar-gap);flex-wrap:wrap;display:flex}.sf-sidebar>:first-child{flex-basis:var(--sf-sidebar-width);flex-grow:1}.sf-sidebar>:last-child,.sf-sidebar--right>:first-child{min-inline-size:var(--sf-sidebar-min-width);flex-grow:999;flex-basis:0}.sf-sidebar--right>:last-child{flex-basis:var(--sf-sidebar-width);flex-grow:1}.sf-sidebar--narrow{--sf-sidebar-width:12rem}.sf-sidebar--wide{--sf-sidebar-width:26rem}.sf-switcher{gap:var(--sf-switcher-gap);flex-wrap:wrap;display:flex}.sf-switcher>*{flex-grow:1;flex-basis:calc((var(--sf-switcher-threshold) - 100%) * 999)}.sf-switcher--no-wrap{flex-wrap:nowrap;overflow-x:auto}.sf-switcher--vertical{flex-direction:column}.sf-grid{grid-template-columns:repeat(auto-fill, minmax(min(var(--sf-grid-min), 100%), 1fr));gap:var(--sf-grid-gap);display:grid}.sf-grid--fit{grid-template-columns:repeat(auto-fit, minmax(min(var(--sf-grid-min), 100%), 1fr))}.sf-grid--xs{--sf-grid-min:var(--sf-grid-min-xs)}.sf-grid--s{--sf-grid-min:var(--sf-grid-min-s)}.sf-grid--m{--sf-grid-min:var(--sf-grid-min-m)}.sf-grid--l{--sf-grid-min:var(--sf-grid-min-l)}.sf-grid--xl{--sf-grid-min:var(--sf-grid-min-xl)}.sf-grid--2xl{--sf-grid-min:var(--sf-grid-min-2xl)}.sf-grid--dense{grid-auto-flow:dense}.sf-icon{inline-size:var(--sf-icon-size,var(--sf-icon-m));block-size:var(--sf-icon-size,var(--sf-icon-m));vertical-align:text-bottom;fill:currentColor;flex-shrink:0;display:inline-block}.sf-icon--xs{--sf-icon-size:var(--sf-icon-xs)}.sf-icon--s{--sf-icon-size:var(--sf-icon-s)}.sf-icon--m{--sf-icon-size:var(--sf-icon-m)}.sf-icon--l{--sf-icon-size:var(--sf-icon-l)}.sf-icon--xl{--sf-icon-size:var(--sf-icon-xl)}.sf-icon--boxed{inline-size:calc(var(--sf-icon-size,var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);block-size:calc(var(--sf-icon-size,var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);padding:var(--sf-icon-box-pad);border:var(--sf-icon-box-border);border-radius:var(--sf-icon-box-radius);background:var(--sf-icon-box-bg);box-sizing:content-box}.sf-cover{min-height:var(--sf-cover-min-height);padding-block:var(--sf-cover-padding);flex-direction:column;display:flex}.sf-cover>.sf-cover__center{margin-block:auto}.sf-cover--min{min-height:50dvh}.sf-cover--max{min-height:0;max-height:100dvh}.sf-cover--padding-s{padding-block:var(--sf-space-xl)}.sf-cover--padding-l{padding-block:var(--sf-space-4xl)}.sf-frame{aspect-ratio:var(--sf-frame-ratio);justify-content:center;align-items:center;display:flex;overflow:hidden}.sf-frame>img,.sf-frame>video{object-fit:cover;width:100%;height:100%}.sf-frame--square{aspect-ratio:var(--sf-ratio-square)}.sf-frame--video{aspect-ratio:var(--sf-ratio-video)}.sf-frame--cinema{aspect-ratio:var(--sf-ratio-cinema)}.sf-frame--portrait{aspect-ratio:var(--sf-ratio-portrait)}.sf-frame--4-3{aspect-ratio:var(--sf-ratio-4-3)}.sf-frame--3-2{aspect-ratio:var(--sf-ratio-3-2)}.sf-frame--golden{aspect-ratio:var(--sf-ratio-golden)}.sf-reel{block-size:var(--sf-reel-height);gap:var(--sf-reel-gap);scroll-snap-type:x mandatory;scroll-padding-inline:var(--sf-reel-gap);overscroll-behavior-x:contain;scrollbar-width:thin;display:flex;overflow-x:auto}.sf-reel>*{flex:0 0 var(--sf-reel-item-width);scroll-snap-align:start}.sf-imposter{position:absolute;inset-block-start:50%;inset-inline-start:50%;translate:-50% -50%}.sf-imposter--fixed{position:fixed}.sf-imposter--contain{max-inline-size:calc(100% - var(--sf-imposter-margin) * 2);max-block-size:calc(100% - var(--sf-imposter-margin) * 2);overflow:auto}.sf-alternate{gap:var(--sf-alternate-gap);display:grid;container:sf-alternate/inline-size}.sf-alternate>*{gap:var(--sf-alternate-inner-gap);align-items:center;display:grid}@container sf-alternate (width>=48em){.sf-alternate>*{grid-template-columns:1fr 1fr}.sf-alternate>:nth-child(2n)>:first-child{order:2}.sf-alternate>:nth-child(2n)>:last-child{order:1}}.sf-pancake{grid-template-rows:auto 1fr auto;min-height:100dvh;display:grid}.sf-grid-1,.sf-grid-2,.sf-grid-3,.sf-grid-4,.sf-grid-6{gap:var(--sf-grid-gap);display:grid}@container (width>=30em){.sf-grid-4{grid-template-columns:repeat(2,1fr)}.sf-grid-6{grid-template-columns:repeat(3,1fr)}}@container (width>=48em){.sf-grid-2{grid-template-columns:repeat(2,1fr)}.sf-grid-3{grid-template-columns:repeat(3,1fr)}.sf-grid-4{grid-template-columns:repeat(4,1fr)}.sf-grid-6{grid-template-columns:repeat(6,1fr)}}.sf-grid-1-2,.sf-grid-2-1,.sf-grid-1-3,.sf-grid-3-1{gap:var(--sf-grid-gap);display:grid}@container (width>=48em){.sf-grid-1-2{grid-template-columns:1fr 2fr}.sf-grid-2-1{grid-template-columns:2fr 1fr}.sf-grid-1-3{grid-template-columns:1fr 3fr}.sf-grid-3-1{grid-template-columns:3fr 1fr}}.sf-content-grid{grid-template-columns:[full-start] minmax(var(--sf-space-gutter), 1fr) [breakout-start] minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2)) [content-start] min(var(--sf-content-width), 100% - var(--sf-space-gutter) * 2) [content-end] minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2)) [breakout-end] minmax(var(--sf-space-gutter), 1fr) [full-end];display:grid}.sf-content-grid>*{grid-column:content}.sf-content-grid>.sf-breakout{grid-column:breakout}.sf-content-grid>.sf-full-bleed{grid-column:full}.sf-bento{grid-template-columns:repeat(var(--sf-bento-cols,var(--sf-bento-cols-default)), 1fr);grid-auto-rows:minmax(var(--sf-bento-row,var(--sf-bento-row-default)), auto);grid-auto-flow:dense;gap:var(--sf-bento-gap);display:grid}@container (width>=30em) and (width<=47.99em){.sf-bento{grid-template-columns:repeat(2,1fr)}}.sf-bento--2{--sf-bento-cols:2}.sf-bento--4{--sf-bento-cols:4}.sf-bento--compact{--sf-bento-row:var(--sf-bento-row-compact)}.sf-bento--tall{--sf-bento-row:var(--sf-bento-row-tall)}.sf-subgrid{grid-template-columns:subgrid;display:grid}.sf-subgrid-rows{grid-template-rows:subgrid;display:grid}.sf-prose{max-width:var(--sf-container-prose);overflow-wrap:break-word}.sf-prose>*+*{margin-block-start:var(--sf-prose-paragraph)}.sf-prose :is(h2,h3,h4)+*{margin-block-start:var(--sf-space-s)}.sf-prose li+li{margin-block-start:var(--sf-space-xs)}.sf-prose :is(blockquote,pre){margin-block:var(--sf-space-m)}.sf-prose img{margin-block:var(--sf-space-m);border-radius:var(--sf-radius-m)}.sf-prose :is(ul,ol){padding-inline-start:1.25em}.sf-prose ul{list-style:outside}.sf-prose ol{list-style:decimal}.sf-prose ::marker{color:var(--sf-color-primary)}.sf-prose figure{margin-block:var(--sf-space-l)}.sf-prose figcaption{font-size:var(--sf-text-s);color:var(--sf-color-text--muted);margin-block-start:var(--sf-space-xs)}.sf-prose table{margin-block:var(--sf-space-m);max-width:100%;display:block;overflow-x:auto}.sf-prose video{margin-block:var(--sf-space-m);border-radius:var(--sf-radius-m)}.sf-prose .sf-not-prose>*+*,.sf-prose .sf-not-prose li+li{margin-block-start:0}.sf-prose .sf-not-prose :is(ul,ol){list-style:revert;padding-inline-start:revert}.sf-prose .sf-not-prose ::marker{color:inherit}.sf-prose .sf-not-prose img{border-radius:0;margin-block:0}.sf-prose .sf-not-prose figure{margin-block:0}.sf-prose .sf-not-prose figcaption{font-size:inherit;color:inherit;margin-block-start:0}.sf-prose .sf-not-prose table{display:revert;overflow-x:revert;max-width:revert;margin-block:0}.sf-prose .sf-not-prose video{border-radius:0;margin-block:0}.sf-flow>*+*{margin-block-start:var(--sf-flow-space)}.sf-truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.sf-line-clamp-2,.sf-line-clamp-3,.sf-line-clamp-N{-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.sf-line-clamp-2{-webkit-line-clamp:2;line-clamp:2}.sf-line-clamp-3{-webkit-line-clamp:3;line-clamp:3}.sf-line-clamp-N{-webkit-line-clamp:var(--sf-line-clamp);line-clamp:var(--sf-line-clamp)}.sf-equal-height{align-items:stretch;display:flex}.sf-equal-height>*{flex:auto;block-size:auto}.sf-aspect{aspect-ratio:var(--sf-aspect)}.sf-scroll-shadow{mask-image:linear-gradient(to bottom, transparent 0, black var(--sf-scroll-shadow-size), black calc(100% - var(--sf-scroll-shadow-size)), transparent 100%);overflow-y:auto}.sf-scroll-snap{scroll-snap-type:y mandatory;overscroll-behavior-y:contain;overflow-y:auto}.sf-scroll-snap>*{scroll-snap-align:start}.sf-overflow-fade{mask-image:linear-gradient(to right, black 0, black calc(100% - var(--sf-scroll-shadow-size)), transparent 100%);overflow:hidden}.sf-no-tap-highlight{-webkit-tap-highlight-color:transparent}.sf-surface--primary{background:var(--sf-color-primary);color:var(--sf-color-text--on-primary)}.sf-surface--secondary{background:var(--sf-color-secondary);color:var(--sf-color-text--on-secondary)}.sf-surface--tertiary{background:var(--sf-color-tertiary);color:var(--sf-color-text--on-tertiary)}.sf-surface--action{background:var(--sf-color-action);color:var(--sf-color-text--on-action)}.sf-surface--neutral{background:var(--sf-color-neutral);color:var(--sf-color-text--on-neutral)}.sf-surface--inverse{background:var(--sf-color-inverse);color:var(--sf-color-text--on-inverse)}.sf-surface--success{background:var(--sf-color-success);color:var(--sf-color-text--on-success)}.sf-surface--warning{background:var(--sf-color-warning);color:var(--sf-color-text--on-warning)}.sf-surface--error{background:var(--sf-color-error);color:var(--sf-color-text--on-error)}.sf-surface--info{background:var(--sf-color-info);color:var(--sf-color-text--on-info)}.sf-surface--danger{background:var(--sf-color-danger);color:var(--sf-color-text--on-danger)}.sf-text-gradient{background-image:var(--sf-gradient-primary);-webkit-background-clip:text;background-clip:text}@supports (background-clip:text){.sf-text-gradient{color:#0000}}.sf-link-external:after{content:var(--sf-link-external-marker);font-size:.85em;text-decoration:none;display:inline-block}.is-hidden{display:none!important}.is-invisible{visibility:hidden}.is-visible{visibility:visible}.is-disabled{opacity:var(--sf-opacity-disabled);pointer-events:none;cursor:not-allowed;user-select:none}.is-readonly{pointer-events:none;user-select:none}.is-loading{pointer-events:none;position:relative;color:#0000!important}.is-loading:after{content:"";border:var(--sf-border-width-2) solid var(--sf-color-border--strong);border-block-start-color:var(--sf-color-action);border-radius:var(--sf-radius-full);width:1em;height:1em;animation:var(--sf-animation-spin);margin-block-start:-.5em;margin-inline-start:-.5em;position:absolute;inset-block-start:50%;inset-inline-start:50%}.is-busy{cursor:progress}.is-pending{opacity:var(--sf-state-pending-opacity);cursor:progress}.is-skeleton{background:linear-gradient(90deg, var(--sf-color-well) 25%, var(--sf-color-raised) 50%, var(--sf-color-well) 75%);animation:var(--sf-animation-shimmer);border-radius:var(--sf-radius-s);pointer-events:none;user-select:none;background-size:200% 100%;color:#0000!important}.is-active{--sf-is-active:1}.is-selected{background-color:var(--sf-color-bg--selected)}.is-current{--sf-is-current:1;font-weight:var(--sf-current-font-weight,var(--sf-font-weight-bold))}.is-highlighted{background-color:var(--sf-color-bg--focus)}.is-pressed{--sf-is-pressed:1;background-color:var(--sf-color-bg--selected)}.is-open{--sf-is-open:1}.is-collapsed{--sf-is-open:0}.is-expanded{--sf-is-open:1}.is-valid{--sf-field-border-color:var(--sf-color-success);--sf-field-text-color:var(--sf-color-success-strong)}.is-invalid{--sf-field-border-color:var(--sf-color-error);--sf-field-text-color:var(--sf-color-error-strong)}.is-warning{--sf-field-border-color:var(--sf-color-warning);--sf-field-text-color:var(--sf-color-warning-strong)}.is-success{--sf-field-border-color:var(--sf-color-success);--sf-field-text-color:var(--sf-color-success-strong)}.is-error{--sf-field-border-color:var(--sf-color-error);--sf-field-text-color:var(--sf-color-error-strong)}.is-info{--sf-field-border-color:var(--sf-color-info);--sf-field-text-color:var(--sf-color-info-strong)}.is-danger{--sf-field-border-color:var(--sf-color-danger);--sf-field-text-color:var(--sf-color-danger-strong)}.is-sticky{top:var(--sf-sticky-offset,0);z-index:var(--sf-z-low);position:sticky}.is-pinned{z-index:var(--sf-z-mid);position:sticky;top:0}.is-fixed{position:fixed}.is-fullscreen{z-index:var(--sf-z-max);position:fixed;inset:0}.is-resizable{resize:both;overflow:auto}.is-clipped{overflow:hidden!important}.is-scrollable{overscroll-behavior:contain;overflow:auto}.is-truncated{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.is-dragging{opacity:var(--sf-opacity-50);cursor:grabbing}.is-drop-target{outline:var(--sf-focus-ring-width) dashed var(--sf-color-action);outline-offset:var(--sf-focus-ring-offset)}.is-draggable{cursor:grab}.is-overlay{position:absolute;inset:0}.is-clickable{cursor:pointer}.is-unselectable{user-select:none}.is-focused{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-focus-ring-color);outline-offset:var(--sf-focus-ring-offset)}.is-empty:empty{display:none}@media (prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}html:focus-within{scroll-behavior:auto}a,button,input,select,textarea,summary{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-duration:var(--sf-duration-fast);transition-timing-function:var(--sf-ease-out)}@supports (view-transition-name:none){::view-transition-old(root),::view-transition-new(root){animation-duration:var(--sf-duration-normal);animation-timing-function:var(--sf-ease-out)}}.sf-fade-in{animation:var(--sf-animation-fade-in)}.sf-fade-out{animation:var(--sf-animation-fade-out)}.sf-slide-in-up{animation:var(--sf-animation-slide-in-up)}.sf-slide-in-down{animation:var(--sf-animation-slide-in-down)}.sf-slide-in-left{animation:var(--sf-animation-slide-in-left)}.sf-slide-in-right{animation:var(--sf-animation-slide-in-right)}.sf-scale-up{animation:var(--sf-animation-scale-up)}.sf-scale-down{animation:var(--sf-animation-scale-down)}.sf-entrance--fade,.sf-entrance--fade-up,.sf-entrance--fade-down,.sf-entrance--fade-left,.sf-entrance--fade-right,.sf-entrance--scale-up{animation-duration:var(--sf-duration-slow);animation-timing-function:var(--sf-ease-out);animation-fill-mode:both}@supports (animation-timeline:view()){.sf-entrance--fade,.sf-entrance--fade-up,.sf-entrance--fade-down,.sf-entrance--fade-left,.sf-entrance--fade-right,.sf-entrance--scale-up{animation-timeline:view();animation-range:var(--sf-scroll-timeline-range-start,entry 0%) var(--sf-scroll-timeline-range-end,cover 30%)}}.sf-entrance--fade{animation-name:sf-fade-in}.sf-entrance--fade-up{animation-name:sf-slide-in-up}.sf-entrance--fade-down{animation-name:sf-slide-in-down}.sf-entrance--fade-left{animation-name:sf-slide-in-left}.sf-entrance--fade-right{animation-name:sf-slide-in-right}.sf-entrance--scale-up{animation-name:sf-scale-up}}@keyframes sf-fade-in{0%{opacity:0}to{opacity:1}}@keyframes sf-fade-out{0%{opacity:1}to{opacity:0}}@keyframes sf-slide-in-up{0%{opacity:0;translate:0 1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-down{0%{opacity:0;translate:0 -1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-left{0%{opacity:0;translate:1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-right{0%{opacity:0;translate:-1rem}to{opacity:1;translate:0}}@keyframes sf-scale-up{0%{opacity:0;scale:.92}to{opacity:1;scale:1}}@keyframes sf-scale-down{0%{opacity:1;scale:1}to{opacity:0;scale:.92}}@keyframes sf-spin{to{rotate:360deg}}@keyframes sf-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@keyframes sf-ping{75%,to{opacity:0;scale:2}}@keyframes sf-blink{0%,50%{opacity:1}50.01%,to{opacity:0}}@keyframes sf-float{0%,to{translate:0}50%{translate:0 -.5rem}}@keyframes sf-color-pulse{0%{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) l c h)}50%{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) calc(l + .25) c h)}to{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) l c h)}}@media (prefers-reduced-motion:no-preference){.sf-color-pulse{animation:var(--sf-animation-color-pulse)}}:focus:not(:focus-visible){outline:none}:focus-visible{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-focus-ring-color)!important;outline-offset:var(--sf-focus-ring-offset)!important}@media (prefers-reduced-motion:reduce){:root{--sf-duration-instant:.01ms;--sf-duration-fast:.01ms;--sf-duration-normal:.01ms;--sf-duration-slow:.01ms;--sf-duration-slower:.01ms}*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.no-motion,.no-motion *,.no-motion :before,.no-motion :after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}@media (prefers-contrast:more){:root{--sf-focus-ring-width:3px;--sf-contrast-bias:.06}hr{border-block-start-width:var(--sf-border-width-2)}}@media (prefers-reduced-transparency:reduce){::backdrop{background-color:var(--sf-color-neutral);opacity:1}}@media (pointer:coarse){button,input[type=button],input[type=submit],input[type=reset],select,summary,a{min-height:var(--sf-touch-target);min-width:var(--sf-touch-target)}}[disabled],[aria-disabled=true]{cursor:not-allowed}.sr-only,.visually-hidden,.sr-only-focusable:not(:focus,:focus-within){clip-path:inset(50%)!important;white-space:nowrap!important;border:0!important;width:1px!important;height:1px!important;margin:-1px!important;padding:0!important;position:absolute!important;overflow:clip!important}.skip-link{top:-100%;left:var(--sf-space-m);z-index:var(--sf-z-max);padding:var(--sf-space-s) var(--sf-space-m);background:var(--sf-color-bg);color:var(--sf-color-text);border:var(--sf-border-width-1) solid var(--sf-color-border);border-radius:var(--sf-radius-m);transition:top var(--sf-duration-fast) var(--sf-ease-out);position:absolute}.skip-link:focus{top:var(--sf-space-m)}.focus-parent:focus-within{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}.sf-clickable-parent{--sf-clickable-overlay-z:1;cursor:pointer;position:relative}.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):after,.sf-clickable-parent [data-overlay-link]:after{content:"";z-index:var(--sf-clickable-overlay-z);pointer-events:auto;position:absolute;inset:0}.sf-clickable-parent :is(button,input,textarea,select,summary,video,audio,[role=button],[data-no-overlay]),.sf-clickable-parent:has([data-overlay-link]) a:where(:any-link):not([data-overlay-link]),.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):not(:first-of-type){z-index:calc(var(--sf-clickable-overlay-z) + 1);position:relative}.sf-clickable-parent:focus-within{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}@media (forced-colors:active){:root{--sf-focus-ring-color:Highlight}:focus-visible{outline-color:highlight!important}:where(*){box-shadow:none;text-shadow:none}input,select,textarea{border:var(--sf-border-width-1) solid}}.print-only{display:none}@media print{@page{size:var(--sf-print-page-size,a4);margin:var(--sf-print-page-margin,2cm)}body{font-size:var(--sf-print-base-size)}*,:before,:after{box-shadow:none;text-shadow:none}a[href]:after{content:" (" attr(href) ")";word-break:break-all;font-size:90%}a[href^=\#]:after,a[href^=javascript\: i]:after{content:""}abbr[title]:after{content:" (" attr(title) ")"}img,svg,video,canvas,figure,table,pre,blockquote{break-inside:avoid}thead{display:table-header-group}tr{break-inside:avoid}h1,h2,h3,h4,h5,h6{break-after:avoid;orphans:3;widows:3}p{orphans:3;widows:3}pre,blockquote{border:var(--sf-border-width-1) solid var(--sf-color-border--strong);padding:var(--sf-space-s)}nav,aside,button,input,select,textarea,audio,video,dialog,[popover],.no-print{display:none!important}.print-only{display:revert!important}details{display:block}details>summary{display:none!important}details:not([open])>:not(summary){display:block}.print-color-exact,.print-color-exact *{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}.print-no-color,.print-no-color *,.print-no-color :before,.print-no-color :after{color:canvastext!important;background:0 0!important}} +@property --sf-color-primary-light{syntax:"";inherits:true;initial-value:oklch(45% .2 264)}@property --sf-color-secondary-light{syntax:"";inherits:true;initial-value:oklch(25% .03 260)}@property --sf-color-tertiary-light{syntax:"";inherits:true;initial-value:oklch(48% .14 310)}@property --sf-color-action-light{syntax:"";inherits:true;initial-value:oklch(60% .16 210)}@property --sf-color-neutral-light{syntax:"";inherits:true;initial-value:oklch(45% .02 260)}@property --sf-color-base-light{syntax:"";inherits:true;initial-value:oklch(98% .005 260)}@property --sf-color-success-light{syntax:"";inherits:true;initial-value:oklch(48% .17 150)}@property --sf-color-warning-light{syntax:"";inherits:true;initial-value:oklch(75% .17 80)}@property --sf-color-error-light{syntax:"";inherits:true;initial-value:oklch(62% .2 35)}@property --sf-color-info-light{syntax:"";inherits:true;initial-value:oklch(48% .15 240)}@property --sf-color-danger-light{syntax:"";inherits:true;initial-value:oklch(48% .24 12)}@property --sf-is-dark{syntax:"";inherits:true;initial-value:0}@property --sf-is-active{syntax:"";inherits:true;initial-value:0}@property --sf-is-current{syntax:"";inherits:true;initial-value:0}@property --sf-is-pressed{syntax:"";inherits:true;initial-value:0}@property --sf-is-open{syntax:"";inherits:true;initial-value:0}:root{--sf-color-scheme:light dark;--sf-lumlocker:.65;--sf-color-primary:light-dark(var(--sf-color-primary-light),var(--sf-color-primary-dark,oklch(from var(--sf-color-primary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-secondary:light-dark(var(--sf-color-secondary-light),var(--sf-color-secondary-dark,oklch(from var(--sf-color-secondary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-tertiary:light-dark(var(--sf-color-tertiary-light),var(--sf-color-tertiary-dark,oklch(from var(--sf-color-tertiary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-action:light-dark(var(--sf-color-action-light),var(--sf-color-action-dark,oklch(from var(--sf-color-action-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-neutral:light-dark(var(--sf-color-neutral-light),var(--sf-color-neutral-dark,oklch(from var(--sf-color-neutral-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-base:light-dark(var(--sf-color-base-light),var(--sf-color-base-dark,oklch(from var(--sf-color-base-light) clamp(.16, calc(1.18 - l), .24) calc(c * .5) h)));--sf-color-success:light-dark(var(--sf-color-success-light),var(--sf-color-success-dark,oklch(from var(--sf-color-success-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-warning:light-dark(var(--sf-color-warning-light),var(--sf-color-warning-dark,oklch(from var(--sf-color-warning-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-error:light-dark(var(--sf-color-error-light),var(--sf-color-error-dark,oklch(from var(--sf-color-error-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-info:light-dark(var(--sf-color-info-light),var(--sf-color-info-dark,oklch(from var(--sf-color-info-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-danger:light-dark(var(--sf-color-danger-light),var(--sf-color-danger-dark,oklch(from var(--sf-color-danger-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-bg:oklch(from var(--sf-color-base) calc(l + .02) c h);--sf-color-surface:var(--sf-color-base);--sf-color-well:oklch(from var(--sf-color-base) calc(l - .02) c h);--sf-color-raised:oklch(from var(--sf-color-base) calc(l + .04) c h);--sf-color-overlay:oklch(from var(--sf-color-base) l c h / .9);--sf-color-inverse:oklch(from var(--sf-color-base) calc(1 - l) c h);--sf-color-text:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.05, calc(l - .4 - var(--sf-contrast-bias)), .35) c h),oklch(from var(--sf-color-neutral) clamp(.7, calc(l + .25 + var(--sf-contrast-bias)), 1) c h));--sf-color-text--secondary:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.15, calc(l - .25 - var(--sf-contrast-bias)), .45) c h),oklch(from var(--sf-color-neutral) clamp(.55, calc(l + .1 + var(--sf-contrast-bias)), .9) c h));--sf-color-text--muted:var(--sf-color-neutral);--sf-color-text--placeholder:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.45, calc(l + .15), .75) c h),oklch(from var(--sf-color-neutral) clamp(.35, calc(l - .1), .65) c h));--sf-color-text--disabled:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.55, calc(l + .25), .82) c h),oklch(from var(--sf-color-neutral) clamp(.25, calc(l - .2), .55) c h));--sf-color-text--inverse:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.85, calc(l + .4), .98) c h),oklch(from var(--sf-color-neutral) clamp(.05, calc(l - .4), .35) c h));--sf-color-heading:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.05, calc(l - .4 - var(--sf-contrast-bias)), .35) c h),oklch(from var(--sf-color-neutral) clamp(.7, calc(l + .25 + var(--sf-contrast-bias)), 1) c h));--sf-color-code-text:oklch(from var(--sf-color-code-bg) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-primary:oklch(from var(--sf-color-primary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-secondary:oklch(from var(--sf-color-secondary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-tertiary:oklch(from var(--sf-color-tertiary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-action:oklch(from var(--sf-color-action) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-neutral:oklch(from var(--sf-color-neutral) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-base:var(--sf-color-text);--sf-color-text--on-inverse:var(--sf-color-text--inverse);--sf-color-text--on-success:oklch(from var(--sf-color-success) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-warning:oklch(from var(--sf-color-warning) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-error:oklch(from var(--sf-color-error) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-info:oklch(from var(--sf-color-info) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-danger:oklch(from var(--sf-color-danger) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-border:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.7, calc(l + .35), .95) .005 h),oklch(from var(--sf-color-neutral) clamp(.25, calc(l - .3), .55) .005 h));--sf-color-border--subtle:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.75, calc(l + .4), .97) .005 h),oklch(from var(--sf-color-neutral) clamp(.2, calc(l - .38), .45) .005 h));--sf-color-border--strong:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.55, calc(l + .1), .85) .02 h),oklch(from var(--sf-color-neutral) clamp(.38, calc(l - .1), .65) .02 h));--sf-color-border--focus:var(--sf-color-action);--sf-color-border--disabled:oklch(from var(--sf-color-border--subtle) l 0 h / .5);--sf-color-border--translucent:oklch(from var(--sf-color-neutral) l c h / .15);--sf-color-link:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .07, .48), 1) c h),oklch(from var(--sf-color-action) clamp(.68, l, 1) c h));--sf-color-link--hover:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .15, .4), 1) c h),oklch(from var(--sf-color-action) clamp(0, max(l + .1, .68), 1) c h));--sf-color-link--active:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .21, .34), 1) c h),oklch(from var(--sf-color-action) clamp(0, max(l + .15, .74), 1) c h));--sf-color-link--visited:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .07, .48), 1) c calc(h + 60)),oklch(from var(--sf-color-action) clamp(.68, l, 1) c calc(h + 60)));--sf-color-link--underline:oklch(from var(--sf-color-action) l c h / .3);--sf-color-link--disabled:var(--sf-color-text--disabled);--sf-color-bg--hover:oklch(from var(--sf-color-neutral) l c h / .08);--sf-color-bg--active:oklch(from var(--sf-color-neutral) l c h / .12);--sf-color-bg--selected:oklch(from var(--sf-color-action) l c h / .1);--sf-color-bg--focus:oklch(from var(--sf-color-action) l c h / .06);--sf-color-bg--disabled:var(--sf-color-well);--sf-color-code-bg:var(--sf-color-well);--sf-color-selection-bg:oklch(from var(--sf-color-action) l c h / .22);--sf-color-selection-text:inherit;--sf-color-mark-bg:oklch(from var(--sf-color-warning) l c h / .25);--sf-color-mark-text:inherit;--sf-color-dim:oklch(0% 0 0/.5);--sf-scrollbar-thumb:var(--sf-color-neutral);--sf-scrollbar-track:transparent;--sf-color-success-subtle:oklch(from var(--sf-color-success) l c h / .12);--sf-color-success-strong:light-dark(oklch(from var(--sf-color-success-light) calc(l - .15) c h),oklch(from var(--sf-color-success) clamp(.7, calc(l + .15), 1) c h));--sf-color-success-muted:oklch(from var(--sf-color-success) l c h / .3);--sf-color-warning-subtle:oklch(from var(--sf-color-warning) l c h / .12);--sf-color-warning-strong:light-dark(oklch(from var(--sf-color-warning-light) calc(l - .25) c h),oklch(from var(--sf-color-warning) clamp(.7, calc(l + .05), 1) c h));--sf-color-warning-muted:oklch(from var(--sf-color-warning) l c h / .3);--sf-color-error-subtle:oklch(from var(--sf-color-error) l c h / .1);--sf-color-error-strong:light-dark(oklch(from var(--sf-color-error-light) calc(l - .1) c h),oklch(from var(--sf-color-error) clamp(.7, calc(l + .15), 1) c h));--sf-color-error-muted:oklch(from var(--sf-color-error) l c h / .3);--sf-color-info-subtle:oklch(from var(--sf-color-info) l c h / .1);--sf-color-info-strong:light-dark(oklch(from var(--sf-color-info-light) calc(l - .1) c h),oklch(from var(--sf-color-info) clamp(.7, calc(l + .15), 1) c h));--sf-color-info-muted:oklch(from var(--sf-color-info) l c h / .3);--sf-color-danger-subtle:oklch(from var(--sf-color-danger) l c h / .1);--sf-color-danger-strong:light-dark(oklch(from var(--sf-color-danger-light) calc(l - .1) c h),oklch(from var(--sf-color-danger) clamp(.7, calc(l + .15), 1) c h));--sf-color-danger-muted:oklch(from var(--sf-color-danger) l c h / .3);--sf-gradient-primary:linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) calc(l - .08) c h));--sf-gradient-secondary:linear-gradient(135deg, var(--sf-color-secondary), oklch(from var(--sf-color-secondary) calc(l - .08) c h));--sf-gradient-tertiary:linear-gradient(135deg, var(--sf-color-tertiary), oklch(from var(--sf-color-tertiary) calc(l - .08) c h));--sf-gradient-brand:linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) l c calc(h + 30)));--sf-gradient-surface:linear-gradient(180deg, var(--sf-color-surface), var(--sf-color-bg));--sf-gradient-fade--r:linear-gradient(to right, transparent, var(--sf-color-bg));--sf-gradient-fade--l:linear-gradient(to left, transparent, var(--sf-color-bg));--sf-gradient-fade--t:linear-gradient(to top, transparent, var(--sf-color-bg));--sf-gradient-fade--b:linear-gradient(to bottom, transparent, var(--sf-color-bg));--sf-shadow-strength:calc(.08 + var(--sf-is-dark) * .17);--sf-shadow-glow-color:var(--sf-color-primary);--sf-shadow-glow:0 0 15px 2px oklch(from var(--sf-shadow-glow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-opacity-disabled:.45;--sf-state-pending-opacity:.7;--sf-focus-ring-color:var(--sf-color-action);--sf-focus-ring-shadow:0 0 0 var(--sf-focus-ring-offset) var(--sf-color-bg), 0 0 0 calc(var(--sf-focus-ring-offset) + var(--sf-focus-ring-width)) var(--sf-focus-ring-color);--sf-caret-color:var(--sf-color-action);--sf-space-scale:1;--sf-text-scale:1;--sf-text-display-scale:1;--sf-radius-scale:1;--sf-motion-scale:1;--sf-font-body:system-ui, -apple-system, sans-serif;--sf-font-heading:var(--sf-font-body);--sf-font-display:var(--sf-font-heading);--sf-font-mono:ui-monospace, monospace;--sf-font-humanist:"Seravek", "Gill Sans Nova", "Ubuntu", "Calibri", "DejaVu Sans", source-sans-pro, sans-serif;--sf-font-geometric:"Avenir", "Montserrat", "Corbel", "URW Gothic", source-sans-pro, sans-serif;--sf-font-slab:"Rockwell", "Rockwell Nova", "Roboto Slab", "DejaVu Serif", "Sitka Small", serif;--sf-font-features:normal;--sf-font-variation:normal;--sf-optical-sizing:auto;--sf-font-weight-thin:100;--sf-font-weight-extralight:200;--sf-font-weight-light:300;--sf-font-weight-normal:400;--sf-font-weight-medium:500;--sf-font-weight-semibold:600;--sf-font-weight-bold:700;--sf-font-weight-extrabold:800;--sf-font-weight-black:900;--sf-font-weight-body:var(--sf-font-weight-normal);--sf-font-weight-heading:var(--sf-font-weight-semibold);--sf-font-weight-display:var(--sf-font-weight-bold);--sf-current-font-weight:var(--sf-font-weight-bold);--sf-text-2xs:clamp(.51rem, calc(.000296296 * (100vw - 22.5rem) + .51rem), .53rem);--sf-text-xs:clamp(.64rem, calc(.000888889 * (100vw - 22.5rem) + .64rem), .7rem);--sf-text-s:clamp(.8rem, calc(.00207407 * (100vw - 22.5rem) + .8rem), .94rem);--sf-text-m:clamp(1rem, calc(.0037037 * (100vw - 22.5rem) + 1rem), 1.25rem);--sf-text-l:clamp(1.25rem, calc(.00622222 * (100vw - 22.5rem) + 1.25rem), 1.67rem);--sf-text-xl:clamp(1.56rem, calc(.00977778 * (100vw - 22.5rem) + 1.56rem), 2.22rem);--sf-text-2xl:clamp(1.95rem, calc(.014963 * (100vw - 22.5rem) + 1.95rem), 2.96rem);--sf-text-3xl:clamp(2.44rem, calc(.0223704 * (100vw - 22.5rem) + 2.44rem), 3.95rem);--sf-text-4xl:clamp(3.05rem, calc(.0327407 * (100vw - 22.5rem) + 3.05rem), 5.26rem);--sf-text-display-s:clamp(2.4rem, calc(.00888889 * (100vw - 22.5rem) + 2.4rem), 3rem);--sf-text-display-m:clamp(3rem, calc(.0148148 * (100vw - 22.5rem) + 3rem), 4rem);--sf-text-display-l:clamp(3.75rem, calc(.0234074 * (100vw - 22.5rem) + 3.75rem), 5.33rem);--sf-leading-tight:1.1;--sf-leading-snug:1.3;--sf-leading-normal:1.5;--sf-leading-relaxed:1.625;--sf-tracking-tight:-.025em;--sf-tracking-normal:0;--sf-tracking-wide:.025em;--sf-tracking-wider:.05em;--sf-tracking-widest:.1em;--sf-icon-xs:.875em;--sf-icon-s:1em;--sf-icon-m:1.5em;--sf-icon-l:2em;--sf-icon-xl:3em;--sf-space-none:0;--sf-space-px:1px;--sf-space-gutter:var(--sf-space-l);--sf-space-2xs:calc(clamp(.51rem, calc(.00488889 * (100vw - 22.5rem) + .51rem), .84rem) * var(--sf-space-scale));--sf-space-xs:calc(clamp(.64rem, calc(.00725926 * (100vw - 22.5rem) + .64rem), 1.13rem) * var(--sf-space-scale));--sf-space-s:calc(clamp(.8rem, calc(.0103704 * (100vw - 22.5rem) + .8rem), 1.5rem) * var(--sf-space-scale));--sf-space-m:calc(clamp(1rem, calc(.0148148 * (100vw - 22.5rem) + 1rem), 2rem) * var(--sf-space-scale));--sf-space-l:calc(clamp(1.25rem, calc(.021037 * (100vw - 22.5rem) + 1.25rem), 2.67rem) * var(--sf-space-scale));--sf-space-xl:calc(clamp(1.56rem, calc(.0294815 * (100vw - 22.5rem) + 1.56rem), 3.55rem) * var(--sf-space-scale));--sf-space-2xl:calc(clamp(1.95rem, calc(.0413333 * (100vw - 22.5rem) + 1.95rem), 4.74rem) * var(--sf-space-scale));--sf-space-3xl:calc(clamp(2.44rem, calc(.0573333 * (100vw - 22.5rem) + 2.44rem), 6.31rem) * var(--sf-space-scale));--sf-space-4xl:calc(clamp(3.05rem, calc(.0795556 * (100vw - 22.5rem) + 3.05rem), 8.42rem) * var(--sf-space-scale));--sf-size-xs:1.5rem;--sf-size-s:2rem;--sf-size-m:2.5rem;--sf-size-l:2.75rem;--sf-size-xl:3.5rem;--sf-container-narrow:38rem;--sf-container-prose:65ch;--sf-container-default:75rem;--sf-container-wide:90rem;--sf-container-full:100%;--sf-ratio-square:1;--sf-ratio-video:16 / 9;--sf-ratio-cinema:21 / 9;--sf-ratio-4-3:4 / 3;--sf-ratio-3-2:3 / 2;--sf-ratio-portrait:3 / 4;--sf-ratio-golden:1.618 / 1;--sf-border-width-hairline:.5px;--sf-border-width-1:1px;--sf-border-width-2:2px;--sf-border-width-3:3px;--sf-border-width-4:4px;--sf-border-style:solid;--sf-border-style-strong:solid;--sf-border-style-soft:dashed;--sf-border-style-dotted:dotted;--sf-divider-width:var(--sf-border-width-1);--sf-divider-style:solid;--sf-divider-color:var(--sf-color-border);--sf-radius-none:0;--sf-radius-xs:calc(2px * var(--sf-radius-scale));--sf-radius-s:calc(4px * var(--sf-radius-scale));--sf-radius-m:calc(8px * var(--sf-radius-scale));--sf-radius-l:calc(12px * var(--sf-radius-scale));--sf-radius-xl:calc(16px * var(--sf-radius-scale));--sf-radius-2xl:calc(24px * var(--sf-radius-scale));--sf-radius-3xl:calc(32px * var(--sf-radius-scale));--sf-radius-4xl:calc(48px * var(--sf-radius-scale));--sf-radius-full:9999px;--sf-radius-pill:var(--sf-radius-full);--sf-radius-outer:calc(var(--sf-radius-m) + var(--sf-component-pad));--sf-shadow-color:oklch(from var(--sf-color-neutral) .15 c h);--sf-shadow-none:none;--sf-shadow-xs:0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7));--sf-shadow-s:0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 2px 6px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, var(--sf-shadow-strength), .7));--sf-shadow-m:0 1px 3px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-shadow-l:0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 8px 24px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), .7)), 0 16px 48px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-shadow-xl:0 2px 8px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 12px 36px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3.5), .7)), 0 24px 72px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7));--sf-shadow-2xl:0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .6), .7)), 0 20px 60px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 4), .7)), 0 40px 100px -8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 5), .7));--sf-shadow-inner:inset 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-text-shadow-none:none;--sf-text-shadow-s:0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), .7));--sf-text-shadow-m:0 2px 4px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-text-shadow-l:0 4px 8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7));--sf-drop-shadow-s:drop-shadow(0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), .7)));--sf-drop-shadow-m:drop-shadow(0 4px 6px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7)));--sf-drop-shadow-l:drop-shadow(0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7)));--sf-blur-xs:4px;--sf-blur-s:8px;--sf-blur-m:16px;--sf-blur-l:32px;--sf-blur-xl:48px;--sf-perspective-near:500px;--sf-perspective-normal:1000px;--sf-perspective-far:2000px;--sf-opacity-0:0;--sf-opacity-10:.1;--sf-opacity-25:.25;--sf-opacity-50:.5;--sf-opacity-75:.75;--sf-opacity-100:1;--sf-duration-none:0s;--sf-duration-instant:calc(.1s * var(--sf-motion-scale));--sf-duration-fast:calc(.15s * var(--sf-motion-scale));--sf-duration-normal:calc(.25s * var(--sf-motion-scale));--sf-duration-slow:calc(.4s * var(--sf-motion-scale));--sf-duration-slower:calc(.6s * var(--sf-motion-scale));--sf-ease-linear:linear;--sf-ease-out:cubic-bezier(.25, 0, .15, 1);--sf-ease-in:cubic-bezier(.5, 0, .75, .25);--sf-ease-in-out:cubic-bezier(.4, 0, .2, 1);--sf-ease-spring:linear(0, .5, 1.1, .95, 1.02, 1);--sf-ease-elastic:linear(0, .3, 1.2, .9, 1.05, 1);--sf-ease-bounce:linear(0, .35 18%, 1 32%, .86 42%, 1.02 56%, .98 72%, 1);--sf-ease-overshoot:linear(0, .6 30%, 1.08 55%, .98 75%, 1);--sf-animation-fade-in:sf-fade-in var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-fade-out:sf-fade-out var(--sf-duration-normal) var(--sf-ease-in) both;--sf-animation-slide-in-up:sf-slide-in-up var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-down:sf-slide-in-down var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-left:sf-slide-in-left var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-right:sf-slide-in-right var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-scale-up:sf-scale-up var(--sf-duration-normal) var(--sf-ease-overshoot) both;--sf-animation-scale-down:sf-scale-down var(--sf-duration-normal) var(--sf-ease-in) both;--sf-animation-color-pulse:sf-color-pulse var(--sf-duration-slow) var(--sf-ease-in-out) infinite;--sf-animation-ping:sf-ping var(--sf-duration-slow) var(--sf-ease-out) infinite;--sf-animation-blink:sf-blink calc(1s * var(--sf-motion-scale)) steps(1, end) infinite;--sf-animation-float:sf-float calc(3s * var(--sf-motion-scale)) var(--sf-ease-in-out) infinite;--sf-animation-spin:sf-spin var(--sf-duration-slower) linear infinite;--sf-animation-shimmer:sf-shimmer calc(1.5s * var(--sf-motion-scale)) ease-in-out infinite;--sf-animation-delay-1:calc(75ms * var(--sf-motion-scale));--sf-animation-delay-2:calc(.15s * var(--sf-motion-scale));--sf-animation-delay-3:calc(.225s * var(--sf-motion-scale));--sf-animation-delay-4:calc(.3s * var(--sf-motion-scale));--sf-animation-delay-5:calc(.375s * var(--sf-motion-scale));--sf-scroll-timeline-range-start:0%;--sf-scroll-timeline-range-end:100%;--sf-mask-scrim-start:var(--sf-space-l);--sf-mask-scrim-end:var(--sf-space-l);--sf-z-below:-1;--sf-z-base:0;--sf-z-raised:1;--sf-z-low:10;--sf-z-mid:100;--sf-z-high:500;--sf-z-top:900;--sf-z-max:9999;--sf-header-height:8rem;--sf-sticky-offset:var(--sf-header-height);--sf-focus-ring-width:2px;--sf-focus-ring-offset:2px;--sf-focus-ring-style:solid;--sf-touch-target:var(--sf-size-l);--sf-contrast-bias:0;--sf-contrast-threshold:.6;--sf-safe-top:env(safe-area-inset-top,0px);--sf-safe-bottom:env(safe-area-inset-bottom,0px);--sf-safe-left:env(safe-area-inset-left,0px);--sf-safe-right:env(safe-area-inset-right,0px);--sf-print-page-margin:2cm;--sf-print-page-size:a4;--sf-print-base-size:11pt;--sf-stroke-thin:1px;--sf-stroke-regular:1.5px;--sf-stroke-bold:2px;--sf-stroke-heavy:3px;--sf-body-font-size:var(--sf-text-m);--sf-body-line-height:var(--sf-leading-normal);--sf-body-font-weight:var(--sf-font-weight-body);--sf-body-font-family:var(--sf-font-body);--sf-body-color:var(--sf-color-text);--sf-body-text-wrap:pretty;--sf-body-strong-weight:var(--sf-font-weight-bold);--sf-body-em-style:italic;--sf-code-font-size:.875em;--sf-heading-font-family:var(--sf-font-heading);--sf-heading-color:var(--sf-color-heading);--sf-heading-text-wrap:balance;--sf-h1-size:var(--sf-text-4xl);--sf-h1-line-height:var(--sf-leading-tight);--sf-h1-font-weight:var(--sf-font-weight-heading);--sf-h1-letter-spacing:var(--sf-tracking-tight);--sf-h2-size:var(--sf-text-3xl);--sf-h2-line-height:var(--sf-leading-tight);--sf-h2-font-weight:var(--sf-font-weight-heading);--sf-h2-letter-spacing:var(--sf-tracking-tight);--sf-h3-size:var(--sf-text-2xl);--sf-h3-line-height:var(--sf-leading-snug);--sf-h3-font-weight:var(--sf-font-weight-heading);--sf-h3-letter-spacing:var(--sf-tracking-normal);--sf-h4-size:var(--sf-text-xl);--sf-h4-line-height:var(--sf-leading-snug);--sf-h4-font-weight:var(--sf-font-weight-heading);--sf-h4-letter-spacing:var(--sf-tracking-normal);--sf-h5-size:var(--sf-text-l);--sf-h5-line-height:var(--sf-leading-normal);--sf-h5-font-weight:var(--sf-font-weight-heading);--sf-h5-letter-spacing:var(--sf-tracking-normal);--sf-h6-size:var(--sf-text-m);--sf-h6-line-height:var(--sf-leading-normal);--sf-h6-font-weight:var(--sf-font-weight-heading);--sf-h6-letter-spacing:var(--sf-tracking-wide);--sf-gap:var(--sf-space-m);--sf-content-gap:var(--sf-space-s);--sf-component-pad:var(--sf-space-m);--sf-field-block:var(--sf-space-l);--sf-field-required-marker:" *";--sf-link-external-marker:" ↗";--sf-section-pad:var(--sf-section-pad--m);--sf-section-pad--xs:var(--sf-space-xl);--sf-section-pad--s:var(--sf-space-2xl);--sf-section-pad--m:var(--sf-space-3xl);--sf-section-pad--l:var(--sf-space-4xl);--sf-section-pad--xl:calc(var(--sf-space-4xl) * 1.5);--sf-section-pad--2xl:calc(var(--sf-space-4xl) * 2);--sf-transition-all:all var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-colors:color var(--sf-duration-normal) var(--sf-ease-out), background-color var(--sf-duration-normal) var(--sf-ease-out), border-color var(--sf-duration-normal) var(--sf-ease-out), text-decoration-color var(--sf-duration-normal) var(--sf-ease-out), fill var(--sf-duration-normal) var(--sf-ease-out), stroke var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-transform:transform var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-opacity:opacity var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-shadow:box-shadow var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-fast:all var(--sf-duration-fast) var(--sf-ease-out);--sf-transition-slow:all var(--sf-duration-slow) var(--sf-ease-in-out);--sf-transition-enter:all var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-exit:all var(--sf-duration-fast) var(--sf-ease-in);--sf-space-gap:var(--sf-gap);--sf-space-content:var(--sf-content-gap);--sf-stack-gap:var(--sf-space-content);--sf-gap-size:var(--sf-space-gap);--sf-box-padding:var(--sf-space-m);--sf-box-border-width:0;--sf-box-border-color:var(--sf-color-border);--sf-center-max:var(--sf-container-default);--sf-center-gutter:var(--sf-space-gutter);--sf-cluster-gap:var(--sf-space-gap);--sf-cluster-align:center;--sf-cluster-justify:flex-start;--sf-sidebar-gap:var(--sf-space-gap);--sf-sidebar-min-width:50%;--sf-sidebar-width:18rem;--sf-switcher-threshold:30rem;--sf-switcher-gap:var(--sf-space-gap);--sf-grid-min:16rem;--sf-grid-gap:var(--sf-space-gap);--sf-grid-min-xs:10rem;--sf-grid-min-s:13rem;--sf-grid-min-m:16rem;--sf-grid-min-l:20rem;--sf-grid-min-xl:24rem;--sf-grid-min-2xl:28rem;--sf-equal-cols:2;--sf-equal-gap:var(--sf-space-gap);--sf-cover-min-height:100dvh;--sf-cover-padding:var(--sf-section-pad);--sf-frame-ratio:16 / 9;--sf-icon-box-pad:.5em;--sf-icon-box-radius:var(--sf-radius-s);--sf-icon-box-bg:var(--sf-color-well);--sf-icon-box-border:var(--sf-border-width-1) solid var(--sf-color-border);--sf-reel-item-width:max-content;--sf-reel-gap:var(--sf-space-gap);--sf-reel-height:auto;--sf-imposter-margin:var(--sf-space-m);--sf-bento-cols-default:3;--sf-bento-row-default:10rem;--sf-bento-row-compact:6rem;--sf-bento-row-tall:16rem;--sf-bento-gap:var(--sf-space-gap);--sf-breakout-width:var(--sf-container-wide);--sf-content-width:var(--sf-container-default);--sf-prose-paragraph:var(--sf-space-content);--sf-alternate-gap:var(--sf-space-content);--sf-alternate-inner-gap:var(--sf-space-gap);--sf-flow-space:var(--sf-space-content);--sf-line-clamp:3;--sf-truncate-suffix:"…";--sf-aspect:16 / 9;--sf-scroll-shadow-size:2rem}*,:before,:after{box-sizing:border-box;margin:0;padding:0}html{text-size-adjust:100%;scroll-padding-top:var(--sf-header-height,3.5rem);scrollbar-gutter:stable;color-scheme:var(--sf-color-scheme,light dark);hanging-punctuation:first last}@supports (interpolate-size:allow-keywords){html{interpolate-size:allow-keywords}}body{min-height:100dvh;min-inline-size:320px}menu,ul,ol{list-style:none}img,picture,video,canvas,svg{max-width:100%;height:auto;display:block}iframe,embed,object{max-width:100%;height:unset;border:none;display:block}button,input,select,textarea{font:inherit;color:inherit;letter-spacing:inherit;color-scheme:inherit;line-height:inherit}button{text-transform:none}search{display:block}fieldset{border:none;min-width:0;padding:0}legend{padding:0}a,button,input,select,textarea,summary{touch-action:manipulation}summary{cursor:pointer;list-style:none}textarea{resize:vertical}button{cursor:pointer}table{border-collapse:collapse;border-spacing:0}[hidden]{display:none!important}dialog,[popover]{color:inherit;background:0 0;border:none;padding:0}[inert]{cursor:default}body{font-family:var(--sf-body-font-family,var(--sf-font-body));font-size:var(--sf-body-font-size,var(--sf-text-m));line-height:var(--sf-body-line-height,var(--sf-leading-normal));font-weight:var(--sf-body-font-weight,var(--sf-font-weight-body));color:var(--sf-body-color,var(--sf-color-text));background-color:var(--sf-color-bg,#fff);font-synthesis:none}h1,h2,h3,h4,h5,h6{font-family:var(--sf-heading-font-family,var(--sf-font-heading));line-height:var(--sf-leading-tight);color:var(--sf-heading-color,var(--sf-color-heading));text-wrap:var(--sf-heading-text-wrap);overflow-wrap:break-word;text-rendering:optimizelegibility;scroll-margin-top:calc(var(--sf-header-height,3.5rem) + var(--sf-space-m))}h1{font-size:var(--sf-h1-size);line-height:var(--sf-h1-line-height);font-weight:var(--sf-h1-font-weight);letter-spacing:var(--sf-h1-letter-spacing)}h2{font-size:var(--sf-h2-size);line-height:var(--sf-h2-line-height);font-weight:var(--sf-h2-font-weight);letter-spacing:var(--sf-h2-letter-spacing)}h3{font-size:var(--sf-h3-size);line-height:var(--sf-h3-line-height);font-weight:var(--sf-h3-font-weight);letter-spacing:var(--sf-h3-letter-spacing)}h4{font-size:var(--sf-h4-size);line-height:var(--sf-h4-line-height);font-weight:var(--sf-h4-font-weight);letter-spacing:var(--sf-h4-letter-spacing)}h5{font-size:var(--sf-h5-size);line-height:var(--sf-h5-line-height);font-weight:var(--sf-h5-font-weight);letter-spacing:var(--sf-h5-letter-spacing)}h6{font-size:var(--sf-h6-size);line-height:var(--sf-h6-line-height);font-weight:var(--sf-h6-font-weight);letter-spacing:var(--sf-h6-letter-spacing)}p{text-wrap:var(--sf-body-text-wrap);overflow-wrap:break-word;orphans:2;widows:2}b,strong{font-weight:var(--sf-body-strong-weight)}em{font-style:var(--sf-body-em-style)}small{font-size:var(--sf-text-s,80%)}mark{background-color:var(--sf-color-mark-bg,yellow);color:var(--sf-color-mark-text,black);border-radius:var(--sf-radius-xs);padding-inline:.2em}abbr[title]{cursor:help;text-decoration:underline dotted}kbd,samp{font-family:var(--sf-font-mono);font-size:inherit}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sup{top:-.5em}sub{bottom:-.25em}a:link{color:var(--sf-color-link);text-decoration-color:var(--sf-color-link--underline);text-underline-offset:.15em;text-underline-position:from-font;transition:color var(--sf-duration-fast) var(--sf-ease-out), text-decoration-color var(--sf-duration-fast) var(--sf-ease-out)}a:hover{color:var(--sf-color-link--hover);text-decoration-color:currentColor}a:visited{color:var(--sf-color-link--visited)}a:active{color:var(--sf-color-link--active)}a:not([class]){text-decoration-skip-ink:auto}a,code{overflow-wrap:break-word}code{font-family:var(--sf-font-mono);font-size:var(--sf-code-font-size);border-radius:var(--sf-radius-xs);background-color:var(--sf-color-code-bg);color:var(--sf-color-code-text,inherit);padding:.125em .3em}pre{font-family:var(--sf-font-mono);font-size:var(--sf-text-s);padding:var(--sf-space-m);border-radius:var(--sf-radius-m);background:var(--sf-color-code-block-bg,var(--sf-color-code-bg));color:var(--sf-color-code-block-text,inherit);tab-size:2;hyphens:none;overflow-x:auto}pre code{color:inherit;font-size:inherit;background:0 0;border:0;border-radius:0;padding:0}svg{fill:currentColor}hr{border:none;border-block-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color)}:target{scroll-margin-top:calc(var(--sf-header-height,3.5rem) + var(--sf-space-m))}::selection{background:var(--sf-color-selection-bg);color:var(--sf-color-selection-text);text-shadow:none}th,caption{text-align:start}address,cite,dfn,var{font-style:normal}input,textarea{caret-color:var(--sf-caret-color)}input,progress{accent-color:var(--sf-color-action)}::backdrop{background-color:var(--sf-color-dim)}optgroup{font:inherit}::file-selector-button{font:inherit}::placeholder{opacity:1;color:var(--sf-color-text--placeholder,currentcolor)}blockquote{border-inline-start:var(--sf-border-width-3) solid var(--sf-color-border);padding-inline-start:var(--sf-space-m);font-style:italic}dt{font-weight:var(--sf-font-weight-heading)}dd{margin-inline-start:var(--sf-space-m)}table{text-align:start;width:100%}th{font-weight:var(--sf-font-weight-heading)}td,th{padding:var(--sf-space-2xs) var(--sf-space-xs);border-block-end:var(--sf-border-width-hairline) solid var(--sf-color-border--subtle)}@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--sf-is-dark:1}}[data-theme=light]{color-scheme:light;--sf-is-dark:0}[data-theme=dark]{color-scheme:dark;--sf-is-dark:1}:root[data-lumlocker]{--sf-color-primary:light-dark(oklch(from var(--sf-color-primary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-primary-dark,var(--sf-color-primary-light)) var(--sf-lumlocker) c h));--sf-color-secondary:light-dark(oklch(from var(--sf-color-secondary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-secondary-dark,var(--sf-color-secondary-light)) var(--sf-lumlocker) c h));--sf-color-tertiary:light-dark(oklch(from var(--sf-color-tertiary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-tertiary-dark,var(--sf-color-tertiary-light)) var(--sf-lumlocker) c h));--sf-color-action:light-dark(oklch(from var(--sf-color-action-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-action-dark,var(--sf-color-action-light)) var(--sf-lumlocker) c h));--sf-color-neutral:light-dark(oklch(from var(--sf-color-neutral-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-neutral-dark,var(--sf-color-neutral-light)) var(--sf-lumlocker) c h))}.sf-section{padding-block:var(--sf-section-pad)}.sf-section--xs{padding-block:var(--sf-section-pad--xs)}.sf-section--s{padding-block:var(--sf-section-pad--s)}.sf-section--m{padding-block:var(--sf-section-pad--m)}.sf-section--l{padding-block:var(--sf-section-pad--l)}.sf-section--xl{padding-block:var(--sf-section-pad--xl)}.sf-section--2xl{padding-block:var(--sf-section-pad--2xl)}.sf-section-group>.sf-section+.sf-section{padding-block-start:0}.sf-section--collapse:has(+.sf-section--collapse){padding-block-end:calc(var(--sf-section-pad) / 2)}.sf-section--collapse+.sf-section--collapse{padding-block-start:calc(var(--sf-section-pad) / 2)}.sf-divider{border:none;border-block-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);margin-block:var(--sf-space-m)}.sf-divider--vertical{border-block-start:none;border-inline-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);margin-block:0;margin-inline:var(--sf-space-m);align-self:stretch}.sf-container{width:100%;max-width:var(--sf-container-default);padding-inline:var(--sf-space-gutter);margin-inline:auto;container:sf-layout/inline-size}.sf-container--narrow{max-width:var(--sf-container-narrow)}.sf-container--prose{max-width:var(--sf-container-prose)}.sf-container--wide{max-width:var(--sf-container-wide)}.sf-container--full{max-width:var(--sf-container-full)}.sf-stack{gap:var(--sf-stack-gap);flex-direction:column;display:flex}.sf-stack--xs{gap:var(--sf-space-xs)}.sf-stack--s{gap:var(--sf-space-s)}.sf-stack--m{gap:var(--sf-space-m)}.sf-stack--l{gap:var(--sf-space-l)}.sf-stack--xl{gap:var(--sf-space-xl)}.sf-stack--2xl{gap:var(--sf-space-2xl)}.sf-stack--center{align-items:center}.sf-stack--end{align-items:flex-end}.sf-stack--stretch{align-items:stretch}.sf-gap{gap:var(--sf-gap-size)}.sf-gap--xs{gap:var(--sf-space-xs)}.sf-gap--s{gap:var(--sf-space-s)}.sf-gap--m{gap:var(--sf-space-m)}.sf-gap--l{gap:var(--sf-space-l)}.sf-gap--xl{gap:var(--sf-space-xl)}.sf-gap--2xl{gap:var(--sf-space-2xl)}.sf-box{padding:var(--sf-box-padding);outline:var(--sf-box-border-width) solid var(--sf-box-border-color)}.sf-center{box-sizing:content-box;max-inline-size:var(--sf-center-max);padding-inline:var(--sf-center-gutter);margin-inline:auto}.sf-center--intrinsic{flex-direction:column;align-items:center;display:flex}.sf-cluster{gap:var(--sf-cluster-gap);align-items:var(--sf-cluster-align);justify-content:var(--sf-cluster-justify);flex-wrap:wrap;display:flex}.sf-cluster--xs{gap:var(--sf-space-xs)}.sf-cluster--s{gap:var(--sf-space-s)}.sf-cluster--m{gap:var(--sf-space-m)}.sf-cluster--l{gap:var(--sf-space-l)}.sf-cluster--xl{gap:var(--sf-space-xl)}.sf-cluster--2xl{gap:var(--sf-space-2xl)}.sf-cluster--no-wrap{flex-wrap:nowrap}.sf-cluster--center{justify-content:center}.sf-cluster--end{justify-content:flex-end}.sf-cluster--between{justify-content:space-between}.sf-sidebar{gap:var(--sf-sidebar-gap);flex-wrap:wrap;display:flex}.sf-sidebar>:first-child{flex-basis:var(--sf-sidebar-width);flex-grow:1}.sf-sidebar>:last-child,.sf-sidebar--right>:first-child{min-inline-size:var(--sf-sidebar-min-width);flex-grow:999;flex-basis:0}.sf-sidebar--right>:last-child{flex-basis:var(--sf-sidebar-width);flex-grow:1}.sf-sidebar--narrow{--sf-sidebar-width:12rem}.sf-sidebar--wide{--sf-sidebar-width:26rem}.sf-switcher{gap:var(--sf-switcher-gap);flex-wrap:wrap;display:flex}.sf-switcher>*{flex-grow:1;flex-basis:calc((var(--sf-switcher-threshold) - 100%) * 999)}.sf-switcher--no-wrap{flex-wrap:nowrap;overflow-x:auto}.sf-switcher--vertical{flex-direction:column}.sf-grid{grid-template-columns:repeat(auto-fill, minmax(min(var(--sf-grid-min), 100%), 1fr));gap:var(--sf-grid-gap);display:grid}.sf-grid--fit{grid-template-columns:repeat(auto-fit, minmax(min(var(--sf-grid-min), 100%), 1fr))}.sf-grid--xs{--sf-grid-min:var(--sf-grid-min-xs)}.sf-grid--s{--sf-grid-min:var(--sf-grid-min-s)}.sf-grid--m{--sf-grid-min:var(--sf-grid-min-m)}.sf-grid--l{--sf-grid-min:var(--sf-grid-min-l)}.sf-grid--xl{--sf-grid-min:var(--sf-grid-min-xl)}.sf-grid--2xl{--sf-grid-min:var(--sf-grid-min-2xl)}.sf-grid--dense{grid-auto-flow:dense}.sf-icon{inline-size:var(--sf-icon-size,var(--sf-icon-m));block-size:var(--sf-icon-size,var(--sf-icon-m));vertical-align:text-bottom;fill:currentColor;flex-shrink:0;display:inline-block}.sf-icon--xs{--sf-icon-size:var(--sf-icon-xs)}.sf-icon--s{--sf-icon-size:var(--sf-icon-s)}.sf-icon--m{--sf-icon-size:var(--sf-icon-m)}.sf-icon--l{--sf-icon-size:var(--sf-icon-l)}.sf-icon--xl{--sf-icon-size:var(--sf-icon-xl)}.sf-icon--boxed{inline-size:calc(var(--sf-icon-size,var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);block-size:calc(var(--sf-icon-size,var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);padding:var(--sf-icon-box-pad);border:var(--sf-icon-box-border);border-radius:var(--sf-icon-box-radius);background:var(--sf-icon-box-bg);box-sizing:content-box}.sf-cover{min-height:var(--sf-cover-min-height);padding-block:var(--sf-cover-padding);flex-direction:column;display:flex}.sf-cover>.sf-cover__center{margin-block:auto}.sf-cover--min{min-height:50dvh}.sf-cover--max{min-height:0;max-height:100dvh}.sf-cover--padding-s{padding-block:var(--sf-space-xl)}.sf-cover--padding-l{padding-block:var(--sf-space-4xl)}.sf-frame{aspect-ratio:var(--sf-frame-ratio);justify-content:center;align-items:center;display:flex;overflow:hidden}.sf-frame>img,.sf-frame>video{object-fit:cover;width:100%;height:100%}.sf-frame--square{aspect-ratio:var(--sf-ratio-square)}.sf-frame--video{aspect-ratio:var(--sf-ratio-video)}.sf-frame--cinema{aspect-ratio:var(--sf-ratio-cinema)}.sf-frame--portrait{aspect-ratio:var(--sf-ratio-portrait)}.sf-frame--4-3{aspect-ratio:var(--sf-ratio-4-3)}.sf-frame--3-2{aspect-ratio:var(--sf-ratio-3-2)}.sf-frame--golden{aspect-ratio:var(--sf-ratio-golden)}.sf-reel{block-size:var(--sf-reel-height);gap:var(--sf-reel-gap);scroll-snap-type:x mandatory;scroll-padding-inline:var(--sf-reel-gap);overscroll-behavior-x:contain;scrollbar-width:thin;display:flex;overflow-x:auto}.sf-reel>*{flex:0 0 var(--sf-reel-item-width);scroll-snap-align:start}.sf-imposter{position:absolute;inset-block-start:50%;inset-inline-start:50%;translate:-50% -50%}.sf-imposter--fixed{position:fixed}.sf-imposter--contain{max-inline-size:calc(100% - var(--sf-imposter-margin) * 2);max-block-size:calc(100% - var(--sf-imposter-margin) * 2);overflow:auto}.sf-alternate{gap:var(--sf-alternate-gap);display:grid;container:sf-alternate/inline-size}.sf-alternate>*{gap:var(--sf-alternate-inner-gap);align-items:center;display:grid}@container sf-alternate (width>=48em){.sf-alternate>*{grid-template-columns:1fr 1fr}.sf-alternate>:nth-child(2n)>:first-child{order:2}.sf-alternate>:nth-child(2n)>:last-child{order:1}}.sf-pancake{grid-template-rows:auto 1fr auto;min-height:100dvh;display:grid}.sf-grid-1,.sf-grid-2,.sf-grid-3,.sf-grid-4,.sf-grid-6{gap:var(--sf-grid-gap);display:grid}@container (width>=30em){.sf-grid-4{grid-template-columns:repeat(2,1fr)}.sf-grid-6{grid-template-columns:repeat(3,1fr)}}@container (width>=48em){.sf-grid-2{grid-template-columns:repeat(2,1fr)}.sf-grid-3{grid-template-columns:repeat(3,1fr)}.sf-grid-4{grid-template-columns:repeat(4,1fr)}.sf-grid-6{grid-template-columns:repeat(6,1fr)}}.sf-grid-1-2,.sf-grid-2-1,.sf-grid-1-3,.sf-grid-3-1{gap:var(--sf-grid-gap);display:grid}@container (width>=48em){.sf-grid-1-2{grid-template-columns:1fr 2fr}.sf-grid-2-1{grid-template-columns:2fr 1fr}.sf-grid-1-3{grid-template-columns:1fr 3fr}.sf-grid-3-1{grid-template-columns:3fr 1fr}}.sf-equal{grid-template-columns:repeat(var(--sf-equal-cols), 1fr);gap:var(--sf-equal-gap);display:grid}.sf-equal--2{--sf-equal-cols:2}.sf-equal--3{--sf-equal-cols:3}.sf-equal--4{--sf-equal-cols:4}.sf-equal--6{--sf-equal-cols:6}.sf-content-grid{grid-template-columns:[full-start] minmax(var(--sf-space-gutter), 1fr) [breakout-start] minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2)) [content-start] min(var(--sf-content-width), 100% - var(--sf-space-gutter) * 2) [content-end] minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2)) [breakout-end] minmax(var(--sf-space-gutter), 1fr) [full-end];display:grid}.sf-content-grid>*{grid-column:content}.sf-content-grid>.sf-breakout{grid-column:breakout}.sf-content-grid>.sf-full-bleed{grid-column:full}.sf-bento{grid-template-columns:repeat(var(--sf-bento-cols,var(--sf-bento-cols-default)), 1fr);grid-auto-rows:minmax(var(--sf-bento-row,var(--sf-bento-row-default)), auto);grid-auto-flow:dense;gap:var(--sf-bento-gap);display:grid}@container (width>=30em) and (width<=47.99em){.sf-bento{grid-template-columns:repeat(2,1fr)}}.sf-bento--2{--sf-bento-cols:2}.sf-bento--4{--sf-bento-cols:4}.sf-bento--compact{--sf-bento-row:var(--sf-bento-row-compact)}.sf-bento--tall{--sf-bento-row:var(--sf-bento-row-tall)}.sf-subgrid{grid-template-columns:subgrid;display:grid}.sf-subgrid-rows{grid-template-rows:subgrid;display:grid}.sf-prose{max-width:var(--sf-container-prose);overflow-wrap:break-word}.sf-prose>*+*{margin-block-start:var(--sf-prose-paragraph)}.sf-prose :is(h2,h3,h4)+*{margin-block-start:var(--sf-space-s)}.sf-prose li+li{margin-block-start:var(--sf-space-xs)}.sf-prose :is(blockquote,pre){margin-block:var(--sf-space-m)}.sf-prose img{margin-block:var(--sf-space-m);border-radius:var(--sf-radius-m)}.sf-prose :is(ul,ol){padding-inline-start:1.25em}.sf-prose ul{list-style:outside}.sf-prose ol{list-style:decimal}.sf-prose ::marker{color:var(--sf-color-primary)}.sf-prose figure{margin-block:var(--sf-space-l)}.sf-prose figcaption{font-size:var(--sf-text-s);color:var(--sf-color-text--muted);margin-block-start:var(--sf-space-xs)}.sf-prose table{margin-block:var(--sf-space-m);max-width:100%;display:block;overflow-x:auto}.sf-prose video{margin-block:var(--sf-space-m);border-radius:var(--sf-radius-m)}.sf-prose .sf-not-prose>*+*,.sf-prose .sf-not-prose li+li{margin-block-start:0}.sf-prose .sf-not-prose :is(ul,ol){list-style:revert;padding-inline-start:revert}.sf-prose .sf-not-prose ::marker{color:inherit}.sf-prose .sf-not-prose img{border-radius:0;margin-block:0}.sf-prose .sf-not-prose figure{margin-block:0}.sf-prose .sf-not-prose figcaption{font-size:inherit;color:inherit;margin-block-start:0}.sf-prose .sf-not-prose table{display:revert;overflow-x:revert;max-width:revert;margin-block:0}.sf-prose .sf-not-prose video{border-radius:0;margin-block:0}.sf-flow>*+*{margin-block-start:var(--sf-flow-space)}.sf-truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.sf-line-clamp-2,.sf-line-clamp-3,.sf-line-clamp-N{-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.sf-line-clamp-2{-webkit-line-clamp:2;line-clamp:2}.sf-line-clamp-3{-webkit-line-clamp:3;line-clamp:3}.sf-line-clamp-N{-webkit-line-clamp:var(--sf-line-clamp);line-clamp:var(--sf-line-clamp)}.sf-equal-height{align-items:stretch;display:flex}.sf-equal-height>*{flex:auto;block-size:auto}.sf-aspect{aspect-ratio:var(--sf-aspect)}.sf-scroll-shadow{mask-image:linear-gradient(to bottom, transparent 0, black var(--sf-scroll-shadow-size), black calc(100% - var(--sf-scroll-shadow-size)), transparent 100%);overflow-y:auto}.sf-scroll-snap{scroll-snap-type:y mandatory;overscroll-behavior-y:contain;overflow-y:auto}.sf-scroll-snap>*{scroll-snap-align:start}.sf-overflow-fade{mask-image:linear-gradient(to right, black 0, black calc(100% - var(--sf-scroll-shadow-size)), transparent 100%);overflow:hidden}.sf-no-tap-highlight{-webkit-tap-highlight-color:transparent}.sf-surface--primary{background:var(--sf-color-primary);color:var(--sf-color-text--on-primary)}.sf-surface--secondary{background:var(--sf-color-secondary);color:var(--sf-color-text--on-secondary)}.sf-surface--tertiary{background:var(--sf-color-tertiary);color:var(--sf-color-text--on-tertiary)}.sf-surface--action{background:var(--sf-color-action);color:var(--sf-color-text--on-action)}.sf-surface--neutral{background:var(--sf-color-neutral);color:var(--sf-color-text--on-neutral)}.sf-surface--inverse{background:var(--sf-color-inverse);color:var(--sf-color-text--on-inverse)}.sf-surface--success{background:var(--sf-color-success);color:var(--sf-color-text--on-success)}.sf-surface--warning{background:var(--sf-color-warning);color:var(--sf-color-text--on-warning)}.sf-surface--error{background:var(--sf-color-error);color:var(--sf-color-text--on-error)}.sf-surface--info{background:var(--sf-color-info);color:var(--sf-color-text--on-info)}.sf-surface--danger{background:var(--sf-color-danger);color:var(--sf-color-text--on-danger)}.sf-text-gradient{background-image:var(--sf-gradient-primary);-webkit-background-clip:text;background-clip:text}@supports (background-clip:text){.sf-text-gradient{color:#0000}}.sf-link-external:after{content:var(--sf-link-external-marker);font-size:.85em;text-decoration:none;display:inline-block}.is-hidden{display:none!important}.is-invisible{visibility:hidden}.is-visible{visibility:visible}.is-disabled{opacity:var(--sf-opacity-disabled);pointer-events:none;cursor:not-allowed;user-select:none}.is-readonly{pointer-events:none;user-select:none}.is-loading{pointer-events:none;position:relative;color:#0000!important}.is-loading:after{content:"";border:var(--sf-border-width-2) solid var(--sf-color-border--strong);border-block-start-color:var(--sf-color-action);border-radius:var(--sf-radius-full);width:1em;height:1em;animation:var(--sf-animation-spin);margin-block-start:-.5em;margin-inline-start:-.5em;position:absolute;inset-block-start:50%;inset-inline-start:50%}.is-busy{cursor:progress}.is-pending{opacity:var(--sf-state-pending-opacity);cursor:progress}.is-skeleton{background:linear-gradient(90deg, var(--sf-color-well) 25%, var(--sf-color-raised) 50%, var(--sf-color-well) 75%);animation:var(--sf-animation-shimmer);border-radius:var(--sf-radius-s);pointer-events:none;user-select:none;background-size:200% 100%;color:#0000!important}.is-active{--sf-is-active:1}.is-selected{background-color:var(--sf-color-bg--selected)}.is-current{--sf-is-current:1;font-weight:var(--sf-current-font-weight,var(--sf-font-weight-bold))}.is-highlighted{background-color:var(--sf-color-bg--focus)}.is-pressed{--sf-is-pressed:1;background-color:var(--sf-color-bg--selected)}.is-open{--sf-is-open:1}.is-collapsed{--sf-is-open:0}.is-expanded{--sf-is-open:1}.is-valid{--sf-field-border-color:var(--sf-color-success);--sf-field-text-color:var(--sf-color-success-strong)}.is-invalid{--sf-field-border-color:var(--sf-color-error);--sf-field-text-color:var(--sf-color-error-strong)}.is-warning{--sf-field-border-color:var(--sf-color-warning);--sf-field-text-color:var(--sf-color-warning-strong)}.is-success{--sf-field-border-color:var(--sf-color-success);--sf-field-text-color:var(--sf-color-success-strong)}.is-error{--sf-field-border-color:var(--sf-color-error);--sf-field-text-color:var(--sf-color-error-strong)}.is-info{--sf-field-border-color:var(--sf-color-info);--sf-field-text-color:var(--sf-color-info-strong)}.is-danger{--sf-field-border-color:var(--sf-color-danger);--sf-field-text-color:var(--sf-color-danger-strong)}.is-sticky{top:var(--sf-sticky-offset,0);z-index:var(--sf-z-low);position:sticky}.is-pinned{z-index:var(--sf-z-mid);position:sticky;top:0}.is-fixed{position:fixed}.is-fullscreen{z-index:var(--sf-z-max);position:fixed;inset:0}.is-resizable{resize:both;overflow:auto}.is-clipped{overflow:hidden!important}.is-scrollable{overscroll-behavior:contain;overflow:auto}.is-truncated{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.is-dragging{opacity:var(--sf-opacity-50);cursor:grabbing}.is-drop-target{outline:var(--sf-focus-ring-width) dashed var(--sf-color-action);outline-offset:var(--sf-focus-ring-offset)}.is-draggable{cursor:grab}.is-overlay{position:absolute;inset:0}.is-clickable{cursor:pointer}.is-unselectable{user-select:none}.is-focused{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-focus-ring-color);outline-offset:var(--sf-focus-ring-offset)}.is-empty:empty{display:none}@media (prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}html:focus-within{scroll-behavior:auto}a,button,input,select,textarea,summary{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-duration:var(--sf-duration-fast);transition-timing-function:var(--sf-ease-out)}@supports (view-transition-name:none){::view-transition-old(root),::view-transition-new(root){animation-duration:var(--sf-duration-normal);animation-timing-function:var(--sf-ease-out)}}.sf-fade-in{animation:var(--sf-animation-fade-in)}.sf-fade-out{animation:var(--sf-animation-fade-out)}.sf-slide-in-up{animation:var(--sf-animation-slide-in-up)}.sf-slide-in-down{animation:var(--sf-animation-slide-in-down)}.sf-slide-in-left{animation:var(--sf-animation-slide-in-left)}.sf-slide-in-right{animation:var(--sf-animation-slide-in-right)}.sf-scale-up{animation:var(--sf-animation-scale-up)}.sf-scale-down{animation:var(--sf-animation-scale-down)}.sf-entrance--fade,.sf-entrance--fade-up,.sf-entrance--fade-down,.sf-entrance--fade-left,.sf-entrance--fade-right,.sf-entrance--scale-up{animation-duration:var(--sf-duration-slow);animation-timing-function:var(--sf-ease-out);animation-fill-mode:both}@supports (animation-timeline:view()){.sf-entrance--fade,.sf-entrance--fade-up,.sf-entrance--fade-down,.sf-entrance--fade-left,.sf-entrance--fade-right,.sf-entrance--scale-up{animation-timeline:view();animation-range:var(--sf-scroll-timeline-range-start,entry 0%) var(--sf-scroll-timeline-range-end,cover 30%)}}.sf-entrance--fade{animation-name:sf-fade-in}.sf-entrance--fade-up{animation-name:sf-slide-in-up}.sf-entrance--fade-down{animation-name:sf-slide-in-down}.sf-entrance--fade-left{animation-name:sf-slide-in-left}.sf-entrance--fade-right{animation-name:sf-slide-in-right}.sf-entrance--scale-up{animation-name:sf-scale-up}}@keyframes sf-fade-in{0%{opacity:0}to{opacity:1}}@keyframes sf-fade-out{0%{opacity:1}to{opacity:0}}@keyframes sf-slide-in-up{0%{opacity:0;translate:0 1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-down{0%{opacity:0;translate:0 -1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-left{0%{opacity:0;translate:1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-right{0%{opacity:0;translate:-1rem}to{opacity:1;translate:0}}@keyframes sf-scale-up{0%{opacity:0;scale:.92}to{opacity:1;scale:1}}@keyframes sf-scale-down{0%{opacity:1;scale:1}to{opacity:0;scale:.92}}@keyframes sf-spin{to{rotate:360deg}}@keyframes sf-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@keyframes sf-ping{75%,to{opacity:0;scale:2}}@keyframes sf-blink{0%,50%{opacity:1}50.01%,to{opacity:0}}@keyframes sf-float{0%,to{translate:0}50%{translate:0 -.5rem}}@keyframes sf-color-pulse{0%{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) l c h)}50%{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) calc(l + .25) c h)}to{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) l c h)}}@media (prefers-reduced-motion:no-preference){.sf-color-pulse{animation:var(--sf-animation-color-pulse)}}:focus:not(:focus-visible){outline:none}:focus-visible{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-focus-ring-color)!important;outline-offset:var(--sf-focus-ring-offset)!important}@media (prefers-reduced-motion:reduce){:root{--sf-duration-instant:.01ms;--sf-duration-fast:.01ms;--sf-duration-normal:.01ms;--sf-duration-slow:.01ms;--sf-duration-slower:.01ms}*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.no-motion,.no-motion *,.no-motion :before,.no-motion :after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}@media (prefers-contrast:more){:root{--sf-focus-ring-width:3px;--sf-contrast-bias:.06}hr{border-block-start-width:var(--sf-border-width-2)}}@media (prefers-reduced-transparency:reduce){::backdrop{background-color:var(--sf-color-neutral);opacity:1}}@media (pointer:coarse){button,input[type=button],input[type=submit],input[type=reset],select,summary,a{min-height:var(--sf-touch-target);min-width:var(--sf-touch-target)}}[disabled],[aria-disabled=true]{cursor:not-allowed}.sr-only,.visually-hidden,.sr-only-focusable:not(:focus,:focus-within){clip-path:inset(50%)!important;white-space:nowrap!important;border:0!important;width:1px!important;height:1px!important;margin:-1px!important;padding:0!important;position:absolute!important;overflow:clip!important}.skip-link{top:-100%;left:var(--sf-space-m);z-index:var(--sf-z-max);padding:var(--sf-space-s) var(--sf-space-m);background:var(--sf-color-bg);color:var(--sf-color-text);border:var(--sf-border-width-1) solid var(--sf-color-border);border-radius:var(--sf-radius-m);transition:top var(--sf-duration-fast) var(--sf-ease-out);position:absolute}.skip-link:focus{top:var(--sf-space-m)}.focus-parent:focus-within{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}.sf-clickable-parent{--sf-clickable-overlay-z:1;cursor:pointer;position:relative}.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):after,.sf-clickable-parent [data-overlay-link]:after{content:"";z-index:var(--sf-clickable-overlay-z);pointer-events:auto;position:absolute;inset:0}.sf-clickable-parent :is(button,input,textarea,select,summary,video,audio,[role=button],[data-no-overlay]),.sf-clickable-parent:has([data-overlay-link]) a:where(:any-link):not([data-overlay-link]),.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):not(:first-of-type){z-index:calc(var(--sf-clickable-overlay-z) + 1);position:relative}.sf-clickable-parent:focus-within{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}@media (forced-colors:active){:root{--sf-focus-ring-color:Highlight}:focus-visible{outline-color:highlight!important}:where(*){box-shadow:none;text-shadow:none}input,select,textarea{border:var(--sf-border-width-1) solid}}.print-only{display:none}@media print{@page{size:var(--sf-print-page-size,a4);margin:var(--sf-print-page-margin,2cm)}body{font-size:var(--sf-print-base-size)}*,:before,:after{box-shadow:none;text-shadow:none}a[href]:after{content:" (" attr(href) ")";word-break:break-all;font-size:90%}a[href^=\#]:after,a[href^=javascript\: i]:after{content:""}abbr[title]:after{content:" (" attr(title) ")"}img,svg,video,canvas,figure,table,pre,blockquote{break-inside:avoid}thead{display:table-header-group}tr{break-inside:avoid}h1,h2,h3,h4,h5,h6{break-after:avoid;orphans:3;widows:3}p{orphans:3;widows:3}pre,blockquote{border:var(--sf-border-width-1) solid var(--sf-color-border--strong);padding:var(--sf-space-s)}nav,aside,button,input,select,textarea,audio,video,dialog,[popover],.no-print{display:none!important}.print-only{display:revert!important}details{display:block}details>summary{display:none!important}details:not([open])>:not(summary){display:block}.print-color-exact,.print-color-exact *{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}.print-no-color,.print-no-color *,.print-no-color :before,.print-no-color :after{color:canvastext!important;background:0 0!important}} /*# sourceMappingURL=slashed.essential.flat.min.css.map */ diff --git a/dist/slashed.essential.flat.min.css.map b/dist/slashed.essential.flat.min.css.map index ec91611e..7570eb4c 100644 --- a/dist/slashed.essential.flat.min.css.map +++ b/dist/slashed.essential.flat.min.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":null,"mappings":"AAkJA,kGACA,qGACA,oGACA,kGACA,mGACA,iGAIA,mGACA,kGACA,+FACA,gGACA,iGAIA,wEAEA,0EACA,2EACA,2EACA,wEAOA,qvyBA4gCA,0DAMA,mLAaA,4CACE,sCAGF,6CAKA,2BAIA,sEAMA,0EAOA,wHAQA,2BAQA,qBAIA,2CAMA,iBAIA,iEAIA,uCAKA,yBAIA,sBAIA,gDAKA,gCAIA,oEAOA,uBAcA,mWAUA,yVAUA,oJACA,oJACA,oJACA,oJACA,oJACA,oJAEA,iFAOA,kDAIA,sCAIA,qCAIA,iJAOA,yDAKA,2DAKA,8EAMA,cACA,kBAcA,oRASA,6EACA,8CACA,4CACA,6CAEA,gCAIA,+MASA,iRAWA,2FASA,sBAIA,0GAKA,mFAIA,0GAMA,4BAIA,uCAIA,iDAIA,mDAIA,gDAIA,sBACA,oCACA,6EAIA,8IAMA,6CAIA,yCAIA,kCAKA,6CAIA,2IA6CA,mCACE,0DAQF,qDACA,mDAuBA,89BAqCA,gDACA,wDACA,sDACA,sDACA,sDACA,wDACA,0DAEA,gEAgBA,oGAGA,gGAQA,kJAKA,4MAkBA,wJAOA,2DACA,yDACA,uDACA,uDAOA,qEAKA,qCACA,mCACA,mCACA,mCACA,qCACA,uCAEA,qCACA,oCACA,uCAQA,0GAWA,iIAOA,4EAWA,gJAOA,uCACA,qCACA,qCACA,qCACA,uCACA,yCAEA,sCACA,2CACA,0CACA,mDAQA,kEAMA,wEAKA,+HAYA,8EAKA,6CACA,2CAQA,oEAMA,wFAKA,uDACA,6CASA,iIAMA,iGAIA,iDACA,+CACA,+CACA,+CACA,iDACA,mDAIA,qCAMA,0LAQA,8CACA,4CACA,4CACA,4CACA,8CAOA,uVAgBA,yHAOA,8CAIA,gCACA,8CACA,sDACA,uDAOA,oHAQA,sEAOA,sDACA,oDACA,sDACA,0DACA,gDACA,gDACA,sDAOA,uNAWA,sEAUA,gGAOA,mCAIA,yJAgBA,0FAMA,kFAMA,sCACE,8CAGA,kDACA,kDAOF,4EAUA,2FAKA,yBACE,+CACA,gDAGF,yBACE,+CACA,+CACA,+CACA,gDAOF,wFAMA,yBACE,2CACA,2CACA,2CACA,4CAQF,saAgBA,uCACA,mDACA,iDAOA,sOAQA,8CACE,+CAGF,+BACA,+BACA,8DACA,wDAMA,uDACA,yDAgCA,uEAKA,2DACA,+DACA,sDACA,6DACA,8EACA,iDACA,gCACA,gCACA,iDACA,gDACA,wHACA,4FACA,gFAKA,+EAMA,iFACA,+CACA,2DACA,8CACA,wFACA,+FACA,6DAQA,qDAcA,uEAMA,mHAOA,mDACA,mDACA,yFAUA,kDAIA,6CAWA,yCAUA,8LAeA,2FAKA,0CASA,mJAeA,6DAiBA,+FACA,qGACA,kGACA,4FACA,+FACA,+FACA,+FACA,+FACA,yFACA,sFACA,4FAmBA,gHAMA,iCACE,+BAmBF,yHAiCA,kCAIA,gCAIA,+BAQA,wGAOA,kDASA,wEAMA,gWAkBA,yBAIA,oEAOA,2RAmBA,4BAMA,2DAIA,mGASA,2DAIA,4EAeA,wBAIA,6BAIA,4BAgBA,+GAKA,6GAKA,iHAKA,iHAKA,2GAKA,wGAKA,8GASA,iFAMA,yDAMA,yBAIA,8DASA,wCASA,sCAIA,yDAKA,wEAUA,0DAKA,4HAKA,0BAQA,sCASA,6BAIA,kCAIA,sJAWA,6BAgCA,8CAEE,4BAKA,uCAMA,+QASA,sCACE,mJAaF,kDACA,oDACA,0DACA,8DACA,8DACA,gEACA,oDACA,wDA4BA,0PAUA,sCACE,iRAWF,6CACA,oDACA,wDACA,wDACA,0DACA,mDASF,iDACA,kDACA,kFACA,qFACA,kFACA,oFACA,oEACA,sEAEA,qCACA,oFAMA,6CACA,0DACA,8DAMA,4QAMA,8CACE,2DAkCF,wCAIA,6KAWA,uCACE,0IAQA,8JAaF,yMAcA,+BACE,uDAOA,sDAQF,6CACE,+DAYF,wBACE,oJAeF,mDAUA,6RAmBA,mVAYA,uCAYA,wKA4CA,iFAYA,2NAgBA,kWAeA,+KAYA,8BACE,sCAIA,iDAIA,2CAQA,6DAwCF,yBAIA,aAEE,+EAKA,yCAMA,kDASA,6EAMA,2DAKA,+CAIA,oEAIA,iCAIA,sBAIA,uDAMA,qBAKA,8GASA,qGAMA,qCASA,sBAGA,uCAGA,gDASA,sHAUA","sources":["slashed.essential.flat.css"],"sourcesContent":["/* SLASHED v0.3.0 — slashed.essential.flat.css (flat) */\n\n/* ─── core/layers.css ─── */\n/* SLASHED — core/layers.css\n CSS cascade layer order. Load this file first, always.\n ─────────────────────────────────────────────────── */\n\n/* ─── core/tokens.css ─── */\n/* ============================================================\n SLASHED — core/tokens.css\n @layer slashed.tokens\n\n ARCHITECTURE — 3 layers:\n 1. @property source tokens — animatable, 6 brand + 5 status (-light only); -dark is optional\n 2. Resolved semantic tokens — light-dark() with auto-derivation of dark from light (relative color syntax)\n 3. Derived tokens — relative color syntax (auto-contrast, tints/shades)\n\n THEMING — override just the 6 -light tokens. Dark mode auto-adapts via\n relative color syntax — zero extra work. For full control over dark mode,\n optionally override the -dark tokens (unregistered custom properties).\n\n Quick branding (6 tokens):\n :root {\n --sf-color-primary-light: oklch(0.45 0.20 264);\n --sf-color-secondary-light: oklch(0.25 0.03 260);\n --sf-color-tertiary-light: oklch(0.48 0.14 310);\n --sf-color-action-light: oklch(0.60 0.16 210);\n --sf-color-neutral-light: oklch(0.45 0.02 260);\n --sf-color-base-light: oklch(0.98 0.005 260);\n }\n Full per-mode control (add -dark overrides):\n :root {\n --sf-color-primary-dark: oklch(0.75 0.18 264);\n }\n\n BEM CONSUMER API — most tokens are intentionally unused by the\n framework itself. Tokens such as --sf-shadow-*, --sf-blur-*, --sf-gap,\n --sf-gradient-* etc. are meant for use in your own BEM classes:\n .card { box-shadow: var(--sf-shadow-m); gap: var(--sf-gap); }\n \"Not used internally\" does NOT mean dead code: every token is exercised\n in docs/demo.html and validated by the regression suite (tests/), so an\n unreferenced token is never untested or orphaned.\n\n RELATION TO tokens.layout.css — the spacing aliases in the \"Spacing\n aliases\" section are the source of truth for --sf-space-gap and\n --sf-space-content in tokens.layout.css. Overriding --sf-gap changes the\n gaps across all layout primitives (cluster, grid, sidebar, etc.).\n\n PUBLIC API vs INTERNAL (token-API freeze contract)\n ─────────────────────────────────────────────────\n Tokens are classified into three stability tiers (PUBLIC,\n PUBLIC-ADVANCED, INTERNAL).\n PUBLIC and PUBLIC-ADVANCED share the same SemVer guarantee\n (removal/rename = major bump after freeze). The distinction is\n documentation-level: ADVANCED tokens are powerful but niche — they\n live in a separate \"Advanced\" section in docs.\n\n PUBLIC — everyday knobs, prominently documented:\n · brand source tokens (--sf-color-{primary,secondary,tertiary,action,neutral,base}-light)\n · status source tokens (--sf-color-{success,warning,error,info,danger}-light)\n · resolved semantic colors (--sf-color-{primary,...,danger}, surfaces, text, borders, links, interactive bg, selection)\n · text-on-color (--sf-color-text--on-*)\n · code colors (--sf-color-code-bg, --sf-color-code-text)\n · status triplets (--sf-color-{success,...,danger}-{subtle,strong,muted})\n · gradients (--sf-gradient-*)\n · shadows (--sf-shadow-{none,xs,s,m,l,xl,2xl,inner})\n · text/drop shadows (--sf-text-shadow-*, --sf-drop-shadow-*)\n · blur (--sf-blur-*)\n · opacity (--sf-opacity-{0,10,25,50,75,100}, --sf-opacity-disabled, --sf-state-pending-opacity)\n · spacing (--sf-space-{none,px,gutter,2xs..4xl})\n · font families (--sf-font-{body,heading,display,mono,humanist,geometric,slab})\n · font weights (--sf-font-weight-{thin..black,body,heading,display})\n · font sizes (--sf-text-{2xs..4xl}, --sf-text-display-{s,m,l})\n · leading (--sf-leading-{tight,snug,normal,relaxed})\n · tracking (--sf-tracking-{tight,normal,wide,wider,widest})\n · icon sizes (--sf-icon-{xs,s,m,l,xl})\n · UI sizes (--sf-size-{xs,s,m,l,xl})\n · containers (--sf-container-{narrow,prose,default,wide,full})\n · ratios (--sf-ratio-{square,video,cinema,portrait,golden,4-3,3-2})\n · border widths (--sf-border-width-{hairline,1,2,3,4})\n · border styles (--sf-border-style, --strong, --soft, --dotted)\n · divider (--sf-divider-{width,style,color})\n · radius (--sf-radius-{none,xs,s,m,l,xl,2xl,3xl,4xl,full,pill})\n · duration (--sf-duration-{none,instant,fast,normal,slow,slower})\n · easing (--sf-ease-{linear,out,in,in-out,spring,elastic,bounce,overshoot})\n · transitions (--sf-transition-{all,colors,transform,opacity,shadow,fast,slow,enter,exit})\n · animations (--sf-animation-{fade-in,fade-out,slide-in-*,scale-*,color-pulse,ping,blink,float,spin,shimmer})\n · animation delays (--sf-animation-delay-{1..5})\n · z-index (--sf-z-{below,base,raised,low,mid,high,top,max})\n · header/sticky (--sf-header-height, --sf-sticky-offset)\n · touch target (--sf-touch-target)\n · focus ring (--sf-focus-ring-{width,offset,style,color})\n · caret (--sf-caret-color)\n · scrollbar (--sf-scrollbar-{thumb,track})\n · BEM aliases (--sf-gap, --sf-content-gap, --sf-component-pad, --sf-field-block,\n --sf-field-required-marker, --sf-link-external-marker)\n · section padding (--sf-section-pad, --sf-section-pad--{xs,s,m,l,xl,2xl})\n · typography aliases (--sf-body-*, --sf-code-font-size, --sf-heading-*, --sf-h{1..6}-*)\n · current-font-weight (--sf-current-font-weight)\n · color scheme (--sf-color-scheme)\n · stroke (--sf-stroke-{thin,regular,bold,heavy})\n · layout tokens — all of tokens.layout.css (per-primitive gaps, sidebar, grid, bento, etc.)\n · macro tokens — all of tokens.macros.css (flow-space, line-clamp, aspect, scroll-shadow-size)\n · palette tokens — all of optional/tokens.palette.css\n · scoped override tokens (--sf-icon-size, --sf-bento-cols, --sf-bento-row,\n --sf-color-code-block-bg, --sf-color-code-block-text,\n --sf-field-border-color, --sf-field-text-color)\n\n PUBLIC-ADVANCED — stable (same SemVer guarantee) but niche/powerful:\n · --sf-lumlocker (LumLocker lightness override)\n · --sf-contrast-bias, --sf-contrast-threshold (text-contrast tuning)\n · --sf-shadow-strength, --sf-shadow-color, --sf-shadow-glow-color (shadow internals)\n · --sf-font-features, --sf-font-variation, --sf-optical-sizing (OpenType)\n · --sf-space-scale, --sf-text-scale, --sf-text-display-scale,\n --sf-radius-scale, --sf-motion-scale (global multipliers)\n · --sf-scroll-timeline-range-{start,end} (scroll-driven animation)\n · --sf-mask-scrim-{start,end} (edge-fade masks)\n · --sf-safe-{top,bottom,left,right} (safe-area insets)\n · --sf-perspective-{near,normal,far} (3D transforms)\n · --sf-truncate-suffix (ellipsis character override)\n · --sf-radius-outer (concentric radius helper)\n · --sf-is-active, --sf-is-current, --sf-is-pressed, --sf-is-open (state flags)\n · --sf-focus-ring-shadow (composite focus shadow)\n · --sf-print-{page-margin,page-size,base-size} (print tokens)\n\n INTERNAL — implementation detail, may change without a major bump:\n · --sf-is-dark (mode flag — set via data-theme, not directly)\n\n CANONICAL-SOURCE ALIASES (intentional synonym hops, kept by design):\n · --sf-space-gap → --sf-gap (layout primitives read this name)\n · --sf-space-content → --sf-content-gap (stack / prose rhythm)\n · --sf-section-pad → --sf-section-pad--m (unsuffixed = the -m default)\n These are deliberate two-name contracts: override the canonical token\n (right side) to move everything downstream. The alias graph is otherwise\n ≤3 hops (per-primitive → layout-system → canonical source), giving three\n override levels: local (style=\"--sf-cluster-gap\"), all-primitives\n (--sf-space-gap), or global (--sf-gap).\n ============================================================ */\n\n\n/* ============================================================\n @property — BRAND COLORS (-light source tokens, animatable)\n Override these 6 tokens to rebrand — dark mode auto-derives.\n Optionally set --sf-color-X-dark (no @property) for full control.\n ============================================================ */\n\n@property --sf-color-primary-light { syntax: \"\"; inherits: true; initial-value: oklch(0.45 0.20 264); }\n@property --sf-color-secondary-light { syntax: \"\"; inherits: true; initial-value: oklch(0.25 0.03 260); }\n@property --sf-color-tertiary-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.14 310); }\n@property --sf-color-action-light { syntax: \"\"; inherits: true; initial-value: oklch(0.60 0.16 210); }\n@property --sf-color-neutral-light { syntax: \"\"; inherits: true; initial-value: oklch(0.45 0.02 260); }\n@property --sf-color-base-light { syntax: \"\"; inherits: true; initial-value: oklch(0.98 0.005 260); }\n\n/* @property — STATUS COLORS (-light source tokens) */\n\n@property --sf-color-success-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.17 150); }\n@property --sf-color-warning-light { syntax: \"\"; inherits: true; initial-value: oklch(0.75 0.17 80); }\n@property --sf-color-error-light { syntax: \"\"; inherits: true; initial-value: oklch(0.62 0.20 35); }\n@property --sf-color-info-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.15 240); }\n@property --sf-color-danger-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.24 12); }\n\n/* Mode flag — drives formula direction for non-color dark overrides.\n Set by base.css via color-scheme switches. */\n@property --sf-is-dark { syntax: \"\"; inherits: true; initial-value: 0; }\n\n@property --sf-is-active { syntax: \"\"; inherits: true; initial-value: 0; }\n@property --sf-is-current { syntax: \"\"; inherits: true; initial-value: 0; }\n@property --sf-is-pressed { syntax: \"\"; inherits: true; initial-value: 0; }\n@property --sf-is-open { syntax: \"\"; inherits: true; initial-value: 0; }\n\n\n/* ============================================================\n DERIVED SEMANTIC TOKENS\n ============================================================ */\n\n:root {\n\n /* ----------------------------------------------------------\n Color scheme\n ---------------------------------------------------------- */\n --sf-color-scheme: light dark;\n\n /* ----------------------------------------------------------\n LumLocker — OKLCH L value used by the\n :root[data-lumlocker] override in core/themes.css.\n Locks all 5 brand colors (primary, secondary, tertiary,\n action, neutral) to one shared lightness while keeping their\n individual hue and chroma. Base is excluded — it must remain\n a light surface for page background.\n Override to change the locked lightness:\n :root { --sf-lumlocker: 0.55 }\n ---------------------------------------------------------- */\n --sf-lumlocker: 0.65;\n\n /* ----------------------------------------------------------\n Resolved color tokens — auto-switch via light-dark().\n Components reference these. Override the -light / -dark\n source tokens above to customise; or override these directly\n with your own light-dark() expression.\n ---------------------------------------------------------- */\n /* Dark auto-derivation formula (brand + status):\n clamp(0.65, 0.95 - l*0.5, 0.88) lightens dark-mode value relative to the light source.\n Base inverts: clamp(0.16, 1.18 - l, 0.24) — near-white flips to near-dark.\n Override any --sf-color-X-dark to take full per-mode control. */\n --sf-color-primary: light-dark(var(--sf-color-primary-light), var(--sf-color-primary-dark, oklch(from var(--sf-color-primary-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-secondary: light-dark(var(--sf-color-secondary-light), var(--sf-color-secondary-dark, oklch(from var(--sf-color-secondary-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-tertiary: light-dark(var(--sf-color-tertiary-light), var(--sf-color-tertiary-dark, oklch(from var(--sf-color-tertiary-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-action: light-dark(var(--sf-color-action-light), var(--sf-color-action-dark, oklch(from var(--sf-color-action-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-neutral: light-dark(var(--sf-color-neutral-light), var(--sf-color-neutral-dark, oklch(from var(--sf-color-neutral-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-base: light-dark(var(--sf-color-base-light), var(--sf-color-base-dark, oklch(from var(--sf-color-base-light) clamp(0.16, calc(1.18 - l), 0.24) calc(c * 0.5) h)));\n\n --sf-color-success: light-dark(var(--sf-color-success-light), var(--sf-color-success-dark, oklch(from var(--sf-color-success-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-warning: light-dark(var(--sf-color-warning-light), var(--sf-color-warning-dark, oklch(from var(--sf-color-warning-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-error: light-dark(var(--sf-color-error-light), var(--sf-color-error-dark, oklch(from var(--sf-color-error-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-info: light-dark(var(--sf-color-info-light), var(--sf-color-info-dark, oklch(from var(--sf-color-info-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-danger: light-dark(var(--sf-color-danger-light), var(--sf-color-danger-dark, oklch(from var(--sf-color-danger-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n\n\n /* ----------------------------------------------------------\n Surfaces — derived from --sf-color-base (auto-adapts)\n ---------------------------------------------------------- */\n --sf-color-bg: oklch(from var(--sf-color-base) calc(l + 0.02) c h);\n --sf-color-surface: var(--sf-color-base);\n --sf-color-well: oklch(from var(--sf-color-base) calc(l - 0.02) c h);\n --sf-color-raised: oklch(from var(--sf-color-base) calc(l + 0.04) c h);\n --sf-color-overlay: oklch(from var(--sf-color-base) l c h / 0.9);\n --sf-color-inverse: oklch(from var(--sf-color-base) calc(1 - l) c h);\n\n\n /* ----------------------------------------------------------\n Text — light and dark require opposite formula directions,\n so each side of light-dark() references its source token.\n ---------------------------------------------------------- */\n --sf-color-text: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h),\n oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1) c h)\n );\n --sf-color-text--secondary: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.15, calc(l - 0.25 - var(--sf-contrast-bias)), 0.45) c h),\n oklch(from var(--sf-color-neutral) clamp(0.55, calc(l + 0.1 + var(--sf-contrast-bias)), 0.90) c h)\n );\n --sf-color-text--muted: var(--sf-color-neutral);\n --sf-color-text--placeholder: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.45, calc(l + 0.15), 0.75) c h),\n oklch(from var(--sf-color-neutral) clamp(0.35, calc(l - 0.1), 0.65) c h)\n );\n --sf-color-text--disabled: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.55, calc(l + 0.25), 0.82) c h),\n oklch(from var(--sf-color-neutral) clamp(0.25, calc(l - 0.2), 0.55) c h)\n );\n --sf-color-text--inverse: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.85, calc(l + 0.4), 0.98) c h),\n oklch(from var(--sf-color-neutral) clamp(0.05, calc(l - 0.4), 0.35) c h)\n );\n --sf-color-heading: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h),\n oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1) c h)\n );\n /* Inline-code text colour. Auto-contrasts with --sf-color-code-bg using\n the same luminance formula as --sf-color-text--on-*. This prevents\n invisible text when code appears inside inverted-colour containers\n (e.g. sf-imposter with a dark background where inherited colour is\n near-white). Override with a tint value if desired. */\n --sf-color-code-text: oklch(from var(--sf-color-code-bg) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n\n\n /* ----------------------------------------------------------\n Text-on-color — sign(var(--sf-contrast-threshold) - l) * 999 is mode-agnostic:\n auto-selects light or dark text based on background luminance.\n Uses resolved tokens — works for any user-supplied color.\n\n CONTRAST NOTE: Guarantees ≥ 3:1 (WCAG AA Large Text / UI).\n Colors in the mid-luminance range (L ≈ 0.52–0.67) may not\n reach 4.5:1 (AA Normal Text) regardless of threshold chosen —\n this is a mathematical limitation of any binary black/white\n decision. The SLASHED configurator will warn users when their\n brand colors fall in this range. For body text on colored\n backgrounds, override --sf-color-text--on-* manually.\n ---------------------------------------------------------- */\n --sf-color-text--on-primary: oklch(from var(--sf-color-primary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-secondary: oklch(from var(--sf-color-secondary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-tertiary: oklch(from var(--sf-color-tertiary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-action: oklch(from var(--sf-color-action) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-neutral: oklch(from var(--sf-color-neutral) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-base: var(--sf-color-text);\n --sf-color-text--on-inverse: var(--sf-color-text--inverse);\n --sf-color-text--on-success: oklch(from var(--sf-color-success) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-warning: oklch(from var(--sf-color-warning) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-error: oklch(from var(--sf-color-error) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-info: oklch(from var(--sf-color-info) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-danger: oklch(from var(--sf-color-danger) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n\n\n /* ----------------------------------------------------------\n Borders — direction-dependent, reference source tokens.\n Light: borders are lighter than neutral (l + offset).\n Dark: borders are darker than neutral (l - offset).\n ---------------------------------------------------------- */\n --sf-color-border: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.70, calc(l + 0.35), 0.95) 0.005 h),\n oklch(from var(--sf-color-neutral) clamp(0.25, calc(l - 0.3), 0.55) 0.005 h)\n );\n --sf-color-border--subtle: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.75, calc(l + 0.4), 0.97) 0.005 h),\n oklch(from var(--sf-color-neutral) clamp(0.20, calc(l - 0.38), 0.45) 0.005 h)\n );\n --sf-color-border--strong: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.55, calc(l + 0.1), 0.85) 0.02 h),\n oklch(from var(--sf-color-neutral) clamp(0.38, calc(l - 0.1), 0.65) 0.02 h)\n );\n --sf-color-border--focus: var(--sf-color-action);\n --sf-color-border--disabled: oklch(from var(--sf-color-border--subtle) l 0 h / 0.5);\n --sf-color-border--translucent: oklch(from var(--sf-color-neutral) l c h / 0.15);\n\n\n /* ----------------------------------------------------------\n Links — derived from --sf-color-action (auto-adapts)\n ---------------------------------------------------------- */\n /* Links keep the action hue but CLAMP lightness toward a contrast-safe\n band — a ceiling in light mode, a floor in dark mode — so link text\n clears WCAG AA (4.5:1) on the page background for the default palette\n AND the large majority of brand overrides (a fixed offset alone only\n fixed the default). Lightness dominates OKLCH contrast but doesn't\n fully determine it: very high-chroma hues (saturated yellow/green) can\n still fall short even at a safe lightness — those remain the consumer's\n responsibility (see docs/theming.md → \"Link contrast\").\n\n NOTE: the consumer-facing :link rule in core/base.css uses\n `a:link` (not bare `a`) so its specificity matches WebKit's UA\n stylesheet `a:link { color: -webkit-link }`. Without that\n pseudo-class the framework's `--sf-color-link` value is\n silently ignored on unvisited anchors in WebKit and\n `-webkit-link` (≈ #00728f in dark mode) paints instead —\n regardless of how this token is computed. PR #73 and several\n follow-up attempts mistakenly chased the formula. */\n --sf-color-link: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.07, 0.48), 1) c h),\n oklch(from var(--sf-color-action) clamp(0.68, l, 1) c h)\n );\n --sf-color-link--hover: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.15, 0.40), 1) c h),\n oklch(from var(--sf-color-action) clamp(0, max(l + 0.10, 0.68), 1) c h)\n );\n --sf-color-link--active: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.21, 0.34), 1) c h),\n oklch(from var(--sf-color-action) clamp(0, max(l + 0.15, 0.74), 1) c h)\n );\n /* Visited link — 60° hue shift from action gives clearly distinct\n chroma without colliding with default tertiary (h ≈ 310). Same\n lightness clamp as the base link. If your brand puts action and\n tertiary close in hue, override directly:\n :root { --sf-color-link--visited: #6b46c1; } */\n --sf-color-link--visited: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.07, 0.48), 1) c calc(h + 60)),\n oklch(from var(--sf-color-action) clamp(0.68, l, 1) c calc(h + 60))\n );\n --sf-color-link--underline: oklch(from var(--sf-color-action) l c h / 0.3);\n --sf-color-link--disabled: var(--sf-color-text--disabled);\n\n\n /* ----------------------------------------------------------\n Interactive states — derived from --sf-color-neutral (auto-adapts)\n ---------------------------------------------------------- */\n --sf-color-bg--hover: oklch(from var(--sf-color-neutral) l c h / 0.08);\n --sf-color-bg--active: oklch(from var(--sf-color-neutral) l c h / 0.12);\n --sf-color-bg--selected: oklch(from var(--sf-color-action) l c h / 0.1);\n --sf-color-bg--focus: oklch(from var(--sf-color-action) l c h / 0.06);\n --sf-color-bg--disabled: var(--sf-color-well);\n --sf-color-code-bg: var(--sf-color-well);\n\n /* ----------------------------------------------------------\n Selection & backdrop\n ---------------------------------------------------------- */\n --sf-color-selection-bg: oklch(from var(--sf-color-action) l c h / 0.22);\n --sf-color-selection-text: inherit;\n --sf-color-mark-bg: oklch(from var(--sf-color-warning) l c h / 0.25);\n --sf-color-mark-text: inherit;\n --sf-color-dim: oklch(0 0 0 / 0.5);\n\n\n /* ----------------------------------------------------------\n Scrollbar\n ---------------------------------------------------------- */\n --sf-scrollbar-thumb: var(--sf-color-neutral);\n --sf-scrollbar-track: transparent;\n\n\n /* ----------------------------------------------------------\n Status triplets — bg/border use same formula both modes;\n text is direction-dependent so uses source tokens.\n error = form validation, input errors\n danger = destructive actions (delete, remove)\n ---------------------------------------------------------- */\n --sf-color-success-subtle: oklch(from var(--sf-color-success) l c h / 0.12);\n --sf-color-success-strong: light-dark(\n oklch(from var(--sf-color-success-light) calc(l - 0.15) c h),\n oklch(from var(--sf-color-success) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-success-muted: oklch(from var(--sf-color-success) l c h / 0.3);\n\n --sf-color-warning-subtle: oklch(from var(--sf-color-warning) l c h / 0.12);\n --sf-color-warning-strong: light-dark(\n oklch(from var(--sf-color-warning-light) calc(l - 0.25) c h),\n oklch(from var(--sf-color-warning) clamp(0.70, calc(l + 0.05), 1) c h)\n );\n --sf-color-warning-muted: oklch(from var(--sf-color-warning) l c h / 0.3);\n\n --sf-color-error-subtle: oklch(from var(--sf-color-error) l c h / 0.1);\n --sf-color-error-strong: light-dark(\n oklch(from var(--sf-color-error-light) calc(l - 0.1) c h),\n oklch(from var(--sf-color-error) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-error-muted: oklch(from var(--sf-color-error) l c h / 0.3);\n\n --sf-color-info-subtle: oklch(from var(--sf-color-info) l c h / 0.1);\n --sf-color-info-strong: light-dark(\n oklch(from var(--sf-color-info-light) calc(l - 0.1) c h),\n oklch(from var(--sf-color-info) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-info-muted: oklch(from var(--sf-color-info) l c h / 0.3);\n\n --sf-color-danger-subtle: oklch(from var(--sf-color-danger) l c h / 0.1);\n --sf-color-danger-strong: light-dark(\n oklch(from var(--sf-color-danger-light) calc(l - 0.1) c h),\n oklch(from var(--sf-color-danger) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-danger-muted: oklch(from var(--sf-color-danger) l c h / 0.3);\n\n\n /* ----------------------------------------------------------\n Gradients — derived from brand colors (auto-adapt)\n ---------------------------------------------------------- */\n --sf-gradient-primary: linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) calc(l - 0.08) c h));\n --sf-gradient-secondary: linear-gradient(135deg, var(--sf-color-secondary), oklch(from var(--sf-color-secondary) calc(l - 0.08) c h));\n --sf-gradient-tertiary: linear-gradient(135deg, var(--sf-color-tertiary), oklch(from var(--sf-color-tertiary) calc(l - 0.08) c h));\n --sf-gradient-brand: linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) l c calc(h + 30)));\n --sf-gradient-surface: linear-gradient(180deg, var(--sf-color-surface), var(--sf-color-bg));\n\n --sf-gradient-fade--r: linear-gradient(to right, transparent, var(--sf-color-bg));\n --sf-gradient-fade--l: linear-gradient(to left, transparent, var(--sf-color-bg));\n --sf-gradient-fade--t: linear-gradient(to top, transparent, var(--sf-color-bg));\n --sf-gradient-fade--b: linear-gradient(to bottom, transparent, var(--sf-color-bg));\n\n\n /* ----------------------------------------------------------\n Shadow — strength scales up in dark mode via --sf-is-dark\n ---------------------------------------------------------- */\n --sf-shadow-strength: calc(0.08 + var(--sf-is-dark) * 0.17);\n --sf-shadow-glow-color: var(--sf-color-primary);\n --sf-shadow-glow: 0 0 15px 2px oklch(from var(--sf-shadow-glow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-opacity-disabled: 0.45;\n --sf-state-pending-opacity: 0.7;\n\n\n /* ----------------------------------------------------------\n Focus / form colors\n ---------------------------------------------------------- */\n --sf-focus-ring-color: var(--sf-color-action);\n --sf-focus-ring-shadow: 0 0 0 var(--sf-focus-ring-offset) var(--sf-color-bg),\n 0 0 0 calc(var(--sf-focus-ring-offset) + var(--sf-focus-ring-width)) var(--sf-focus-ring-color);\n --sf-caret-color: var(--sf-color-action);\n\n}\n\n\n\n\n/* ============================================================\n NON-COLOR DESIGN TOKENS\n ============================================================ */\n\n:root {\n\n /* ----------------------------------------------------------\n Scale multipliers\n ---------------------------------------------------------- */\n --sf-space-scale: 1;\n --sf-text-scale: 1;\n --sf-text-display-scale: 1;\n --sf-radius-scale: 1;\n --sf-motion-scale: 1;\n\n\n /* ----------------------------------------------------------\n Font families\n ---------------------------------------------------------- */\n --sf-font-body: system-ui, -apple-system, sans-serif;\n --sf-font-heading: var(--sf-font-body);\n --sf-font-display: var(--sf-font-heading);\n --sf-font-mono: ui-monospace, monospace;\n\n /* Optional system-font stacks (zero-cost, no @import) */\n --sf-font-humanist: \"Seravek\", \"Gill Sans Nova\", \"Ubuntu\", \"Calibri\", \"DejaVu Sans\", source-sans-pro, sans-serif;\n --sf-font-geometric: \"Avenir\", \"Montserrat\", \"Corbel\", \"URW Gothic\", source-sans-pro, sans-serif;\n --sf-font-slab: \"Rockwell\", \"Rockwell Nova\", \"Roboto Slab\", \"DejaVu Serif\", \"Sitka Small\", serif;\n\n /* Opt-in OpenType / variable-font controls (token-only, not applied by the framework) */\n --sf-font-features: normal; /* e.g. \"cv11\", \"ss01\" */\n --sf-font-variation: normal; /* e.g. \"wght\" 450, \"opsz\" 32 */\n --sf-optical-sizing: auto;\n\n\n /* ----------------------------------------------------------\n Font weights\n ---------------------------------------------------------- */\n /* Numeric scale */\n --sf-font-weight-thin: 100;\n --sf-font-weight-extralight: 200;\n --sf-font-weight-light: 300;\n --sf-font-weight-normal: 400;\n --sf-font-weight-medium: 500;\n --sf-font-weight-semibold: 600;\n --sf-font-weight-bold: 700;\n --sf-font-weight-extrabold: 800;\n --sf-font-weight-black: 900;\n\n /* Semantic aliases — reference the numeric scale */\n --sf-font-weight-body: var(--sf-font-weight-normal);\n --sf-font-weight-heading: var(--sf-font-weight-semibold);\n --sf-font-weight-display: var(--sf-font-weight-bold);\n\n /* State token — consumed by .is-current in states.css.\n Override to match your nav design (e.g. semibold for thin fonts). */\n --sf-current-font-weight: var(--sf-font-weight-bold);\n\n\n /* ----------------------------------------------------------\n Font sizes — fluid\n ---------------------------------------------------------- */\n --sf-text-2xs: clamp(0.51rem, calc(0.00029629629629629656 * (100vw - 22.5rem) + 0.51rem), 0.53rem);\n --sf-text-xs: clamp(0.64rem, calc(0.0008888888888888881 * (100vw - 22.5rem) + 0.64rem), 0.7rem);\n --sf-text-s: clamp(0.8rem, calc(0.002074074074074073 * (100vw - 22.5rem) + 0.8rem), 0.94rem);\n --sf-text-m: clamp(1rem, calc(0.003703703703703704 * (100vw - 22.5rem) + 1rem), 1.25rem);\n --sf-text-l: clamp(1.25rem, calc(0.006222222222222221 * (100vw - 22.5rem) + 1.25rem), 1.67rem);\n --sf-text-xl: clamp(1.56rem, calc(0.00977777777777778 * (100vw - 22.5rem) + 1.56rem), 2.22rem);\n --sf-text-2xl: clamp(1.95rem, calc(0.014962962962962963 * (100vw - 22.5rem) + 1.95rem), 2.96rem);\n --sf-text-3xl: clamp(2.44rem, calc(0.022370370370370374 * (100vw - 22.5rem) + 2.44rem), 3.95rem);\n --sf-text-4xl: clamp(3.05rem, calc(0.03274074074074074 * (100vw - 22.5rem) + 3.05rem), 5.26rem);\n\n --sf-text-display-s: clamp(2.4rem, calc(0.00888888888888889 * (100vw - 22.5rem) + 2.4rem), 3rem);\n --sf-text-display-m: clamp(3rem, calc(0.014814814814814815 * (100vw - 22.5rem) + 3rem), 4rem);\n --sf-text-display-l: clamp(3.75rem, calc(0.023407407407407408 * (100vw - 22.5rem) + 3.75rem), 5.33rem);\n\n\n /* ----------------------------------------------------------\n Line heights & letter spacing\n ---------------------------------------------------------- */\n --sf-leading-tight: 1.1;\n --sf-leading-snug: 1.3;\n --sf-leading-normal: 1.5;\n --sf-leading-relaxed: 1.625;\n\n --sf-tracking-tight: -0.025em;\n --sf-tracking-normal: 0;\n --sf-tracking-wide: 0.025em;\n --sf-tracking-wider: 0.05em;\n --sf-tracking-widest: 0.1em;\n\n\n /* ----------------------------------------------------------\n Icon sizes — em-based by design.\n Icons are sized in em so they scale with the surrounding\n text: an icon in a button at --sf-text-m renders at 1rem;\n moved into a heading at --sf-text-2xl, the same icon scales\n up automatically.\n Global scaling flows transitively through --sf-text-scale\n (parent text grows → em-icons grow) — there is no separate\n --sf-icon-scale knob by design. clamp() is intentionally\n absent here: making icons fluid INDEPENDENTLY of context\n would break the relationship to surrounding text.\n Capped at --xl (3em ≈ 48px on default text); above that the\n element ceases to be an icon and becomes an illustration.\n ---------------------------------------------------------- */\n --sf-icon-xs: 0.875em;\n --sf-icon-s: 1em;\n --sf-icon-m: 1.5em;\n --sf-icon-l: 2em;\n --sf-icon-xl: 3em;\n\n\n /* ----------------------------------------------------------\n Spacing — fixed\n ---------------------------------------------------------- */\n --sf-space-none: 0;\n --sf-space-px: 1px;\n --sf-space-gutter: var(--sf-space-l);\n\n /* Spacing — fluid */\n --sf-space-2xs: calc(clamp(0.51rem, calc(0.004888888888888888 * (100vw - 22.5rem) + 0.51rem), 0.84rem) * var(--sf-space-scale));\n --sf-space-xs: calc(clamp(0.64rem, calc(0.007259259259259258 * (100vw - 22.5rem) + 0.64rem), 1.13rem) * var(--sf-space-scale));\n --sf-space-s: calc(clamp(0.8rem, calc(0.01037037037037037 * (100vw - 22.5rem) + 0.8rem), 1.5rem) * var(--sf-space-scale));\n --sf-space-m: calc(clamp(1rem, calc(0.014814814814814815 * (100vw - 22.5rem) + 1rem), 2rem) * var(--sf-space-scale));\n --sf-space-l: calc(clamp(1.25rem, calc(0.021037037037037035 * (100vw - 22.5rem) + 1.25rem), 2.67rem) * var(--sf-space-scale));\n --sf-space-xl: calc(clamp(1.56rem, calc(0.029481481481481477 * (100vw - 22.5rem) + 1.56rem), 3.55rem) * var(--sf-space-scale));\n --sf-space-2xl: calc(clamp(1.95rem, calc(0.04133333333333333 * (100vw - 22.5rem) + 1.95rem), 4.74rem) * var(--sf-space-scale));\n --sf-space-3xl: calc(clamp(2.44rem, calc(0.057333333333333326 * (100vw - 22.5rem) + 2.44rem), 6.31rem) * var(--sf-space-scale));\n --sf-space-4xl: calc(clamp(3.05rem, calc(0.07955555555555556 * (100vw - 22.5rem) + 3.05rem), 8.42rem) * var(--sf-space-scale));\n\n\n /* ----------------------------------------------------------\n UI sizes\n ---------------------------------------------------------- */\n --sf-size-xs: 1.5rem;\n --sf-size-s: 2rem;\n --sf-size-m: 2.5rem;\n --sf-size-l: 2.75rem;\n --sf-size-xl: 3.5rem;\n\n\n /* ----------------------------------------------------------\n Containers\n ---------------------------------------------------------- */\n --sf-container-narrow: 38rem;\n --sf-container-prose: 65ch;\n --sf-container-default: 75rem;\n --sf-container-wide: 90rem;\n --sf-container-full: 100%;\n\n\n /* ----------------------------------------------------------\n Aspect ratios\n ---------------------------------------------------------- */\n --sf-ratio-square: 1;\n --sf-ratio-video: 16 / 9;\n --sf-ratio-cinema: 21 / 9;\n --sf-ratio-4-3: 4 / 3;\n --sf-ratio-3-2: 3 / 2;\n --sf-ratio-portrait: 3 / 4;\n --sf-ratio-golden: 1.618 / 1;\n\n\n /* ----------------------------------------------------------\n Border widths\n --sf-border-width-hairline: device-pixel-dependent. On 1x DPR\n displays (Windows non-Retina) the browser will round 0.5px up\n to 1px, so it visually matches --sf-border-width-1. On 2x+\n DPR (macOS/iOS Retina, most modern phones) it renders as a\n true half-pixel hairline. Use it for subtle separators where\n 1px on low-DPR is acceptable.\n ---------------------------------------------------------- */\n --sf-border-width-hairline: 0.5px;\n --sf-border-width-1: 1px;\n --sf-border-width-2: 2px;\n --sf-border-width-3: 3px;\n --sf-border-width-4: 4px;\n\n /* ----------------------------------------------------------\n Border styles — paired with --sf-border-width-* and\n --sf-color-border*. Consumer BEM:\n .my-card { border: var(--sf-border-width-1) var(--sf-border-style) var(--sf-color-border); }\n Override per element to switch decorative styles without\n rewriting the rule.\n ---------------------------------------------------------- */\n --sf-border-style: solid; /* default */\n --sf-border-style-strong: solid; /* alias for emphasis */\n --sf-border-style-soft: dashed;\n --sf-border-style-dotted: dotted;\n\n /* ----------------------------------------------------------\n Divider — global separator system. Drives `hr` (base) and\n the `.sf-divider` layout primitive.\n ---------------------------------------------------------- */\n --sf-divider-width: var(--sf-border-width-1);\n --sf-divider-style: solid;\n --sf-divider-color: var(--sf-color-border);\n\n\n /* ----------------------------------------------------------\n Border radius\n --sf-radius-full: intentionally not scaled by --sf-radius-scale.\n A pill/circle is a topological constant, not a relative size:\n any --sf-radius-scale: 0 (\"sharp\" design system) should still\n render pill buttons as pills. 9999px is a magic-number convention\n large enough to clip to half the shorter edge for any realistic UI.\n ---------------------------------------------------------- */\n --sf-radius-none: 0;\n --sf-radius-xs: calc(2px * var(--sf-radius-scale));\n --sf-radius-s: calc(4px * var(--sf-radius-scale));\n --sf-radius-m: calc(8px * var(--sf-radius-scale));\n --sf-radius-l: calc(12px * var(--sf-radius-scale));\n --sf-radius-xl: calc(16px * var(--sf-radius-scale));\n --sf-radius-2xl: calc(24px * var(--sf-radius-scale));\n --sf-radius-3xl: calc(32px * var(--sf-radius-scale));\n --sf-radius-4xl: calc(48px * var(--sf-radius-scale));\n --sf-radius-full: 9999px;\n /* Pill — semantic alias of --sf-radius-full. Reach for this name\n on rectangles you want capsule-shaped (badges, tags, chips); use\n --sf-radius-full when you mean \"as round as topologically possible\". */\n --sf-radius-pill: var(--sf-radius-full);\n /* Concentric helper — outer radius for a container that wraps\n content padded by --sf-component-pad with inner radius m.\n Keeps inner elements visually proportional inside a padded box.\n Override per use:\n .my-card { border-radius: var(--sf-radius-outer); }\n Maths: outer = inner + padding ⇒ aligned curves at corners. */\n --sf-radius-outer: calc(var(--sf-radius-m) + var(--sf-component-pad));\n\n\n /* ----------------------------------------------------------\n Shadows — BEM consumer API\n Use in your own classes: .card { box-shadow: var(--sf-shadow-m); }\n ---------------------------------------------------------- */\n /* Shadow tint — near-black derived from the neutral; the tint simply\n inherits the neutral's own chroma/hue, so a colourless neutral yields\n colourless shadows. Forced dark in both modes; --sf-shadow-strength\n handles dark-mode intensity. Override to a brand colour for explicitly\n tinted shadows — keep it dark or you get bright slabs:\n oklch(from var(--sf-color-primary) 0.15 c h). */\n --sf-shadow-color: oklch(from var(--sf-color-neutral) 0.15 c h);\n\n --sf-shadow-none: none;\n --sf-shadow-xs: 0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7));\n --sf-shadow-s: 0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 2px 6px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, var(--sf-shadow-strength), 0.7));\n --sf-shadow-m: 0 1px 3px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-shadow-l: 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 8px 24px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), 0.7)),\n 0 16px 48px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-shadow-xl: 0 2px 8px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 12px 36px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3.5), 0.7)),\n 0 24px 72px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7));\n --sf-shadow-2xl: 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.6), 0.7)),\n 0 20px 60px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 4), 0.7)),\n 0 40px 100px -8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 5), 0.7));\n --sf-shadow-inner: inset 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n\n\n /* ----------------------------------------------------------\n Text shadows — share --sf-shadow-color / --sf-shadow-strength\n ---------------------------------------------------------- */\n --sf-text-shadow-none: none;\n --sf-text-shadow-s: 0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7));\n --sf-text-shadow-m: 0 2px 4px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-text-shadow-l: 0 4px 8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7));\n\n /* Drop shadows — for filter: drop-shadow() (respects alpha edges) */\n --sf-drop-shadow-s: drop-shadow(0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7)));\n --sf-drop-shadow-m: drop-shadow(0 4px 6px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7)));\n --sf-drop-shadow-l: drop-shadow(0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7)));\n\n\n /* ----------------------------------------------------------\n Blur & opacity\n ---------------------------------------------------------- */\n --sf-blur-xs: 4px;\n --sf-blur-s: 8px;\n --sf-blur-m: 16px;\n --sf-blur-l: 32px;\n --sf-blur-xl: 48px;\n\n /* 3D perspective */\n --sf-perspective-near: 500px;\n --sf-perspective-normal: 1000px;\n --sf-perspective-far: 2000px;\n\n --sf-opacity-0: 0;\n --sf-opacity-10: 0.1;\n --sf-opacity-25: 0.25;\n --sf-opacity-50: 0.5;\n --sf-opacity-75: 0.75;\n --sf-opacity-100: 1;\n\n\n /* ----------------------------------------------------------\n Motion & easing\n ---------------------------------------------------------- */\n --sf-duration-none: 0ms;\n --sf-duration-instant: calc(100ms * var(--sf-motion-scale));\n --sf-duration-fast: calc(150ms * var(--sf-motion-scale));\n --sf-duration-normal: calc(250ms * var(--sf-motion-scale));\n --sf-duration-slow: calc(400ms * var(--sf-motion-scale));\n --sf-duration-slower: calc(600ms * var(--sf-motion-scale));\n\n --sf-ease-linear: linear;\n --sf-ease-out: cubic-bezier(0.25, 0, 0.15, 1);\n --sf-ease-in: cubic-bezier(0.5, 0, 0.75, 0.25);\n --sf-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);\n --sf-ease-spring: linear(0, 0.5, 1.1, 0.95, 1.02, 1);\n --sf-ease-elastic: linear(0, 0.3, 1.2, 0.9, 1.05, 1);\n --sf-ease-bounce: linear(0, 0.35 18%, 1 32%, 0.86 42%, 1.02 56%, 0.98 72%, 1);\n --sf-ease-overshoot: linear(0, 0.6 30%, 1.08 55%, 0.98 75%, 1);\n\n\n /* ----------------------------------------------------------\n Animation presets — pair keyframes (core/motion.css) with\n duration + easing. Use directly: animation: var(--sf-animation-fade-in);\n ---------------------------------------------------------- */\n --sf-animation-fade-in: sf-fade-in var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-fade-out: sf-fade-out var(--sf-duration-normal) var(--sf-ease-in) both;\n --sf-animation-slide-in-up: sf-slide-in-up var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-slide-in-down: sf-slide-in-down var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-slide-in-left: sf-slide-in-left var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-slide-in-right: sf-slide-in-right var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-scale-up: sf-scale-up var(--sf-duration-normal) var(--sf-ease-overshoot) both;\n --sf-animation-scale-down: sf-scale-down var(--sf-duration-normal) var(--sf-ease-in) both;\n --sf-animation-color-pulse: sf-color-pulse var(--sf-duration-slow) var(--sf-ease-in-out) infinite;\n --sf-animation-ping: sf-ping var(--sf-duration-slow) var(--sf-ease-out) infinite;\n --sf-animation-blink: sf-blink calc(1s * var(--sf-motion-scale)) steps(1, end) infinite;\n --sf-animation-float: sf-float calc(3s * var(--sf-motion-scale)) var(--sf-ease-in-out) infinite;\n --sf-animation-spin: sf-spin var(--sf-duration-slower) linear infinite;\n --sf-animation-shimmer: sf-shimmer calc(1.5s * var(--sf-motion-scale)) ease-in-out infinite;\n\n /* Stagger delays — apply to successive items for entrance choreography\n (e.g. style=\"animation-delay: var(--sf-animation-delay-2)\"). Tokens\n only; SLASHED ships no .sf-stagger utility class (by design — apply via token presets or your own BEM rules). */\n --sf-animation-delay-1: calc(75ms * var(--sf-motion-scale));\n --sf-animation-delay-2: calc(150ms * var(--sf-motion-scale));\n --sf-animation-delay-3: calc(225ms * var(--sf-motion-scale));\n --sf-animation-delay-4: calc(300ms * var(--sf-motion-scale));\n --sf-animation-delay-5: calc(375ms * var(--sf-motion-scale));\n\n\n /* ----------------------------------------------------------\n Scroll-driven animation range — consumer API. Attach these\n to your own scroll-timeline via animation-range. Not consumed\n by the framework itself; intended for BEM component authors.\n ---------------------------------------------------------- */\n --sf-scroll-timeline-range-start: 0%;\n --sf-scroll-timeline-range-end: 100%;\n\n\n /* ----------------------------------------------------------\n Mask scrim — edge-fade stops for scroll reels / overflow.\n Use in a mask-image gradient to fade content near an edge:\n mask-image: linear-gradient(to right,\n transparent 0, #000 var(--sf-mask-scrim-start),\n #000 calc(100% - var(--sf-mask-scrim-end)), transparent 100%);\n ---------------------------------------------------------- */\n --sf-mask-scrim-start: var(--sf-space-l);\n --sf-mask-scrim-end: var(--sf-space-l);\n\n\n /* ----------------------------------------------------------\n Z-index\n ---------------------------------------------------------- */\n --sf-z-below: -1;\n --sf-z-base: 0;\n --sf-z-raised: 1;\n --sf-z-low: 10;\n --sf-z-mid: 100;\n --sf-z-high: 500;\n --sf-z-top: 900;\n --sf-z-max: 9999;\n\n\n /* ----------------------------------------------------------\n Layout & a11y\n ---------------------------------------------------------- */\n --sf-header-height: 8rem;\n --sf-sticky-offset: var(--sf-header-height);\n --sf-focus-ring-width: 2px;\n --sf-focus-ring-offset: 2px;\n --sf-focus-ring-style: solid;\n --sf-touch-target: var(--sf-size-l);\n /* Global text-contrast knob. Nudges derived reading-text colours\n toward the extremes (darker in light mode, lighter in dark mode).\n Positive = more contrast; 0 = no change. Consumed by\n --sf-color-text, --sf-color-text--secondary, --sf-color-heading. */\n --sf-contrast-bias: 0;\n /* Lightness crossover for text-on-color auto-contrast. Colours with\n L above this value get dark text; below get light text. Default 0.6\n guarantees ≥ 3:1 for most colours. Shift down (e.g. 0.55) if your\n brand sits in the 0.52-0.60 range and you prefer white text on it. */\n --sf-contrast-threshold: 0.6;\n\n --sf-safe-top: env(safe-area-inset-top, 0px);\n --sf-safe-bottom: env(safe-area-inset-bottom, 0px);\n --sf-safe-left: env(safe-area-inset-left, 0px);\n --sf-safe-right: env(safe-area-inset-right, 0px);\n\n\n /* ----------------------------------------------------------\n Print\n ---------------------------------------------------------- */\n --sf-print-page-margin: 2cm;\n --sf-print-page-size: a4;\n --sf-print-base-size: 11pt;\n\n\n /* ----------------------------------------------------------\n Stroke widths\n ---------------------------------------------------------- */\n --sf-stroke-thin: 1px;\n --sf-stroke-regular: 1.5px;\n --sf-stroke-bold: 2px;\n --sf-stroke-heavy: 3px;\n\n}\n\n\n/* ============================================================\n TYPOGRAPHY ALIASES\n ============================================================ */\n\n:root {\n --sf-body-font-size: var(--sf-text-m);\n --sf-body-line-height: var(--sf-leading-normal);\n --sf-body-font-weight: var(--sf-font-weight-body);\n --sf-body-font-family: var(--sf-font-body);\n --sf-body-color: var(--sf-color-text);\n --sf-body-text-wrap: pretty;\n --sf-body-strong-weight: var(--sf-font-weight-bold);\n --sf-body-em-style: italic;\n\n --sf-code-font-size: 0.875em;\n\n --sf-heading-font-family: var(--sf-font-heading);\n --sf-heading-color: var(--sf-color-heading);\n --sf-heading-text-wrap: balance;\n\n --sf-h1-size: var(--sf-text-4xl);\n --sf-h1-line-height: var(--sf-leading-tight);\n --sf-h1-font-weight: var(--sf-font-weight-heading);\n --sf-h1-letter-spacing: var(--sf-tracking-tight);\n\n --sf-h2-size: var(--sf-text-3xl);\n --sf-h2-line-height: var(--sf-leading-tight);\n --sf-h2-font-weight: var(--sf-font-weight-heading);\n --sf-h2-letter-spacing: var(--sf-tracking-tight);\n\n --sf-h3-size: var(--sf-text-2xl);\n --sf-h3-line-height: var(--sf-leading-snug);\n --sf-h3-font-weight: var(--sf-font-weight-heading);\n --sf-h3-letter-spacing: var(--sf-tracking-normal);\n\n --sf-h4-size: var(--sf-text-xl);\n --sf-h4-line-height: var(--sf-leading-snug);\n --sf-h4-font-weight: var(--sf-font-weight-heading);\n --sf-h4-letter-spacing: var(--sf-tracking-normal);\n\n --sf-h5-size: var(--sf-text-l);\n --sf-h5-line-height: var(--sf-leading-normal);\n --sf-h5-font-weight: var(--sf-font-weight-heading);\n --sf-h5-letter-spacing: var(--sf-tracking-normal);\n\n --sf-h6-size: var(--sf-text-m);\n --sf-h6-line-height: var(--sf-leading-normal);\n --sf-h6-font-weight: var(--sf-font-weight-heading);\n --sf-h6-letter-spacing: var(--sf-tracking-wide);\n\n\n /* ----------------------------------------------------------\n Spacing aliases — BEM consumer API\n Use in your own BEM classes: .nav { gap: var(--sf-gap); }\n Source of truth for tokens.layout.css:\n --sf-space-gap → var(--sf-gap) (all layout primitives)\n --sf-space-content → var(--sf-content-gap) (stack, prose)\n ---------------------------------------------------------- */\n --sf-gap: var(--sf-space-m); /* gap between BEM elements */\n --sf-content-gap: var(--sf-space-s); /* rhythm within a component */\n --sf-component-pad: var(--sf-space-m); /* standard component padding */\n --sf-field-block: var(--sf-space-l); /* form-field block spacing */\n --sf-field-required-marker: \" *\";\n /* Leading space is baked into the default so consumers can fully\n disable the indicator with `--sf-link-external-marker: \"\"` —\n without the space a bare \"\" still left a stray space after the\n link text. Override with your own glyph (include leading space\n if you want one): `:root { --sf-link-external-marker: \" →\" }`. */\n --sf-link-external-marker: \" \\2197\"; /* \" ↗\" — consumed by .sf-link-external */\n\n\n /* ----------------------------------------------------------\n Section padding\n ---------------------------------------------------------- */\n --sf-section-pad: var(--sf-section-pad--m);\n --sf-section-pad--xs: var(--sf-space-xl);\n --sf-section-pad--s: var(--sf-space-2xl);\n --sf-section-pad--m: var(--sf-space-3xl);\n --sf-section-pad--l: var(--sf-space-4xl);\n --sf-section-pad--xl: calc(var(--sf-space-4xl) * 1.5);\n --sf-section-pad--2xl: calc(var(--sf-space-4xl) * 2);\n\n\n /* ----------------------------------------------------------\n Transition shorthands\n\n --sf-transition-all transitions ALL properties — convenient but\n a performance footgun (forces the browser to watch every computed\n value). Prefer the granular tokens below for production UI.\n ---------------------------------------------------------- */\n --sf-transition-all: all var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-colors:\n color var(--sf-duration-normal) var(--sf-ease-out),\n background-color var(--sf-duration-normal) var(--sf-ease-out),\n border-color var(--sf-duration-normal) var(--sf-ease-out),\n text-decoration-color var(--sf-duration-normal) var(--sf-ease-out),\n fill var(--sf-duration-normal) var(--sf-ease-out),\n stroke var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-transform: transform var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-opacity: opacity var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-shadow: box-shadow var(--sf-duration-normal) var(--sf-ease-out);\n\n --sf-transition-fast: all var(--sf-duration-fast) var(--sf-ease-out);\n --sf-transition-slow: all var(--sf-duration-slow) var(--sf-ease-in-out);\n --sf-transition-enter: all var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-exit: all var(--sf-duration-fast) var(--sf-ease-in);\n}\n\n/* ─── core/tokens.layout.css ─── */\n/* ============================================================\n SLASHED — core/tokens.layout.css\n Semantic tokens for layout primitives.\n Load before: core/layout.css\n Layer: slashed.tokens\n Prefix: --sf-\n\n DEPENDENCIES — requires tokens.css (loaded earlier).\n Spacing aliases inherit from the BEM consumer API in tokens.css:\n --sf-space-gap → var(--sf-gap) from tokens.css\n --sf-space-content → var(--sf-content-gap) from tokens.css\n Override --sf-gap or --sf-content-gap (the canonical source) to change\n the gaps across all layout primitives at once. --sf-space-gap and\n --sf-space-content are CANONICAL-SOURCE ALIASES — a second public name\n kept for layout-primitive readability, not a redundant indirection.\n\n PUBLIC API — every --sf-*-gap / --sf-*-padding / --sf-*-min / etc. here\n is a per-primitive override knob (e.g. style=\"--sf-cluster-gap: …\").\n No INTERNAL tokens live in this file.\n\n Each layout primitive (cluster, grid, sidebar…) has its own\n tokens (--sf-cluster-gap etc.) for local overrides.\n ============================================================ */\n\n:root {\n\n /* ----------------------------------------------------------\n Shared spacing aliases\n ---------------------------------------------------------- */\n --sf-space-gap: var(--sf-gap); /* component-level gap */\n --sf-space-content: var(--sf-content-gap); /* within-component rhythm */\n\n /* ----------------------------------------------------------\n Stack\n ---------------------------------------------------------- */\n --sf-stack-gap: var(--sf-space-content);\n\n /* ----------------------------------------------------------\n Box\n ---------------------------------------------------------- */\n --sf-box-padding: var(--sf-space-m);\n --sf-box-border-width: 0;\n --sf-box-border-color: var(--sf-color-border);\n\n /* ----------------------------------------------------------\n Center\n ---------------------------------------------------------- */\n --sf-center-max: var(--sf-container-default);\n --sf-center-gutter: var(--sf-space-gutter);\n\n /* ----------------------------------------------------------\n Cluster\n ---------------------------------------------------------- */\n --sf-cluster-gap: var(--sf-space-gap);\n --sf-cluster-align: center;\n --sf-cluster-justify: flex-start;\n\n /* ----------------------------------------------------------\n Sidebar\n ---------------------------------------------------------- */\n --sf-sidebar-gap: var(--sf-space-gap);\n --sf-sidebar-min-width: 50%; /* content column minimum */\n --sf-sidebar-width: 18rem; /* sidebar panel default size */\n\n /* ----------------------------------------------------------\n Switcher\n ---------------------------------------------------------- */\n --sf-switcher-threshold: 30rem;\n --sf-switcher-gap: var(--sf-space-gap);\n\n /* ----------------------------------------------------------\n Grid (auto-fill, breakpoint-free)\n ---------------------------------------------------------- */\n --sf-grid-min: 16rem;\n --sf-grid-gap: var(--sf-space-gap);\n\n --sf-grid-min-xs: 10rem;\n --sf-grid-min-s: 13rem;\n --sf-grid-min-m: 16rem;\n --sf-grid-min-l: 20rem;\n --sf-grid-min-xl: 24rem;\n --sf-grid-min-2xl: 28rem;\n\n /* ----------------------------------------------------------\n Cover\n ---------------------------------------------------------- */\n --sf-cover-min-height: 100dvh;\n --sf-cover-padding: var(--sf-section-pad);\n\n /* ----------------------------------------------------------\n Frame\n ---------------------------------------------------------- */\n --sf-frame-ratio: 16 / 9; /* literal , valid in aspect-ratio */\n\n /* ----------------------------------------------------------\n Icon (boxed variant) — extras for .sf-icon--boxed.\n The naked .sf-icon sizing tokens (--sf-icon-xs..xl) live in\n core/tokens.css alongside the typography scale.\n ---------------------------------------------------------- */\n --sf-icon-box-pad: 0.5em;\n --sf-icon-box-radius: var(--sf-radius-s);\n --sf-icon-box-bg: var(--sf-color-well);\n --sf-icon-box-border: var(--sf-border-width-1) solid var(--sf-color-border);\n\n /* ----------------------------------------------------------\n Reel\n ---------------------------------------------------------- */\n --sf-reel-item-width: max-content;\n --sf-reel-gap: var(--sf-space-gap);\n --sf-reel-height: auto;\n\n /* ----------------------------------------------------------\n Imposter (centered absolute/fixed overlay)\n ---------------------------------------------------------- */\n --sf-imposter-margin: var(--sf-space-m);\n\n /* ----------------------------------------------------------\n Bento grid\n ---------------------------------------------------------- */\n --sf-bento-cols-default: 3;\n --sf-bento-row-default: 10rem;\n --sf-bento-row-compact: 6rem;\n --sf-bento-row-tall: 16rem;\n --sf-bento-gap: var(--sf-space-gap);\n\n /* ----------------------------------------------------------\n Content grid (breakout pattern)\n ---------------------------------------------------------- */\n --sf-breakout-width: var(--sf-container-wide);\n --sf-content-width: var(--sf-container-default);\n\n /* ----------------------------------------------------------\n Prose\n ---------------------------------------------------------- */\n --sf-prose-paragraph: var(--sf-space-content);\n\n /* ----------------------------------------------------------\n Alternate (zigzag)\n ---------------------------------------------------------- */\n --sf-alternate-gap: var(--sf-space-content);\n --sf-alternate-inner-gap: var(--sf-space-gap);\n\n}\n\n/* ─── core/tokens.macros.css ─── */\n/* ============================================================\n SLASHED — core/tokens.macros.css\n Semantic tokens for macros (recipes / patterns).\n Load before: core/macros.css\n Layer: slashed.tokens\n Prefix: --sf-\n\n DEPENDENCIES — requires tokens.css (loaded earlier).\n Macro tokens reference --sf-space-* / --sf-radius-* / etc.\n\n PUBLIC API — every token here is a per-recipe override knob\n (e.g. style=\"--sf-line-clamp: 5\"). No INTERNAL tokens live in\n this file.\n\n Architectural parallel — these tokens stand alongside\n tokens.layout.css: same prefix, same shape, same status\n (essential, public, override-friendly).\n ============================================================ */\n\n:root {\n\n /* ----------------------------------------------------------\n Flow — distance between flow children (lobotomized owl).\n Consumed by .sf-flow > * + *.\n ---------------------------------------------------------- */\n --sf-flow-space: var(--sf-space-content);\n\n /* ----------------------------------------------------------\n Line clamp — default line count for .sf-line-clamp-N.\n Override per element: style=\"--sf-line-clamp: 5\".\n The fixed-N variants .sf-line-clamp-2 / -3 don't read this\n token (they hardcode the value), so the default only\n applies to .sf-line-clamp-N callers.\n ---------------------------------------------------------- */\n --sf-line-clamp: 3;\n\n /* ----------------------------------------------------------\n Truncate — ellipsis character for .sf-truncate.\n Stays as a token so consumers can swap to ASCII \"...\" or a\n localised character without rewriting the rule. The CSS\n text-overflow: ellipsis property doesn't read it directly;\n it's exposed for consumer-built variants.\n ---------------------------------------------------------- */\n --sf-truncate-suffix: \"\\2026\";\n\n /* ----------------------------------------------------------\n Aspect ratio — default for .sf-aspect (generic ratio container,\n distinct from layout's .sf-frame which has its own --sf-frame-ratio).\n ---------------------------------------------------------- */\n --sf-aspect: 16 / 9;\n\n /* ----------------------------------------------------------\n Scroll shadow / overflow fade — mask gradient size.\n Used by .sf-scroll-shadow (top+bottom) and .sf-overflow-fade\n (end-edge horizontal fade).\n ---------------------------------------------------------- */\n --sf-scroll-shadow-size: 2rem;\n\n}\n\n/* ─── core/reset.css ─── */\n/* SLASHED — core/reset.css\n @layer slashed.reset\n ─────────────────────────────────────────────────── */\n\n\n*, *::before, *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\nhtml {\n text-size-adjust: 100%;\n scroll-padding-top: var(--sf-header-height, 3.5rem);\n scrollbar-gutter: stable;\n color-scheme: var(--sf-color-scheme, light dark);\n hanging-punctuation: first last;\n}\n\n/* interpolate-size enables animations to/from intrinsic sizing\n keywords (auto, min-content, max-content, fit-content). Shipped:\n Chrome 129 (Aug 2024), Safari 18 (Sep 2024). Firefox: behind a\n flag as of 2026-Q2. Wrapped in @supports so unsupporting engines\n get clean progressive enhancement, not an unknown-declaration. */\n@supports (interpolate-size: allow-keywords) {\n html { interpolate-size: allow-keywords; }\n}\n\nbody {\n min-height: 100dvh;\n min-inline-size: 320px;\n}\n\nmenu, ul, ol {\n list-style: none;\n}\n\nimg, picture, video, canvas, svg {\n display: block;\n max-width: 100%;\n height: auto;\n}\n\niframe, embed, object {\n display: block;\n max-width: 100%;\n height: unset;\n border: none;\n}\n\nbutton, input, select, textarea {\n font: inherit;\n color: inherit;\n letter-spacing: inherit;\n color-scheme: inherit;\n line-height: inherit;\n}\n\nbutton {\n text-transform: none;\n}\n\n::file-selector-button {\n font: inherit;\n}\n\nsearch {\n display: block;\n}\n\nfieldset {\n border: none;\n padding: 0;\n min-width: 0;\n}\n\nlegend {\n padding: 0;\n}\n\na, button, input, select, textarea, summary {\n touch-action: manipulation;\n}\n\nsummary {\n list-style: none;\n cursor: pointer;\n}\n\ntextarea {\n resize: vertical;\n}\n\nbutton {\n cursor: pointer;\n}\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\n[hidden] {\n display: none !important;\n}\n\ndialog, [popover] {\n padding: 0;\n border: none;\n background: none;\n color: inherit;\n}\n\n[inert] {\n cursor: default;\n}\n\n/* ─── core/base.css ─── */\n/* SLASHED — core/base.css\n @layer slashed.base\n ─────────────────────────────────────────────────── */\n\n\n/* Dark/light mode lives in core/themes.css (slashed.themes layer).\n The baseline color-scheme: var(--sf-color-scheme, light dark) is\n set on html in core/reset.css. */\n\nbody {\n font-family: var(--sf-body-font-family, var(--sf-font-body));\n font-size: var(--sf-body-font-size, var(--sf-text-m));\n line-height: var(--sf-body-line-height, var(--sf-leading-normal));\n font-weight: var(--sf-body-font-weight, var(--sf-font-weight-body));\n color: var(--sf-body-color, var(--sf-color-text));\n background-color: var(--sf-color-bg, #fff);\n font-synthesis: none;\n}\n\nh1, h2, h3, h4, h5, h6 {\n font-family: var(--sf-heading-font-family, var(--sf-font-heading));\n line-height: var(--sf-leading-tight);\n color: var(--sf-heading-color, var(--sf-color-heading));\n text-wrap: var(--sf-heading-text-wrap);\n overflow-wrap: break-word;\n text-rendering: optimizelegibility;\n scroll-margin-top: calc(var(--sf-header-height, 3.5rem) + var(--sf-space-m));\n}\n\nh1 { font-size: var(--sf-h1-size); line-height: var(--sf-h1-line-height); font-weight: var(--sf-h1-font-weight); letter-spacing: var(--sf-h1-letter-spacing); }\nh2 { font-size: var(--sf-h2-size); line-height: var(--sf-h2-line-height); font-weight: var(--sf-h2-font-weight); letter-spacing: var(--sf-h2-letter-spacing); }\nh3 { font-size: var(--sf-h3-size); line-height: var(--sf-h3-line-height); font-weight: var(--sf-h3-font-weight); letter-spacing: var(--sf-h3-letter-spacing); }\nh4 { font-size: var(--sf-h4-size); line-height: var(--sf-h4-line-height); font-weight: var(--sf-h4-font-weight); letter-spacing: var(--sf-h4-letter-spacing); }\nh5 { font-size: var(--sf-h5-size); line-height: var(--sf-h5-line-height); font-weight: var(--sf-h5-font-weight); letter-spacing: var(--sf-h5-letter-spacing); }\nh6 { font-size: var(--sf-h6-size); line-height: var(--sf-h6-line-height); font-weight: var(--sf-h6-font-weight); letter-spacing: var(--sf-h6-letter-spacing); }\n\np {\n text-wrap: var(--sf-body-text-wrap);\n overflow-wrap: break-word;\n orphans: 2;\n widows: 2;\n}\n\nb, strong {\n font-weight: var(--sf-body-strong-weight);\n}\n\nem {\n font-style: var(--sf-body-em-style);\n}\n\nsmall {\n font-size: var(--sf-text-s, 80%);\n}\n\nmark {\n background-color: var(--sf-color-mark-bg, yellow);\n color: var(--sf-color-mark-text, black);\n border-radius: var(--sf-radius-xs);\n padding-inline: 0.2em;\n}\n\nabbr[title] {\n text-decoration: underline dotted;\n cursor: help;\n}\n\nkbd, samp {\n font-family: var(--sf-font-mono);\n font-size: inherit;\n}\n\nsub, sup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsup { top: -0.5em; }\nsub { bottom: -0.25em; }\n\n/* `a:link` (specificity 0,1,1) rather than bare `a` (0,0,1) so the\n author rule wins over WebKit's UA stylesheet rule\n `a:link { color: -webkit-link; }`. The bare-`a` form silently\n loses to the UA `:link` rule on unvisited anchors in WebKit,\n which paints `-webkit-link` (≈ #00728f in dark mode) regardless\n of whatever `--sf-color-link` resolves to — producing axe's\n 3.12 : 1 dark-mode contrast failure that survived four attempts\n to \"fix\" the link colour formula (PR #73, then three iterations\n of PR #76). The colour formula was never the problem; the\n selector specificity was. Hover / visited / active rules already\n used pseudo-classes so they had matching specificity and were\n never affected. */\na:link {\n color: var(--sf-color-link);\n text-decoration-color: var(--sf-color-link--underline);\n text-underline-offset: 0.15em;\n text-underline-position: from-font;\n transition:\n color var(--sf-duration-fast) var(--sf-ease-out),\n text-decoration-color var(--sf-duration-fast) var(--sf-ease-out);\n}\na:hover { color: var(--sf-color-link--hover); text-decoration-color: currentcolor; }\na:visited { color: var(--sf-color-link--visited); }\na:active { color: var(--sf-color-link--active); }\na:not([class]) { text-decoration-skip-ink: auto; }\n\na, code {\n overflow-wrap: break-word;\n}\n\ncode {\n font-family: var(--sf-font-mono);\n font-size: var(--sf-code-font-size);\n padding: 0.125em 0.3em;\n border-radius: var(--sf-radius-xs);\n background-color: var(--sf-color-code-bg);\n color: var(--sf-color-code-text, inherit);\n}\n\npre {\n font-family: var(--sf-font-mono);\n font-size: var(--sf-text-s);\n padding: var(--sf-space-m);\n border-radius: var(--sf-radius-m);\n background: var(--sf-color-code-block-bg, var(--sf-color-code-bg));\n color: var(--sf-color-code-block-text, inherit);\n overflow-x: auto;\n tab-size: 2;\n hyphens: none;\n}\npre code {\n background: none;\n color: inherit;\n border: 0;\n padding: 0;\n font-size: inherit;\n border-radius: 0;\n}\n\nsvg {\n fill: currentcolor;\n}\n\nhr {\n border: none;\n border-block-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);\n}\n\n:target {\n scroll-margin-top: calc(var(--sf-header-height, 3.5rem) + var(--sf-space-m));\n}\n\n::selection {\n background: var(--sf-color-selection-bg);\n color: var(--sf-color-selection-text);\n text-shadow: none;\n}\n\nth, caption {\n text-align: start;\n}\n\naddress, cite, dfn, var {\n font-style: normal;\n}\n\ninput, textarea {\n caret-color: var(--sf-caret-color);\n}\n\ninput, progress {\n accent-color: var(--sf-color-action);\n}\n\n::backdrop {\n background-color: var(--sf-color-dim);\n}\n\noptgroup { font: inherit; }\n::file-selector-button { font: inherit; }\n::placeholder { opacity: 1; color: var(--sf-color-text--placeholder, currentcolor); }\n\n/* Minimal rich-block defaults — just enough to distinguish from

.\n Full styling lives in .sf-prose (core/layout.css). */\nblockquote {\n border-inline-start: var(--sf-border-width-3) solid var(--sf-color-border);\n padding-inline-start: var(--sf-space-m);\n font-style: italic;\n}\n\ndt {\n font-weight: var(--sf-font-weight-heading);\n}\n\ndd {\n margin-inline-start: var(--sf-space-m);\n}\n\ntable {\n width: 100%;\n text-align: start;\n}\n\nth {\n font-weight: var(--sf-font-weight-heading);\n}\n\ntd, th {\n padding: var(--sf-space-2xs) var(--sf-space-xs);\n border-block-end: var(--sf-border-width-hairline) solid var(--sf-color-border--subtle);\n}\n\n/* ─── core/themes.css ─── */\n/* SLASHED — core/themes.css\n @layer slashed.themes\n\n Theme switching: token reassignments only. No new selectors,\n no new properties — strictly remaps existing tokens based on\n system preference and explicit data-theme attributes.\n\n Cascade position is significant. slashed.themes sits ABOVE\n slashed.{states, utilities, components, layout, base} (see\n core/layers.css), so a theme's token reassignment cannot be\n accidentally beaten by a component or utility rule of equal\n specificity. Only consumer code in slashed.overrides (or\n unlayered CSS) can override a theme.\n\n USAGE\n → follows OS preference\n → whole page dark\n

→ only this section dark\n
→ only this section light\n\n The base color-scheme: var(--sf-color-scheme, light dark)\n declaration lives in core/reset.css and provides the baseline.\n This layer flips it.\n\n MULTI-BRAND — scope a palette to any subtree by re-declaring the 6\n source tokens under a selector (e.g. [data-brand=\"x\"]). See\n optional/theme-example.css and docs/theming.md.\n\n THEME TRANSITION — toggling [data-theme] via document.startViewTransition()\n animates through the ::view-transition(root) rules in core/motion.css.\n For a plain cross-fade of colours, opt into a transition class on \n (example in optional/theme-example.css) — never transition every colour\n globally and permanently (perf footgun).\n ─────────────────────────────────────────────────── */\n\n\n/* OS preference — only when no data-theme is set on :root.\n A section-level [data-theme] still wins via the explicit\n selectors below (higher specificity within the same layer). */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme]) {\n color-scheme: dark;\n --sf-is-dark: 1;\n }\n}\n\n/* Explicit theme — works on :root (whole page) or any element\n (section-level theming). */\n[data-theme=\"light\"] { color-scheme: light; --sf-is-dark: 0; }\n[data-theme=\"dark\"] { color-scheme: dark; --sf-is-dark: 1; }\n\n\n/* LumLocker — opt-in via [data-lumlocker] on :root.\n Locks the 5 brand colors (primary, secondary, tertiary, action,\n neutral) to one shared OKLCH L value (--sf-lumlocker,\n default 0.65 in core/tokens.css), keeping each color's own hue\n and chroma. Useful when a brand picker produces colors with\n wildly different L values that look mismatched in weight.\n\n Base is excluded by design — base IS the page surface (L ~0.98 in\n light mode, ~0.20 in dark) and locking it would destroy the page\n background.\n\n Default state: OFF. Without the attribute, every brand keeps its\n own L. Add :root[data-lumlocker] (or set via JS) to enable.\n\n Per-color escape: override --sf-color-X under\n :root[data-lumlocker] to opt that one color out:\n :root[data-lumlocker] { --sf-color-primary: #yourColor; }\n\n The dark-side input falls back to -light when -dark isn't\n explicitly set, so this works even without per-mode overrides. */\n:root[data-lumlocker] {\n --sf-color-primary: light-dark(\n oklch(from var(--sf-color-primary-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-primary-dark, var(--sf-color-primary-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-secondary: light-dark(\n oklch(from var(--sf-color-secondary-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-secondary-dark, var(--sf-color-secondary-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-tertiary: light-dark(\n oklch(from var(--sf-color-tertiary-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-tertiary-dark, var(--sf-color-tertiary-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-action: light-dark(\n oklch(from var(--sf-color-action-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-action-dark, var(--sf-color-action-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-neutral: light-dark(\n oklch(from var(--sf-color-neutral-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-neutral-dark, var(--sf-color-neutral-light)) var(--sf-lumlocker) c h)\n );\n}\n\n/* ─── core/layout.css ─── */\n/* ============================================================\n SLASHED — core/layout.css\n ★ de facto required for most projects\n Layout primitives + structural patterns\n Requires: core/tokens.css, core/tokens.layout.css\n ============================================================ */\n\n\n/* -- Section -------------------------------------------------\n Page section with vertical padding. Size modifiers\n override the scoped token.\n ---------------------------------------------------------- */\n\n.sf-section { padding-block: var(--sf-section-pad); }\n.sf-section--xs { padding-block: var(--sf-section-pad--xs); }\n.sf-section--s { padding-block: var(--sf-section-pad--s); }\n.sf-section--m { padding-block: var(--sf-section-pad--m); }\n.sf-section--l { padding-block: var(--sf-section-pad--l); }\n.sf-section--xl { padding-block: var(--sf-section-pad--xl); }\n.sf-section--2xl { padding-block: var(--sf-section-pad--2xl); }\n\n.sf-section-group > .sf-section + .sf-section { padding-block-start: 0; }\n\n/* Collapse modifier — when two adjacent sections share this modifier,\n they redistribute padding 50/50 across the boundary so total spacing\n stays one full padding gap (instead of two). Works regardless of\n background color: each section always has half its padding on\n the boundary side, so content never touches a color edge.\n\n Use when sections are siblings outside a .sf-section-group wrapper\n and may share OR differ in background.\n\n Note: uses --sf-section-pad (the canonical alias = m by default).\n For other sizes, set --sf-section-pad locally on each section:\n
\n ---------------------------------------------------------- */\n.sf-section--collapse:has(+ .sf-section--collapse) {\n padding-block-end: calc(var(--sf-section-pad) / 2);\n}\n.sf-section--collapse + .sf-section--collapse {\n padding-block-start: calc(var(--sf-section-pad) / 2);\n}\n\n/* -- Divider -------------------------------------------------\n Standalone separator primitive. Token-driven; pairs with the\n `--sf-divider-*` tokens and mirrors the `hr` element default.\n ---------------------------------------------------------- */\n.sf-divider {\n border: none;\n border-block-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);\n margin-block: var(--sf-space-m);\n}\n.sf-divider--vertical {\n border-block-start: none;\n border-inline-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);\n margin-block: 0;\n margin-inline: var(--sf-space-m);\n align-self: stretch;\n}\n\n/* -- Container -----------------------------------------------\n Max-width container, centered with side gutters. Establishes\n a named inline-size container `sf-layout` so children can\n target it precisely with `@container sf-layout (...)`.\n The framework's own `.sf-bento` and `.sf-grid-*` rules use\n anonymous `@container` queries on purpose — they react to\n the nearest inline-size ancestor, which lets a consumer\n embed them inside any container without rewiring.\n ---------------------------------------------------------- */\n\n.sf-container {\n container: sf-layout / inline-size;\n width: 100%;\n max-width: var(--sf-container-default);\n margin-inline: auto;\n padding-inline: var(--sf-space-gutter);\n}\n.sf-container--narrow { max-width: var(--sf-container-narrow); }\n.sf-container--prose { max-width: var(--sf-container-prose); }\n.sf-container--wide { max-width: var(--sf-container-wide); }\n.sf-container--full { max-width: var(--sf-container-full); }\n\n/* -- Stack ---------------------------------------------------\n Flex column with even vertical spacing between children.\n Override: style=\"--sf-stack-gap: 2rem\"\n ---------------------------------------------------------- */\n\n.sf-stack {\n display: flex;\n flex-direction: column;\n gap: var(--sf-stack-gap);\n}\n.sf-stack--xs { gap: var(--sf-space-xs); }\n.sf-stack--s { gap: var(--sf-space-s); }\n.sf-stack--m { gap: var(--sf-space-m); }\n.sf-stack--l { gap: var(--sf-space-l); }\n.sf-stack--xl { gap: var(--sf-space-xl); }\n.sf-stack--2xl { gap: var(--sf-space-2xl); }\n\n.sf-stack--center { align-items: center; }\n.sf-stack--end { align-items: flex-end; }\n.sf-stack--stretch { align-items: stretch; }\n\n/* -- Box -----------------------------------------------------\n Isolated unit with padding and an optional border.\n Outline instead of border — doesn't disturb the box model when toggled.\n Override: style=\"--sf-box-padding: 1rem; --sf-box-border-width: 1px\"\n ---------------------------------------------------------- */\n\n.sf-box {\n padding: var(--sf-box-padding);\n outline: var(--sf-box-border-width) solid var(--sf-box-border-color);\n}\n\n/* -- Center --------------------------------------------------\n Centers content with a max-inline-size and side gutters.\n content-box: max-inline-size applies to content only; padding is additive.\n Override: style=\"--sf-center-max: 60rem\"\n ---------------------------------------------------------- */\n\n.sf-center {\n box-sizing: content-box;\n margin-inline: auto;\n max-inline-size: var(--sf-center-max);\n padding-inline: var(--sf-center-gutter);\n}\n\n.sf-center--intrinsic {\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n\n/* -- Cluster -------------------------------------------------\n Flex-wrap group of variable-width items.\n Override: style=\"--sf-cluster-gap: 0.5rem; --sf-cluster-justify: center\"\n ---------------------------------------------------------- */\n\n.sf-cluster {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sf-cluster-gap);\n align-items: var(--sf-cluster-align);\n justify-content: var(--sf-cluster-justify);\n}\n.sf-cluster--xs { gap: var(--sf-space-xs); }\n.sf-cluster--s { gap: var(--sf-space-s); }\n.sf-cluster--m { gap: var(--sf-space-m); }\n.sf-cluster--l { gap: var(--sf-space-l); }\n.sf-cluster--xl { gap: var(--sf-space-xl); }\n.sf-cluster--2xl { gap: var(--sf-space-2xl); }\n\n.sf-cluster--no-wrap { flex-wrap: nowrap; }\n.sf-cluster--center { justify-content: center; }\n.sf-cluster--end { justify-content: flex-end; }\n.sf-cluster--between { justify-content: space-between; }\n\n/* -- Sidebar -------------------------------------------------\n Two-column layout: first-child = sidebar, last-child = main content.\n Collapses when the viewport is too narrow (flex-basis trick).\n Override: style=\"--sf-sidebar-width: 20rem\"\n ---------------------------------------------------------- */\n\n.sf-sidebar {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sf-sidebar-gap);\n}\n\n.sf-sidebar > :first-child {\n flex-basis: var(--sf-sidebar-width);\n flex-grow: 1;\n}\n\n.sf-sidebar > :last-child {\n flex-basis: 0;\n flex-grow: 999;\n min-inline-size: var(--sf-sidebar-min-width);\n}\n\n.sf-sidebar--right > :first-child {\n flex-basis: 0;\n flex-grow: 999;\n min-inline-size: var(--sf-sidebar-min-width);\n}\n\n.sf-sidebar--right > :last-child {\n flex-basis: var(--sf-sidebar-width);\n flex-grow: 1;\n}\n\n.sf-sidebar--narrow { --sf-sidebar-width: 12rem; }\n.sf-sidebar--wide { --sf-sidebar-width: 26rem; }\n\n/* -- Switcher ------------------------------------------------\n Switches between a column (narrow viewport) and a row (wide).\n Uses calc((threshold - 100%) * 999) — zero media queries.\n Override: style=\"--sf-switcher-threshold: 40rem\"\n ---------------------------------------------------------- */\n\n.sf-switcher {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sf-switcher-gap);\n}\n\n.sf-switcher > * {\n flex-grow: 1;\n flex-basis: calc((var(--sf-switcher-threshold) - 100%) * 999);\n}\n\n.sf-switcher--no-wrap { flex-wrap: nowrap; overflow-x: auto; }\n.sf-switcher--vertical { flex-direction: column; }\n\n/* -- Grid ----------------------------------------------------\n Auto-fill CSS grid with no breakpoints. --fit switches to auto-fit.\n auto-fill: empty tracks preserved, last item doesn't stretch.\n auto-fit (--fit): empty tracks collapsed, items stretch to fill the gap.\n Override: style=\"--sf-grid-min: 12rem; --sf-grid-gap: 1rem\"\n ---------------------------------------------------------- */\n\n.sf-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(min(var(--sf-grid-min), 100%), 1fr));\n gap: var(--sf-grid-gap);\n}\n\n.sf-grid--fit {\n grid-template-columns: repeat(auto-fit, minmax(min(var(--sf-grid-min), 100%), 1fr));\n}\n\n.sf-grid--xs { --sf-grid-min: var(--sf-grid-min-xs); }\n.sf-grid--s { --sf-grid-min: var(--sf-grid-min-s); }\n.sf-grid--m { --sf-grid-min: var(--sf-grid-min-m); }\n.sf-grid--l { --sf-grid-min: var(--sf-grid-min-l); }\n.sf-grid--xl { --sf-grid-min: var(--sf-grid-min-xl); }\n.sf-grid--2xl { --sf-grid-min: var(--sf-grid-min-2xl); }\n\n/* Dense packing — let later items backfill earlier gaps (as .sf-bento\n does). Applies to any grid, including the fixed-column .sf-grid-N. */\n.sf-grid--dense { grid-auto-flow: dense; }\n\n/* -- Icon ----------------------------------------------------\n Inline icon sizing primitive. Uses em-based --sf-icon-* tokens\n so an icon scales with its surrounding text. Defaults to -m.\n ---------------------------------------------------------- */\n.sf-icon {\n display: inline-block;\n inline-size: var(--sf-icon-size, var(--sf-icon-m));\n block-size: var(--sf-icon-size, var(--sf-icon-m));\n flex-shrink: 0;\n vertical-align: text-bottom;\n fill: currentcolor;\n}\n.sf-icon--xs { --sf-icon-size: var(--sf-icon-xs); }\n.sf-icon--s { --sf-icon-size: var(--sf-icon-s); }\n.sf-icon--m { --sf-icon-size: var(--sf-icon-m); }\n.sf-icon--l { --sf-icon-size: var(--sf-icon-l); }\n.sf-icon--xl { --sf-icon-size: var(--sf-icon-xl); }\n\n/* Boxed variant — wraps the icon in a padded, bordered, optionally\n coloured frame. Adds inline-/block-size on top of the naked icon\n size so the visible glyph stays at --sf-icon-size and the box\n grows around it (content-box). Override per icon:\n */\n.sf-icon--boxed {\n inline-size: calc(var(--sf-icon-size, var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);\n block-size: calc(var(--sf-icon-size, var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);\n padding: var(--sf-icon-box-pad);\n border: var(--sf-icon-box-border);\n border-radius: var(--sf-icon-box-radius);\n background: var(--sf-icon-box-bg);\n box-sizing: content-box;\n}\n\n/* -- Cover ---------------------------------------------------\n Full-height flex column. A .sf-cover__center child is\n vertically centered via margin-block: auto.\n Override: style=\"--sf-cover-min-height: 50dvh\"\n ---------------------------------------------------------- */\n\n.sf-cover {\n display: flex;\n flex-direction: column;\n min-height: var(--sf-cover-min-height);\n padding-block: var(--sf-cover-padding);\n}\n\n.sf-cover > .sf-cover__center {\n margin-block: auto;\n}\n\n.sf-cover--min { min-height: 50dvh; }\n.sf-cover--max { min-height: 0; max-height: 100dvh; }\n.sf-cover--padding-s { padding-block: var(--sf-space-xl); }\n.sf-cover--padding-l { padding-block: var(--sf-space-4xl); }\n\n/* -- Frame ---------------------------------------------------\n Aspect-ratio container. Media children: fill + cover.\n Override: style=\"--sf-frame-ratio: 4 / 3\"\n ---------------------------------------------------------- */\n\n.sf-frame {\n aspect-ratio: var(--sf-frame-ratio);\n overflow: hidden;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n.sf-frame > img,\n.sf-frame > video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.sf-frame--square { aspect-ratio: var(--sf-ratio-square); }\n.sf-frame--video { aspect-ratio: var(--sf-ratio-video); }\n.sf-frame--cinema { aspect-ratio: var(--sf-ratio-cinema); }\n.sf-frame--portrait { aspect-ratio: var(--sf-ratio-portrait); }\n.sf-frame--4-3 { aspect-ratio: var(--sf-ratio-4-3); }\n.sf-frame--3-2 { aspect-ratio: var(--sf-ratio-3-2); }\n.sf-frame--golden { aspect-ratio: var(--sf-ratio-golden); }\n\n/* -- Reel ----------------------------------------------------\n Horizontal scroll with scroll-snap. Each child snaps to start.\n Override: style=\"--sf-reel-gap: 1rem; --sf-reel-item-width: 280px\"\n ---------------------------------------------------------- */\n\n.sf-reel {\n display: flex;\n block-size: var(--sf-reel-height);\n gap: var(--sf-reel-gap);\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n scroll-padding-inline: var(--sf-reel-gap);\n overscroll-behavior-x: contain;\n scrollbar-width: thin;\n}\n\n.sf-reel > * {\n flex: 0 0 var(--sf-reel-item-width);\n scroll-snap-align: start;\n}\n\n/* -- Imposter ------------------------------------------------\n Element centered absolutely within its parent.\n The parent must have position: relative.\n ---------------------------------------------------------- */\n\n.sf-imposter {\n position: absolute;\n inset-block-start: 50%;\n inset-inline-start: 50%;\n translate: -50% -50%;\n}\n\n.sf-imposter--fixed {\n position: fixed;\n}\n\n.sf-imposter--contain {\n max-inline-size: calc(100% - var(--sf-imposter-margin) * 2);\n max-block-size: calc(100% - var(--sf-imposter-margin) * 2);\n overflow: auto;\n}\n\n/* -- Alternate (zigzag / media-object) -----------------------\n Alternating two-column layouts (container-query responsive).\n Every other row reverses its children's order.\n\n Establishes a named container `sf-alternate` and binds the\n responsive query to that name — so a `.sf-alternate` nested\n inside another container (e.g. `.sf-bento`) still flips on\n its OWN width, not the outer container's.\n ---------------------------------------------------------- */\n\n.sf-alternate {\n display: grid;\n gap: var(--sf-alternate-gap);\n container: sf-alternate / inline-size;\n}\n\n.sf-alternate > * {\n display: grid;\n gap: var(--sf-alternate-inner-gap);\n align-items: center;\n}\n\n@container sf-alternate (min-width: 48em) {\n .sf-alternate > * {\n grid-template-columns: 1fr 1fr;\n }\n .sf-alternate > :nth-child(even) > :first-child { order: 2; }\n .sf-alternate > :nth-child(even) > :last-child { order: 1; }\n}\n\n/* -- Pancake (sticky footer) ---------------------------------\n Grid with three rows: header / main(1fr) / footer.\n ---------------------------------------------------------- */\n\n.sf-pancake {\n display: grid;\n grid-template-rows: auto 1fr auto;\n min-height: 100dvh;\n}\n\n/* -- Fixed column grids (container-query responsive) ---------\n Responsive fixed-column grids with no media queries.\n ---------------------------------------------------------- */\n\n.sf-grid-1, .sf-grid-2, .sf-grid-3, .sf-grid-4, .sf-grid-6 {\n display: grid;\n gap: var(--sf-grid-gap);\n}\n\n@container (min-width: 30em) {\n .sf-grid-4 { grid-template-columns: repeat(2, 1fr); }\n .sf-grid-6 { grid-template-columns: repeat(3, 1fr); }\n}\n\n@container (min-width: 48em) {\n .sf-grid-2 { grid-template-columns: repeat(2, 1fr); }\n .sf-grid-3 { grid-template-columns: repeat(3, 1fr); }\n .sf-grid-4 { grid-template-columns: repeat(4, 1fr); }\n .sf-grid-6 { grid-template-columns: repeat(6, 1fr); }\n}\n\n/* -- Ratio grids ---------------------------------------------\n Two-column ratio grids, container-query responsive.\n ---------------------------------------------------------- */\n\n.sf-grid-1-2, .sf-grid-2-1,\n.sf-grid-1-3, .sf-grid-3-1 {\n display: grid;\n gap: var(--sf-grid-gap);\n}\n\n@container (min-width: 48em) {\n .sf-grid-1-2 { grid-template-columns: 1fr 2fr; }\n .sf-grid-2-1 { grid-template-columns: 2fr 1fr; }\n .sf-grid-1-3 { grid-template-columns: 1fr 3fr; }\n .sf-grid-3-1 { grid-template-columns: 3fr 1fr; }\n}\n\n/* -- Content grid (breakout pattern) -------------------------\n Full-width grid that lets items break out to breakout or full\n width. Children default to the content column.\n ---------------------------------------------------------- */\n\n.sf-content-grid {\n display: grid;\n grid-template-columns:\n [full-start]\n minmax(var(--sf-space-gutter), 1fr)\n [breakout-start]\n minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2))\n [content-start]\n min(var(--sf-content-width), 100% - var(--sf-space-gutter) * 2)\n [content-end]\n minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2))\n [breakout-end]\n minmax(var(--sf-space-gutter), 1fr)\n [full-end];\n}\n\n.sf-content-grid > * { grid-column: content; }\n.sf-content-grid > .sf-breakout { grid-column: breakout; }\n.sf-content-grid > .sf-full-bleed { grid-column: full; }\n\n/* -- Bento grid ----------------------------------------------\n Free-form grid with auto-flow dense, responsive via CQ.\n Override: style=\"--sf-bento-cols: 4; --sf-bento-row: 12rem\"\n ---------------------------------------------------------- */\n\n.sf-bento {\n display: grid;\n grid-template-columns: repeat(var(--sf-bento-cols, var(--sf-bento-cols-default)), 1fr);\n grid-auto-rows: minmax(var(--sf-bento-row, var(--sf-bento-row-default)), auto);\n grid-auto-flow: dense;\n gap: var(--sf-bento-gap);\n}\n\n@container (min-width: 30em) and (max-width: 47.99em) {\n .sf-bento { grid-template-columns: repeat(2, 1fr); }\n}\n\n.sf-bento--2 { --sf-bento-cols: 2; }\n.sf-bento--4 { --sf-bento-cols: 4; }\n.sf-bento--compact { --sf-bento-row: var(--sf-bento-row-compact); }\n.sf-bento--tall { --sf-bento-row: var(--sf-bento-row-tall); }\n\n/* -- Subgrid -------------------------------------------------\n A grid child that inherits its parent's tracks.\n ---------------------------------------------------------- */\n\n.sf-subgrid { display: grid; grid-template-columns: subgrid; }\n.sf-subgrid-rows { display: grid; grid-template-rows: subgrid; }\n\n/* ─── core/macros.css ─── */\n/* ============================================================\n SLASHED — core/macros.css\n ★ shipped in essential bundle\n Macro-classes (recipes / patterns).\n Requires: core/tokens.css, core/tokens.macros.css\n Layer: slashed.macros\n\n Macro-classes vs layout primitives:\n · Layout primitive = answers \"where do my children go?\"\n (e.g. .sf-stack, .sf-grid, .sf-cluster)\n · Macro-class = answers \"what does this element DO or\n LOOK LIKE?\" (e.g. .sf-prose, .sf-flow, .sf-truncate)\n\n Cascade position:\n layout → components → macros → utilities → states → …\n Macros may compose with primitives and components, but a\n single-property utility still wins on the same selector.\n\n PUBLIC API — every class here is part of the public surface.\n Tokens consumed by these classes are listed in\n core/tokens.macros.css.\n ============================================================ */\n\n\n/* -- Prose ---------------------------------------------------\n Long-form text column with automatic vertical rhythm.\n .sf-not-prose resets styles inside a prose block.\n ---------------------------------------------------------- */\n\n.sf-prose {\n max-width: var(--sf-container-prose);\n overflow-wrap: break-word;\n}\n\n.sf-prose > * + * { margin-block-start: var(--sf-prose-paragraph); }\n.sf-prose :is(h2, h3, h4) + * { margin-block-start: var(--sf-space-s); }\n.sf-prose li + li { margin-block-start: var(--sf-space-xs); }\n.sf-prose :is(blockquote, pre) { margin-block: var(--sf-space-m); }\n.sf-prose img { margin-block: var(--sf-space-m); border-radius: var(--sf-radius-m); }\n.sf-prose :is(ul, ol) { padding-inline-start: 1.25em; }\n.sf-prose ul { list-style: disc; }\n.sf-prose ol { list-style: decimal; }\n.sf-prose ::marker { color: var(--sf-color-primary); }\n.sf-prose figure { margin-block: var(--sf-space-l); }\n.sf-prose figcaption { font-size: var(--sf-text-s); color: var(--sf-color-text--muted); margin-block-start: var(--sf-space-xs); }\n.sf-prose table { margin-block: var(--sf-space-m); display: block; overflow-x: auto; max-width: 100%; }\n.sf-prose video { margin-block: var(--sf-space-m); border-radius: var(--sf-radius-m); }\n\n/* .sf-not-prose resets — specificity 0-2-1 intentionally exceeds the\n general 0-2-0 budget because these rules must override the .sf-prose\n child selectors above (also 0-2-0). This is a documented exception. */\n.sf-prose .sf-not-prose > * + * { margin-block-start: 0; }\n.sf-prose .sf-not-prose li + li { margin-block-start: 0; }\n/* `revert` in a cascade layer rolls back past all layers to the UA\n stylesheet — restoring browser-default list styling. In the flat\n bundle (layers stripped), `revert` falls back to the UA stylesheet\n directly. Both paths produce the same result: native list bullets. */\n.sf-prose .sf-not-prose :is(ul,ol) { list-style: revert; padding-inline-start: revert; }\n.sf-prose .sf-not-prose ::marker { color: inherit; }\n.sf-prose .sf-not-prose img { margin-block: 0; border-radius: 0; }\n.sf-prose .sf-not-prose figure { margin-block: 0; }\n.sf-prose .sf-not-prose figcaption { font-size: inherit; color: inherit; margin-block-start: 0; }\n.sf-prose .sf-not-prose table { margin-block: 0; display: revert; overflow-x: revert; max-width: revert; }\n.sf-prose .sf-not-prose video { margin-block: 0; border-radius: 0; }\n\n/* -- Flow ----------------------------------------------------\n Heydon Pickering's \"lobotomized owl\" — every flow child gets\n equal margin-block-start. Useful for content blocks where you\n don't know in advance which elements will be present.\n Override: style=\"--sf-flow-space: 2rem\"\n ---------------------------------------------------------- */\n.sf-flow > * + * {\n margin-block-start: var(--sf-flow-space);\n}\n\n/* -- Truncate / line-clamp -----------------------------------\n Single-line ellipsis (.sf-truncate) and N-line clamp\n (.sf-line-clamp-2 / -3 / -N). The fixed-count variants\n hardcode -webkit-line-clamp; -N reads --sf-line-clamp.\n\n Note: -webkit-line-clamp is not vendor-locked despite the\n prefix — every modern browser implements it as the de-facto\n standard. The unprefixed CSS Overflow 4 `line-clamp` exists\n but is not yet widely shipped.\n ---------------------------------------------------------- */\n.sf-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.sf-line-clamp-2,\n.sf-line-clamp-3,\n.sf-line-clamp-N {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n.sf-line-clamp-2 { -webkit-line-clamp: 2; line-clamp: 2; }\n.sf-line-clamp-3 { -webkit-line-clamp: 3; line-clamp: 3; }\n.sf-line-clamp-N {\n -webkit-line-clamp: var(--sf-line-clamp);\n line-clamp: var(--sf-line-clamp);\n}\n\n/* -- Equal height --------------------------------------------\n Force grid/flex children to share the tallest one's height.\n Works in flex (default `align-items: stretch`) and is\n re-asserted here for cases where a wrapper resets it.\n ---------------------------------------------------------- */\n.sf-equal-height {\n display: flex;\n align-items: stretch;\n}\n.sf-equal-height > * {\n block-size: auto;\n flex: 1 1 auto;\n}\n\n/* -- Aspect --------------------------------------------------\n Generic aspect-ratio container. Distinct from layout's\n .sf-frame which targets media (object-fit: cover) — .sf-aspect\n is content-agnostic.\n Override: style=\"--sf-aspect: 4 / 3\"\n ---------------------------------------------------------- */\n.sf-aspect {\n aspect-ratio: var(--sf-aspect);\n}\n\n/* -- Scroll shadow -------------------------------------------\n Top + bottom mask gradient that reveals when content is\n scrolled inside a vertical-scroll container. Pure CSS,\n no JS. Combine with overflow-y: auto on the same element.\n Override: style=\"--sf-scroll-shadow-size: 3rem\"\n ---------------------------------------------------------- */\n.sf-scroll-shadow {\n overflow-y: auto;\n mask-image:\n linear-gradient(to bottom,\n transparent 0,\n black var(--sf-scroll-shadow-size),\n black calc(100% - var(--sf-scroll-shadow-size)),\n transparent 100%);\n}\n\n/* -- Scroll snap ---------------------------------------------\n Vertical scroll-snap container for vertically stacked sections.\n Pairs with consumer-supplied scroll-snap-align on children.\n For horizontal snap, use the .sf-reel layout primitive.\n ---------------------------------------------------------- */\n.sf-scroll-snap {\n overflow-y: auto;\n scroll-snap-type: y mandatory;\n overscroll-behavior-y: contain;\n}\n.sf-scroll-snap > * {\n scroll-snap-align: start;\n}\n\n/* -- Overflow fade -------------------------------------------\n End-edge horizontal fade for clipped inline content\n (e.g. a row of tags overflowing a card). Pure mask, so it\n respects the element's actual background.\n ---------------------------------------------------------- */\n.sf-overflow-fade {\n overflow: hidden;\n mask-image: linear-gradient(\n to right,\n black 0,\n black calc(100% - var(--sf-scroll-shadow-size)),\n transparent 100%\n );\n}\n\n/* -- No tap highlight ----------------------------------------\n Suppresses the WebKit/Android grey tap-highlight overlay on\n interactive elements where it conflicts with the framework's\n own .is-active / hover treatment.\n ---------------------------------------------------------- */\n.sf-no-tap-highlight {\n -webkit-tap-highlight-color: transparent;\n}\n\n/* -- Surface -------------------------------------------------\n Sets background + auto-flipped foreground in one class.\n Combines --sf-color-X (resolved semantic, mode-aware) with\n the matching --sf-color-text--on-X (auto-contrast) so text\n legibility is automatic without consumer math.\n\n 11 variants cover all brand + status + inverse colors. Base is\n excluded — base IS the page surface; use plain\n background: var(--sf-color-bg) if you need it explicitly.\n\n Composes naturally with .sf-section / .sf-card / .sf-stack:\n
\n ---------------------------------------------------------- */\n.sf-surface--primary { background: var(--sf-color-primary); color: var(--sf-color-text--on-primary); }\n.sf-surface--secondary { background: var(--sf-color-secondary); color: var(--sf-color-text--on-secondary); }\n.sf-surface--tertiary { background: var(--sf-color-tertiary); color: var(--sf-color-text--on-tertiary); }\n.sf-surface--action { background: var(--sf-color-action); color: var(--sf-color-text--on-action); }\n.sf-surface--neutral { background: var(--sf-color-neutral); color: var(--sf-color-text--on-neutral); }\n.sf-surface--inverse { background: var(--sf-color-inverse); color: var(--sf-color-text--on-inverse); }\n.sf-surface--success { background: var(--sf-color-success); color: var(--sf-color-text--on-success); }\n.sf-surface--warning { background: var(--sf-color-warning); color: var(--sf-color-text--on-warning); }\n.sf-surface--error { background: var(--sf-color-error); color: var(--sf-color-text--on-error); }\n.sf-surface--info { background: var(--sf-color-info); color: var(--sf-color-text--on-info); }\n.sf-surface--danger { background: var(--sf-color-danger); color: var(--sf-color-text--on-danger); }\n\n/* -- Text gradient -------------------------------------------\n Fills text with a gradient. Defaults to --sf-gradient-primary\n (auto-adapts to brand + dark mode). Override the background-image\n per-instance for any other gradient or radial:\n

\n\n Selection on gradient text inherits the clipping — that's a\n known browser limitation across all engines, accepted as-is.\n\n The `color: transparent` declaration is gated behind a feature\n query for background-clip:text. Without that gate, an engine\n that ignored background-clip:text would render the text fully\n invisible (transparent over no clipped fill). All current\n evergreen browsers support either the standard or the\n -webkit- prefixed form, so the supports check is a defensive\n guard rather than a real-world fallback.\n ---------------------------------------------------------- */\n.sf-text-gradient {\n background-image: var(--sf-gradient-primary);\n /* stylelint-disable-next-line property-no-vendor-prefix -- required for Safari */\n -webkit-background-clip: text;\n background-clip: text;\n}\n@supports ((background-clip: text) or (-webkit-background-clip: text)) {\n .sf-text-gradient {\n color: transparent;\n }\n}\n\n/* -- External link indication --------------------------------\n Adds an external-link glyph after the link text. Opt-in per\n element via class — for global auto-detection, write your\n own rule in unlayered CSS:\n a[rel~=\"external\"]:not(.no-external)::after {\n content: var(--sf-link-external-marker);\n }\n\n Marker is tokenised so consumers can swap to \" →\", \" ⤴\", or\n unset (empty string fully disables it — the default token\n value bakes the leading space in, so `\"\"` leaves nothing\n after the link):\n :root { --sf-link-external-marker: \"\" }\n ---------------------------------------------------------- */\n.sf-link-external::after {\n content: var(--sf-link-external-marker);\n display: inline-block;\n font-size: 0.85em;\n text-decoration: none;\n}\n\n/* ─── core/states.css ─── */\n/* SLASHED — core/states.css\n @layer slashed.states\n\n Stateful classes toggled by JS or ARIA attributes.\n Prefix: .is-* (exclusive to this layer — never used in utilities).\n\n These are NOT utility classes. They describe dynamic,\n runtime states that change in response to user interaction\n or application logic. Utilities describe static intent;\n states describe current condition.\n\n Design principles:\n ─ Generic states only — component-specific overrides belong\n in slashed.components (e.g. .sf-modal.is-open).\n ─ Tokens first — every value references a design token.\n ─ Low specificity — single class, no nesting.\n ─ Minimal property sets — states set the MINIMUM needed\n to communicate the state. Components layer on top.\n ─────────────────────────────────────────────────── */\n\n\n/* ============================================================\n VISIBILITY\n ============================================================ */\n\n.is-hidden {\n display: none !important;\n}\n\n.is-invisible {\n visibility: hidden;\n}\n\n.is-visible {\n visibility: visible;\n}\n\n/* ============================================================\n INTERACTIVITY — DISABLED / READONLY\n ============================================================ */\n\n.is-disabled {\n opacity: var(--sf-opacity-disabled);\n pointer-events: none;\n cursor: not-allowed;\n user-select: none;\n}\n\n.is-readonly {\n pointer-events: none;\n user-select: none;\n}\n\n/* ============================================================\n LOADING / ASYNC FEEDBACK\n ============================================================ */\n\n.is-loading {\n color: transparent !important;\n pointer-events: none;\n position: relative;\n}\n\n.is-loading::after {\n content: \"\";\n position: absolute;\n inset-block-start: 50%;\n inset-inline-start: 50%;\n width: 1em;\n height: 1em;\n margin-block-start: -0.5em;\n margin-inline-start: -0.5em;\n border: var(--sf-border-width-2) solid var(--sf-color-border--strong);\n border-block-start-color: var(--sf-color-action);\n border-radius: var(--sf-radius-full);\n /* Reduced-motion: accessibility.css kills animation-duration via\n !important on *, *::before, *::after — so this spinner stops\n even without a local reduced-motion wrapper. */\n animation: var(--sf-animation-spin);\n}\n\n.is-busy {\n cursor: progress;\n}\n\n.is-pending {\n /* Optimistic UI — request in flight but content still visible/usable\n (unlike .is-loading, which masks content with a spinner). */\n opacity: var(--sf-state-pending-opacity);\n cursor: progress;\n}\n\n.is-skeleton {\n color: transparent !important;\n background: linear-gradient(\n 90deg,\n var(--sf-color-well) 25%,\n var(--sf-color-raised) 50%,\n var(--sf-color-well) 75%\n );\n background-size: 200% 100%;\n animation: var(--sf-animation-shimmer);\n border-radius: var(--sf-radius-s);\n pointer-events: none;\n user-select: none;\n}\n\n/* ============================================================\n ACTIVE / SELECTED / CURRENT — navigation & toggling\n ============================================================ */\n\n.is-active {\n /* Generic active indicator — components add visual specifics.\n Sets a scoped token for components to consume. */\n --sf-is-active: 1;\n}\n\n.is-selected {\n background-color: var(--sf-color-bg--selected);\n}\n\n.is-current {\n /* Navigation: marks the current page/route.\n Components style via .sf-nav .is-current etc.\n Weight is tokenised so consumers can override without specificity battles:\n :root { --sf-current-font-weight: var(--sf-font-weight-semibold); } */\n --sf-is-current: 1;\n font-weight: var(--sf-current-font-weight, var(--sf-font-weight-bold));\n}\n\n.is-highlighted {\n background-color: var(--sf-color-bg--focus);\n}\n\n.is-pressed {\n /* Toggle buttons (aria-pressed=\"true\"). Components add specifics. */\n --sf-is-pressed: 1;\n background-color: var(--sf-color-bg--selected);\n}\n\n/* ============================================================\n OPEN / CLOSED — disclosure widgets, modals, dropdowns\n\n .is-open / .is-collapsed — a thing is shown vs hidden (modal,\n dropdown, drawer). .is-expanded — maps to aria-expanded on a\n disclosure/accordion trigger. They share the --sf-is-open flag\n but signal different intent to consumers.\n ============================================================ */\n\n.is-open {\n --sf-is-open: 1;\n}\n\n.is-collapsed {\n --sf-is-open: 0;\n}\n\n.is-expanded {\n --sf-is-open: 1;\n}\n\n/* ============================================================\n VALIDATION / FEEDBACK\n\n Overlap semantics (see docs/states.md):\n · .is-invalid / .is-valid — form-field validation (maps to\n aria-invalid). Use on inputs.\n · .is-error / .is-success — general component feedback, not tied\n to a single field (a failed save, a completed step).\n · .is-danger — destructive-action context (a delete button), NOT\n a validation result. Distinct from .is-error.\n ============================================================ */\n\n.is-valid {\n --sf-field-border-color: var(--sf-color-success);\n --sf-field-text-color: var(--sf-color-success-strong);\n}\n\n.is-invalid {\n --sf-field-border-color: var(--sf-color-error);\n --sf-field-text-color: var(--sf-color-error-strong);\n}\n\n.is-warning {\n --sf-field-border-color: var(--sf-color-warning);\n --sf-field-text-color: var(--sf-color-warning-strong);\n}\n\n.is-success {\n --sf-field-border-color: var(--sf-color-success);\n --sf-field-text-color: var(--sf-color-success-strong);\n}\n\n.is-error {\n --sf-field-border-color: var(--sf-color-error);\n --sf-field-text-color: var(--sf-color-error-strong);\n}\n\n.is-info {\n --sf-field-border-color: var(--sf-color-info);\n --sf-field-text-color: var(--sf-color-info-strong);\n}\n\n.is-danger {\n --sf-field-border-color: var(--sf-color-danger);\n --sf-field-text-color: var(--sf-color-danger-strong);\n}\n\n/* ============================================================\n POSITION / STICKINESS\n ============================================================ */\n\n.is-sticky {\n position: sticky;\n top: var(--sf-sticky-offset, 0);\n z-index: var(--sf-z-low);\n}\n\n.is-pinned {\n position: sticky;\n top: 0;\n z-index: var(--sf-z-mid);\n}\n\n.is-fixed {\n position: fixed;\n}\n\n.is-fullscreen {\n /* App-managed fullscreen overlay. For the native Fullscreen API,\n prefer the `:fullscreen` pseudo-class; this is the JS-toggled\n equivalent for elements not entered via that API. */\n position: fixed;\n inset: 0;\n z-index: var(--sf-z-max);\n}\n\n.is-resizable {\n resize: both;\n overflow: auto;\n}\n\n/* ============================================================\n OVERFLOW / CLIPPING\n ============================================================ */\n\n.is-clipped {\n overflow: hidden !important;\n}\n\n.is-scrollable {\n overflow: auto;\n overscroll-behavior: contain;\n}\n\n.is-truncated {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* ============================================================\n DRAG & DROP\n ============================================================ */\n\n.is-dragging {\n opacity: var(--sf-opacity-50);\n cursor: grabbing;\n}\n\n.is-drop-target {\n outline: var(--sf-focus-ring-width) dashed var(--sf-color-action);\n outline-offset: var(--sf-focus-ring-offset);\n}\n\n.is-draggable {\n cursor: grab;\n}\n\n/* ============================================================\n OVERLAY / BACKDROP\n ============================================================ */\n\n.is-overlay {\n position: absolute;\n inset: 0;\n}\n\n/* ============================================================\n FOCUS / INTERACTION MODIFIERS\n ============================================================ */\n\n.is-clickable {\n cursor: pointer;\n}\n\n.is-unselectable {\n user-select: none;\n}\n\n.is-focused {\n /* Programmatic / JS-driven focus indicator, independent of\n :focus-visible. Normal weight so consumers can restyle. */\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-focus-ring-color);\n outline-offset: var(--sf-focus-ring-offset);\n}\n\n/* ============================================================\n EMPTY STATE\n ============================================================ */\n\n.is-empty:empty {\n display: none;\n}\n\n/* ============================================================\n KEYFRAMES (used by states above)\n\n Spinner and shimmer keyframes live in core/motion.css alongside\n the rest of the framework's @keyframes. They are referenced from\n here by name (sf-spin, sf-shimmer) and are inert until applied.\n If you load core/states.css without core/motion.css, define them\n in your own CSS or copy them from motion.css.\n ============================================================ */\n\n/* ─── core/motion.css ─── */\n/* SLASHED — core/motion.css\n @layer slashed.motion\n\n Extends the base interactive transition (defined in base.css)\n with a broader property set and adds scroll/view-transition\n behaviour. Optional and independently loadable.\n\n Reduced-motion handling is intentionally NOT duplicated here:\n accessibility.css (a higher cascade layer, shipped in the\n 'essential' bundle) neutralises animation/transition via\n property-level !important. The no-preference wrapper below is\n a defensive opt-in stance — animations apply only when the\n user has not requested their absence — and remains correct\n even if motion.css is served without accessibility.css.\n ─────────────────────────────────────────────────── */\n\n\n@media (prefers-reduced-motion: no-preference) {\n\n html {\n scroll-behavior: smooth;\n }\n\n /* Keyboard navigation must not be hijacked by smooth scroll. */\n html:focus-within {\n scroll-behavior: auto;\n }\n\n /* Broadens the base.css link transition to all interactive\n elements and the full set of animatable UI properties. */\n a, button, input, select, textarea, summary {\n transition-property:\n color, background-color, border-color, text-decoration-color,\n fill, stroke, opacity, box-shadow, transform, filter,\n backdrop-filter;\n transition-duration: var(--sf-duration-fast);\n transition-timing-function: var(--sf-ease-out);\n }\n\n @supports (view-transition-name: none) {\n ::view-transition-old(root),\n ::view-transition-new(root) {\n animation-duration: var(--sf-duration-normal);\n animation-timing-function: var(--sf-ease-out);\n }\n }\n\n /* Animation presets — opt-in classes. Scoped to no-preference so\n reduced-motion users never trigger them (accessibility.css also\n neutralises animation-duration as a hard guarantee). These are\n DECORATIVE: under reduced motion they simply don't run, so never\n rely on one (e.g. .sf-fade-out) as the sole way to show/hide\n content — drive real state with hidden/display/JS instead. */\n .sf-fade-in { animation: var(--sf-animation-fade-in); }\n .sf-fade-out { animation: var(--sf-animation-fade-out); }\n .sf-slide-in-up { animation: var(--sf-animation-slide-in-up); }\n .sf-slide-in-down { animation: var(--sf-animation-slide-in-down); }\n .sf-slide-in-left { animation: var(--sf-animation-slide-in-left); }\n .sf-slide-in-right { animation: var(--sf-animation-slide-in-right); }\n .sf-scale-up { animation: var(--sf-animation-scale-up); }\n .sf-scale-down { animation: var(--sf-animation-scale-down); }\n\n /* Scroll-driven entrance effects — element animates into view\n as it enters the viewport. Uses CSS scroll-driven animations\n via animation-timeline: view(). Range tokens\n (--sf-scroll-timeline-range-{start,end}, declared in\n core/tokens.css) control where the animation starts and ends\n along the scroll progress.\n\n Example:\n
\n\n Reduced-motion: gated by the no-preference wrapper above, so\n these are inert when the user opts out. The core animation\n (translate/opacity) still composes correctly with reduced-motion\n neutralisation in accessibility.css if a consumer accidentally\n writes them outside the gate.\n\n Browser support — animation-timeline is shipping in Chrome 115+\n and Firefox 145+ (Nov 2026). Safari does not yet support it\n (tracked at WebKit bug 264057); on those engines the @supports\n block below is skipped, so the rule falls back to a one-shot\n time-driven animation playing once at --sf-duration-slow.\n animation-duration is set unconditionally because without it\n a non-supporting engine would default to 0s and snap to the\n end state instantly — there'd be no visible entrance at all.\n Where animation-timeline is supported the timeline drives\n progress and the duration is ignored. */\n .sf-entrance--fade,\n .sf-entrance--fade-up,\n .sf-entrance--fade-down,\n .sf-entrance--fade-left,\n .sf-entrance--fade-right,\n .sf-entrance--scale-up {\n animation-duration: var(--sf-duration-slow);\n animation-timing-function: var(--sf-ease-out);\n animation-fill-mode: both;\n }\n @supports (animation-timeline: view()) {\n .sf-entrance--fade,\n .sf-entrance--fade-up,\n .sf-entrance--fade-down,\n .sf-entrance--fade-left,\n .sf-entrance--fade-right,\n .sf-entrance--scale-up {\n animation-timeline: view();\n animation-range: var(--sf-scroll-timeline-range-start, entry 0%)\n var(--sf-scroll-timeline-range-end, cover 30%);\n }\n }\n .sf-entrance--fade { animation-name: sf-fade-in; }\n .sf-entrance--fade-up { animation-name: sf-slide-in-up; }\n .sf-entrance--fade-down { animation-name: sf-slide-in-down; }\n .sf-entrance--fade-left { animation-name: sf-slide-in-left; }\n .sf-entrance--fade-right { animation-name: sf-slide-in-right; }\n .sf-entrance--scale-up { animation-name: sf-scale-up; }\n\n}\n\n/* Keyframes — inert until referenced, so they live outside the\n no-preference wrapper (consumers may reference them at will).\n Spinner and shimmer are used by .is-loading / .is-skeleton in\n core/states.css; the rest power the .sf-* animation classes\n above. */\n@keyframes sf-fade-in { from { opacity: 0; } to { opacity: 1; } }\n@keyframes sf-fade-out { from { opacity: 1; } to { opacity: 0; } }\n@keyframes sf-slide-in-up { from { opacity: 0; translate: 0 1rem; } to { opacity: 1; translate: 0 0; } }\n@keyframes sf-slide-in-down { from { opacity: 0; translate: 0 -1rem; } to { opacity: 1; translate: 0 0; } }\n@keyframes sf-slide-in-left { from { opacity: 0; translate: 1rem 0; } to { opacity: 1; translate: 0 0; } }\n@keyframes sf-slide-in-right { from { opacity: 0; translate: -1rem 0; } to { opacity: 1; translate: 0 0; } }\n@keyframes sf-scale-up { from { opacity: 0; scale: 0.92; } to { opacity: 1; scale: 1; } }\n@keyframes sf-scale-down { from { opacity: 1; scale: 1; } to { opacity: 0; scale: 0.92; } }\n\n@keyframes sf-spin { to { rotate: 360deg; } }\n@keyframes sf-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }\n\n/* Notification ping (expanding fading ring), cursor blink, gentle float.\n Keyframes only — apply via your own BEM/component rules or the\n --sf-animation-ping / -blink / -float presets. No utility classes\n ship for these (by design). */\n@keyframes sf-ping { 75%, 100% { scale: 2; opacity: 0; } }\n@keyframes sf-blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }\n@keyframes sf-float { 0%, 100% { translate: 0 0; } 50% { translate: 0 -0.5rem; } }\n\n/* @property color interpolation — proves that registered custom\n properties animate smoothly in oklch. Because --sf-color-primary-light\n is registered with syntax:\"\", the browser interpolates the\n intermediate color values frame-by-frame. Opt-in via .sf-color-pulse. */\n@keyframes sf-color-pulse {\n 0% { --sf-color-primary-light: oklch(from var(--sf-color-primary-light) l c h); }\n 50% { --sf-color-primary-light: oklch(from var(--sf-color-primary-light) calc(l + 0.25) c h); }\n 100% { --sf-color-primary-light: oklch(from var(--sf-color-primary-light) l c h); }\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .sf-color-pulse {\n animation: var(--sf-animation-color-pulse);\n }\n}\n\n/* @starting-style — entry animations for elements appearing in the\n top layer (dialog, [popover]) or via display:none → block. SLASHED\n ships no global @starting-style rules because the from-state is\n element/component specific. Consumers opt in per component, e.g.:\n dialog[open] { opacity: 1; @starting-style { opacity: 0; } }\n Tracked for the future components layer. */\n\n/* ─── core/accessibility.css ─── */\n/* SLASHED — core/accessibility.css\n @layer slashed.accessibility\n\n Critical a11y rules use !important to survive unlayered CSS and\n third-party resets (unlayered normal beats any layered normal).\n Protection is SELECTIVE — only rules whose override is a genuine\n user-facing barrier are hardened. Rules that are legitimately\n customised (touch targets, skip-link position, contrast boost,\n cursor) are left normal on purpose.\n\n Audit note: reset.css/base.css contain no !important on outline,\n animation-* or transition-*, so the reversed important-layer order\n cannot let a lower layer defeat these rules.\n ─────────────────────────────────────────────────── */\n\n\n/* ----------------------------------------------------------\n Focus — keyboard visibility is WCAG 2.4.7, hard barrier.\n Ring definition lives here (not base.css) so all focus\n logic is in one high-priority layer. Hardened.\n ---------------------------------------------------------- */\n:focus:not(:focus-visible) {\n outline: none;\n}\n\n:focus-visible {\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-focus-ring-color) !important;\n outline-offset: var(--sf-focus-ring-offset) !important;\n}\n\n/* ----------------------------------------------------------\n Reduced motion. Token override helps components that read\n duration tokens in other contexts; the property-level\n !important block is the real guarantee and works\n independently of tokens (and of unlayered token overrides).\n ---------------------------------------------------------- */\n@media (prefers-reduced-motion: reduce) {\n :root {\n --sf-duration-instant: 0.01ms;\n --sf-duration-fast: 0.01ms;\n --sf-duration-normal: 0.01ms;\n --sf-duration-slow: 0.01ms;\n --sf-duration-slower: 0.01ms;\n }\n\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n scroll-behavior: auto !important;\n }\n}\n\n/* Manual motion opt-out — same suppression as the OS preference above,\n but consumer-toggled (e.g. a site-level \"reduce motion\" switch). Works\n regardless of the OS setting. */\n.no-motion,\n.no-motion *,\n.no-motion *::before,\n.no-motion *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n scroll-behavior: auto !important;\n}\n\n/* ----------------------------------------------------------\n High contrast — enhancement, not a barrier. Left normal:\n if overridden the focus ring still exists, just thinner.\n ---------------------------------------------------------- */\n@media (prefers-contrast: more) {\n :root {\n --sf-focus-ring-width: 3px;\n /* Push derived reading-text colours toward the extremes (see the\n --sf-contrast-bias knob in tokens.css). */\n --sf-contrast-bias: 0.06;\n }\n\n hr {\n border-block-start-width: var(--sf-border-width-2);\n }\n}\n\n/* ----------------------------------------------------------\n Reduced transparency.\n ---------------------------------------------------------- */\n@media (prefers-reduced-transparency: reduce) {\n ::backdrop {\n background-color: var(--sf-color-neutral);\n opacity: 1;\n }\n}\n\n/* ----------------------------------------------------------\n Minimum touch targets — WCAG 2.5.5 has documented\n exceptions; dense desktop-first UIs legitimately go below\n 44px with other hit-area strategies. Left normal ON PURPOSE\n so it stays a recommendation, not an unbreakable rule.\n ---------------------------------------------------------- */\n@media (pointer: coarse) {\n button,\n input[type=\"button\"],\n input[type=\"submit\"],\n input[type=\"reset\"],\n select,\n summary,\n a {\n min-height: var(--sf-touch-target);\n min-width: var(--sf-touch-target);\n }\n}\n\n/* ----------------------------------------------------------\n Disabled states — cosmetic, left normal.\n ---------------------------------------------------------- */\n[disabled],\n[aria-disabled=\"true\"] {\n cursor: not-allowed;\n}\n\n/* ----------------------------------------------------------\n Screen-reader-only. Atomic contract: a partial override\n either exposes hidden content visually or hides intended\n content. All-or-nothing, so the whole set is hardened.\n ---------------------------------------------------------- */\n.sr-only,\n.visually-hidden,\n.sr-only-focusable:not(:focus, :focus-within) {\n position: absolute !important;\n width: 1px !important;\n height: 1px !important;\n padding: 0 !important;\n margin: -1px !important;\n overflow: hidden !important; /* fallback for Safari <16 */\n overflow: clip !important;\n clip-path: inset(50%) !important;\n white-space: nowrap !important;\n border: 0 !important;\n}\n\n/* ----------------------------------------------------------\n Skip link — appearance belongs to the integrator's design.\n Left normal so position/colour can be themed freely.\n ---------------------------------------------------------- */\n.skip-link {\n position: absolute;\n top: -100%;\n left: var(--sf-space-m);\n z-index: var(--sf-z-max);\n padding: var(--sf-space-s) var(--sf-space-m);\n background: var(--sf-color-bg);\n color: var(--sf-color-text);\n border: var(--sf-border-width-1) solid var(--sf-color-border);\n border-radius: var(--sf-radius-m);\n transition: top var(--sf-duration-fast) var(--sf-ease-out);\n}\n.skip-link:focus {\n top: var(--sf-space-m);\n}\n\n/* ----------------------------------------------------------\n Focus-parent — an a11y *pattern*, not a state. Lives in the\n accessibility layer (was previously in states.css; moved in\n v0.3.0) so that all focus-related a11y rules share one home\n and survive third-party resets at the same priority as the\n focus-visible ring above. Cannot be hardened with !important\n because the outline shorthand is consumer-customisable.\n ---------------------------------------------------------- */\n.focus-parent:focus-within {\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-color-border--focus);\n outline-offset: var(--sf-focus-ring-offset);\n}\n\n/* ----------------------------------------------------------\n Clickable-parent — the card-with-link a11y pattern.\n Applied to the card container. Two modes:\n\n Automatic (single primary link):\n
\n

Title

\n

Description

\n \n
\n\n Explicit (multiple links — add [data-overlay-link] to the primary):\n
\n

Title

\n

Description

\n Category ← lifted automatically\n ← lifted automatically\n
\n\n Mechanic: the link's ::after fills the card via position:absolute;\n inset:0. It is contained by .sf-clickable-parent (position:relative)\n only if the link itself has no positioned ancestor between it and the\n card — so no element containing the overlay link should be given\n position:relative. Flow children are therefore intentionally left at\n position:static; they sit below the overlay in stacking order.\n\n Trade-off: card text is NOT user-selectable by mouse. A JavaScript\n click-delegate could restore selectability while preserving\n full-card navigation.\n\n Key features:\n • [data-overlay-link] explicit mode — reliable multi-link cards\n • `:focus-within` card ring — always included, no extra recipe\n • --sf-clickable-overlay-z — escape parent stacking contexts\n • Wider interactive-element exceptions (summary, video, audio…)\n\n Tune --sf-clickable-overlay-z to escape a parent stacking context.\n [data-no-overlay] opts any descendant above the overlay.\n ---------------------------------------------------------- */\n.sf-clickable-parent {\n --sf-clickable-overlay-z: 1;\n position: relative;\n cursor: pointer;\n}\n\n/* Overlay: the link's ::after fills the containing card.\n Automatic mode — every :any-link gets the overlay; works reliably\n for single-link cards. Secondary links that also receive ::after are\n lifted to position:relative (interactive rule below), which scopes\n their ::after to themselves and keeps them independently clickable.\n Explicit mode — only [data-overlay-link] fills the card. */\n.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link)::after,\n.sf-clickable-parent [data-overlay-link]::after {\n content: \"\";\n position: absolute;\n inset: 0;\n z-index: var(--sf-clickable-overlay-z);\n pointer-events: auto;\n}\n\n/* Interactive descendants beat the overlay and stay functional.\n Explicit mode: every non-designated :any-link is lifted — robust at\n any nesting depth, no :first-of-type fragility.\n Automatic mode: links that are :not(:first-of-type) among siblings\n are lifted. Sibling-scoped — use explicit mode for cards where\n secondary links live at different nesting depths.\n [data-no-overlay] is the per-element consumer escape hatch. */\n.sf-clickable-parent :is(\n button, input, textarea, select,\n summary, video, audio,\n [role=\"button\"], [data-no-overlay]\n),\n.sf-clickable-parent:has([data-overlay-link]) a:where(:any-link):not([data-overlay-link]),\n.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):not(:first-of-type) {\n position: relative;\n z-index: calc(var(--sf-clickable-overlay-z) + 1);\n}\n\n/* Card-level focus ring — keyboard users see which card is active.\n The focus ring is always included without requiring a separate\n recipe. Suppress with outline:none\n if the card design provides its own focus indicator. */\n.sf-clickable-parent:focus-within {\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-color-border--focus);\n outline-offset: var(--sf-focus-ring-offset);\n}\n\n/* ----------------------------------------------------------\n Forced colors (Windows High Contrast). Outline-based focus\n already survives; we only (a) adopt the system Highlight\n colour for the ring and (b) drop decorative shadows that\n forced-colors would otherwise flatten into solid slabs.\n Shadow removal is zero-specificity so any rule can win it back.\n ---------------------------------------------------------- */\n@media (forced-colors: active) {\n :root {\n --sf-focus-ring-color: Highlight;\n }\n\n :focus-visible {\n outline-color: Highlight !important;\n }\n\n :where(*) {\n box-shadow: none;\n text-shadow: none;\n }\n\n /* Form controls otherwise lose their visible boundary in forced-colors\n (their token-driven border colour is replaced by the system canvas).\n Re-assert a system-coloured border so fields stay distinguishable. */\n input,\n select,\n textarea {\n border: var(--sf-border-width-1) solid;\n }\n}\n\n/* ─── core/print.css ─── */\n/* SLASHED — core/print.css\n @layer slashed.print\n\n Print rules and @page definition. Print tokens (--sf-print-*)\n are declared in core/tokens.css.\n\n Color contract (Phase 3):\n authored colour is preserved by default. Browsers apply their own\n ink-saving heuristics via the default `print-color-adjust: economy`,\n so semantic colours (badges, status pills, , syntax highlighting)\n reach paper intact. Two opt-in classes give consumers control:\n\n .print-color-exact → force every descendant to print colours\n verbatim (for colour-coded data that loses\n meaning when the browser flattens it).\n .print-no-color → force everything to ink-on-paper:\n transparent backgrounds + system text colour.\n The legacy blanket reset, now opt-in.\n\n `!important` is reserved for selectors whose semantics require\n defeating consumer-authored CSS:\n - `.no-print` and the hide-list (nav, aside, button, …) — these\n are content negative-space and must vanish from print regardless\n of authored display rules.\n - `details > summary` — disclosure widget carries no print value.\n - The two opt-in colour classes above — when the consumer marks a\n region with them they are explicitly asking to override authored\n colour, so `!important` is the contract.\n ─────────────────────────────────────────────────── */\n\n\n/* Screen default: print-only content is hidden until the page is printed. */\n.print-only {\n display: none;\n}\n\n@media print {\n\n @page {\n size: var(--sf-print-page-size, a4);\n margin: var(--sf-print-page-margin, 2cm);\n }\n\n body {\n font-size: var(--sf-print-base-size);\n }\n\n /* Decorative shadows are paper-noise. No `!important` — an authored\n shadow that explicitly survives into print is the consumer's call. */\n *,\n *::before,\n *::after {\n box-shadow: none;\n text-shadow: none;\n }\n\n /* Expand link URLs after the link text so paper-readers can follow\n them. Anchors and javascript: pseudo-links are excluded. */\n a[href]::after {\n content: \" (\" attr(href) \")\";\n font-size: 90%;\n word-break: break-all;\n }\n\n a[href^=\"#\"]::after,\n a[href^=\"javascript:\" i]::after {\n content: \"\";\n }\n\n abbr[title]::after {\n content: \" (\" attr(title) \")\";\n }\n\n img, svg, video, canvas, figure, table, pre, blockquote {\n break-inside: avoid;\n }\n\n thead {\n display: table-header-group;\n }\n\n tr {\n break-inside: avoid;\n }\n\n h1, h2, h3, h4, h5, h6 {\n break-after: avoid;\n orphans: 3;\n widows: 3;\n }\n\n p {\n orphans: 3;\n widows: 3;\n }\n\n pre, blockquote {\n border: var(--sf-border-width-1) solid var(--sf-color-border--strong);\n padding: var(--sf-space-s);\n }\n\n /* Hide non-essential interactive and decorative elements.\n `!important` is justified: these elements are content negative-space\n in print, and consumer-authored display rules must not bring them\n back accidentally. */\n nav, aside, button, input, select, textarea,\n audio, video, dialog, [popover], .no-print {\n display: none !important;\n }\n\n /* Reveal print-only content (overrides the screen default above). */\n .print-only {\n display: revert !important;\n }\n\n /* Preserve
content regardless of open state. Only\n `summary` keeps `!important` — the disclosure chevron carries no\n print value and the consumer should not be able to bring it back\n inadvertently. The other two rules win against the UA stylesheet\n on layered-author > UA precedence alone. */\n details {\n display: block;\n }\n details > summary {\n display: none !important;\n }\n details:not([open]) > :not(summary) {\n display: block;\n }\n\n /* ── Opt-in colour treatment ─────────────────────────────────── */\n\n /* Force colour-faithful rendering. Use on regions where a tinted\n background or coloured text encodes meaning (status pills,\n severity callouts, syntax highlighting, charts). */\n .print-color-exact,\n .print-color-exact * {\n print-color-adjust: exact !important;\n -webkit-print-color-adjust: exact !important;\n }\n\n /* Opt back into the legacy blanket reset: transparent backgrounds\n and system text colour everywhere inside the marked subtree.\n Use on regions where ink-saving is the contract (corporate\n boilerplate forms, append-to-existing-document templates). */\n .print-no-color,\n .print-no-color *,\n .print-no-color *::before,\n .print-no-color *::after {\n background: transparent !important;\n color: CanvasText !important;\n }\n\n}\n"],"names":[]} \ No newline at end of file +{"version":3,"sourceRoot":null,"mappings":"AAkJA,kGACA,qGACA,oGACA,kGACA,mGACA,iGAIA,mGACA,kGACA,+FACA,gGACA,iGAIA,wEAEA,0EACA,2EACA,2EACA,wEAOA,40yBAuhCA,0DAMA,mLAaA,4CACE,sCAGF,6CAKA,2BAIA,sEAMA,0EAOA,wHAQA,2BAQA,qBAIA,2CAMA,iBAIA,iEAIA,uCAKA,yBAIA,sBAIA,gDAKA,gCAIA,oEAOA,uBAcA,mWAUA,yVAUA,oJACA,oJACA,oJACA,oJACA,oJACA,oJAEA,iFAOA,kDAIA,sCAIA,qCAIA,iJAOA,yDAKA,2DAKA,8EAMA,cACA,kBAcA,oRASA,6EACA,8CACA,4CACA,6CAEA,gCAIA,+MASA,iRAWA,2FASA,sBAIA,0GAKA,mFAIA,0GAMA,4BAIA,uCAIA,iDAIA,mDAIA,gDAIA,sBACA,oCACA,6EAIA,8IAMA,6CAIA,yCAIA,kCAKA,6CAIA,2IA6CA,mCACE,0DAQF,qDACA,mDAuBA,89BAqCA,gDACA,wDACA,sDACA,sDACA,sDACA,wDACA,0DAEA,gEAgBA,oGAGA,gGAQA,kJAKA,4MAkBA,wJAOA,2DACA,yDACA,uDACA,uDAOA,qEAKA,qCACA,mCACA,mCACA,mCACA,qCACA,uCAEA,qCACA,oCACA,uCAUA,+BACA,mCACA,iCACA,iCACA,iCACA,mCACA,qCAQA,0GAWA,iIAOA,4EAWA,gJAOA,uCACA,qCACA,qCACA,qCACA,uCACA,yCAEA,sCACA,2CACA,0CACA,mDAQA,kEAMA,wEAKA,+HAYA,8EAKA,6CACA,2CAQA,oEAMA,wFAKA,uDACA,6CASA,iIAMA,iGAIA,iDACA,+CACA,+CACA,+CACA,iDACA,mDAIA,qCAMA,0LAQA,8CACA,4CACA,4CACA,4CACA,8CAOA,uVAgBA,yHAOA,8CAIA,gCACA,8CACA,sDACA,uDAOA,oHAQA,sEAOA,sDACA,oDACA,sDACA,0DACA,gDACA,gDACA,sDAOA,uNAWA,sEAUA,gGAOA,mCAIA,yJAgBA,0FAMA,kFAMA,sCACE,8CAGA,kDACA,kDAOF,4EAUA,2FAKA,yBACE,+CACA,gDAGF,yBACE,+CACA,+CACA,+CACA,gDAOF,wFAMA,yBACE,2CACA,2CACA,2CACA,4CAUF,uGAKA,+BACA,+BACA,+BACA,+BAOA,saAgBA,uCACA,mDACA,iDAOA,sOAQA,8CACE,+CAGF,+BACA,+BACA,8DACA,wDAMA,uDACA,yDAgCA,uEAKA,2DACA,+DACA,sDACA,6DACA,8EACA,iDACA,gCACA,gCACA,iDACA,gDACA,wHACA,4FACA,gFAKA,+EAMA,iFACA,+CACA,2DACA,8CACA,wFACA,+FACA,6DAQA,qDAcA,uEAMA,mHAOA,mDACA,mDACA,yFAUA,kDAIA,6CAWA,yCAUA,8LAeA,2FAKA,0CASA,mJAeA,6DAiBA,+FACA,qGACA,kGACA,4FACA,+FACA,+FACA,+FACA,+FACA,yFACA,sFACA,4FAmBA,gHAMA,iCACE,+BAmBF,yHAiCA,kCAIA,gCAIA,+BAQA,wGAOA,kDASA,wEAMA,gWAkBA,yBAIA,oEAOA,2RAmBA,4BAMA,2DAIA,mGASA,2DAIA,4EAeA,wBAIA,6BAIA,4BAgBA,+GAKA,6GAKA,iHAKA,iHAKA,2GAKA,wGAKA,8GASA,iFAMA,yDAMA,yBAIA,8DASA,wCASA,sCAIA,yDAKA,wEAUA,0DAKA,4HAKA,0BAQA,sCASA,6BAIA,kCAIA,sJAWA,6BAgCA,8CAEE,4BAKA,uCAMA,+QASA,sCACE,mJAaF,kDACA,oDACA,0DACA,8DACA,8DACA,gEACA,oDACA,wDA4BA,0PAUA,sCACE,iRAWF,6CACA,oDACA,wDACA,wDACA,0DACA,mDASF,iDACA,kDACA,kFACA,qFACA,kFACA,oFACA,oEACA,sEAEA,qCACA,oFAMA,6CACA,0DACA,8DAMA,4QAMA,8CACE,2DAkCF,wCAIA,6KAWA,uCACE,0IAQA,8JAaF,yMAcA,+BACE,uDAOA,sDAQF,6CACE,+DAYF,wBACE,oJAeF,mDAUA,6RAmBA,mVAYA,uCAYA,wKA4CA,iFAYA,2NAgBA,kWAeA,+KAYA,8BACE,sCAIA,iDAIA,2CAQA,6DAwCF,yBAIA,aAEE,+EAKA,yCAMA,kDASA,6EAMA,2DAKA,+CAIA,oEAIA,iCAIA,sBAIA,uDAMA,qBAKA,8GASA,qGAMA,qCASA,sBAGA,uCAGA,gDASA,sHAUA","sources":["slashed.essential.flat.css"],"sourcesContent":["/* SLASHED v0.3.0 — slashed.essential.flat.css (flat) */\n\n/* ─── core/layers.css ─── */\n/* SLASHED — core/layers.css\n CSS cascade layer order. Load this file first, always.\n ─────────────────────────────────────────────────── */\n\n/* ─── core/tokens.css ─── */\n/* ============================================================\n SLASHED — core/tokens.css\n @layer slashed.tokens\n\n ARCHITECTURE — 3 layers:\n 1. @property source tokens — animatable, 6 brand + 5 status (-light only); -dark is optional\n 2. Resolved semantic tokens — light-dark() with auto-derivation of dark from light (relative color syntax)\n 3. Derived tokens — relative color syntax (auto-contrast, tints/shades)\n\n THEMING — override just the 6 -light tokens. Dark mode auto-adapts via\n relative color syntax — zero extra work. For full control over dark mode,\n optionally override the -dark tokens (unregistered custom properties).\n\n Quick branding (6 tokens):\n :root {\n --sf-color-primary-light: oklch(0.45 0.20 264);\n --sf-color-secondary-light: oklch(0.25 0.03 260);\n --sf-color-tertiary-light: oklch(0.48 0.14 310);\n --sf-color-action-light: oklch(0.60 0.16 210);\n --sf-color-neutral-light: oklch(0.45 0.02 260);\n --sf-color-base-light: oklch(0.98 0.005 260);\n }\n Full per-mode control (add -dark overrides):\n :root {\n --sf-color-primary-dark: oklch(0.75 0.18 264);\n }\n\n BEM CONSUMER API — most tokens are intentionally unused by the\n framework itself. Tokens such as --sf-shadow-*, --sf-blur-*, --sf-gap,\n --sf-gradient-* etc. are meant for use in your own BEM classes:\n .card { box-shadow: var(--sf-shadow-m); gap: var(--sf-gap); }\n \"Not used internally\" does NOT mean dead code: every token is exercised\n in docs/demo.html and validated by the regression suite (tests/), so an\n unreferenced token is never untested or orphaned.\n\n RELATION TO tokens.layout.css — the spacing aliases in the \"Spacing\n aliases\" section are the source of truth for --sf-space-gap and\n --sf-space-content in tokens.layout.css. Overriding --sf-gap changes the\n gaps across all layout primitives (cluster, grid, sidebar, etc.).\n\n PUBLIC API vs INTERNAL (token-API freeze contract)\n ─────────────────────────────────────────────────\n Tokens are classified into three stability tiers (PUBLIC,\n PUBLIC-ADVANCED, INTERNAL).\n PUBLIC and PUBLIC-ADVANCED share the same SemVer guarantee\n (removal/rename = major bump after freeze). The distinction is\n documentation-level: ADVANCED tokens are powerful but niche — they\n live in a separate \"Advanced\" section in docs.\n\n PUBLIC — everyday knobs, prominently documented:\n · brand source tokens (--sf-color-{primary,secondary,tertiary,action,neutral,base}-light)\n · status source tokens (--sf-color-{success,warning,error,info,danger}-light)\n · resolved semantic colors (--sf-color-{primary,...,danger}, surfaces, text, borders, links, interactive bg, selection)\n · text-on-color (--sf-color-text--on-*)\n · code colors (--sf-color-code-bg, --sf-color-code-text)\n · status triplets (--sf-color-{success,...,danger}-{subtle,strong,muted})\n · gradients (--sf-gradient-*)\n · shadows (--sf-shadow-{none,xs,s,m,l,xl,2xl,inner})\n · text/drop shadows (--sf-text-shadow-*, --sf-drop-shadow-*)\n · blur (--sf-blur-*)\n · opacity (--sf-opacity-{0,10,25,50,75,100}, --sf-opacity-disabled, --sf-state-pending-opacity)\n · spacing (--sf-space-{none,px,gutter,2xs..4xl})\n · font families (--sf-font-{body,heading,display,mono,humanist,geometric,slab})\n · font weights (--sf-font-weight-{thin..black,body,heading,display})\n · font sizes (--sf-text-{2xs..4xl}, --sf-text-display-{s,m,l})\n · leading (--sf-leading-{tight,snug,normal,relaxed})\n · tracking (--sf-tracking-{tight,normal,wide,wider,widest})\n · icon sizes (--sf-icon-{xs,s,m,l,xl})\n · UI sizes (--sf-size-{xs,s,m,l,xl})\n · containers (--sf-container-{narrow,prose,default,wide,full})\n · ratios (--sf-ratio-{square,video,cinema,portrait,golden,4-3,3-2})\n · border widths (--sf-border-width-{hairline,1,2,3,4})\n · border styles (--sf-border-style, --strong, --soft, --dotted)\n · divider (--sf-divider-{width,style,color})\n · radius (--sf-radius-{none,xs,s,m,l,xl,2xl,3xl,4xl,full,pill})\n · duration (--sf-duration-{none,instant,fast,normal,slow,slower})\n · easing (--sf-ease-{linear,out,in,in-out,spring,elastic,bounce,overshoot})\n · transitions (--sf-transition-{all,colors,transform,opacity,shadow,fast,slow,enter,exit})\n · animations (--sf-animation-{fade-in,fade-out,slide-in-*,scale-*,color-pulse,ping,blink,float,spin,shimmer})\n · animation delays (--sf-animation-delay-{1..5})\n · z-index (--sf-z-{below,base,raised,low,mid,high,top,max})\n · header/sticky (--sf-header-height, --sf-sticky-offset)\n · touch target (--sf-touch-target)\n · focus ring (--sf-focus-ring-{width,offset,style,color})\n · caret (--sf-caret-color)\n · scrollbar (--sf-scrollbar-{thumb,track})\n · BEM aliases (--sf-gap, --sf-content-gap, --sf-component-pad, --sf-field-block,\n --sf-field-required-marker, --sf-link-external-marker)\n · section padding (--sf-section-pad, --sf-section-pad--{xs,s,m,l,xl,2xl})\n · typography aliases (--sf-body-*, --sf-code-font-size, --sf-heading-*, --sf-h{1..6}-*)\n · current-font-weight (--sf-current-font-weight)\n · color scheme (--sf-color-scheme)\n · stroke (--sf-stroke-{thin,regular,bold,heavy})\n · layout tokens — all of tokens.layout.css (per-primitive gaps, sidebar, grid, bento, etc.)\n · macro tokens — all of tokens.macros.css (flow-space, line-clamp, aspect, scroll-shadow-size)\n · palette tokens — all of optional/tokens.palette.css\n · scoped override tokens (--sf-icon-size, --sf-bento-cols, --sf-bento-row,\n --sf-color-code-block-bg, --sf-color-code-block-text,\n --sf-field-border-color, --sf-field-text-color)\n\n PUBLIC-ADVANCED — stable (same SemVer guarantee) but niche/powerful:\n · --sf-lumlocker (LumLocker lightness override)\n · --sf-contrast-bias, --sf-contrast-threshold (text-contrast tuning)\n · --sf-shadow-strength, --sf-shadow-color, --sf-shadow-glow-color (shadow internals)\n · --sf-font-features, --sf-font-variation, --sf-optical-sizing (OpenType)\n · --sf-space-scale, --sf-text-scale, --sf-text-display-scale,\n --sf-radius-scale, --sf-motion-scale (global multipliers)\n · --sf-scroll-timeline-range-{start,end} (scroll-driven animation)\n · --sf-mask-scrim-{start,end} (edge-fade masks)\n · --sf-safe-{top,bottom,left,right} (safe-area insets)\n · --sf-perspective-{near,normal,far} (3D transforms)\n · --sf-truncate-suffix (ellipsis character override)\n · --sf-radius-outer (concentric radius helper)\n · --sf-is-active, --sf-is-current, --sf-is-pressed, --sf-is-open (state flags)\n · --sf-focus-ring-shadow (composite focus shadow)\n · --sf-print-{page-margin,page-size,base-size} (print tokens)\n\n INTERNAL — implementation detail, may change without a major bump:\n · --sf-is-dark (mode flag — set via data-theme, not directly)\n\n CANONICAL-SOURCE ALIASES (intentional synonym hops, kept by design):\n · --sf-space-gap → --sf-gap (layout primitives read this name)\n · --sf-space-content → --sf-content-gap (stack / prose rhythm)\n · --sf-section-pad → --sf-section-pad--m (unsuffixed = the -m default)\n These are deliberate two-name contracts: override the canonical token\n (right side) to move everything downstream. The alias graph is otherwise\n ≤3 hops (per-primitive → layout-system → canonical source), giving three\n override levels: local (style=\"--sf-cluster-gap\"), all-primitives\n (--sf-space-gap), or global (--sf-gap).\n ============================================================ */\n\n\n/* ============================================================\n @property — BRAND COLORS (-light source tokens, animatable)\n Override these 6 tokens to rebrand — dark mode auto-derives.\n Optionally set --sf-color-X-dark (no @property) for full control.\n ============================================================ */\n\n@property --sf-color-primary-light { syntax: \"\"; inherits: true; initial-value: oklch(0.45 0.20 264); }\n@property --sf-color-secondary-light { syntax: \"\"; inherits: true; initial-value: oklch(0.25 0.03 260); }\n@property --sf-color-tertiary-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.14 310); }\n@property --sf-color-action-light { syntax: \"\"; inherits: true; initial-value: oklch(0.60 0.16 210); }\n@property --sf-color-neutral-light { syntax: \"\"; inherits: true; initial-value: oklch(0.45 0.02 260); }\n@property --sf-color-base-light { syntax: \"\"; inherits: true; initial-value: oklch(0.98 0.005 260); }\n\n/* @property — STATUS COLORS (-light source tokens) */\n\n@property --sf-color-success-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.17 150); }\n@property --sf-color-warning-light { syntax: \"\"; inherits: true; initial-value: oklch(0.75 0.17 80); }\n@property --sf-color-error-light { syntax: \"\"; inherits: true; initial-value: oklch(0.62 0.20 35); }\n@property --sf-color-info-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.15 240); }\n@property --sf-color-danger-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.24 12); }\n\n/* Mode flag — drives formula direction for non-color dark overrides.\n Set by base.css via color-scheme switches. */\n@property --sf-is-dark { syntax: \"\"; inherits: true; initial-value: 0; }\n\n@property --sf-is-active { syntax: \"\"; inherits: true; initial-value: 0; }\n@property --sf-is-current { syntax: \"\"; inherits: true; initial-value: 0; }\n@property --sf-is-pressed { syntax: \"\"; inherits: true; initial-value: 0; }\n@property --sf-is-open { syntax: \"\"; inherits: true; initial-value: 0; }\n\n\n/* ============================================================\n DERIVED SEMANTIC TOKENS\n ============================================================ */\n\n:root {\n\n /* ----------------------------------------------------------\n Color scheme\n ---------------------------------------------------------- */\n --sf-color-scheme: light dark;\n\n /* ----------------------------------------------------------\n LumLocker — OKLCH L value used by the\n :root[data-lumlocker] override in core/themes.css.\n Locks all 5 brand colors (primary, secondary, tertiary,\n action, neutral) to one shared lightness while keeping their\n individual hue and chroma. Base is excluded — it must remain\n a light surface for page background.\n Override to change the locked lightness:\n :root { --sf-lumlocker: 0.55 }\n ---------------------------------------------------------- */\n --sf-lumlocker: 0.65;\n\n /* ----------------------------------------------------------\n Resolved color tokens — auto-switch via light-dark().\n Components reference these. Override the -light / -dark\n source tokens above to customise; or override these directly\n with your own light-dark() expression.\n ---------------------------------------------------------- */\n /* Dark auto-derivation formula (brand + status):\n clamp(0.65, 0.95 - l*0.5, 0.88) lightens dark-mode value relative to the light source.\n Base inverts: clamp(0.16, 1.18 - l, 0.24) — near-white flips to near-dark.\n Override any --sf-color-X-dark to take full per-mode control. */\n --sf-color-primary: light-dark(var(--sf-color-primary-light), var(--sf-color-primary-dark, oklch(from var(--sf-color-primary-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-secondary: light-dark(var(--sf-color-secondary-light), var(--sf-color-secondary-dark, oklch(from var(--sf-color-secondary-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-tertiary: light-dark(var(--sf-color-tertiary-light), var(--sf-color-tertiary-dark, oklch(from var(--sf-color-tertiary-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-action: light-dark(var(--sf-color-action-light), var(--sf-color-action-dark, oklch(from var(--sf-color-action-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-neutral: light-dark(var(--sf-color-neutral-light), var(--sf-color-neutral-dark, oklch(from var(--sf-color-neutral-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-base: light-dark(var(--sf-color-base-light), var(--sf-color-base-dark, oklch(from var(--sf-color-base-light) clamp(0.16, calc(1.18 - l), 0.24) calc(c * 0.5) h)));\n\n --sf-color-success: light-dark(var(--sf-color-success-light), var(--sf-color-success-dark, oklch(from var(--sf-color-success-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-warning: light-dark(var(--sf-color-warning-light), var(--sf-color-warning-dark, oklch(from var(--sf-color-warning-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-error: light-dark(var(--sf-color-error-light), var(--sf-color-error-dark, oklch(from var(--sf-color-error-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-info: light-dark(var(--sf-color-info-light), var(--sf-color-info-dark, oklch(from var(--sf-color-info-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-danger: light-dark(var(--sf-color-danger-light), var(--sf-color-danger-dark, oklch(from var(--sf-color-danger-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n\n\n /* ----------------------------------------------------------\n Surfaces — derived from --sf-color-base (auto-adapts)\n ---------------------------------------------------------- */\n --sf-color-bg: oklch(from var(--sf-color-base) calc(l + 0.02) c h);\n --sf-color-surface: var(--sf-color-base);\n --sf-color-well: oklch(from var(--sf-color-base) calc(l - 0.02) c h);\n --sf-color-raised: oklch(from var(--sf-color-base) calc(l + 0.04) c h);\n --sf-color-overlay: oklch(from var(--sf-color-base) l c h / 0.9);\n --sf-color-inverse: oklch(from var(--sf-color-base) calc(1 - l) c h);\n\n\n /* ----------------------------------------------------------\n Text — light and dark require opposite formula directions,\n so each side of light-dark() references its source token.\n ---------------------------------------------------------- */\n --sf-color-text: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h),\n oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1) c h)\n );\n --sf-color-text--secondary: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.15, calc(l - 0.25 - var(--sf-contrast-bias)), 0.45) c h),\n oklch(from var(--sf-color-neutral) clamp(0.55, calc(l + 0.1 + var(--sf-contrast-bias)), 0.90) c h)\n );\n --sf-color-text--muted: var(--sf-color-neutral);\n --sf-color-text--placeholder: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.45, calc(l + 0.15), 0.75) c h),\n oklch(from var(--sf-color-neutral) clamp(0.35, calc(l - 0.1), 0.65) c h)\n );\n --sf-color-text--disabled: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.55, calc(l + 0.25), 0.82) c h),\n oklch(from var(--sf-color-neutral) clamp(0.25, calc(l - 0.2), 0.55) c h)\n );\n --sf-color-text--inverse: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.85, calc(l + 0.4), 0.98) c h),\n oklch(from var(--sf-color-neutral) clamp(0.05, calc(l - 0.4), 0.35) c h)\n );\n --sf-color-heading: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h),\n oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1) c h)\n );\n /* Inline-code text colour. Auto-contrasts with --sf-color-code-bg using\n the same luminance formula as --sf-color-text--on-*. This prevents\n invisible text when code appears inside inverted-colour containers\n (e.g. sf-imposter with a dark background where inherited colour is\n near-white). Override with a tint value if desired. */\n --sf-color-code-text: oklch(from var(--sf-color-code-bg) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n\n\n /* ----------------------------------------------------------\n Text-on-color — sign(var(--sf-contrast-threshold) - l) * 999 is mode-agnostic:\n auto-selects light or dark text based on background luminance.\n Uses resolved tokens — works for any user-supplied color.\n\n CONTRAST NOTE: Guarantees ≥ 3:1 (WCAG AA Large Text / UI).\n Colors in the mid-luminance range (L ≈ 0.52–0.67) may not\n reach 4.5:1 (AA Normal Text) regardless of threshold chosen —\n this is a mathematical limitation of any binary black/white\n decision. The SLASHED configurator will warn users when their\n brand colors fall in this range. For body text on colored\n backgrounds, override --sf-color-text--on-* manually.\n ---------------------------------------------------------- */\n --sf-color-text--on-primary: oklch(from var(--sf-color-primary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-secondary: oklch(from var(--sf-color-secondary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-tertiary: oklch(from var(--sf-color-tertiary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-action: oklch(from var(--sf-color-action) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-neutral: oklch(from var(--sf-color-neutral) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-base: var(--sf-color-text);\n --sf-color-text--on-inverse: var(--sf-color-text--inverse);\n --sf-color-text--on-success: oklch(from var(--sf-color-success) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-warning: oklch(from var(--sf-color-warning) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-error: oklch(from var(--sf-color-error) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-info: oklch(from var(--sf-color-info) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-danger: oklch(from var(--sf-color-danger) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n\n\n /* ----------------------------------------------------------\n Borders — direction-dependent, reference source tokens.\n Light: borders are lighter than neutral (l + offset).\n Dark: borders are darker than neutral (l - offset).\n ---------------------------------------------------------- */\n --sf-color-border: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.70, calc(l + 0.35), 0.95) 0.005 h),\n oklch(from var(--sf-color-neutral) clamp(0.25, calc(l - 0.3), 0.55) 0.005 h)\n );\n --sf-color-border--subtle: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.75, calc(l + 0.4), 0.97) 0.005 h),\n oklch(from var(--sf-color-neutral) clamp(0.20, calc(l - 0.38), 0.45) 0.005 h)\n );\n --sf-color-border--strong: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.55, calc(l + 0.1), 0.85) 0.02 h),\n oklch(from var(--sf-color-neutral) clamp(0.38, calc(l - 0.1), 0.65) 0.02 h)\n );\n --sf-color-border--focus: var(--sf-color-action);\n --sf-color-border--disabled: oklch(from var(--sf-color-border--subtle) l 0 h / 0.5);\n --sf-color-border--translucent: oklch(from var(--sf-color-neutral) l c h / 0.15);\n\n\n /* ----------------------------------------------------------\n Links — derived from --sf-color-action (auto-adapts)\n ---------------------------------------------------------- */\n /* Links keep the action hue but CLAMP lightness toward a contrast-safe\n band — a ceiling in light mode, a floor in dark mode — so link text\n clears WCAG AA (4.5:1) on the page background for the default palette\n AND the large majority of brand overrides (a fixed offset alone only\n fixed the default). Lightness dominates OKLCH contrast but doesn't\n fully determine it: very high-chroma hues (saturated yellow/green) can\n still fall short even at a safe lightness — those remain the consumer's\n responsibility (see docs/theming.md → \"Link contrast\").\n\n NOTE: the consumer-facing :link rule in core/base.css uses\n `a:link` (not bare `a`) so its specificity matches WebKit's UA\n stylesheet `a:link { color: -webkit-link }`. Without that\n pseudo-class the framework's `--sf-color-link` value is\n silently ignored on unvisited anchors in WebKit and\n `-webkit-link` (≈ #00728f in dark mode) paints instead —\n regardless of how this token is computed. PR #73 and several\n follow-up attempts mistakenly chased the formula. */\n --sf-color-link: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.07, 0.48), 1) c h),\n oklch(from var(--sf-color-action) clamp(0.68, l, 1) c h)\n );\n --sf-color-link--hover: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.15, 0.40), 1) c h),\n oklch(from var(--sf-color-action) clamp(0, max(l + 0.10, 0.68), 1) c h)\n );\n --sf-color-link--active: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.21, 0.34), 1) c h),\n oklch(from var(--sf-color-action) clamp(0, max(l + 0.15, 0.74), 1) c h)\n );\n /* Visited link — 60° hue shift from action gives clearly distinct\n chroma without colliding with default tertiary (h ≈ 310). Same\n lightness clamp as the base link. If your brand puts action and\n tertiary close in hue, override directly:\n :root { --sf-color-link--visited: #6b46c1; } */\n --sf-color-link--visited: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.07, 0.48), 1) c calc(h + 60)),\n oklch(from var(--sf-color-action) clamp(0.68, l, 1) c calc(h + 60))\n );\n --sf-color-link--underline: oklch(from var(--sf-color-action) l c h / 0.3);\n --sf-color-link--disabled: var(--sf-color-text--disabled);\n\n\n /* ----------------------------------------------------------\n Interactive states — derived from --sf-color-neutral (auto-adapts)\n ---------------------------------------------------------- */\n --sf-color-bg--hover: oklch(from var(--sf-color-neutral) l c h / 0.08);\n --sf-color-bg--active: oklch(from var(--sf-color-neutral) l c h / 0.12);\n --sf-color-bg--selected: oklch(from var(--sf-color-action) l c h / 0.1);\n --sf-color-bg--focus: oklch(from var(--sf-color-action) l c h / 0.06);\n --sf-color-bg--disabled: var(--sf-color-well);\n --sf-color-code-bg: var(--sf-color-well);\n\n /* ----------------------------------------------------------\n Selection & backdrop\n ---------------------------------------------------------- */\n --sf-color-selection-bg: oklch(from var(--sf-color-action) l c h / 0.22);\n --sf-color-selection-text: inherit;\n --sf-color-mark-bg: oklch(from var(--sf-color-warning) l c h / 0.25);\n --sf-color-mark-text: inherit;\n --sf-color-dim: oklch(0 0 0 / 0.5);\n\n\n /* ----------------------------------------------------------\n Scrollbar\n ---------------------------------------------------------- */\n --sf-scrollbar-thumb: var(--sf-color-neutral);\n --sf-scrollbar-track: transparent;\n\n\n /* ----------------------------------------------------------\n Status triplets — bg/border use same formula both modes;\n text is direction-dependent so uses source tokens.\n error = form validation, input errors\n danger = destructive actions (delete, remove)\n ---------------------------------------------------------- */\n --sf-color-success-subtle: oklch(from var(--sf-color-success) l c h / 0.12);\n --sf-color-success-strong: light-dark(\n oklch(from var(--sf-color-success-light) calc(l - 0.15) c h),\n oklch(from var(--sf-color-success) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-success-muted: oklch(from var(--sf-color-success) l c h / 0.3);\n\n --sf-color-warning-subtle: oklch(from var(--sf-color-warning) l c h / 0.12);\n --sf-color-warning-strong: light-dark(\n oklch(from var(--sf-color-warning-light) calc(l - 0.25) c h),\n oklch(from var(--sf-color-warning) clamp(0.70, calc(l + 0.05), 1) c h)\n );\n --sf-color-warning-muted: oklch(from var(--sf-color-warning) l c h / 0.3);\n\n --sf-color-error-subtle: oklch(from var(--sf-color-error) l c h / 0.1);\n --sf-color-error-strong: light-dark(\n oklch(from var(--sf-color-error-light) calc(l - 0.1) c h),\n oklch(from var(--sf-color-error) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-error-muted: oklch(from var(--sf-color-error) l c h / 0.3);\n\n --sf-color-info-subtle: oklch(from var(--sf-color-info) l c h / 0.1);\n --sf-color-info-strong: light-dark(\n oklch(from var(--sf-color-info-light) calc(l - 0.1) c h),\n oklch(from var(--sf-color-info) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-info-muted: oklch(from var(--sf-color-info) l c h / 0.3);\n\n --sf-color-danger-subtle: oklch(from var(--sf-color-danger) l c h / 0.1);\n --sf-color-danger-strong: light-dark(\n oklch(from var(--sf-color-danger-light) calc(l - 0.1) c h),\n oklch(from var(--sf-color-danger) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-danger-muted: oklch(from var(--sf-color-danger) l c h / 0.3);\n\n\n /* ----------------------------------------------------------\n Gradients — derived from brand colors (auto-adapt)\n ---------------------------------------------------------- */\n --sf-gradient-primary: linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) calc(l - 0.08) c h));\n --sf-gradient-secondary: linear-gradient(135deg, var(--sf-color-secondary), oklch(from var(--sf-color-secondary) calc(l - 0.08) c h));\n --sf-gradient-tertiary: linear-gradient(135deg, var(--sf-color-tertiary), oklch(from var(--sf-color-tertiary) calc(l - 0.08) c h));\n --sf-gradient-brand: linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) l c calc(h + 30)));\n --sf-gradient-surface: linear-gradient(180deg, var(--sf-color-surface), var(--sf-color-bg));\n\n --sf-gradient-fade--r: linear-gradient(to right, transparent, var(--sf-color-bg));\n --sf-gradient-fade--l: linear-gradient(to left, transparent, var(--sf-color-bg));\n --sf-gradient-fade--t: linear-gradient(to top, transparent, var(--sf-color-bg));\n --sf-gradient-fade--b: linear-gradient(to bottom, transparent, var(--sf-color-bg));\n\n\n /* ----------------------------------------------------------\n Shadow — strength scales up in dark mode via --sf-is-dark\n ---------------------------------------------------------- */\n --sf-shadow-strength: calc(0.08 + var(--sf-is-dark) * 0.17);\n --sf-shadow-glow-color: var(--sf-color-primary);\n --sf-shadow-glow: 0 0 15px 2px oklch(from var(--sf-shadow-glow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-opacity-disabled: 0.45;\n --sf-state-pending-opacity: 0.7;\n\n\n /* ----------------------------------------------------------\n Focus / form colors\n ---------------------------------------------------------- */\n --sf-focus-ring-color: var(--sf-color-action);\n --sf-focus-ring-shadow: 0 0 0 var(--sf-focus-ring-offset) var(--sf-color-bg),\n 0 0 0 calc(var(--sf-focus-ring-offset) + var(--sf-focus-ring-width)) var(--sf-focus-ring-color);\n --sf-caret-color: var(--sf-color-action);\n\n}\n\n\n\n\n/* ============================================================\n NON-COLOR DESIGN TOKENS\n ============================================================ */\n\n:root {\n\n /* ----------------------------------------------------------\n Scale multipliers\n ---------------------------------------------------------- */\n --sf-space-scale: 1;\n --sf-text-scale: 1;\n --sf-text-display-scale: 1;\n --sf-radius-scale: 1;\n --sf-motion-scale: 1;\n\n\n /* ----------------------------------------------------------\n Font families\n ---------------------------------------------------------- */\n --sf-font-body: system-ui, -apple-system, sans-serif;\n --sf-font-heading: var(--sf-font-body);\n --sf-font-display: var(--sf-font-heading);\n --sf-font-mono: ui-monospace, monospace;\n\n /* Optional system-font stacks (zero-cost, no @import) */\n --sf-font-humanist: \"Seravek\", \"Gill Sans Nova\", \"Ubuntu\", \"Calibri\", \"DejaVu Sans\", source-sans-pro, sans-serif;\n --sf-font-geometric: \"Avenir\", \"Montserrat\", \"Corbel\", \"URW Gothic\", source-sans-pro, sans-serif;\n --sf-font-slab: \"Rockwell\", \"Rockwell Nova\", \"Roboto Slab\", \"DejaVu Serif\", \"Sitka Small\", serif;\n\n /* Opt-in OpenType / variable-font controls (token-only, not applied by the framework) */\n --sf-font-features: normal; /* e.g. \"cv11\", \"ss01\" */\n --sf-font-variation: normal; /* e.g. \"wght\" 450, \"opsz\" 32 */\n --sf-optical-sizing: auto;\n\n\n /* ----------------------------------------------------------\n Font weights\n ---------------------------------------------------------- */\n /* Numeric scale */\n --sf-font-weight-thin: 100;\n --sf-font-weight-extralight: 200;\n --sf-font-weight-light: 300;\n --sf-font-weight-normal: 400;\n --sf-font-weight-medium: 500;\n --sf-font-weight-semibold: 600;\n --sf-font-weight-bold: 700;\n --sf-font-weight-extrabold: 800;\n --sf-font-weight-black: 900;\n\n /* Semantic aliases — reference the numeric scale */\n --sf-font-weight-body: var(--sf-font-weight-normal);\n --sf-font-weight-heading: var(--sf-font-weight-semibold);\n --sf-font-weight-display: var(--sf-font-weight-bold);\n\n /* State token — consumed by .is-current in states.css.\n Override to match your nav design (e.g. semibold for thin fonts). */\n --sf-current-font-weight: var(--sf-font-weight-bold);\n\n\n /* ----------------------------------------------------------\n Font sizes — fluid\n ---------------------------------------------------------- */\n --sf-text-2xs: clamp(0.51rem, calc(0.00029629629629629656 * (100vw - 22.5rem) + 0.51rem), 0.53rem);\n --sf-text-xs: clamp(0.64rem, calc(0.0008888888888888881 * (100vw - 22.5rem) + 0.64rem), 0.7rem);\n --sf-text-s: clamp(0.8rem, calc(0.002074074074074073 * (100vw - 22.5rem) + 0.8rem), 0.94rem);\n --sf-text-m: clamp(1rem, calc(0.003703703703703704 * (100vw - 22.5rem) + 1rem), 1.25rem);\n --sf-text-l: clamp(1.25rem, calc(0.006222222222222221 * (100vw - 22.5rem) + 1.25rem), 1.67rem);\n --sf-text-xl: clamp(1.56rem, calc(0.00977777777777778 * (100vw - 22.5rem) + 1.56rem), 2.22rem);\n --sf-text-2xl: clamp(1.95rem, calc(0.014962962962962963 * (100vw - 22.5rem) + 1.95rem), 2.96rem);\n --sf-text-3xl: clamp(2.44rem, calc(0.022370370370370374 * (100vw - 22.5rem) + 2.44rem), 3.95rem);\n --sf-text-4xl: clamp(3.05rem, calc(0.03274074074074074 * (100vw - 22.5rem) + 3.05rem), 5.26rem);\n\n --sf-text-display-s: clamp(2.4rem, calc(0.00888888888888889 * (100vw - 22.5rem) + 2.4rem), 3rem);\n --sf-text-display-m: clamp(3rem, calc(0.014814814814814815 * (100vw - 22.5rem) + 3rem), 4rem);\n --sf-text-display-l: clamp(3.75rem, calc(0.023407407407407408 * (100vw - 22.5rem) + 3.75rem), 5.33rem);\n\n\n /* ----------------------------------------------------------\n Line heights & letter spacing\n ---------------------------------------------------------- */\n --sf-leading-tight: 1.1;\n --sf-leading-snug: 1.3;\n --sf-leading-normal: 1.5;\n --sf-leading-relaxed: 1.625;\n\n --sf-tracking-tight: -0.025em;\n --sf-tracking-normal: 0;\n --sf-tracking-wide: 0.025em;\n --sf-tracking-wider: 0.05em;\n --sf-tracking-widest: 0.1em;\n\n\n /* ----------------------------------------------------------\n Icon sizes — em-based by design.\n Icons are sized in em so they scale with the surrounding\n text: an icon in a button at --sf-text-m renders at 1rem;\n moved into a heading at --sf-text-2xl, the same icon scales\n up automatically.\n Global scaling flows transitively through --sf-text-scale\n (parent text grows → em-icons grow) — there is no separate\n --sf-icon-scale knob by design. clamp() is intentionally\n absent here: making icons fluid INDEPENDENTLY of context\n would break the relationship to surrounding text.\n Capped at --xl (3em ≈ 48px on default text); above that the\n element ceases to be an icon and becomes an illustration.\n ---------------------------------------------------------- */\n --sf-icon-xs: 0.875em;\n --sf-icon-s: 1em;\n --sf-icon-m: 1.5em;\n --sf-icon-l: 2em;\n --sf-icon-xl: 3em;\n\n\n /* ----------------------------------------------------------\n Spacing — fixed\n ---------------------------------------------------------- */\n --sf-space-none: 0;\n --sf-space-px: 1px;\n --sf-space-gutter: var(--sf-space-l);\n\n /* Spacing — fluid */\n --sf-space-2xs: calc(clamp(0.51rem, calc(0.004888888888888888 * (100vw - 22.5rem) + 0.51rem), 0.84rem) * var(--sf-space-scale));\n --sf-space-xs: calc(clamp(0.64rem, calc(0.007259259259259258 * (100vw - 22.5rem) + 0.64rem), 1.13rem) * var(--sf-space-scale));\n --sf-space-s: calc(clamp(0.8rem, calc(0.01037037037037037 * (100vw - 22.5rem) + 0.8rem), 1.5rem) * var(--sf-space-scale));\n --sf-space-m: calc(clamp(1rem, calc(0.014814814814814815 * (100vw - 22.5rem) + 1rem), 2rem) * var(--sf-space-scale));\n --sf-space-l: calc(clamp(1.25rem, calc(0.021037037037037035 * (100vw - 22.5rem) + 1.25rem), 2.67rem) * var(--sf-space-scale));\n --sf-space-xl: calc(clamp(1.56rem, calc(0.029481481481481477 * (100vw - 22.5rem) + 1.56rem), 3.55rem) * var(--sf-space-scale));\n --sf-space-2xl: calc(clamp(1.95rem, calc(0.04133333333333333 * (100vw - 22.5rem) + 1.95rem), 4.74rem) * var(--sf-space-scale));\n --sf-space-3xl: calc(clamp(2.44rem, calc(0.057333333333333326 * (100vw - 22.5rem) + 2.44rem), 6.31rem) * var(--sf-space-scale));\n --sf-space-4xl: calc(clamp(3.05rem, calc(0.07955555555555556 * (100vw - 22.5rem) + 3.05rem), 8.42rem) * var(--sf-space-scale));\n\n\n /* ----------------------------------------------------------\n UI sizes\n ---------------------------------------------------------- */\n --sf-size-xs: 1.5rem;\n --sf-size-s: 2rem;\n --sf-size-m: 2.5rem;\n --sf-size-l: 2.75rem;\n --sf-size-xl: 3.5rem;\n\n\n /* ----------------------------------------------------------\n Containers\n ---------------------------------------------------------- */\n --sf-container-narrow: 38rem;\n --sf-container-prose: 65ch;\n --sf-container-default: 75rem;\n --sf-container-wide: 90rem;\n --sf-container-full: 100%;\n\n\n /* ----------------------------------------------------------\n Aspect ratios\n ---------------------------------------------------------- */\n --sf-ratio-square: 1;\n --sf-ratio-video: 16 / 9;\n --sf-ratio-cinema: 21 / 9;\n --sf-ratio-4-3: 4 / 3;\n --sf-ratio-3-2: 3 / 2;\n --sf-ratio-portrait: 3 / 4;\n --sf-ratio-golden: 1.618 / 1;\n\n\n /* ----------------------------------------------------------\n Border widths\n --sf-border-width-hairline: device-pixel-dependent. On 1x DPR\n displays (Windows non-Retina) the browser will round 0.5px up\n to 1px, so it visually matches --sf-border-width-1. On 2x+\n DPR (macOS/iOS Retina, most modern phones) it renders as a\n true half-pixel hairline. Use it for subtle separators where\n 1px on low-DPR is acceptable.\n ---------------------------------------------------------- */\n --sf-border-width-hairline: 0.5px;\n --sf-border-width-1: 1px;\n --sf-border-width-2: 2px;\n --sf-border-width-3: 3px;\n --sf-border-width-4: 4px;\n\n /* ----------------------------------------------------------\n Border styles — paired with --sf-border-width-* and\n --sf-color-border*. Consumer BEM:\n .my-card { border: var(--sf-border-width-1) var(--sf-border-style) var(--sf-color-border); }\n Override per element to switch decorative styles without\n rewriting the rule.\n ---------------------------------------------------------- */\n --sf-border-style: solid; /* default */\n --sf-border-style-strong: solid; /* alias for emphasis */\n --sf-border-style-soft: dashed;\n --sf-border-style-dotted: dotted;\n\n /* ----------------------------------------------------------\n Divider — global separator system. Drives `hr` (base) and\n the `.sf-divider` layout primitive.\n ---------------------------------------------------------- */\n --sf-divider-width: var(--sf-border-width-1);\n --sf-divider-style: solid;\n --sf-divider-color: var(--sf-color-border);\n\n\n /* ----------------------------------------------------------\n Border radius\n --sf-radius-full: intentionally not scaled by --sf-radius-scale.\n A pill/circle is a topological constant, not a relative size:\n any --sf-radius-scale: 0 (\"sharp\" design system) should still\n render pill buttons as pills. 9999px is a magic-number convention\n large enough to clip to half the shorter edge for any realistic UI.\n ---------------------------------------------------------- */\n --sf-radius-none: 0;\n --sf-radius-xs: calc(2px * var(--sf-radius-scale));\n --sf-radius-s: calc(4px * var(--sf-radius-scale));\n --sf-radius-m: calc(8px * var(--sf-radius-scale));\n --sf-radius-l: calc(12px * var(--sf-radius-scale));\n --sf-radius-xl: calc(16px * var(--sf-radius-scale));\n --sf-radius-2xl: calc(24px * var(--sf-radius-scale));\n --sf-radius-3xl: calc(32px * var(--sf-radius-scale));\n --sf-radius-4xl: calc(48px * var(--sf-radius-scale));\n --sf-radius-full: 9999px;\n /* Pill — semantic alias of --sf-radius-full. Reach for this name\n on rectangles you want capsule-shaped (badges, tags, chips); use\n --sf-radius-full when you mean \"as round as topologically possible\". */\n --sf-radius-pill: var(--sf-radius-full);\n /* Concentric helper — outer radius for a container that wraps\n content padded by --sf-component-pad with inner radius m.\n Keeps inner elements visually proportional inside a padded box.\n Override per use:\n .my-card { border-radius: var(--sf-radius-outer); }\n Maths: outer = inner + padding ⇒ aligned curves at corners. */\n --sf-radius-outer: calc(var(--sf-radius-m) + var(--sf-component-pad));\n\n\n /* ----------------------------------------------------------\n Shadows — BEM consumer API\n Use in your own classes: .card { box-shadow: var(--sf-shadow-m); }\n ---------------------------------------------------------- */\n /* Shadow tint — near-black derived from the neutral; the tint simply\n inherits the neutral's own chroma/hue, so a colourless neutral yields\n colourless shadows. Forced dark in both modes; --sf-shadow-strength\n handles dark-mode intensity. Override to a brand colour for explicitly\n tinted shadows — keep it dark or you get bright slabs:\n oklch(from var(--sf-color-primary) 0.15 c h). */\n --sf-shadow-color: oklch(from var(--sf-color-neutral) 0.15 c h);\n\n --sf-shadow-none: none;\n --sf-shadow-xs: 0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7));\n --sf-shadow-s: 0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 2px 6px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, var(--sf-shadow-strength), 0.7));\n --sf-shadow-m: 0 1px 3px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-shadow-l: 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 8px 24px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), 0.7)),\n 0 16px 48px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-shadow-xl: 0 2px 8px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 12px 36px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3.5), 0.7)),\n 0 24px 72px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7));\n --sf-shadow-2xl: 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.6), 0.7)),\n 0 20px 60px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 4), 0.7)),\n 0 40px 100px -8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 5), 0.7));\n --sf-shadow-inner: inset 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n\n\n /* ----------------------------------------------------------\n Text shadows — share --sf-shadow-color / --sf-shadow-strength\n ---------------------------------------------------------- */\n --sf-text-shadow-none: none;\n --sf-text-shadow-s: 0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7));\n --sf-text-shadow-m: 0 2px 4px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-text-shadow-l: 0 4px 8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7));\n\n /* Drop shadows — for filter: drop-shadow() (respects alpha edges) */\n --sf-drop-shadow-s: drop-shadow(0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7)));\n --sf-drop-shadow-m: drop-shadow(0 4px 6px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7)));\n --sf-drop-shadow-l: drop-shadow(0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7)));\n\n\n /* ----------------------------------------------------------\n Blur & opacity\n ---------------------------------------------------------- */\n --sf-blur-xs: 4px;\n --sf-blur-s: 8px;\n --sf-blur-m: 16px;\n --sf-blur-l: 32px;\n --sf-blur-xl: 48px;\n\n /* 3D perspective */\n --sf-perspective-near: 500px;\n --sf-perspective-normal: 1000px;\n --sf-perspective-far: 2000px;\n\n --sf-opacity-0: 0;\n --sf-opacity-10: 0.1;\n --sf-opacity-25: 0.25;\n --sf-opacity-50: 0.5;\n --sf-opacity-75: 0.75;\n --sf-opacity-100: 1;\n\n\n /* ----------------------------------------------------------\n Motion & easing\n ---------------------------------------------------------- */\n --sf-duration-none: 0ms;\n --sf-duration-instant: calc(100ms * var(--sf-motion-scale));\n --sf-duration-fast: calc(150ms * var(--sf-motion-scale));\n --sf-duration-normal: calc(250ms * var(--sf-motion-scale));\n --sf-duration-slow: calc(400ms * var(--sf-motion-scale));\n --sf-duration-slower: calc(600ms * var(--sf-motion-scale));\n\n --sf-ease-linear: linear;\n --sf-ease-out: cubic-bezier(0.25, 0, 0.15, 1);\n --sf-ease-in: cubic-bezier(0.5, 0, 0.75, 0.25);\n --sf-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);\n --sf-ease-spring: linear(0, 0.5, 1.1, 0.95, 1.02, 1);\n --sf-ease-elastic: linear(0, 0.3, 1.2, 0.9, 1.05, 1);\n --sf-ease-bounce: linear(0, 0.35 18%, 1 32%, 0.86 42%, 1.02 56%, 0.98 72%, 1);\n --sf-ease-overshoot: linear(0, 0.6 30%, 1.08 55%, 0.98 75%, 1);\n\n\n /* ----------------------------------------------------------\n Animation presets — pair keyframes (core/motion.css) with\n duration + easing. Use directly: animation: var(--sf-animation-fade-in);\n ---------------------------------------------------------- */\n --sf-animation-fade-in: sf-fade-in var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-fade-out: sf-fade-out var(--sf-duration-normal) var(--sf-ease-in) both;\n --sf-animation-slide-in-up: sf-slide-in-up var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-slide-in-down: sf-slide-in-down var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-slide-in-left: sf-slide-in-left var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-slide-in-right: sf-slide-in-right var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-scale-up: sf-scale-up var(--sf-duration-normal) var(--sf-ease-overshoot) both;\n --sf-animation-scale-down: sf-scale-down var(--sf-duration-normal) var(--sf-ease-in) both;\n --sf-animation-color-pulse: sf-color-pulse var(--sf-duration-slow) var(--sf-ease-in-out) infinite;\n --sf-animation-ping: sf-ping var(--sf-duration-slow) var(--sf-ease-out) infinite;\n --sf-animation-blink: sf-blink calc(1s * var(--sf-motion-scale)) steps(1, end) infinite;\n --sf-animation-float: sf-float calc(3s * var(--sf-motion-scale)) var(--sf-ease-in-out) infinite;\n --sf-animation-spin: sf-spin var(--sf-duration-slower) linear infinite;\n --sf-animation-shimmer: sf-shimmer calc(1.5s * var(--sf-motion-scale)) ease-in-out infinite;\n\n /* Stagger delays — apply to successive items for entrance choreography\n (e.g. style=\"animation-delay: var(--sf-animation-delay-2)\"). Tokens\n only; SLASHED ships no .sf-stagger utility class (by design — apply via token presets or your own BEM rules). */\n --sf-animation-delay-1: calc(75ms * var(--sf-motion-scale));\n --sf-animation-delay-2: calc(150ms * var(--sf-motion-scale));\n --sf-animation-delay-3: calc(225ms * var(--sf-motion-scale));\n --sf-animation-delay-4: calc(300ms * var(--sf-motion-scale));\n --sf-animation-delay-5: calc(375ms * var(--sf-motion-scale));\n\n\n /* ----------------------------------------------------------\n Scroll-driven animation range — consumer API. Attach these\n to your own scroll-timeline via animation-range. Not consumed\n by the framework itself; intended for BEM component authors.\n ---------------------------------------------------------- */\n --sf-scroll-timeline-range-start: 0%;\n --sf-scroll-timeline-range-end: 100%;\n\n\n /* ----------------------------------------------------------\n Mask scrim — edge-fade stops for scroll reels / overflow.\n Use in a mask-image gradient to fade content near an edge:\n mask-image: linear-gradient(to right,\n transparent 0, #000 var(--sf-mask-scrim-start),\n #000 calc(100% - var(--sf-mask-scrim-end)), transparent 100%);\n ---------------------------------------------------------- */\n --sf-mask-scrim-start: var(--sf-space-l);\n --sf-mask-scrim-end: var(--sf-space-l);\n\n\n /* ----------------------------------------------------------\n Z-index\n ---------------------------------------------------------- */\n --sf-z-below: -1;\n --sf-z-base: 0;\n --sf-z-raised: 1;\n --sf-z-low: 10;\n --sf-z-mid: 100;\n --sf-z-high: 500;\n --sf-z-top: 900;\n --sf-z-max: 9999;\n\n\n /* ----------------------------------------------------------\n Layout & a11y\n ---------------------------------------------------------- */\n --sf-header-height: 8rem;\n --sf-sticky-offset: var(--sf-header-height);\n --sf-focus-ring-width: 2px;\n --sf-focus-ring-offset: 2px;\n --sf-focus-ring-style: solid;\n --sf-touch-target: var(--sf-size-l);\n /* Global text-contrast knob. Nudges derived reading-text colours\n toward the extremes (darker in light mode, lighter in dark mode).\n Positive = more contrast; 0 = no change. Consumed by\n --sf-color-text, --sf-color-text--secondary, --sf-color-heading. */\n --sf-contrast-bias: 0;\n /* Lightness crossover for text-on-color auto-contrast. Colours with\n L above this value get dark text; below get light text. Default 0.6\n guarantees ≥ 3:1 for most colours. Shift down (e.g. 0.55) if your\n brand sits in the 0.52-0.60 range and you prefer white text on it. */\n --sf-contrast-threshold: 0.6;\n\n --sf-safe-top: env(safe-area-inset-top, 0px);\n --sf-safe-bottom: env(safe-area-inset-bottom, 0px);\n --sf-safe-left: env(safe-area-inset-left, 0px);\n --sf-safe-right: env(safe-area-inset-right, 0px);\n\n\n /* ----------------------------------------------------------\n Print\n ---------------------------------------------------------- */\n --sf-print-page-margin: 2cm;\n --sf-print-page-size: a4;\n --sf-print-base-size: 11pt;\n\n\n /* ----------------------------------------------------------\n Stroke widths\n ---------------------------------------------------------- */\n --sf-stroke-thin: 1px;\n --sf-stroke-regular: 1.5px;\n --sf-stroke-bold: 2px;\n --sf-stroke-heavy: 3px;\n\n}\n\n\n/* ============================================================\n TYPOGRAPHY ALIASES\n ============================================================ */\n\n:root {\n --sf-body-font-size: var(--sf-text-m);\n --sf-body-line-height: var(--sf-leading-normal);\n --sf-body-font-weight: var(--sf-font-weight-body);\n --sf-body-font-family: var(--sf-font-body);\n --sf-body-color: var(--sf-color-text);\n --sf-body-text-wrap: pretty;\n --sf-body-strong-weight: var(--sf-font-weight-bold);\n --sf-body-em-style: italic;\n\n --sf-code-font-size: 0.875em;\n\n --sf-heading-font-family: var(--sf-font-heading);\n --sf-heading-color: var(--sf-color-heading);\n --sf-heading-text-wrap: balance;\n\n --sf-h1-size: var(--sf-text-4xl);\n --sf-h1-line-height: var(--sf-leading-tight);\n --sf-h1-font-weight: var(--sf-font-weight-heading);\n --sf-h1-letter-spacing: var(--sf-tracking-tight);\n\n --sf-h2-size: var(--sf-text-3xl);\n --sf-h2-line-height: var(--sf-leading-tight);\n --sf-h2-font-weight: var(--sf-font-weight-heading);\n --sf-h2-letter-spacing: var(--sf-tracking-tight);\n\n --sf-h3-size: var(--sf-text-2xl);\n --sf-h3-line-height: var(--sf-leading-snug);\n --sf-h3-font-weight: var(--sf-font-weight-heading);\n --sf-h3-letter-spacing: var(--sf-tracking-normal);\n\n --sf-h4-size: var(--sf-text-xl);\n --sf-h4-line-height: var(--sf-leading-snug);\n --sf-h4-font-weight: var(--sf-font-weight-heading);\n --sf-h4-letter-spacing: var(--sf-tracking-normal);\n\n --sf-h5-size: var(--sf-text-l);\n --sf-h5-line-height: var(--sf-leading-normal);\n --sf-h5-font-weight: var(--sf-font-weight-heading);\n --sf-h5-letter-spacing: var(--sf-tracking-normal);\n\n --sf-h6-size: var(--sf-text-m);\n --sf-h6-line-height: var(--sf-leading-normal);\n --sf-h6-font-weight: var(--sf-font-weight-heading);\n --sf-h6-letter-spacing: var(--sf-tracking-wide);\n\n\n /* ----------------------------------------------------------\n Spacing aliases — BEM consumer API\n Use in your own BEM classes: .nav { gap: var(--sf-gap); }\n Source of truth for tokens.layout.css:\n --sf-space-gap → var(--sf-gap) (all layout primitives)\n --sf-space-content → var(--sf-content-gap) (stack, prose)\n ---------------------------------------------------------- */\n --sf-gap: var(--sf-space-m); /* gap between BEM elements */\n --sf-content-gap: var(--sf-space-s); /* rhythm within a component */\n --sf-component-pad: var(--sf-space-m); /* standard component padding */\n --sf-field-block: var(--sf-space-l); /* form-field block spacing */\n --sf-field-required-marker: \" *\";\n /* Leading space is baked into the default so consumers can fully\n disable the indicator with `--sf-link-external-marker: \"\"` —\n without the space a bare \"\" still left a stray space after the\n link text. Override with your own glyph (include leading space\n if you want one): `:root { --sf-link-external-marker: \" →\" }`. */\n --sf-link-external-marker: \" \\2197\"; /* \" ↗\" — consumed by .sf-link-external */\n\n\n /* ----------------------------------------------------------\n Section padding\n ---------------------------------------------------------- */\n --sf-section-pad: var(--sf-section-pad--m);\n --sf-section-pad--xs: var(--sf-space-xl);\n --sf-section-pad--s: var(--sf-space-2xl);\n --sf-section-pad--m: var(--sf-space-3xl);\n --sf-section-pad--l: var(--sf-space-4xl);\n --sf-section-pad--xl: calc(var(--sf-space-4xl) * 1.5);\n --sf-section-pad--2xl: calc(var(--sf-space-4xl) * 2);\n\n\n /* ----------------------------------------------------------\n Transition shorthands\n\n --sf-transition-all transitions ALL properties — convenient but\n a performance footgun (forces the browser to watch every computed\n value). Prefer the granular tokens below for production UI.\n ---------------------------------------------------------- */\n --sf-transition-all: all var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-colors:\n color var(--sf-duration-normal) var(--sf-ease-out),\n background-color var(--sf-duration-normal) var(--sf-ease-out),\n border-color var(--sf-duration-normal) var(--sf-ease-out),\n text-decoration-color var(--sf-duration-normal) var(--sf-ease-out),\n fill var(--sf-duration-normal) var(--sf-ease-out),\n stroke var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-transform: transform var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-opacity: opacity var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-shadow: box-shadow var(--sf-duration-normal) var(--sf-ease-out);\n\n --sf-transition-fast: all var(--sf-duration-fast) var(--sf-ease-out);\n --sf-transition-slow: all var(--sf-duration-slow) var(--sf-ease-in-out);\n --sf-transition-enter: all var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-exit: all var(--sf-duration-fast) var(--sf-ease-in);\n}\n\n/* ─── core/tokens.layout.css ─── */\n/* ============================================================\n SLASHED — core/tokens.layout.css\n Semantic tokens for layout primitives.\n Load before: core/layout.css\n Layer: slashed.tokens\n Prefix: --sf-\n\n DEPENDENCIES — requires tokens.css (loaded earlier).\n Spacing aliases inherit from the BEM consumer API in tokens.css:\n --sf-space-gap → var(--sf-gap) from tokens.css\n --sf-space-content → var(--sf-content-gap) from tokens.css\n Override --sf-gap or --sf-content-gap (the canonical source) to change\n the gaps across all layout primitives at once. --sf-space-gap and\n --sf-space-content are CANONICAL-SOURCE ALIASES — a second public name\n kept for layout-primitive readability, not a redundant indirection.\n\n PUBLIC API — every --sf-*-gap / --sf-*-padding / --sf-*-min / etc. here\n is a per-primitive override knob (e.g. style=\"--sf-cluster-gap: …\").\n No INTERNAL tokens live in this file.\n\n Each layout primitive (cluster, grid, sidebar…) has its own\n tokens (--sf-cluster-gap etc.) for local overrides.\n ============================================================ */\n\n:root {\n\n /* ----------------------------------------------------------\n Shared spacing aliases\n ---------------------------------------------------------- */\n --sf-space-gap: var(--sf-gap); /* component-level gap */\n --sf-space-content: var(--sf-content-gap); /* within-component rhythm */\n\n /* ----------------------------------------------------------\n Stack\n ---------------------------------------------------------- */\n --sf-stack-gap: var(--sf-space-content);\n\n /* ----------------------------------------------------------\n Gap\n ---------------------------------------------------------- */\n --sf-gap-size: var(--sf-space-gap);\n\n /* ----------------------------------------------------------\n Box\n ---------------------------------------------------------- */\n --sf-box-padding: var(--sf-space-m);\n --sf-box-border-width: 0;\n --sf-box-border-color: var(--sf-color-border);\n\n /* ----------------------------------------------------------\n Center\n ---------------------------------------------------------- */\n --sf-center-max: var(--sf-container-default);\n --sf-center-gutter: var(--sf-space-gutter);\n\n /* ----------------------------------------------------------\n Cluster\n ---------------------------------------------------------- */\n --sf-cluster-gap: var(--sf-space-gap);\n --sf-cluster-align: center;\n --sf-cluster-justify: flex-start;\n\n /* ----------------------------------------------------------\n Sidebar\n ---------------------------------------------------------- */\n --sf-sidebar-gap: var(--sf-space-gap);\n --sf-sidebar-min-width: 50%; /* content column minimum */\n --sf-sidebar-width: 18rem; /* sidebar panel default size */\n\n /* ----------------------------------------------------------\n Switcher\n ---------------------------------------------------------- */\n --sf-switcher-threshold: 30rem;\n --sf-switcher-gap: var(--sf-space-gap);\n\n /* ----------------------------------------------------------\n Grid (auto-fill, breakpoint-free)\n ---------------------------------------------------------- */\n --sf-grid-min: 16rem;\n --sf-grid-gap: var(--sf-space-gap);\n\n --sf-grid-min-xs: 10rem;\n --sf-grid-min-s: 13rem;\n --sf-grid-min-m: 16rem;\n --sf-grid-min-l: 20rem;\n --sf-grid-min-xl: 24rem;\n --sf-grid-min-2xl: 28rem;\n\n /* ----------------------------------------------------------\n Equal columns (fixed, non-responsive)\n ---------------------------------------------------------- */\n --sf-equal-cols: 2;\n --sf-equal-gap: var(--sf-space-gap);\n\n /* ----------------------------------------------------------\n Cover\n ---------------------------------------------------------- */\n --sf-cover-min-height: 100dvh;\n --sf-cover-padding: var(--sf-section-pad);\n\n /* ----------------------------------------------------------\n Frame\n ---------------------------------------------------------- */\n --sf-frame-ratio: 16 / 9; /* literal , valid in aspect-ratio */\n\n /* ----------------------------------------------------------\n Icon (boxed variant) — extras for .sf-icon--boxed.\n The naked .sf-icon sizing tokens (--sf-icon-xs..xl) live in\n core/tokens.css alongside the typography scale.\n ---------------------------------------------------------- */\n --sf-icon-box-pad: 0.5em;\n --sf-icon-box-radius: var(--sf-radius-s);\n --sf-icon-box-bg: var(--sf-color-well);\n --sf-icon-box-border: var(--sf-border-width-1) solid var(--sf-color-border);\n\n /* ----------------------------------------------------------\n Reel\n ---------------------------------------------------------- */\n --sf-reel-item-width: max-content;\n --sf-reel-gap: var(--sf-space-gap);\n --sf-reel-height: auto;\n\n /* ----------------------------------------------------------\n Imposter (centered absolute/fixed overlay)\n ---------------------------------------------------------- */\n --sf-imposter-margin: var(--sf-space-m);\n\n /* ----------------------------------------------------------\n Bento grid\n ---------------------------------------------------------- */\n --sf-bento-cols-default: 3;\n --sf-bento-row-default: 10rem;\n --sf-bento-row-compact: 6rem;\n --sf-bento-row-tall: 16rem;\n --sf-bento-gap: var(--sf-space-gap);\n\n /* ----------------------------------------------------------\n Content grid (breakout pattern)\n ---------------------------------------------------------- */\n --sf-breakout-width: var(--sf-container-wide);\n --sf-content-width: var(--sf-container-default);\n\n /* ----------------------------------------------------------\n Prose\n ---------------------------------------------------------- */\n --sf-prose-paragraph: var(--sf-space-content);\n\n /* ----------------------------------------------------------\n Alternate (zigzag)\n ---------------------------------------------------------- */\n --sf-alternate-gap: var(--sf-space-content);\n --sf-alternate-inner-gap: var(--sf-space-gap);\n\n}\n\n/* ─── core/tokens.macros.css ─── */\n/* ============================================================\n SLASHED — core/tokens.macros.css\n Semantic tokens for macros (recipes / patterns).\n Load before: core/macros.css\n Layer: slashed.tokens\n Prefix: --sf-\n\n DEPENDENCIES — requires tokens.css (loaded earlier).\n Macro tokens reference --sf-space-* / --sf-radius-* / etc.\n\n PUBLIC API — every token here is a per-recipe override knob\n (e.g. style=\"--sf-line-clamp: 5\"). No INTERNAL tokens live in\n this file.\n\n Architectural parallel — these tokens stand alongside\n tokens.layout.css: same prefix, same shape, same status\n (essential, public, override-friendly).\n ============================================================ */\n\n:root {\n\n /* ----------------------------------------------------------\n Flow — distance between flow children (lobotomized owl).\n Consumed by .sf-flow > * + *.\n ---------------------------------------------------------- */\n --sf-flow-space: var(--sf-space-content);\n\n /* ----------------------------------------------------------\n Line clamp — default line count for .sf-line-clamp-N.\n Override per element: style=\"--sf-line-clamp: 5\".\n The fixed-N variants .sf-line-clamp-2 / -3 don't read this\n token (they hardcode the value), so the default only\n applies to .sf-line-clamp-N callers.\n ---------------------------------------------------------- */\n --sf-line-clamp: 3;\n\n /* ----------------------------------------------------------\n Truncate — ellipsis character for .sf-truncate.\n Stays as a token so consumers can swap to ASCII \"...\" or a\n localised character without rewriting the rule. The CSS\n text-overflow: ellipsis property doesn't read it directly;\n it's exposed for consumer-built variants.\n ---------------------------------------------------------- */\n --sf-truncate-suffix: \"\\2026\";\n\n /* ----------------------------------------------------------\n Aspect ratio — default for .sf-aspect (generic ratio container,\n distinct from layout's .sf-frame which has its own --sf-frame-ratio).\n ---------------------------------------------------------- */\n --sf-aspect: 16 / 9;\n\n /* ----------------------------------------------------------\n Scroll shadow / overflow fade — mask gradient size.\n Used by .sf-scroll-shadow (top+bottom) and .sf-overflow-fade\n (end-edge horizontal fade).\n ---------------------------------------------------------- */\n --sf-scroll-shadow-size: 2rem;\n\n}\n\n/* ─── core/reset.css ─── */\n/* SLASHED — core/reset.css\n @layer slashed.reset\n ─────────────────────────────────────────────────── */\n\n\n*, *::before, *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\nhtml {\n text-size-adjust: 100%;\n scroll-padding-top: var(--sf-header-height, 3.5rem);\n scrollbar-gutter: stable;\n color-scheme: var(--sf-color-scheme, light dark);\n hanging-punctuation: first last;\n}\n\n/* interpolate-size enables animations to/from intrinsic sizing\n keywords (auto, min-content, max-content, fit-content). Shipped:\n Chrome 129 (Aug 2024), Safari 18 (Sep 2024). Firefox: behind a\n flag as of 2026-Q2. Wrapped in @supports so unsupporting engines\n get clean progressive enhancement, not an unknown-declaration. */\n@supports (interpolate-size: allow-keywords) {\n html { interpolate-size: allow-keywords; }\n}\n\nbody {\n min-height: 100dvh;\n min-inline-size: 320px;\n}\n\nmenu, ul, ol {\n list-style: none;\n}\n\nimg, picture, video, canvas, svg {\n display: block;\n max-width: 100%;\n height: auto;\n}\n\niframe, embed, object {\n display: block;\n max-width: 100%;\n height: unset;\n border: none;\n}\n\nbutton, input, select, textarea {\n font: inherit;\n color: inherit;\n letter-spacing: inherit;\n color-scheme: inherit;\n line-height: inherit;\n}\n\nbutton {\n text-transform: none;\n}\n\n::file-selector-button {\n font: inherit;\n}\n\nsearch {\n display: block;\n}\n\nfieldset {\n border: none;\n padding: 0;\n min-width: 0;\n}\n\nlegend {\n padding: 0;\n}\n\na, button, input, select, textarea, summary {\n touch-action: manipulation;\n}\n\nsummary {\n list-style: none;\n cursor: pointer;\n}\n\ntextarea {\n resize: vertical;\n}\n\nbutton {\n cursor: pointer;\n}\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\n[hidden] {\n display: none !important;\n}\n\ndialog, [popover] {\n padding: 0;\n border: none;\n background: none;\n color: inherit;\n}\n\n[inert] {\n cursor: default;\n}\n\n/* ─── core/base.css ─── */\n/* SLASHED — core/base.css\n @layer slashed.base\n ─────────────────────────────────────────────────── */\n\n\n/* Dark/light mode lives in core/themes.css (slashed.themes layer).\n The baseline color-scheme: var(--sf-color-scheme, light dark) is\n set on html in core/reset.css. */\n\nbody {\n font-family: var(--sf-body-font-family, var(--sf-font-body));\n font-size: var(--sf-body-font-size, var(--sf-text-m));\n line-height: var(--sf-body-line-height, var(--sf-leading-normal));\n font-weight: var(--sf-body-font-weight, var(--sf-font-weight-body));\n color: var(--sf-body-color, var(--sf-color-text));\n background-color: var(--sf-color-bg, #fff);\n font-synthesis: none;\n}\n\nh1, h2, h3, h4, h5, h6 {\n font-family: var(--sf-heading-font-family, var(--sf-font-heading));\n line-height: var(--sf-leading-tight);\n color: var(--sf-heading-color, var(--sf-color-heading));\n text-wrap: var(--sf-heading-text-wrap);\n overflow-wrap: break-word;\n text-rendering: optimizelegibility;\n scroll-margin-top: calc(var(--sf-header-height, 3.5rem) + var(--sf-space-m));\n}\n\nh1 { font-size: var(--sf-h1-size); line-height: var(--sf-h1-line-height); font-weight: var(--sf-h1-font-weight); letter-spacing: var(--sf-h1-letter-spacing); }\nh2 { font-size: var(--sf-h2-size); line-height: var(--sf-h2-line-height); font-weight: var(--sf-h2-font-weight); letter-spacing: var(--sf-h2-letter-spacing); }\nh3 { font-size: var(--sf-h3-size); line-height: var(--sf-h3-line-height); font-weight: var(--sf-h3-font-weight); letter-spacing: var(--sf-h3-letter-spacing); }\nh4 { font-size: var(--sf-h4-size); line-height: var(--sf-h4-line-height); font-weight: var(--sf-h4-font-weight); letter-spacing: var(--sf-h4-letter-spacing); }\nh5 { font-size: var(--sf-h5-size); line-height: var(--sf-h5-line-height); font-weight: var(--sf-h5-font-weight); letter-spacing: var(--sf-h5-letter-spacing); }\nh6 { font-size: var(--sf-h6-size); line-height: var(--sf-h6-line-height); font-weight: var(--sf-h6-font-weight); letter-spacing: var(--sf-h6-letter-spacing); }\n\np {\n text-wrap: var(--sf-body-text-wrap);\n overflow-wrap: break-word;\n orphans: 2;\n widows: 2;\n}\n\nb, strong {\n font-weight: var(--sf-body-strong-weight);\n}\n\nem {\n font-style: var(--sf-body-em-style);\n}\n\nsmall {\n font-size: var(--sf-text-s, 80%);\n}\n\nmark {\n background-color: var(--sf-color-mark-bg, yellow);\n color: var(--sf-color-mark-text, black);\n border-radius: var(--sf-radius-xs);\n padding-inline: 0.2em;\n}\n\nabbr[title] {\n text-decoration: underline dotted;\n cursor: help;\n}\n\nkbd, samp {\n font-family: var(--sf-font-mono);\n font-size: inherit;\n}\n\nsub, sup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsup { top: -0.5em; }\nsub { bottom: -0.25em; }\n\n/* `a:link` (specificity 0,1,1) rather than bare `a` (0,0,1) so the\n author rule wins over WebKit's UA stylesheet rule\n `a:link { color: -webkit-link; }`. The bare-`a` form silently\n loses to the UA `:link` rule on unvisited anchors in WebKit,\n which paints `-webkit-link` (≈ #00728f in dark mode) regardless\n of whatever `--sf-color-link` resolves to — producing axe's\n 3.12 : 1 dark-mode contrast failure that survived four attempts\n to \"fix\" the link colour formula (PR #73, then three iterations\n of PR #76). The colour formula was never the problem; the\n selector specificity was. Hover / visited / active rules already\n used pseudo-classes so they had matching specificity and were\n never affected. */\na:link {\n color: var(--sf-color-link);\n text-decoration-color: var(--sf-color-link--underline);\n text-underline-offset: 0.15em;\n text-underline-position: from-font;\n transition:\n color var(--sf-duration-fast) var(--sf-ease-out),\n text-decoration-color var(--sf-duration-fast) var(--sf-ease-out);\n}\na:hover { color: var(--sf-color-link--hover); text-decoration-color: currentcolor; }\na:visited { color: var(--sf-color-link--visited); }\na:active { color: var(--sf-color-link--active); }\na:not([class]) { text-decoration-skip-ink: auto; }\n\na, code {\n overflow-wrap: break-word;\n}\n\ncode {\n font-family: var(--sf-font-mono);\n font-size: var(--sf-code-font-size);\n padding: 0.125em 0.3em;\n border-radius: var(--sf-radius-xs);\n background-color: var(--sf-color-code-bg);\n color: var(--sf-color-code-text, inherit);\n}\n\npre {\n font-family: var(--sf-font-mono);\n font-size: var(--sf-text-s);\n padding: var(--sf-space-m);\n border-radius: var(--sf-radius-m);\n background: var(--sf-color-code-block-bg, var(--sf-color-code-bg));\n color: var(--sf-color-code-block-text, inherit);\n overflow-x: auto;\n tab-size: 2;\n hyphens: none;\n}\npre code {\n background: none;\n color: inherit;\n border: 0;\n padding: 0;\n font-size: inherit;\n border-radius: 0;\n}\n\nsvg {\n fill: currentcolor;\n}\n\nhr {\n border: none;\n border-block-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);\n}\n\n:target {\n scroll-margin-top: calc(var(--sf-header-height, 3.5rem) + var(--sf-space-m));\n}\n\n::selection {\n background: var(--sf-color-selection-bg);\n color: var(--sf-color-selection-text);\n text-shadow: none;\n}\n\nth, caption {\n text-align: start;\n}\n\naddress, cite, dfn, var {\n font-style: normal;\n}\n\ninput, textarea {\n caret-color: var(--sf-caret-color);\n}\n\ninput, progress {\n accent-color: var(--sf-color-action);\n}\n\n::backdrop {\n background-color: var(--sf-color-dim);\n}\n\noptgroup { font: inherit; }\n::file-selector-button { font: inherit; }\n::placeholder { opacity: 1; color: var(--sf-color-text--placeholder, currentcolor); }\n\n/* Minimal rich-block defaults — just enough to distinguish from

.\n Full styling lives in .sf-prose (core/layout.css). */\nblockquote {\n border-inline-start: var(--sf-border-width-3) solid var(--sf-color-border);\n padding-inline-start: var(--sf-space-m);\n font-style: italic;\n}\n\ndt {\n font-weight: var(--sf-font-weight-heading);\n}\n\ndd {\n margin-inline-start: var(--sf-space-m);\n}\n\ntable {\n width: 100%;\n text-align: start;\n}\n\nth {\n font-weight: var(--sf-font-weight-heading);\n}\n\ntd, th {\n padding: var(--sf-space-2xs) var(--sf-space-xs);\n border-block-end: var(--sf-border-width-hairline) solid var(--sf-color-border--subtle);\n}\n\n/* ─── core/themes.css ─── */\n/* SLASHED — core/themes.css\n @layer slashed.themes\n\n Theme switching: token reassignments only. No new selectors,\n no new properties — strictly remaps existing tokens based on\n system preference and explicit data-theme attributes.\n\n Cascade position is significant. slashed.themes sits ABOVE\n slashed.{states, utilities, components, layout, base} (see\n core/layers.css), so a theme's token reassignment cannot be\n accidentally beaten by a component or utility rule of equal\n specificity. Only consumer code in slashed.overrides (or\n unlayered CSS) can override a theme.\n\n USAGE\n → follows OS preference\n → whole page dark\n

→ only this section dark\n
→ only this section light\n\n The base color-scheme: var(--sf-color-scheme, light dark)\n declaration lives in core/reset.css and provides the baseline.\n This layer flips it.\n\n MULTI-BRAND — scope a palette to any subtree by re-declaring the 6\n source tokens under a selector (e.g. [data-brand=\"x\"]). See\n optional/theme-example.css and docs/theming.md.\n\n THEME TRANSITION — toggling [data-theme] via document.startViewTransition()\n animates through the ::view-transition(root) rules in core/motion.css.\n For a plain cross-fade of colours, opt into a transition class on \n (example in optional/theme-example.css) — never transition every colour\n globally and permanently (perf footgun).\n ─────────────────────────────────────────────────── */\n\n\n/* OS preference — only when no data-theme is set on :root.\n A section-level [data-theme] still wins via the explicit\n selectors below (higher specificity within the same layer). */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme]) {\n color-scheme: dark;\n --sf-is-dark: 1;\n }\n}\n\n/* Explicit theme — works on :root (whole page) or any element\n (section-level theming). */\n[data-theme=\"light\"] { color-scheme: light; --sf-is-dark: 0; }\n[data-theme=\"dark\"] { color-scheme: dark; --sf-is-dark: 1; }\n\n\n/* LumLocker — opt-in via [data-lumlocker] on :root.\n Locks the 5 brand colors (primary, secondary, tertiary, action,\n neutral) to one shared OKLCH L value (--sf-lumlocker,\n default 0.65 in core/tokens.css), keeping each color's own hue\n and chroma. Useful when a brand picker produces colors with\n wildly different L values that look mismatched in weight.\n\n Base is excluded by design — base IS the page surface (L ~0.98 in\n light mode, ~0.20 in dark) and locking it would destroy the page\n background.\n\n Default state: OFF. Without the attribute, every brand keeps its\n own L. Add :root[data-lumlocker] (or set via JS) to enable.\n\n Per-color escape: override --sf-color-X under\n :root[data-lumlocker] to opt that one color out:\n :root[data-lumlocker] { --sf-color-primary: #yourColor; }\n\n The dark-side input falls back to -light when -dark isn't\n explicitly set, so this works even without per-mode overrides. */\n:root[data-lumlocker] {\n --sf-color-primary: light-dark(\n oklch(from var(--sf-color-primary-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-primary-dark, var(--sf-color-primary-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-secondary: light-dark(\n oklch(from var(--sf-color-secondary-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-secondary-dark, var(--sf-color-secondary-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-tertiary: light-dark(\n oklch(from var(--sf-color-tertiary-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-tertiary-dark, var(--sf-color-tertiary-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-action: light-dark(\n oklch(from var(--sf-color-action-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-action-dark, var(--sf-color-action-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-neutral: light-dark(\n oklch(from var(--sf-color-neutral-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-neutral-dark, var(--sf-color-neutral-light)) var(--sf-lumlocker) c h)\n );\n}\n\n/* ─── core/layout.css ─── */\n/* ============================================================\n SLASHED — core/layout.css\n ★ de facto required for most projects\n Layout primitives + structural patterns\n Requires: core/tokens.css, core/tokens.layout.css\n ============================================================ */\n\n\n/* -- Section -------------------------------------------------\n Page section with vertical padding. Size modifiers\n override the scoped token.\n ---------------------------------------------------------- */\n\n.sf-section { padding-block: var(--sf-section-pad); }\n.sf-section--xs { padding-block: var(--sf-section-pad--xs); }\n.sf-section--s { padding-block: var(--sf-section-pad--s); }\n.sf-section--m { padding-block: var(--sf-section-pad--m); }\n.sf-section--l { padding-block: var(--sf-section-pad--l); }\n.sf-section--xl { padding-block: var(--sf-section-pad--xl); }\n.sf-section--2xl { padding-block: var(--sf-section-pad--2xl); }\n\n.sf-section-group > .sf-section + .sf-section { padding-block-start: 0; }\n\n/* Collapse modifier — when two adjacent sections share this modifier,\n they redistribute padding 50/50 across the boundary so total spacing\n stays one full padding gap (instead of two). Works regardless of\n background color: each section always has half its padding on\n the boundary side, so content never touches a color edge.\n\n Use when sections are siblings outside a .sf-section-group wrapper\n and may share OR differ in background.\n\n Note: uses --sf-section-pad (the canonical alias = m by default).\n For other sizes, set --sf-section-pad locally on each section:\n
\n ---------------------------------------------------------- */\n.sf-section--collapse:has(+ .sf-section--collapse) {\n padding-block-end: calc(var(--sf-section-pad) / 2);\n}\n.sf-section--collapse + .sf-section--collapse {\n padding-block-start: calc(var(--sf-section-pad) / 2);\n}\n\n/* -- Divider -------------------------------------------------\n Standalone separator primitive. Token-driven; pairs with the\n `--sf-divider-*` tokens and mirrors the `hr` element default.\n ---------------------------------------------------------- */\n.sf-divider {\n border: none;\n border-block-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);\n margin-block: var(--sf-space-m);\n}\n.sf-divider--vertical {\n border-block-start: none;\n border-inline-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);\n margin-block: 0;\n margin-inline: var(--sf-space-m);\n align-self: stretch;\n}\n\n/* -- Container -----------------------------------------------\n Max-width container, centered with side gutters. Establishes\n a named inline-size container `sf-layout` so children can\n target it precisely with `@container sf-layout (...)`.\n The framework's own `.sf-bento` and `.sf-grid-*` rules use\n anonymous `@container` queries on purpose — they react to\n the nearest inline-size ancestor, which lets a consumer\n embed them inside any container without rewiring.\n ---------------------------------------------------------- */\n\n.sf-container {\n container: sf-layout / inline-size;\n width: 100%;\n max-width: var(--sf-container-default);\n margin-inline: auto;\n padding-inline: var(--sf-space-gutter);\n}\n.sf-container--narrow { max-width: var(--sf-container-narrow); }\n.sf-container--prose { max-width: var(--sf-container-prose); }\n.sf-container--wide { max-width: var(--sf-container-wide); }\n.sf-container--full { max-width: var(--sf-container-full); }\n\n/* -- Stack ---------------------------------------------------\n Flex column with even vertical spacing between children.\n Override: style=\"--sf-stack-gap: 2rem\"\n ---------------------------------------------------------- */\n\n.sf-stack {\n display: flex;\n flex-direction: column;\n gap: var(--sf-stack-gap);\n}\n.sf-stack--xs { gap: var(--sf-space-xs); }\n.sf-stack--s { gap: var(--sf-space-s); }\n.sf-stack--m { gap: var(--sf-space-m); }\n.sf-stack--l { gap: var(--sf-space-l); }\n.sf-stack--xl { gap: var(--sf-space-xl); }\n.sf-stack--2xl { gap: var(--sf-space-2xl); }\n\n.sf-stack--center { align-items: center; }\n.sf-stack--end { align-items: flex-end; }\n.sf-stack--stretch { align-items: stretch; }\n\n/* -- Gap ----------------------------------------------------\n Injects gap into any existing flex or grid container without\n imposing a display type or axis. Unlike .sf-stack (flex\n column) or .sf-cluster (flex-wrap row), .sf-gap is layout-\n agnostic — use it on elements that already have their own\n display context.\n Override: style=\"--sf-gap-size: 2rem\"\n ---------------------------------------------------------- */\n.sf-gap { gap: var(--sf-gap-size); }\n.sf-gap--xs { gap: var(--sf-space-xs); }\n.sf-gap--s { gap: var(--sf-space-s); }\n.sf-gap--m { gap: var(--sf-space-m); }\n.sf-gap--l { gap: var(--sf-space-l); }\n.sf-gap--xl { gap: var(--sf-space-xl); }\n.sf-gap--2xl { gap: var(--sf-space-2xl); }\n\n/* -- Box -----------------------------------------------------\n Isolated unit with padding and an optional border.\n Outline instead of border — doesn't disturb the box model when toggled.\n Override: style=\"--sf-box-padding: 1rem; --sf-box-border-width: 1px\"\n ---------------------------------------------------------- */\n\n.sf-box {\n padding: var(--sf-box-padding);\n outline: var(--sf-box-border-width) solid var(--sf-box-border-color);\n}\n\n/* -- Center --------------------------------------------------\n Centers content with a max-inline-size and side gutters.\n content-box: max-inline-size applies to content only; padding is additive.\n Override: style=\"--sf-center-max: 60rem\"\n ---------------------------------------------------------- */\n\n.sf-center {\n box-sizing: content-box;\n margin-inline: auto;\n max-inline-size: var(--sf-center-max);\n padding-inline: var(--sf-center-gutter);\n}\n\n.sf-center--intrinsic {\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n\n/* -- Cluster -------------------------------------------------\n Flex-wrap group of variable-width items.\n Override: style=\"--sf-cluster-gap: 0.5rem; --sf-cluster-justify: center\"\n ---------------------------------------------------------- */\n\n.sf-cluster {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sf-cluster-gap);\n align-items: var(--sf-cluster-align);\n justify-content: var(--sf-cluster-justify);\n}\n.sf-cluster--xs { gap: var(--sf-space-xs); }\n.sf-cluster--s { gap: var(--sf-space-s); }\n.sf-cluster--m { gap: var(--sf-space-m); }\n.sf-cluster--l { gap: var(--sf-space-l); }\n.sf-cluster--xl { gap: var(--sf-space-xl); }\n.sf-cluster--2xl { gap: var(--sf-space-2xl); }\n\n.sf-cluster--no-wrap { flex-wrap: nowrap; }\n.sf-cluster--center { justify-content: center; }\n.sf-cluster--end { justify-content: flex-end; }\n.sf-cluster--between { justify-content: space-between; }\n\n/* -- Sidebar -------------------------------------------------\n Two-column layout: first-child = sidebar, last-child = main content.\n Collapses when the viewport is too narrow (flex-basis trick).\n Override: style=\"--sf-sidebar-width: 20rem\"\n ---------------------------------------------------------- */\n\n.sf-sidebar {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sf-sidebar-gap);\n}\n\n.sf-sidebar > :first-child {\n flex-basis: var(--sf-sidebar-width);\n flex-grow: 1;\n}\n\n.sf-sidebar > :last-child {\n flex-basis: 0;\n flex-grow: 999;\n min-inline-size: var(--sf-sidebar-min-width);\n}\n\n.sf-sidebar--right > :first-child {\n flex-basis: 0;\n flex-grow: 999;\n min-inline-size: var(--sf-sidebar-min-width);\n}\n\n.sf-sidebar--right > :last-child {\n flex-basis: var(--sf-sidebar-width);\n flex-grow: 1;\n}\n\n.sf-sidebar--narrow { --sf-sidebar-width: 12rem; }\n.sf-sidebar--wide { --sf-sidebar-width: 26rem; }\n\n/* -- Switcher ------------------------------------------------\n Switches between a column (narrow viewport) and a row (wide).\n Uses calc((threshold - 100%) * 999) — zero media queries.\n Override: style=\"--sf-switcher-threshold: 40rem\"\n ---------------------------------------------------------- */\n\n.sf-switcher {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sf-switcher-gap);\n}\n\n.sf-switcher > * {\n flex-grow: 1;\n flex-basis: calc((var(--sf-switcher-threshold) - 100%) * 999);\n}\n\n.sf-switcher--no-wrap { flex-wrap: nowrap; overflow-x: auto; }\n.sf-switcher--vertical { flex-direction: column; }\n\n/* -- Grid ----------------------------------------------------\n Auto-fill CSS grid with no breakpoints. --fit switches to auto-fit.\n auto-fill: empty tracks preserved, last item doesn't stretch.\n auto-fit (--fit): empty tracks collapsed, items stretch to fill the gap.\n Override: style=\"--sf-grid-min: 12rem; --sf-grid-gap: 1rem\"\n ---------------------------------------------------------- */\n\n.sf-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(min(var(--sf-grid-min), 100%), 1fr));\n gap: var(--sf-grid-gap);\n}\n\n.sf-grid--fit {\n grid-template-columns: repeat(auto-fit, minmax(min(var(--sf-grid-min), 100%), 1fr));\n}\n\n.sf-grid--xs { --sf-grid-min: var(--sf-grid-min-xs); }\n.sf-grid--s { --sf-grid-min: var(--sf-grid-min-s); }\n.sf-grid--m { --sf-grid-min: var(--sf-grid-min-m); }\n.sf-grid--l { --sf-grid-min: var(--sf-grid-min-l); }\n.sf-grid--xl { --sf-grid-min: var(--sf-grid-min-xl); }\n.sf-grid--2xl { --sf-grid-min: var(--sf-grid-min-2xl); }\n\n/* Dense packing — let later items backfill earlier gaps (as .sf-bento\n does). Applies to any grid, including the fixed-column .sf-grid-N. */\n.sf-grid--dense { grid-auto-flow: dense; }\n\n/* -- Icon ----------------------------------------------------\n Inline icon sizing primitive. Uses em-based --sf-icon-* tokens\n so an icon scales with its surrounding text. Defaults to -m.\n ---------------------------------------------------------- */\n.sf-icon {\n display: inline-block;\n inline-size: var(--sf-icon-size, var(--sf-icon-m));\n block-size: var(--sf-icon-size, var(--sf-icon-m));\n flex-shrink: 0;\n vertical-align: text-bottom;\n fill: currentcolor;\n}\n.sf-icon--xs { --sf-icon-size: var(--sf-icon-xs); }\n.sf-icon--s { --sf-icon-size: var(--sf-icon-s); }\n.sf-icon--m { --sf-icon-size: var(--sf-icon-m); }\n.sf-icon--l { --sf-icon-size: var(--sf-icon-l); }\n.sf-icon--xl { --sf-icon-size: var(--sf-icon-xl); }\n\n/* Boxed variant — wraps the icon in a padded, bordered, optionally\n coloured frame. Adds inline-/block-size on top of the naked icon\n size so the visible glyph stays at --sf-icon-size and the box\n grows around it (content-box). Override per icon:\n */\n.sf-icon--boxed {\n inline-size: calc(var(--sf-icon-size, var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);\n block-size: calc(var(--sf-icon-size, var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);\n padding: var(--sf-icon-box-pad);\n border: var(--sf-icon-box-border);\n border-radius: var(--sf-icon-box-radius);\n background: var(--sf-icon-box-bg);\n box-sizing: content-box;\n}\n\n/* -- Cover ---------------------------------------------------\n Full-height flex column. A .sf-cover__center child is\n vertically centered via margin-block: auto.\n Override: style=\"--sf-cover-min-height: 50dvh\"\n ---------------------------------------------------------- */\n\n.sf-cover {\n display: flex;\n flex-direction: column;\n min-height: var(--sf-cover-min-height);\n padding-block: var(--sf-cover-padding);\n}\n\n.sf-cover > .sf-cover__center {\n margin-block: auto;\n}\n\n.sf-cover--min { min-height: 50dvh; }\n.sf-cover--max { min-height: 0; max-height: 100dvh; }\n.sf-cover--padding-s { padding-block: var(--sf-space-xl); }\n.sf-cover--padding-l { padding-block: var(--sf-space-4xl); }\n\n/* -- Frame ---------------------------------------------------\n Aspect-ratio container. Media children: fill + cover.\n Override: style=\"--sf-frame-ratio: 4 / 3\"\n ---------------------------------------------------------- */\n\n.sf-frame {\n aspect-ratio: var(--sf-frame-ratio);\n overflow: hidden;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n.sf-frame > img,\n.sf-frame > video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.sf-frame--square { aspect-ratio: var(--sf-ratio-square); }\n.sf-frame--video { aspect-ratio: var(--sf-ratio-video); }\n.sf-frame--cinema { aspect-ratio: var(--sf-ratio-cinema); }\n.sf-frame--portrait { aspect-ratio: var(--sf-ratio-portrait); }\n.sf-frame--4-3 { aspect-ratio: var(--sf-ratio-4-3); }\n.sf-frame--3-2 { aspect-ratio: var(--sf-ratio-3-2); }\n.sf-frame--golden { aspect-ratio: var(--sf-ratio-golden); }\n\n/* -- Reel ----------------------------------------------------\n Horizontal scroll with scroll-snap. Each child snaps to start.\n Override: style=\"--sf-reel-gap: 1rem; --sf-reel-item-width: 280px\"\n ---------------------------------------------------------- */\n\n.sf-reel {\n display: flex;\n block-size: var(--sf-reel-height);\n gap: var(--sf-reel-gap);\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n scroll-padding-inline: var(--sf-reel-gap);\n overscroll-behavior-x: contain;\n scrollbar-width: thin;\n}\n\n.sf-reel > * {\n flex: 0 0 var(--sf-reel-item-width);\n scroll-snap-align: start;\n}\n\n/* -- Imposter ------------------------------------------------\n Element centered absolutely within its parent.\n The parent must have position: relative.\n ---------------------------------------------------------- */\n\n.sf-imposter {\n position: absolute;\n inset-block-start: 50%;\n inset-inline-start: 50%;\n translate: -50% -50%;\n}\n\n.sf-imposter--fixed {\n position: fixed;\n}\n\n.sf-imposter--contain {\n max-inline-size: calc(100% - var(--sf-imposter-margin) * 2);\n max-block-size: calc(100% - var(--sf-imposter-margin) * 2);\n overflow: auto;\n}\n\n/* -- Alternate (zigzag / media-object) -----------------------\n Alternating two-column layouts (container-query responsive).\n Every other row reverses its children's order.\n\n Establishes a named container `sf-alternate` and binds the\n responsive query to that name — so a `.sf-alternate` nested\n inside another container (e.g. `.sf-bento`) still flips on\n its OWN width, not the outer container's.\n ---------------------------------------------------------- */\n\n.sf-alternate {\n display: grid;\n gap: var(--sf-alternate-gap);\n container: sf-alternate / inline-size;\n}\n\n.sf-alternate > * {\n display: grid;\n gap: var(--sf-alternate-inner-gap);\n align-items: center;\n}\n\n@container sf-alternate (min-width: 48em) {\n .sf-alternate > * {\n grid-template-columns: 1fr 1fr;\n }\n .sf-alternate > :nth-child(even) > :first-child { order: 2; }\n .sf-alternate > :nth-child(even) > :last-child { order: 1; }\n}\n\n/* -- Pancake (sticky footer) ---------------------------------\n Grid with three rows: header / main(1fr) / footer.\n ---------------------------------------------------------- */\n\n.sf-pancake {\n display: grid;\n grid-template-rows: auto 1fr auto;\n min-height: 100dvh;\n}\n\n/* -- Fixed column grids (container-query responsive) ---------\n Responsive fixed-column grids with no media queries.\n ---------------------------------------------------------- */\n\n.sf-grid-1, .sf-grid-2, .sf-grid-3, .sf-grid-4, .sf-grid-6 {\n display: grid;\n gap: var(--sf-grid-gap);\n}\n\n@container (min-width: 30em) {\n .sf-grid-4 { grid-template-columns: repeat(2, 1fr); }\n .sf-grid-6 { grid-template-columns: repeat(3, 1fr); }\n}\n\n@container (min-width: 48em) {\n .sf-grid-2 { grid-template-columns: repeat(2, 1fr); }\n .sf-grid-3 { grid-template-columns: repeat(3, 1fr); }\n .sf-grid-4 { grid-template-columns: repeat(4, 1fr); }\n .sf-grid-6 { grid-template-columns: repeat(6, 1fr); }\n}\n\n/* -- Ratio grids ---------------------------------------------\n Two-column ratio grids, container-query responsive.\n ---------------------------------------------------------- */\n\n.sf-grid-1-2, .sf-grid-2-1,\n.sf-grid-1-3, .sf-grid-3-1 {\n display: grid;\n gap: var(--sf-grid-gap);\n}\n\n@container (min-width: 48em) {\n .sf-grid-1-2 { grid-template-columns: 1fr 2fr; }\n .sf-grid-2-1 { grid-template-columns: 2fr 1fr; }\n .sf-grid-1-3 { grid-template-columns: 1fr 3fr; }\n .sf-grid-3-1 { grid-template-columns: 3fr 1fr; }\n}\n\n/* -- Equal columns ------------------------------------------\n Fixed N-column grid. Column count NEVER collapses — unlike\n .sf-grid-N which uses container queries to stack at small\n sizes. Use when the layout must remain N columns at all\n widths; the caller is responsible for overflow handling.\n Override: style=\"--sf-equal-cols: 3\"\n ---------------------------------------------------------- */\n.sf-equal {\n display: grid;\n grid-template-columns: repeat(var(--sf-equal-cols), 1fr);\n gap: var(--sf-equal-gap);\n}\n.sf-equal--2 { --sf-equal-cols: 2; }\n.sf-equal--3 { --sf-equal-cols: 3; }\n.sf-equal--4 { --sf-equal-cols: 4; }\n.sf-equal--6 { --sf-equal-cols: 6; }\n\n/* -- Content grid (breakout pattern) -------------------------\n Full-width grid that lets items break out to breakout or full\n width. Children default to the content column.\n ---------------------------------------------------------- */\n\n.sf-content-grid {\n display: grid;\n grid-template-columns:\n [full-start]\n minmax(var(--sf-space-gutter), 1fr)\n [breakout-start]\n minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2))\n [content-start]\n min(var(--sf-content-width), 100% - var(--sf-space-gutter) * 2)\n [content-end]\n minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2))\n [breakout-end]\n minmax(var(--sf-space-gutter), 1fr)\n [full-end];\n}\n\n.sf-content-grid > * { grid-column: content; }\n.sf-content-grid > .sf-breakout { grid-column: breakout; }\n.sf-content-grid > .sf-full-bleed { grid-column: full; }\n\n/* -- Bento grid ----------------------------------------------\n Free-form grid with auto-flow dense, responsive via CQ.\n Override: style=\"--sf-bento-cols: 4; --sf-bento-row: 12rem\"\n ---------------------------------------------------------- */\n\n.sf-bento {\n display: grid;\n grid-template-columns: repeat(var(--sf-bento-cols, var(--sf-bento-cols-default)), 1fr);\n grid-auto-rows: minmax(var(--sf-bento-row, var(--sf-bento-row-default)), auto);\n grid-auto-flow: dense;\n gap: var(--sf-bento-gap);\n}\n\n@container (min-width: 30em) and (max-width: 47.99em) {\n .sf-bento { grid-template-columns: repeat(2, 1fr); }\n}\n\n.sf-bento--2 { --sf-bento-cols: 2; }\n.sf-bento--4 { --sf-bento-cols: 4; }\n.sf-bento--compact { --sf-bento-row: var(--sf-bento-row-compact); }\n.sf-bento--tall { --sf-bento-row: var(--sf-bento-row-tall); }\n\n/* -- Subgrid -------------------------------------------------\n A grid child that inherits its parent's tracks.\n ---------------------------------------------------------- */\n\n.sf-subgrid { display: grid; grid-template-columns: subgrid; }\n.sf-subgrid-rows { display: grid; grid-template-rows: subgrid; }\n\n/* ─── core/macros.css ─── */\n/* ============================================================\n SLASHED — core/macros.css\n ★ shipped in essential bundle\n Macro-classes (recipes / patterns).\n Requires: core/tokens.css, core/tokens.macros.css\n Layer: slashed.macros\n\n Macro-classes vs layout primitives:\n · Layout primitive = answers \"where do my children go?\"\n (e.g. .sf-stack, .sf-grid, .sf-cluster)\n · Macro-class = answers \"what does this element DO or\n LOOK LIKE?\" (e.g. .sf-prose, .sf-flow, .sf-truncate)\n\n Cascade position:\n layout → components → macros → utilities → states → …\n Macros may compose with primitives and components, but a\n single-property utility still wins on the same selector.\n\n PUBLIC API — every class here is part of the public surface.\n Tokens consumed by these classes are listed in\n core/tokens.macros.css.\n ============================================================ */\n\n\n/* -- Prose ---------------------------------------------------\n Long-form text column with automatic vertical rhythm.\n .sf-not-prose resets styles inside a prose block.\n ---------------------------------------------------------- */\n\n.sf-prose {\n max-width: var(--sf-container-prose);\n overflow-wrap: break-word;\n}\n\n.sf-prose > * + * { margin-block-start: var(--sf-prose-paragraph); }\n.sf-prose :is(h2, h3, h4) + * { margin-block-start: var(--sf-space-s); }\n.sf-prose li + li { margin-block-start: var(--sf-space-xs); }\n.sf-prose :is(blockquote, pre) { margin-block: var(--sf-space-m); }\n.sf-prose img { margin-block: var(--sf-space-m); border-radius: var(--sf-radius-m); }\n.sf-prose :is(ul, ol) { padding-inline-start: 1.25em; }\n.sf-prose ul { list-style: disc; }\n.sf-prose ol { list-style: decimal; }\n.sf-prose ::marker { color: var(--sf-color-primary); }\n.sf-prose figure { margin-block: var(--sf-space-l); }\n.sf-prose figcaption { font-size: var(--sf-text-s); color: var(--sf-color-text--muted); margin-block-start: var(--sf-space-xs); }\n.sf-prose table { margin-block: var(--sf-space-m); display: block; overflow-x: auto; max-width: 100%; }\n.sf-prose video { margin-block: var(--sf-space-m); border-radius: var(--sf-radius-m); }\n\n/* .sf-not-prose resets — specificity 0-2-1 intentionally exceeds the\n general 0-2-0 budget because these rules must override the .sf-prose\n child selectors above (also 0-2-0). This is a documented exception. */\n.sf-prose .sf-not-prose > * + * { margin-block-start: 0; }\n.sf-prose .sf-not-prose li + li { margin-block-start: 0; }\n/* `revert` in a cascade layer rolls back past all layers to the UA\n stylesheet — restoring browser-default list styling. In the flat\n bundle (layers stripped), `revert` falls back to the UA stylesheet\n directly. Both paths produce the same result: native list bullets. */\n.sf-prose .sf-not-prose :is(ul,ol) { list-style: revert; padding-inline-start: revert; }\n.sf-prose .sf-not-prose ::marker { color: inherit; }\n.sf-prose .sf-not-prose img { margin-block: 0; border-radius: 0; }\n.sf-prose .sf-not-prose figure { margin-block: 0; }\n.sf-prose .sf-not-prose figcaption { font-size: inherit; color: inherit; margin-block-start: 0; }\n.sf-prose .sf-not-prose table { margin-block: 0; display: revert; overflow-x: revert; max-width: revert; }\n.sf-prose .sf-not-prose video { margin-block: 0; border-radius: 0; }\n\n/* -- Flow ----------------------------------------------------\n Heydon Pickering's \"lobotomized owl\" — every flow child gets\n equal margin-block-start. Useful for content blocks where you\n don't know in advance which elements will be present.\n Override: style=\"--sf-flow-space: 2rem\"\n ---------------------------------------------------------- */\n.sf-flow > * + * {\n margin-block-start: var(--sf-flow-space);\n}\n\n/* -- Truncate / line-clamp -----------------------------------\n Single-line ellipsis (.sf-truncate) and N-line clamp\n (.sf-line-clamp-2 / -3 / -N). The fixed-count variants\n hardcode -webkit-line-clamp; -N reads --sf-line-clamp.\n\n Note: -webkit-line-clamp is not vendor-locked despite the\n prefix — every modern browser implements it as the de-facto\n standard. The unprefixed CSS Overflow 4 `line-clamp` exists\n but is not yet widely shipped.\n ---------------------------------------------------------- */\n.sf-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.sf-line-clamp-2,\n.sf-line-clamp-3,\n.sf-line-clamp-N {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n.sf-line-clamp-2 { -webkit-line-clamp: 2; line-clamp: 2; }\n.sf-line-clamp-3 { -webkit-line-clamp: 3; line-clamp: 3; }\n.sf-line-clamp-N {\n -webkit-line-clamp: var(--sf-line-clamp);\n line-clamp: var(--sf-line-clamp);\n}\n\n/* -- Equal height --------------------------------------------\n Force grid/flex children to share the tallest one's height.\n Works in flex (default `align-items: stretch`) and is\n re-asserted here for cases where a wrapper resets it.\n ---------------------------------------------------------- */\n.sf-equal-height {\n display: flex;\n align-items: stretch;\n}\n.sf-equal-height > * {\n block-size: auto;\n flex: 1 1 auto;\n}\n\n/* -- Aspect --------------------------------------------------\n Generic aspect-ratio container. Distinct from layout's\n .sf-frame which targets media (object-fit: cover) — .sf-aspect\n is content-agnostic.\n Override: style=\"--sf-aspect: 4 / 3\"\n ---------------------------------------------------------- */\n.sf-aspect {\n aspect-ratio: var(--sf-aspect);\n}\n\n/* -- Scroll shadow -------------------------------------------\n Top + bottom mask gradient that reveals when content is\n scrolled inside a vertical-scroll container. Pure CSS,\n no JS. Combine with overflow-y: auto on the same element.\n Override: style=\"--sf-scroll-shadow-size: 3rem\"\n ---------------------------------------------------------- */\n.sf-scroll-shadow {\n overflow-y: auto;\n mask-image:\n linear-gradient(to bottom,\n transparent 0,\n black var(--sf-scroll-shadow-size),\n black calc(100% - var(--sf-scroll-shadow-size)),\n transparent 100%);\n}\n\n/* -- Scroll snap ---------------------------------------------\n Vertical scroll-snap container for vertically stacked sections.\n Pairs with consumer-supplied scroll-snap-align on children.\n For horizontal snap, use the .sf-reel layout primitive.\n ---------------------------------------------------------- */\n.sf-scroll-snap {\n overflow-y: auto;\n scroll-snap-type: y mandatory;\n overscroll-behavior-y: contain;\n}\n.sf-scroll-snap > * {\n scroll-snap-align: start;\n}\n\n/* -- Overflow fade -------------------------------------------\n End-edge horizontal fade for clipped inline content\n (e.g. a row of tags overflowing a card). Pure mask, so it\n respects the element's actual background.\n ---------------------------------------------------------- */\n.sf-overflow-fade {\n overflow: hidden;\n mask-image: linear-gradient(\n to right,\n black 0,\n black calc(100% - var(--sf-scroll-shadow-size)),\n transparent 100%\n );\n}\n\n/* -- No tap highlight ----------------------------------------\n Suppresses the WebKit/Android grey tap-highlight overlay on\n interactive elements where it conflicts with the framework's\n own .is-active / hover treatment.\n ---------------------------------------------------------- */\n.sf-no-tap-highlight {\n -webkit-tap-highlight-color: transparent;\n}\n\n/* -- Surface -------------------------------------------------\n Sets background + auto-flipped foreground in one class.\n Combines --sf-color-X (resolved semantic, mode-aware) with\n the matching --sf-color-text--on-X (auto-contrast) so text\n legibility is automatic without consumer math.\n\n 11 variants cover all brand + status + inverse colors. Base is\n excluded — base IS the page surface; use plain\n background: var(--sf-color-bg) if you need it explicitly.\n\n Composes naturally with .sf-section / .sf-card / .sf-stack:\n
\n ---------------------------------------------------------- */\n.sf-surface--primary { background: var(--sf-color-primary); color: var(--sf-color-text--on-primary); }\n.sf-surface--secondary { background: var(--sf-color-secondary); color: var(--sf-color-text--on-secondary); }\n.sf-surface--tertiary { background: var(--sf-color-tertiary); color: var(--sf-color-text--on-tertiary); }\n.sf-surface--action { background: var(--sf-color-action); color: var(--sf-color-text--on-action); }\n.sf-surface--neutral { background: var(--sf-color-neutral); color: var(--sf-color-text--on-neutral); }\n.sf-surface--inverse { background: var(--sf-color-inverse); color: var(--sf-color-text--on-inverse); }\n.sf-surface--success { background: var(--sf-color-success); color: var(--sf-color-text--on-success); }\n.sf-surface--warning { background: var(--sf-color-warning); color: var(--sf-color-text--on-warning); }\n.sf-surface--error { background: var(--sf-color-error); color: var(--sf-color-text--on-error); }\n.sf-surface--info { background: var(--sf-color-info); color: var(--sf-color-text--on-info); }\n.sf-surface--danger { background: var(--sf-color-danger); color: var(--sf-color-text--on-danger); }\n\n/* -- Text gradient -------------------------------------------\n Fills text with a gradient. Defaults to --sf-gradient-primary\n (auto-adapts to brand + dark mode). Override the background-image\n per-instance for any other gradient or radial:\n

\n\n Selection on gradient text inherits the clipping — that's a\n known browser limitation across all engines, accepted as-is.\n\n The `color: transparent` declaration is gated behind a feature\n query for background-clip:text. Without that gate, an engine\n that ignored background-clip:text would render the text fully\n invisible (transparent over no clipped fill). All current\n evergreen browsers support either the standard or the\n -webkit- prefixed form, so the supports check is a defensive\n guard rather than a real-world fallback.\n ---------------------------------------------------------- */\n.sf-text-gradient {\n background-image: var(--sf-gradient-primary);\n /* stylelint-disable-next-line property-no-vendor-prefix -- required for Safari */\n -webkit-background-clip: text;\n background-clip: text;\n}\n@supports ((background-clip: text) or (-webkit-background-clip: text)) {\n .sf-text-gradient {\n color: transparent;\n }\n}\n\n/* -- External link indication --------------------------------\n Adds an external-link glyph after the link text. Opt-in per\n element via class — for global auto-detection, write your\n own rule in unlayered CSS:\n a[rel~=\"external\"]:not(.no-external)::after {\n content: var(--sf-link-external-marker);\n }\n\n Marker is tokenised so consumers can swap to \" →\", \" ⤴\", or\n unset (empty string fully disables it — the default token\n value bakes the leading space in, so `\"\"` leaves nothing\n after the link):\n :root { --sf-link-external-marker: \"\" }\n ---------------------------------------------------------- */\n.sf-link-external::after {\n content: var(--sf-link-external-marker);\n display: inline-block;\n font-size: 0.85em;\n text-decoration: none;\n}\n\n/* ─── core/states.css ─── */\n/* SLASHED — core/states.css\n @layer slashed.states\n\n Stateful classes toggled by JS or ARIA attributes.\n Prefix: .is-* (exclusive to this layer — never used in utilities).\n\n These are NOT utility classes. They describe dynamic,\n runtime states that change in response to user interaction\n or application logic. Utilities describe static intent;\n states describe current condition.\n\n Design principles:\n ─ Generic states only — component-specific overrides belong\n in slashed.components (e.g. .sf-modal.is-open).\n ─ Tokens first — every value references a design token.\n ─ Low specificity — single class, no nesting.\n ─ Minimal property sets — states set the MINIMUM needed\n to communicate the state. Components layer on top.\n ─────────────────────────────────────────────────── */\n\n\n/* ============================================================\n VISIBILITY\n ============================================================ */\n\n.is-hidden {\n display: none !important;\n}\n\n.is-invisible {\n visibility: hidden;\n}\n\n.is-visible {\n visibility: visible;\n}\n\n/* ============================================================\n INTERACTIVITY — DISABLED / READONLY\n ============================================================ */\n\n.is-disabled {\n opacity: var(--sf-opacity-disabled);\n pointer-events: none;\n cursor: not-allowed;\n user-select: none;\n}\n\n.is-readonly {\n pointer-events: none;\n user-select: none;\n}\n\n/* ============================================================\n LOADING / ASYNC FEEDBACK\n ============================================================ */\n\n.is-loading {\n color: transparent !important;\n pointer-events: none;\n position: relative;\n}\n\n.is-loading::after {\n content: \"\";\n position: absolute;\n inset-block-start: 50%;\n inset-inline-start: 50%;\n width: 1em;\n height: 1em;\n margin-block-start: -0.5em;\n margin-inline-start: -0.5em;\n border: var(--sf-border-width-2) solid var(--sf-color-border--strong);\n border-block-start-color: var(--sf-color-action);\n border-radius: var(--sf-radius-full);\n /* Reduced-motion: accessibility.css kills animation-duration via\n !important on *, *::before, *::after — so this spinner stops\n even without a local reduced-motion wrapper. */\n animation: var(--sf-animation-spin);\n}\n\n.is-busy {\n cursor: progress;\n}\n\n.is-pending {\n /* Optimistic UI — request in flight but content still visible/usable\n (unlike .is-loading, which masks content with a spinner). */\n opacity: var(--sf-state-pending-opacity);\n cursor: progress;\n}\n\n.is-skeleton {\n color: transparent !important;\n background: linear-gradient(\n 90deg,\n var(--sf-color-well) 25%,\n var(--sf-color-raised) 50%,\n var(--sf-color-well) 75%\n );\n background-size: 200% 100%;\n animation: var(--sf-animation-shimmer);\n border-radius: var(--sf-radius-s);\n pointer-events: none;\n user-select: none;\n}\n\n/* ============================================================\n ACTIVE / SELECTED / CURRENT — navigation & toggling\n ============================================================ */\n\n.is-active {\n /* Generic active indicator — components add visual specifics.\n Sets a scoped token for components to consume. */\n --sf-is-active: 1;\n}\n\n.is-selected {\n background-color: var(--sf-color-bg--selected);\n}\n\n.is-current {\n /* Navigation: marks the current page/route.\n Components style via .sf-nav .is-current etc.\n Weight is tokenised so consumers can override without specificity battles:\n :root { --sf-current-font-weight: var(--sf-font-weight-semibold); } */\n --sf-is-current: 1;\n font-weight: var(--sf-current-font-weight, var(--sf-font-weight-bold));\n}\n\n.is-highlighted {\n background-color: var(--sf-color-bg--focus);\n}\n\n.is-pressed {\n /* Toggle buttons (aria-pressed=\"true\"). Components add specifics. */\n --sf-is-pressed: 1;\n background-color: var(--sf-color-bg--selected);\n}\n\n/* ============================================================\n OPEN / CLOSED — disclosure widgets, modals, dropdowns\n\n .is-open / .is-collapsed — a thing is shown vs hidden (modal,\n dropdown, drawer). .is-expanded — maps to aria-expanded on a\n disclosure/accordion trigger. They share the --sf-is-open flag\n but signal different intent to consumers.\n ============================================================ */\n\n.is-open {\n --sf-is-open: 1;\n}\n\n.is-collapsed {\n --sf-is-open: 0;\n}\n\n.is-expanded {\n --sf-is-open: 1;\n}\n\n/* ============================================================\n VALIDATION / FEEDBACK\n\n Overlap semantics (see docs/states.md):\n · .is-invalid / .is-valid — form-field validation (maps to\n aria-invalid). Use on inputs.\n · .is-error / .is-success — general component feedback, not tied\n to a single field (a failed save, a completed step).\n · .is-danger — destructive-action context (a delete button), NOT\n a validation result. Distinct from .is-error.\n ============================================================ */\n\n.is-valid {\n --sf-field-border-color: var(--sf-color-success);\n --sf-field-text-color: var(--sf-color-success-strong);\n}\n\n.is-invalid {\n --sf-field-border-color: var(--sf-color-error);\n --sf-field-text-color: var(--sf-color-error-strong);\n}\n\n.is-warning {\n --sf-field-border-color: var(--sf-color-warning);\n --sf-field-text-color: var(--sf-color-warning-strong);\n}\n\n.is-success {\n --sf-field-border-color: var(--sf-color-success);\n --sf-field-text-color: var(--sf-color-success-strong);\n}\n\n.is-error {\n --sf-field-border-color: var(--sf-color-error);\n --sf-field-text-color: var(--sf-color-error-strong);\n}\n\n.is-info {\n --sf-field-border-color: var(--sf-color-info);\n --sf-field-text-color: var(--sf-color-info-strong);\n}\n\n.is-danger {\n --sf-field-border-color: var(--sf-color-danger);\n --sf-field-text-color: var(--sf-color-danger-strong);\n}\n\n/* ============================================================\n POSITION / STICKINESS\n ============================================================ */\n\n.is-sticky {\n position: sticky;\n top: var(--sf-sticky-offset, 0);\n z-index: var(--sf-z-low);\n}\n\n.is-pinned {\n position: sticky;\n top: 0;\n z-index: var(--sf-z-mid);\n}\n\n.is-fixed {\n position: fixed;\n}\n\n.is-fullscreen {\n /* App-managed fullscreen overlay. For the native Fullscreen API,\n prefer the `:fullscreen` pseudo-class; this is the JS-toggled\n equivalent for elements not entered via that API. */\n position: fixed;\n inset: 0;\n z-index: var(--sf-z-max);\n}\n\n.is-resizable {\n resize: both;\n overflow: auto;\n}\n\n/* ============================================================\n OVERFLOW / CLIPPING\n ============================================================ */\n\n.is-clipped {\n overflow: hidden !important;\n}\n\n.is-scrollable {\n overflow: auto;\n overscroll-behavior: contain;\n}\n\n.is-truncated {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* ============================================================\n DRAG & DROP\n ============================================================ */\n\n.is-dragging {\n opacity: var(--sf-opacity-50);\n cursor: grabbing;\n}\n\n.is-drop-target {\n outline: var(--sf-focus-ring-width) dashed var(--sf-color-action);\n outline-offset: var(--sf-focus-ring-offset);\n}\n\n.is-draggable {\n cursor: grab;\n}\n\n/* ============================================================\n OVERLAY / BACKDROP\n ============================================================ */\n\n.is-overlay {\n position: absolute;\n inset: 0;\n}\n\n/* ============================================================\n FOCUS / INTERACTION MODIFIERS\n ============================================================ */\n\n.is-clickable {\n cursor: pointer;\n}\n\n.is-unselectable {\n user-select: none;\n}\n\n.is-focused {\n /* Programmatic / JS-driven focus indicator, independent of\n :focus-visible. Normal weight so consumers can restyle. */\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-focus-ring-color);\n outline-offset: var(--sf-focus-ring-offset);\n}\n\n/* ============================================================\n EMPTY STATE\n ============================================================ */\n\n.is-empty:empty {\n display: none;\n}\n\n/* ============================================================\n KEYFRAMES (used by states above)\n\n Spinner and shimmer keyframes live in core/motion.css alongside\n the rest of the framework's @keyframes. They are referenced from\n here by name (sf-spin, sf-shimmer) and are inert until applied.\n If you load core/states.css without core/motion.css, define them\n in your own CSS or copy them from motion.css.\n ============================================================ */\n\n/* ─── core/motion.css ─── */\n/* SLASHED — core/motion.css\n @layer slashed.motion\n\n Extends the base interactive transition (defined in base.css)\n with a broader property set and adds scroll/view-transition\n behaviour. Optional and independently loadable.\n\n Reduced-motion handling is intentionally NOT duplicated here:\n accessibility.css (a higher cascade layer, shipped in the\n 'essential' bundle) neutralises animation/transition via\n property-level !important. The no-preference wrapper below is\n a defensive opt-in stance — animations apply only when the\n user has not requested their absence — and remains correct\n even if motion.css is served without accessibility.css.\n ─────────────────────────────────────────────────── */\n\n\n@media (prefers-reduced-motion: no-preference) {\n\n html {\n scroll-behavior: smooth;\n }\n\n /* Keyboard navigation must not be hijacked by smooth scroll. */\n html:focus-within {\n scroll-behavior: auto;\n }\n\n /* Broadens the base.css link transition to all interactive\n elements and the full set of animatable UI properties. */\n a, button, input, select, textarea, summary {\n transition-property:\n color, background-color, border-color, text-decoration-color,\n fill, stroke, opacity, box-shadow, transform, filter,\n backdrop-filter;\n transition-duration: var(--sf-duration-fast);\n transition-timing-function: var(--sf-ease-out);\n }\n\n @supports (view-transition-name: none) {\n ::view-transition-old(root),\n ::view-transition-new(root) {\n animation-duration: var(--sf-duration-normal);\n animation-timing-function: var(--sf-ease-out);\n }\n }\n\n /* Animation presets — opt-in classes. Scoped to no-preference so\n reduced-motion users never trigger them (accessibility.css also\n neutralises animation-duration as a hard guarantee). These are\n DECORATIVE: under reduced motion they simply don't run, so never\n rely on one (e.g. .sf-fade-out) as the sole way to show/hide\n content — drive real state with hidden/display/JS instead. */\n .sf-fade-in { animation: var(--sf-animation-fade-in); }\n .sf-fade-out { animation: var(--sf-animation-fade-out); }\n .sf-slide-in-up { animation: var(--sf-animation-slide-in-up); }\n .sf-slide-in-down { animation: var(--sf-animation-slide-in-down); }\n .sf-slide-in-left { animation: var(--sf-animation-slide-in-left); }\n .sf-slide-in-right { animation: var(--sf-animation-slide-in-right); }\n .sf-scale-up { animation: var(--sf-animation-scale-up); }\n .sf-scale-down { animation: var(--sf-animation-scale-down); }\n\n /* Scroll-driven entrance effects — element animates into view\n as it enters the viewport. Uses CSS scroll-driven animations\n via animation-timeline: view(). Range tokens\n (--sf-scroll-timeline-range-{start,end}, declared in\n core/tokens.css) control where the animation starts and ends\n along the scroll progress.\n\n Example:\n
\n\n Reduced-motion: gated by the no-preference wrapper above, so\n these are inert when the user opts out. The core animation\n (translate/opacity) still composes correctly with reduced-motion\n neutralisation in accessibility.css if a consumer accidentally\n writes them outside the gate.\n\n Browser support — animation-timeline is shipping in Chrome 115+\n and Firefox 145+ (Nov 2026). Safari does not yet support it\n (tracked at WebKit bug 264057); on those engines the @supports\n block below is skipped, so the rule falls back to a one-shot\n time-driven animation playing once at --sf-duration-slow.\n animation-duration is set unconditionally because without it\n a non-supporting engine would default to 0s and snap to the\n end state instantly — there'd be no visible entrance at all.\n Where animation-timeline is supported the timeline drives\n progress and the duration is ignored. */\n .sf-entrance--fade,\n .sf-entrance--fade-up,\n .sf-entrance--fade-down,\n .sf-entrance--fade-left,\n .sf-entrance--fade-right,\n .sf-entrance--scale-up {\n animation-duration: var(--sf-duration-slow);\n animation-timing-function: var(--sf-ease-out);\n animation-fill-mode: both;\n }\n @supports (animation-timeline: view()) {\n .sf-entrance--fade,\n .sf-entrance--fade-up,\n .sf-entrance--fade-down,\n .sf-entrance--fade-left,\n .sf-entrance--fade-right,\n .sf-entrance--scale-up {\n animation-timeline: view();\n animation-range: var(--sf-scroll-timeline-range-start, entry 0%)\n var(--sf-scroll-timeline-range-end, cover 30%);\n }\n }\n .sf-entrance--fade { animation-name: sf-fade-in; }\n .sf-entrance--fade-up { animation-name: sf-slide-in-up; }\n .sf-entrance--fade-down { animation-name: sf-slide-in-down; }\n .sf-entrance--fade-left { animation-name: sf-slide-in-left; }\n .sf-entrance--fade-right { animation-name: sf-slide-in-right; }\n .sf-entrance--scale-up { animation-name: sf-scale-up; }\n\n}\n\n/* Keyframes — inert until referenced, so they live outside the\n no-preference wrapper (consumers may reference them at will).\n Spinner and shimmer are used by .is-loading / .is-skeleton in\n core/states.css; the rest power the .sf-* animation classes\n above. */\n@keyframes sf-fade-in { from { opacity: 0; } to { opacity: 1; } }\n@keyframes sf-fade-out { from { opacity: 1; } to { opacity: 0; } }\n@keyframes sf-slide-in-up { from { opacity: 0; translate: 0 1rem; } to { opacity: 1; translate: 0 0; } }\n@keyframes sf-slide-in-down { from { opacity: 0; translate: 0 -1rem; } to { opacity: 1; translate: 0 0; } }\n@keyframes sf-slide-in-left { from { opacity: 0; translate: 1rem 0; } to { opacity: 1; translate: 0 0; } }\n@keyframes sf-slide-in-right { from { opacity: 0; translate: -1rem 0; } to { opacity: 1; translate: 0 0; } }\n@keyframes sf-scale-up { from { opacity: 0; scale: 0.92; } to { opacity: 1; scale: 1; } }\n@keyframes sf-scale-down { from { opacity: 1; scale: 1; } to { opacity: 0; scale: 0.92; } }\n\n@keyframes sf-spin { to { rotate: 360deg; } }\n@keyframes sf-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }\n\n/* Notification ping (expanding fading ring), cursor blink, gentle float.\n Keyframes only — apply via your own BEM/component rules or the\n --sf-animation-ping / -blink / -float presets. No utility classes\n ship for these (by design). */\n@keyframes sf-ping { 75%, 100% { scale: 2; opacity: 0; } }\n@keyframes sf-blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }\n@keyframes sf-float { 0%, 100% { translate: 0 0; } 50% { translate: 0 -0.5rem; } }\n\n/* @property color interpolation — proves that registered custom\n properties animate smoothly in oklch. Because --sf-color-primary-light\n is registered with syntax:\"\", the browser interpolates the\n intermediate color values frame-by-frame. Opt-in via .sf-color-pulse. */\n@keyframes sf-color-pulse {\n 0% { --sf-color-primary-light: oklch(from var(--sf-color-primary-light) l c h); }\n 50% { --sf-color-primary-light: oklch(from var(--sf-color-primary-light) calc(l + 0.25) c h); }\n 100% { --sf-color-primary-light: oklch(from var(--sf-color-primary-light) l c h); }\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .sf-color-pulse {\n animation: var(--sf-animation-color-pulse);\n }\n}\n\n/* @starting-style — entry animations for elements appearing in the\n top layer (dialog, [popover]) or via display:none → block. SLASHED\n ships no global @starting-style rules because the from-state is\n element/component specific. Consumers opt in per component, e.g.:\n dialog[open] { opacity: 1; @starting-style { opacity: 0; } }\n Tracked for the future components layer. */\n\n/* ─── core/accessibility.css ─── */\n/* SLASHED — core/accessibility.css\n @layer slashed.accessibility\n\n Critical a11y rules use !important to survive unlayered CSS and\n third-party resets (unlayered normal beats any layered normal).\n Protection is SELECTIVE — only rules whose override is a genuine\n user-facing barrier are hardened. Rules that are legitimately\n customised (touch targets, skip-link position, contrast boost,\n cursor) are left normal on purpose.\n\n Audit note: reset.css/base.css contain no !important on outline,\n animation-* or transition-*, so the reversed important-layer order\n cannot let a lower layer defeat these rules.\n ─────────────────────────────────────────────────── */\n\n\n/* ----------------------------------------------------------\n Focus — keyboard visibility is WCAG 2.4.7, hard barrier.\n Ring definition lives here (not base.css) so all focus\n logic is in one high-priority layer. Hardened.\n ---------------------------------------------------------- */\n:focus:not(:focus-visible) {\n outline: none;\n}\n\n:focus-visible {\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-focus-ring-color) !important;\n outline-offset: var(--sf-focus-ring-offset) !important;\n}\n\n/* ----------------------------------------------------------\n Reduced motion. Token override helps components that read\n duration tokens in other contexts; the property-level\n !important block is the real guarantee and works\n independently of tokens (and of unlayered token overrides).\n ---------------------------------------------------------- */\n@media (prefers-reduced-motion: reduce) {\n :root {\n --sf-duration-instant: 0.01ms;\n --sf-duration-fast: 0.01ms;\n --sf-duration-normal: 0.01ms;\n --sf-duration-slow: 0.01ms;\n --sf-duration-slower: 0.01ms;\n }\n\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n scroll-behavior: auto !important;\n }\n}\n\n/* Manual motion opt-out — same suppression as the OS preference above,\n but consumer-toggled (e.g. a site-level \"reduce motion\" switch). Works\n regardless of the OS setting. */\n.no-motion,\n.no-motion *,\n.no-motion *::before,\n.no-motion *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n scroll-behavior: auto !important;\n}\n\n/* ----------------------------------------------------------\n High contrast — enhancement, not a barrier. Left normal:\n if overridden the focus ring still exists, just thinner.\n ---------------------------------------------------------- */\n@media (prefers-contrast: more) {\n :root {\n --sf-focus-ring-width: 3px;\n /* Push derived reading-text colours toward the extremes (see the\n --sf-contrast-bias knob in tokens.css). */\n --sf-contrast-bias: 0.06;\n }\n\n hr {\n border-block-start-width: var(--sf-border-width-2);\n }\n}\n\n/* ----------------------------------------------------------\n Reduced transparency.\n ---------------------------------------------------------- */\n@media (prefers-reduced-transparency: reduce) {\n ::backdrop {\n background-color: var(--sf-color-neutral);\n opacity: 1;\n }\n}\n\n/* ----------------------------------------------------------\n Minimum touch targets — WCAG 2.5.5 has documented\n exceptions; dense desktop-first UIs legitimately go below\n 44px with other hit-area strategies. Left normal ON PURPOSE\n so it stays a recommendation, not an unbreakable rule.\n ---------------------------------------------------------- */\n@media (pointer: coarse) {\n button,\n input[type=\"button\"],\n input[type=\"submit\"],\n input[type=\"reset\"],\n select,\n summary,\n a {\n min-height: var(--sf-touch-target);\n min-width: var(--sf-touch-target);\n }\n}\n\n/* ----------------------------------------------------------\n Disabled states — cosmetic, left normal.\n ---------------------------------------------------------- */\n[disabled],\n[aria-disabled=\"true\"] {\n cursor: not-allowed;\n}\n\n/* ----------------------------------------------------------\n Screen-reader-only. Atomic contract: a partial override\n either exposes hidden content visually or hides intended\n content. All-or-nothing, so the whole set is hardened.\n ---------------------------------------------------------- */\n.sr-only,\n.visually-hidden,\n.sr-only-focusable:not(:focus, :focus-within) {\n position: absolute !important;\n width: 1px !important;\n height: 1px !important;\n padding: 0 !important;\n margin: -1px !important;\n overflow: hidden !important; /* fallback for Safari <16 */\n overflow: clip !important;\n clip-path: inset(50%) !important;\n white-space: nowrap !important;\n border: 0 !important;\n}\n\n/* ----------------------------------------------------------\n Skip link — appearance belongs to the integrator's design.\n Left normal so position/colour can be themed freely.\n ---------------------------------------------------------- */\n.skip-link {\n position: absolute;\n top: -100%;\n left: var(--sf-space-m);\n z-index: var(--sf-z-max);\n padding: var(--sf-space-s) var(--sf-space-m);\n background: var(--sf-color-bg);\n color: var(--sf-color-text);\n border: var(--sf-border-width-1) solid var(--sf-color-border);\n border-radius: var(--sf-radius-m);\n transition: top var(--sf-duration-fast) var(--sf-ease-out);\n}\n.skip-link:focus {\n top: var(--sf-space-m);\n}\n\n/* ----------------------------------------------------------\n Focus-parent — an a11y *pattern*, not a state. Lives in the\n accessibility layer (was previously in states.css; moved in\n v0.3.0) so that all focus-related a11y rules share one home\n and survive third-party resets at the same priority as the\n focus-visible ring above. Cannot be hardened with !important\n because the outline shorthand is consumer-customisable.\n ---------------------------------------------------------- */\n.focus-parent:focus-within {\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-color-border--focus);\n outline-offset: var(--sf-focus-ring-offset);\n}\n\n/* ----------------------------------------------------------\n Clickable-parent — the card-with-link a11y pattern.\n Applied to the card container. Two modes:\n\n Automatic (single primary link):\n
\n

Title

\n

Description

\n \n
\n\n Explicit (multiple links — add [data-overlay-link] to the primary):\n
\n

Title

\n

Description

\n Category ← lifted automatically\n ← lifted automatically\n
\n\n Mechanic: the link's ::after fills the card via position:absolute;\n inset:0. It is contained by .sf-clickable-parent (position:relative)\n only if the link itself has no positioned ancestor between it and the\n card — so no element containing the overlay link should be given\n position:relative. Flow children are therefore intentionally left at\n position:static; they sit below the overlay in stacking order.\n\n Trade-off: card text is NOT user-selectable by mouse. A JavaScript\n click-delegate could restore selectability while preserving\n full-card navigation.\n\n Key features:\n • [data-overlay-link] explicit mode — reliable multi-link cards\n • `:focus-within` card ring — always included, no extra recipe\n • --sf-clickable-overlay-z — escape parent stacking contexts\n • Wider interactive-element exceptions (summary, video, audio…)\n\n Tune --sf-clickable-overlay-z to escape a parent stacking context.\n [data-no-overlay] opts any descendant above the overlay.\n ---------------------------------------------------------- */\n.sf-clickable-parent {\n --sf-clickable-overlay-z: 1;\n position: relative;\n cursor: pointer;\n}\n\n/* Overlay: the link's ::after fills the containing card.\n Automatic mode — every :any-link gets the overlay; works reliably\n for single-link cards. Secondary links that also receive ::after are\n lifted to position:relative (interactive rule below), which scopes\n their ::after to themselves and keeps them independently clickable.\n Explicit mode — only [data-overlay-link] fills the card. */\n.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link)::after,\n.sf-clickable-parent [data-overlay-link]::after {\n content: \"\";\n position: absolute;\n inset: 0;\n z-index: var(--sf-clickable-overlay-z);\n pointer-events: auto;\n}\n\n/* Interactive descendants beat the overlay and stay functional.\n Explicit mode: every non-designated :any-link is lifted — robust at\n any nesting depth, no :first-of-type fragility.\n Automatic mode: links that are :not(:first-of-type) among siblings\n are lifted. Sibling-scoped — use explicit mode for cards where\n secondary links live at different nesting depths.\n [data-no-overlay] is the per-element consumer escape hatch. */\n.sf-clickable-parent :is(\n button, input, textarea, select,\n summary, video, audio,\n [role=\"button\"], [data-no-overlay]\n),\n.sf-clickable-parent:has([data-overlay-link]) a:where(:any-link):not([data-overlay-link]),\n.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):not(:first-of-type) {\n position: relative;\n z-index: calc(var(--sf-clickable-overlay-z) + 1);\n}\n\n/* Card-level focus ring — keyboard users see which card is active.\n The focus ring is always included without requiring a separate\n recipe. Suppress with outline:none\n if the card design provides its own focus indicator. */\n.sf-clickable-parent:focus-within {\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-color-border--focus);\n outline-offset: var(--sf-focus-ring-offset);\n}\n\n/* ----------------------------------------------------------\n Forced colors (Windows High Contrast). Outline-based focus\n already survives; we only (a) adopt the system Highlight\n colour for the ring and (b) drop decorative shadows that\n forced-colors would otherwise flatten into solid slabs.\n Shadow removal is zero-specificity so any rule can win it back.\n ---------------------------------------------------------- */\n@media (forced-colors: active) {\n :root {\n --sf-focus-ring-color: Highlight;\n }\n\n :focus-visible {\n outline-color: Highlight !important;\n }\n\n :where(*) {\n box-shadow: none;\n text-shadow: none;\n }\n\n /* Form controls otherwise lose their visible boundary in forced-colors\n (their token-driven border colour is replaced by the system canvas).\n Re-assert a system-coloured border so fields stay distinguishable. */\n input,\n select,\n textarea {\n border: var(--sf-border-width-1) solid;\n }\n}\n\n/* ─── core/print.css ─── */\n/* SLASHED — core/print.css\n @layer slashed.print\n\n Print rules and @page definition. Print tokens (--sf-print-*)\n are declared in core/tokens.css.\n\n Color contract (Phase 3):\n authored colour is preserved by default. Browsers apply their own\n ink-saving heuristics via the default `print-color-adjust: economy`,\n so semantic colours (badges, status pills, , syntax highlighting)\n reach paper intact. Two opt-in classes give consumers control:\n\n .print-color-exact → force every descendant to print colours\n verbatim (for colour-coded data that loses\n meaning when the browser flattens it).\n .print-no-color → force everything to ink-on-paper:\n transparent backgrounds + system text colour.\n The legacy blanket reset, now opt-in.\n\n `!important` is reserved for selectors whose semantics require\n defeating consumer-authored CSS:\n - `.no-print` and the hide-list (nav, aside, button, …) — these\n are content negative-space and must vanish from print regardless\n of authored display rules.\n - `details > summary` — disclosure widget carries no print value.\n - The two opt-in colour classes above — when the consumer marks a\n region with them they are explicitly asking to override authored\n colour, so `!important` is the contract.\n ─────────────────────────────────────────────────── */\n\n\n/* Screen default: print-only content is hidden until the page is printed. */\n.print-only {\n display: none;\n}\n\n@media print {\n\n @page {\n size: var(--sf-print-page-size, a4);\n margin: var(--sf-print-page-margin, 2cm);\n }\n\n body {\n font-size: var(--sf-print-base-size);\n }\n\n /* Decorative shadows are paper-noise. No `!important` — an authored\n shadow that explicitly survives into print is the consumer's call. */\n *,\n *::before,\n *::after {\n box-shadow: none;\n text-shadow: none;\n }\n\n /* Expand link URLs after the link text so paper-readers can follow\n them. Anchors and javascript: pseudo-links are excluded. */\n a[href]::after {\n content: \" (\" attr(href) \")\";\n font-size: 90%;\n word-break: break-all;\n }\n\n a[href^=\"#\"]::after,\n a[href^=\"javascript:\" i]::after {\n content: \"\";\n }\n\n abbr[title]::after {\n content: \" (\" attr(title) \")\";\n }\n\n img, svg, video, canvas, figure, table, pre, blockquote {\n break-inside: avoid;\n }\n\n thead {\n display: table-header-group;\n }\n\n tr {\n break-inside: avoid;\n }\n\n h1, h2, h3, h4, h5, h6 {\n break-after: avoid;\n orphans: 3;\n widows: 3;\n }\n\n p {\n orphans: 3;\n widows: 3;\n }\n\n pre, blockquote {\n border: var(--sf-border-width-1) solid var(--sf-color-border--strong);\n padding: var(--sf-space-s);\n }\n\n /* Hide non-essential interactive and decorative elements.\n `!important` is justified: these elements are content negative-space\n in print, and consumer-authored display rules must not bring them\n back accidentally. */\n nav, aside, button, input, select, textarea,\n audio, video, dialog, [popover], .no-print {\n display: none !important;\n }\n\n /* Reveal print-only content (overrides the screen default above). */\n .print-only {\n display: revert !important;\n }\n\n /* Preserve
content regardless of open state. Only\n `summary` keeps `!important` — the disclosure chevron carries no\n print value and the consumer should not be able to bring it back\n inadvertently. The other two rules win against the UA stylesheet\n on layered-author > UA precedence alone. */\n details {\n display: block;\n }\n details > summary {\n display: none !important;\n }\n details:not([open]) > :not(summary) {\n display: block;\n }\n\n /* ── Opt-in colour treatment ─────────────────────────────────── */\n\n /* Force colour-faithful rendering. Use on regions where a tinted\n background or coloured text encodes meaning (status pills,\n severity callouts, syntax highlighting, charts). */\n .print-color-exact,\n .print-color-exact * {\n print-color-adjust: exact !important;\n -webkit-print-color-adjust: exact !important;\n }\n\n /* Opt back into the legacy blanket reset: transparent backgrounds\n and system text colour everywhere inside the marked subtree.\n Use on regions where ink-saving is the contract (corporate\n boilerplate forms, append-to-existing-document templates). */\n .print-no-color,\n .print-no-color *,\n .print-no-color *::before,\n .print-no-color *::after {\n background: transparent !important;\n color: CanvasText !important;\n }\n\n}\n"],"names":[]} \ No newline at end of file diff --git a/dist/slashed.essential.min.css b/dist/slashed.essential.min.css index ed99a234..4aafca5b 100644 --- a/dist/slashed.essential.min.css +++ b/dist/slashed.essential.min.css @@ -1,2 +1,2 @@ -@layer slashed.tokens{@property --sf-color-primary-light{syntax:"";inherits:true;initial-value:oklch(45% .2 264)}@property --sf-color-secondary-light{syntax:"";inherits:true;initial-value:oklch(25% .03 260)}@property --sf-color-tertiary-light{syntax:"";inherits:true;initial-value:oklch(48% .14 310)}@property --sf-color-action-light{syntax:"";inherits:true;initial-value:oklch(60% .16 210)}@property --sf-color-neutral-light{syntax:"";inherits:true;initial-value:oklch(45% .02 260)}@property --sf-color-base-light{syntax:"";inherits:true;initial-value:oklch(98% .005 260)}@property --sf-color-success-light{syntax:"";inherits:true;initial-value:oklch(48% .17 150)}@property --sf-color-warning-light{syntax:"";inherits:true;initial-value:oklch(75% .17 80)}@property --sf-color-error-light{syntax:"";inherits:true;initial-value:oklch(62% .2 35)}@property --sf-color-info-light{syntax:"";inherits:true;initial-value:oklch(48% .15 240)}@property --sf-color-danger-light{syntax:"";inherits:true;initial-value:oklch(48% .24 12)}@property --sf-is-dark{syntax:"";inherits:true;initial-value:0}@property --sf-is-active{syntax:"";inherits:true;initial-value:0}@property --sf-is-current{syntax:"";inherits:true;initial-value:0}@property --sf-is-pressed{syntax:"";inherits:true;initial-value:0}@property --sf-is-open{syntax:"";inherits:true;initial-value:0}:root{--sf-color-scheme:light dark;--sf-lumlocker:.65;--sf-color-primary:light-dark(var(--sf-color-primary-light),var(--sf-color-primary-dark,oklch(from var(--sf-color-primary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-secondary:light-dark(var(--sf-color-secondary-light),var(--sf-color-secondary-dark,oklch(from var(--sf-color-secondary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-tertiary:light-dark(var(--sf-color-tertiary-light),var(--sf-color-tertiary-dark,oklch(from var(--sf-color-tertiary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-action:light-dark(var(--sf-color-action-light),var(--sf-color-action-dark,oklch(from var(--sf-color-action-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-neutral:light-dark(var(--sf-color-neutral-light),var(--sf-color-neutral-dark,oklch(from var(--sf-color-neutral-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-base:light-dark(var(--sf-color-base-light),var(--sf-color-base-dark,oklch(from var(--sf-color-base-light) clamp(.16, calc(1.18 - l), .24) calc(c * .5) h)));--sf-color-success:light-dark(var(--sf-color-success-light),var(--sf-color-success-dark,oklch(from var(--sf-color-success-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-warning:light-dark(var(--sf-color-warning-light),var(--sf-color-warning-dark,oklch(from var(--sf-color-warning-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-error:light-dark(var(--sf-color-error-light),var(--sf-color-error-dark,oklch(from var(--sf-color-error-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-info:light-dark(var(--sf-color-info-light),var(--sf-color-info-dark,oklch(from var(--sf-color-info-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-danger:light-dark(var(--sf-color-danger-light),var(--sf-color-danger-dark,oklch(from var(--sf-color-danger-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-bg:oklch(from var(--sf-color-base) calc(l + .02) c h);--sf-color-surface:var(--sf-color-base);--sf-color-well:oklch(from var(--sf-color-base) calc(l - .02) c h);--sf-color-raised:oklch(from var(--sf-color-base) calc(l + .04) c h);--sf-color-overlay:oklch(from var(--sf-color-base) l c h / .9);--sf-color-inverse:oklch(from var(--sf-color-base) calc(1 - l) c h);--sf-color-text:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.05, calc(l - .4 - var(--sf-contrast-bias)), .35) c h),oklch(from var(--sf-color-neutral) clamp(.7, calc(l + .25 + var(--sf-contrast-bias)), 1) c h));--sf-color-text--secondary:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.15, calc(l - .25 - var(--sf-contrast-bias)), .45) c h),oklch(from var(--sf-color-neutral) clamp(.55, calc(l + .1 + var(--sf-contrast-bias)), .9) c h));--sf-color-text--muted:var(--sf-color-neutral);--sf-color-text--placeholder:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.45, calc(l + .15), .75) c h),oklch(from var(--sf-color-neutral) clamp(.35, calc(l - .1), .65) c h));--sf-color-text--disabled:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.55, calc(l + .25), .82) c h),oklch(from var(--sf-color-neutral) clamp(.25, calc(l - .2), .55) c h));--sf-color-text--inverse:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.85, calc(l + .4), .98) c h),oklch(from var(--sf-color-neutral) clamp(.05, calc(l - .4), .35) c h));--sf-color-heading:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.05, calc(l - .4 - var(--sf-contrast-bias)), .35) c h),oklch(from var(--sf-color-neutral) clamp(.7, calc(l + .25 + var(--sf-contrast-bias)), 1) c h));--sf-color-code-text:oklch(from var(--sf-color-code-bg) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-primary:oklch(from var(--sf-color-primary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-secondary:oklch(from var(--sf-color-secondary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-tertiary:oklch(from var(--sf-color-tertiary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-action:oklch(from var(--sf-color-action) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-neutral:oklch(from var(--sf-color-neutral) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-base:var(--sf-color-text);--sf-color-text--on-inverse:var(--sf-color-text--inverse);--sf-color-text--on-success:oklch(from var(--sf-color-success) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-warning:oklch(from var(--sf-color-warning) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-error:oklch(from var(--sf-color-error) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-info:oklch(from var(--sf-color-info) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-danger:oklch(from var(--sf-color-danger) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-border:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.7, calc(l + .35), .95) .005 h),oklch(from var(--sf-color-neutral) clamp(.25, calc(l - .3), .55) .005 h));--sf-color-border--subtle:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.75, calc(l + .4), .97) .005 h),oklch(from var(--sf-color-neutral) clamp(.2, calc(l - .38), .45) .005 h));--sf-color-border--strong:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.55, calc(l + .1), .85) .02 h),oklch(from var(--sf-color-neutral) clamp(.38, calc(l - .1), .65) .02 h));--sf-color-border--focus:var(--sf-color-action);--sf-color-border--disabled:oklch(from var(--sf-color-border--subtle) l 0 h / .5);--sf-color-border--translucent:oklch(from var(--sf-color-neutral) l c h / .15);--sf-color-link:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .07, .48), 1) c h),oklch(from var(--sf-color-action) clamp(.68, l, 1) c h));--sf-color-link--hover:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .15, .4), 1) c h),oklch(from var(--sf-color-action) clamp(0, max(l + .1, .68), 1) c h));--sf-color-link--active:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .21, .34), 1) c h),oklch(from var(--sf-color-action) clamp(0, max(l + .15, .74), 1) c h));--sf-color-link--visited:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .07, .48), 1) c calc(h + 60)),oklch(from var(--sf-color-action) clamp(.68, l, 1) c calc(h + 60)));--sf-color-link--underline:oklch(from var(--sf-color-action) l c h / .3);--sf-color-link--disabled:var(--sf-color-text--disabled);--sf-color-bg--hover:oklch(from var(--sf-color-neutral) l c h / .08);--sf-color-bg--active:oklch(from var(--sf-color-neutral) l c h / .12);--sf-color-bg--selected:oklch(from var(--sf-color-action) l c h / .1);--sf-color-bg--focus:oklch(from var(--sf-color-action) l c h / .06);--sf-color-bg--disabled:var(--sf-color-well);--sf-color-code-bg:var(--sf-color-well);--sf-color-selection-bg:oklch(from var(--sf-color-action) l c h / .22);--sf-color-selection-text:inherit;--sf-color-mark-bg:oklch(from var(--sf-color-warning) l c h / .25);--sf-color-mark-text:inherit;--sf-color-dim:oklch(0% 0 0/.5);--sf-scrollbar-thumb:var(--sf-color-neutral);--sf-scrollbar-track:transparent;--sf-color-success-subtle:oklch(from var(--sf-color-success) l c h / .12);--sf-color-success-strong:light-dark(oklch(from var(--sf-color-success-light) calc(l - .15) c h),oklch(from var(--sf-color-success) clamp(.7, calc(l + .15), 1) c h));--sf-color-success-muted:oklch(from var(--sf-color-success) l c h / .3);--sf-color-warning-subtle:oklch(from var(--sf-color-warning) l c h / .12);--sf-color-warning-strong:light-dark(oklch(from var(--sf-color-warning-light) calc(l - .25) c h),oklch(from var(--sf-color-warning) clamp(.7, calc(l + .05), 1) c h));--sf-color-warning-muted:oklch(from var(--sf-color-warning) l c h / .3);--sf-color-error-subtle:oklch(from var(--sf-color-error) l c h / .1);--sf-color-error-strong:light-dark(oklch(from var(--sf-color-error-light) calc(l - .1) c h),oklch(from var(--sf-color-error) clamp(.7, calc(l + .15), 1) c h));--sf-color-error-muted:oklch(from var(--sf-color-error) l c h / .3);--sf-color-info-subtle:oklch(from var(--sf-color-info) l c h / .1);--sf-color-info-strong:light-dark(oklch(from var(--sf-color-info-light) calc(l - .1) c h),oklch(from var(--sf-color-info) clamp(.7, calc(l + .15), 1) c h));--sf-color-info-muted:oklch(from var(--sf-color-info) l c h / .3);--sf-color-danger-subtle:oklch(from var(--sf-color-danger) l c h / .1);--sf-color-danger-strong:light-dark(oklch(from var(--sf-color-danger-light) calc(l - .1) c h),oklch(from var(--sf-color-danger) clamp(.7, calc(l + .15), 1) c h));--sf-color-danger-muted:oklch(from var(--sf-color-danger) l c h / .3);--sf-gradient-primary:linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) calc(l - .08) c h));--sf-gradient-secondary:linear-gradient(135deg, var(--sf-color-secondary), oklch(from var(--sf-color-secondary) calc(l - .08) c h));--sf-gradient-tertiary:linear-gradient(135deg, var(--sf-color-tertiary), oklch(from var(--sf-color-tertiary) calc(l - .08) c h));--sf-gradient-brand:linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) l c calc(h + 30)));--sf-gradient-surface:linear-gradient(180deg, var(--sf-color-surface), var(--sf-color-bg));--sf-gradient-fade--r:linear-gradient(to right, transparent, var(--sf-color-bg));--sf-gradient-fade--l:linear-gradient(to left, transparent, var(--sf-color-bg));--sf-gradient-fade--t:linear-gradient(to top, transparent, var(--sf-color-bg));--sf-gradient-fade--b:linear-gradient(to bottom, transparent, var(--sf-color-bg));--sf-shadow-strength:calc(.08 + var(--sf-is-dark) * .17);--sf-shadow-glow-color:var(--sf-color-primary);--sf-shadow-glow:0 0 15px 2px oklch(from var(--sf-shadow-glow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-opacity-disabled:.45;--sf-state-pending-opacity:.7;--sf-focus-ring-color:var(--sf-color-action);--sf-focus-ring-shadow:0 0 0 var(--sf-focus-ring-offset) var(--sf-color-bg), 0 0 0 calc(var(--sf-focus-ring-offset) + var(--sf-focus-ring-width)) var(--sf-focus-ring-color);--sf-caret-color:var(--sf-color-action);--sf-space-scale:1;--sf-text-scale:1;--sf-text-display-scale:1;--sf-radius-scale:1;--sf-motion-scale:1;--sf-font-body:system-ui, -apple-system, sans-serif;--sf-font-heading:var(--sf-font-body);--sf-font-display:var(--sf-font-heading);--sf-font-mono:ui-monospace, monospace;--sf-font-humanist:"Seravek", "Gill Sans Nova", "Ubuntu", "Calibri", "DejaVu Sans", source-sans-pro, sans-serif;--sf-font-geometric:"Avenir", "Montserrat", "Corbel", "URW Gothic", source-sans-pro, sans-serif;--sf-font-slab:"Rockwell", "Rockwell Nova", "Roboto Slab", "DejaVu Serif", "Sitka Small", serif;--sf-font-features:normal;--sf-font-variation:normal;--sf-optical-sizing:auto;--sf-font-weight-thin:100;--sf-font-weight-extralight:200;--sf-font-weight-light:300;--sf-font-weight-normal:400;--sf-font-weight-medium:500;--sf-font-weight-semibold:600;--sf-font-weight-bold:700;--sf-font-weight-extrabold:800;--sf-font-weight-black:900;--sf-font-weight-body:var(--sf-font-weight-normal);--sf-font-weight-heading:var(--sf-font-weight-semibold);--sf-font-weight-display:var(--sf-font-weight-bold);--sf-current-font-weight:var(--sf-font-weight-bold);--sf-text-2xs:clamp(.51rem, calc(.000296296 * (100vw - 22.5rem) + .51rem), .53rem);--sf-text-xs:clamp(.64rem, calc(.000888889 * (100vw - 22.5rem) + .64rem), .7rem);--sf-text-s:clamp(.8rem, calc(.00207407 * (100vw - 22.5rem) + .8rem), .94rem);--sf-text-m:clamp(1rem, calc(.0037037 * (100vw - 22.5rem) + 1rem), 1.25rem);--sf-text-l:clamp(1.25rem, calc(.00622222 * (100vw - 22.5rem) + 1.25rem), 1.67rem);--sf-text-xl:clamp(1.56rem, calc(.00977778 * (100vw - 22.5rem) + 1.56rem), 2.22rem);--sf-text-2xl:clamp(1.95rem, calc(.014963 * (100vw - 22.5rem) + 1.95rem), 2.96rem);--sf-text-3xl:clamp(2.44rem, calc(.0223704 * (100vw - 22.5rem) + 2.44rem), 3.95rem);--sf-text-4xl:clamp(3.05rem, calc(.0327407 * (100vw - 22.5rem) + 3.05rem), 5.26rem);--sf-text-display-s:clamp(2.4rem, calc(.00888889 * (100vw - 22.5rem) + 2.4rem), 3rem);--sf-text-display-m:clamp(3rem, calc(.0148148 * (100vw - 22.5rem) + 3rem), 4rem);--sf-text-display-l:clamp(3.75rem, calc(.0234074 * (100vw - 22.5rem) + 3.75rem), 5.33rem);--sf-leading-tight:1.1;--sf-leading-snug:1.3;--sf-leading-normal:1.5;--sf-leading-relaxed:1.625;--sf-tracking-tight:-.025em;--sf-tracking-normal:0;--sf-tracking-wide:.025em;--sf-tracking-wider:.05em;--sf-tracking-widest:.1em;--sf-icon-xs:.875em;--sf-icon-s:1em;--sf-icon-m:1.5em;--sf-icon-l:2em;--sf-icon-xl:3em;--sf-space-none:0;--sf-space-px:1px;--sf-space-gutter:var(--sf-space-l);--sf-space-2xs:calc(clamp(.51rem, calc(.00488889 * (100vw - 22.5rem) + .51rem), .84rem) * var(--sf-space-scale));--sf-space-xs:calc(clamp(.64rem, calc(.00725926 * (100vw - 22.5rem) + .64rem), 1.13rem) * var(--sf-space-scale));--sf-space-s:calc(clamp(.8rem, calc(.0103704 * (100vw - 22.5rem) + .8rem), 1.5rem) * var(--sf-space-scale));--sf-space-m:calc(clamp(1rem, calc(.0148148 * (100vw - 22.5rem) + 1rem), 2rem) * var(--sf-space-scale));--sf-space-l:calc(clamp(1.25rem, calc(.021037 * (100vw - 22.5rem) + 1.25rem), 2.67rem) * var(--sf-space-scale));--sf-space-xl:calc(clamp(1.56rem, calc(.0294815 * (100vw - 22.5rem) + 1.56rem), 3.55rem) * var(--sf-space-scale));--sf-space-2xl:calc(clamp(1.95rem, calc(.0413333 * (100vw - 22.5rem) + 1.95rem), 4.74rem) * var(--sf-space-scale));--sf-space-3xl:calc(clamp(2.44rem, calc(.0573333 * (100vw - 22.5rem) + 2.44rem), 6.31rem) * var(--sf-space-scale));--sf-space-4xl:calc(clamp(3.05rem, calc(.0795556 * (100vw - 22.5rem) + 3.05rem), 8.42rem) * var(--sf-space-scale));--sf-size-xs:1.5rem;--sf-size-s:2rem;--sf-size-m:2.5rem;--sf-size-l:2.75rem;--sf-size-xl:3.5rem;--sf-container-narrow:38rem;--sf-container-prose:65ch;--sf-container-default:75rem;--sf-container-wide:90rem;--sf-container-full:100%;--sf-ratio-square:1;--sf-ratio-video:16 / 9;--sf-ratio-cinema:21 / 9;--sf-ratio-4-3:4 / 3;--sf-ratio-3-2:3 / 2;--sf-ratio-portrait:3 / 4;--sf-ratio-golden:1.618 / 1;--sf-border-width-hairline:.5px;--sf-border-width-1:1px;--sf-border-width-2:2px;--sf-border-width-3:3px;--sf-border-width-4:4px;--sf-border-style:solid;--sf-border-style-strong:solid;--sf-border-style-soft:dashed;--sf-border-style-dotted:dotted;--sf-divider-width:var(--sf-border-width-1);--sf-divider-style:solid;--sf-divider-color:var(--sf-color-border);--sf-radius-none:0;--sf-radius-xs:calc(2px * var(--sf-radius-scale));--sf-radius-s:calc(4px * var(--sf-radius-scale));--sf-radius-m:calc(8px * var(--sf-radius-scale));--sf-radius-l:calc(12px * var(--sf-radius-scale));--sf-radius-xl:calc(16px * var(--sf-radius-scale));--sf-radius-2xl:calc(24px * var(--sf-radius-scale));--sf-radius-3xl:calc(32px * var(--sf-radius-scale));--sf-radius-4xl:calc(48px * var(--sf-radius-scale));--sf-radius-full:9999px;--sf-radius-pill:var(--sf-radius-full);--sf-radius-outer:calc(var(--sf-radius-m) + var(--sf-component-pad));--sf-shadow-color:oklch(from var(--sf-color-neutral) .15 c h);--sf-shadow-none:none;--sf-shadow-xs:0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7));--sf-shadow-s:0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 2px 6px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, var(--sf-shadow-strength), .7));--sf-shadow-m:0 1px 3px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-shadow-l:0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 8px 24px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), .7)), 0 16px 48px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-shadow-xl:0 2px 8px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 12px 36px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3.5), .7)), 0 24px 72px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7));--sf-shadow-2xl:0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .6), .7)), 0 20px 60px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 4), .7)), 0 40px 100px -8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 5), .7));--sf-shadow-inner:inset 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-text-shadow-none:none;--sf-text-shadow-s:0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), .7));--sf-text-shadow-m:0 2px 4px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-text-shadow-l:0 4px 8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7));--sf-drop-shadow-s:drop-shadow(0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), .7)));--sf-drop-shadow-m:drop-shadow(0 4px 6px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7)));--sf-drop-shadow-l:drop-shadow(0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7)));--sf-blur-xs:4px;--sf-blur-s:8px;--sf-blur-m:16px;--sf-blur-l:32px;--sf-blur-xl:48px;--sf-perspective-near:500px;--sf-perspective-normal:1000px;--sf-perspective-far:2000px;--sf-opacity-0:0;--sf-opacity-10:.1;--sf-opacity-25:.25;--sf-opacity-50:.5;--sf-opacity-75:.75;--sf-opacity-100:1;--sf-duration-none:0s;--sf-duration-instant:calc(.1s * var(--sf-motion-scale));--sf-duration-fast:calc(.15s * var(--sf-motion-scale));--sf-duration-normal:calc(.25s * var(--sf-motion-scale));--sf-duration-slow:calc(.4s * var(--sf-motion-scale));--sf-duration-slower:calc(.6s * var(--sf-motion-scale));--sf-ease-linear:linear;--sf-ease-out:cubic-bezier(.25, 0, .15, 1);--sf-ease-in:cubic-bezier(.5, 0, .75, .25);--sf-ease-in-out:cubic-bezier(.4, 0, .2, 1);--sf-ease-spring:linear(0, .5, 1.1, .95, 1.02, 1);--sf-ease-elastic:linear(0, .3, 1.2, .9, 1.05, 1);--sf-ease-bounce:linear(0, .35 18%, 1 32%, .86 42%, 1.02 56%, .98 72%, 1);--sf-ease-overshoot:linear(0, .6 30%, 1.08 55%, .98 75%, 1);--sf-animation-fade-in:sf-fade-in var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-fade-out:sf-fade-out var(--sf-duration-normal) var(--sf-ease-in) both;--sf-animation-slide-in-up:sf-slide-in-up var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-down:sf-slide-in-down var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-left:sf-slide-in-left var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-right:sf-slide-in-right var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-scale-up:sf-scale-up var(--sf-duration-normal) var(--sf-ease-overshoot) both;--sf-animation-scale-down:sf-scale-down var(--sf-duration-normal) var(--sf-ease-in) both;--sf-animation-color-pulse:sf-color-pulse var(--sf-duration-slow) var(--sf-ease-in-out) infinite;--sf-animation-ping:sf-ping var(--sf-duration-slow) var(--sf-ease-out) infinite;--sf-animation-blink:sf-blink calc(1s * var(--sf-motion-scale)) steps(1, end) infinite;--sf-animation-float:sf-float calc(3s * var(--sf-motion-scale)) var(--sf-ease-in-out) infinite;--sf-animation-spin:sf-spin var(--sf-duration-slower) linear infinite;--sf-animation-shimmer:sf-shimmer calc(1.5s * var(--sf-motion-scale)) ease-in-out infinite;--sf-animation-delay-1:calc(75ms * var(--sf-motion-scale));--sf-animation-delay-2:calc(.15s * var(--sf-motion-scale));--sf-animation-delay-3:calc(.225s * var(--sf-motion-scale));--sf-animation-delay-4:calc(.3s * var(--sf-motion-scale));--sf-animation-delay-5:calc(.375s * var(--sf-motion-scale));--sf-scroll-timeline-range-start:0%;--sf-scroll-timeline-range-end:100%;--sf-mask-scrim-start:var(--sf-space-l);--sf-mask-scrim-end:var(--sf-space-l);--sf-z-below:-1;--sf-z-base:0;--sf-z-raised:1;--sf-z-low:10;--sf-z-mid:100;--sf-z-high:500;--sf-z-top:900;--sf-z-max:9999;--sf-header-height:8rem;--sf-sticky-offset:var(--sf-header-height);--sf-focus-ring-width:2px;--sf-focus-ring-offset:2px;--sf-focus-ring-style:solid;--sf-touch-target:var(--sf-size-l);--sf-contrast-bias:0;--sf-contrast-threshold:.6;--sf-safe-top:env(safe-area-inset-top,0px);--sf-safe-bottom:env(safe-area-inset-bottom,0px);--sf-safe-left:env(safe-area-inset-left,0px);--sf-safe-right:env(safe-area-inset-right,0px);--sf-print-page-margin:2cm;--sf-print-page-size:a4;--sf-print-base-size:11pt;--sf-stroke-thin:1px;--sf-stroke-regular:1.5px;--sf-stroke-bold:2px;--sf-stroke-heavy:3px;--sf-body-font-size:var(--sf-text-m);--sf-body-line-height:var(--sf-leading-normal);--sf-body-font-weight:var(--sf-font-weight-body);--sf-body-font-family:var(--sf-font-body);--sf-body-color:var(--sf-color-text);--sf-body-text-wrap:pretty;--sf-body-strong-weight:var(--sf-font-weight-bold);--sf-body-em-style:italic;--sf-code-font-size:.875em;--sf-heading-font-family:var(--sf-font-heading);--sf-heading-color:var(--sf-color-heading);--sf-heading-text-wrap:balance;--sf-h1-size:var(--sf-text-4xl);--sf-h1-line-height:var(--sf-leading-tight);--sf-h1-font-weight:var(--sf-font-weight-heading);--sf-h1-letter-spacing:var(--sf-tracking-tight);--sf-h2-size:var(--sf-text-3xl);--sf-h2-line-height:var(--sf-leading-tight);--sf-h2-font-weight:var(--sf-font-weight-heading);--sf-h2-letter-spacing:var(--sf-tracking-tight);--sf-h3-size:var(--sf-text-2xl);--sf-h3-line-height:var(--sf-leading-snug);--sf-h3-font-weight:var(--sf-font-weight-heading);--sf-h3-letter-spacing:var(--sf-tracking-normal);--sf-h4-size:var(--sf-text-xl);--sf-h4-line-height:var(--sf-leading-snug);--sf-h4-font-weight:var(--sf-font-weight-heading);--sf-h4-letter-spacing:var(--sf-tracking-normal);--sf-h5-size:var(--sf-text-l);--sf-h5-line-height:var(--sf-leading-normal);--sf-h5-font-weight:var(--sf-font-weight-heading);--sf-h5-letter-spacing:var(--sf-tracking-normal);--sf-h6-size:var(--sf-text-m);--sf-h6-line-height:var(--sf-leading-normal);--sf-h6-font-weight:var(--sf-font-weight-heading);--sf-h6-letter-spacing:var(--sf-tracking-wide);--sf-gap:var(--sf-space-m);--sf-content-gap:var(--sf-space-s);--sf-component-pad:var(--sf-space-m);--sf-field-block:var(--sf-space-l);--sf-field-required-marker:" *";--sf-link-external-marker:" ↗";--sf-section-pad:var(--sf-section-pad--m);--sf-section-pad--xs:var(--sf-space-xl);--sf-section-pad--s:var(--sf-space-2xl);--sf-section-pad--m:var(--sf-space-3xl);--sf-section-pad--l:var(--sf-space-4xl);--sf-section-pad--xl:calc(var(--sf-space-4xl) * 1.5);--sf-section-pad--2xl:calc(var(--sf-space-4xl) * 2);--sf-transition-all:all var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-colors:color var(--sf-duration-normal) var(--sf-ease-out), background-color var(--sf-duration-normal) var(--sf-ease-out), border-color var(--sf-duration-normal) var(--sf-ease-out), text-decoration-color var(--sf-duration-normal) var(--sf-ease-out), fill var(--sf-duration-normal) var(--sf-ease-out), stroke var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-transform:transform var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-opacity:opacity var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-shadow:box-shadow var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-fast:all var(--sf-duration-fast) var(--sf-ease-out);--sf-transition-slow:all var(--sf-duration-slow) var(--sf-ease-in-out);--sf-transition-enter:all var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-exit:all var(--sf-duration-fast) var(--sf-ease-in);--sf-space-gap:var(--sf-gap);--sf-space-content:var(--sf-content-gap);--sf-stack-gap:var(--sf-space-content);--sf-box-padding:var(--sf-space-m);--sf-box-border-width:0;--sf-box-border-color:var(--sf-color-border);--sf-center-max:var(--sf-container-default);--sf-center-gutter:var(--sf-space-gutter);--sf-cluster-gap:var(--sf-space-gap);--sf-cluster-align:center;--sf-cluster-justify:flex-start;--sf-sidebar-gap:var(--sf-space-gap);--sf-sidebar-min-width:50%;--sf-sidebar-width:18rem;--sf-switcher-threshold:30rem;--sf-switcher-gap:var(--sf-space-gap);--sf-grid-min:16rem;--sf-grid-gap:var(--sf-space-gap);--sf-grid-min-xs:10rem;--sf-grid-min-s:13rem;--sf-grid-min-m:16rem;--sf-grid-min-l:20rem;--sf-grid-min-xl:24rem;--sf-grid-min-2xl:28rem;--sf-cover-min-height:100dvh;--sf-cover-padding:var(--sf-section-pad);--sf-frame-ratio:16 / 9;--sf-icon-box-pad:.5em;--sf-icon-box-radius:var(--sf-radius-s);--sf-icon-box-bg:var(--sf-color-well);--sf-icon-box-border:var(--sf-border-width-1) solid var(--sf-color-border);--sf-reel-item-width:max-content;--sf-reel-gap:var(--sf-space-gap);--sf-reel-height:auto;--sf-imposter-margin:var(--sf-space-m);--sf-bento-cols-default:3;--sf-bento-row-default:10rem;--sf-bento-row-compact:6rem;--sf-bento-row-tall:16rem;--sf-bento-gap:var(--sf-space-gap);--sf-breakout-width:var(--sf-container-wide);--sf-content-width:var(--sf-container-default);--sf-prose-paragraph:var(--sf-space-content);--sf-alternate-gap:var(--sf-space-content);--sf-alternate-inner-gap:var(--sf-space-gap);--sf-flow-space:var(--sf-space-content);--sf-line-clamp:3;--sf-truncate-suffix:"…";--sf-aspect:16 / 9;--sf-scroll-shadow-size:2rem}}@layer slashed.reset{*,:before,:after{box-sizing:border-box;margin:0;padding:0}html{text-size-adjust:100%;scroll-padding-top:var(--sf-header-height,3.5rem);scrollbar-gutter:stable;color-scheme:var(--sf-color-scheme,light dark);hanging-punctuation:first last}@supports (interpolate-size:allow-keywords){html{interpolate-size:allow-keywords}}body{min-height:100dvh;min-inline-size:320px}menu,ul,ol{list-style:none}img,picture,video,canvas,svg{max-width:100%;height:auto;display:block}iframe,embed,object{max-width:100%;height:unset;border:none;display:block}button,input,select,textarea{font:inherit;color:inherit;letter-spacing:inherit;color-scheme:inherit;line-height:inherit}button{text-transform:none}::file-selector-button{font:inherit}search{display:block}fieldset{border:none;min-width:0;padding:0}legend{padding:0}a,button,input,select,textarea,summary{touch-action:manipulation}summary{cursor:pointer;list-style:none}textarea{resize:vertical}button{cursor:pointer}table{border-collapse:collapse;border-spacing:0}[hidden]{display:none!important}dialog,[popover]{color:inherit;background:0 0;border:none;padding:0}[inert]{cursor:default}}@layer slashed.base{body{font-family:var(--sf-body-font-family,var(--sf-font-body));font-size:var(--sf-body-font-size,var(--sf-text-m));line-height:var(--sf-body-line-height,var(--sf-leading-normal));font-weight:var(--sf-body-font-weight,var(--sf-font-weight-body));color:var(--sf-body-color,var(--sf-color-text));background-color:var(--sf-color-bg,#fff);font-synthesis:none}h1,h2,h3,h4,h5,h6{font-family:var(--sf-heading-font-family,var(--sf-font-heading));line-height:var(--sf-leading-tight);color:var(--sf-heading-color,var(--sf-color-heading));text-wrap:var(--sf-heading-text-wrap);overflow-wrap:break-word;text-rendering:optimizelegibility;scroll-margin-top:calc(var(--sf-header-height,3.5rem) + var(--sf-space-m))}h1{font-size:var(--sf-h1-size);line-height:var(--sf-h1-line-height);font-weight:var(--sf-h1-font-weight);letter-spacing:var(--sf-h1-letter-spacing)}h2{font-size:var(--sf-h2-size);line-height:var(--sf-h2-line-height);font-weight:var(--sf-h2-font-weight);letter-spacing:var(--sf-h2-letter-spacing)}h3{font-size:var(--sf-h3-size);line-height:var(--sf-h3-line-height);font-weight:var(--sf-h3-font-weight);letter-spacing:var(--sf-h3-letter-spacing)}h4{font-size:var(--sf-h4-size);line-height:var(--sf-h4-line-height);font-weight:var(--sf-h4-font-weight);letter-spacing:var(--sf-h4-letter-spacing)}h5{font-size:var(--sf-h5-size);line-height:var(--sf-h5-line-height);font-weight:var(--sf-h5-font-weight);letter-spacing:var(--sf-h5-letter-spacing)}h6{font-size:var(--sf-h6-size);line-height:var(--sf-h6-line-height);font-weight:var(--sf-h6-font-weight);letter-spacing:var(--sf-h6-letter-spacing)}p{text-wrap:var(--sf-body-text-wrap);overflow-wrap:break-word;orphans:2;widows:2}b,strong{font-weight:var(--sf-body-strong-weight)}em{font-style:var(--sf-body-em-style)}small{font-size:var(--sf-text-s,80%)}mark{background-color:var(--sf-color-mark-bg,yellow);color:var(--sf-color-mark-text,black);border-radius:var(--sf-radius-xs);padding-inline:.2em}abbr[title]{cursor:help;text-decoration:underline dotted}kbd,samp{font-family:var(--sf-font-mono);font-size:inherit}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sup{top:-.5em}sub{bottom:-.25em}a:link{color:var(--sf-color-link);text-decoration-color:var(--sf-color-link--underline);text-underline-offset:.15em;text-underline-position:from-font;transition:color var(--sf-duration-fast) var(--sf-ease-out), text-decoration-color var(--sf-duration-fast) var(--sf-ease-out)}a:hover{color:var(--sf-color-link--hover);text-decoration-color:currentColor}a:visited{color:var(--sf-color-link--visited)}a:active{color:var(--sf-color-link--active)}a:not([class]){text-decoration-skip-ink:auto}a,code{overflow-wrap:break-word}code{font-family:var(--sf-font-mono);font-size:var(--sf-code-font-size);border-radius:var(--sf-radius-xs);background-color:var(--sf-color-code-bg);color:var(--sf-color-code-text,inherit);padding:.125em .3em}pre{font-family:var(--sf-font-mono);font-size:var(--sf-text-s);padding:var(--sf-space-m);border-radius:var(--sf-radius-m);background:var(--sf-color-code-block-bg,var(--sf-color-code-bg));color:var(--sf-color-code-block-text,inherit);tab-size:2;hyphens:none;overflow-x:auto}pre code{color:inherit;font-size:inherit;background:0 0;border:0;border-radius:0;padding:0}svg{fill:currentColor}hr{border:none;border-block-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color)}:target{scroll-margin-top:calc(var(--sf-header-height,3.5rem) + var(--sf-space-m))}::selection{background:var(--sf-color-selection-bg);color:var(--sf-color-selection-text);text-shadow:none}th,caption{text-align:start}address,cite,dfn,var{font-style:normal}input,textarea{caret-color:var(--sf-caret-color)}input,progress{accent-color:var(--sf-color-action)}::backdrop{background-color:var(--sf-color-dim)}optgroup{font:inherit}::file-selector-button{font:inherit}::placeholder{opacity:1;color:var(--sf-color-text--placeholder,currentcolor)}blockquote{border-inline-start:var(--sf-border-width-3) solid var(--sf-color-border);padding-inline-start:var(--sf-space-m);font-style:italic}dt{font-weight:var(--sf-font-weight-heading)}dd{margin-inline-start:var(--sf-space-m)}table{text-align:start;width:100%}th{font-weight:var(--sf-font-weight-heading)}td,th{padding:var(--sf-space-2xs) var(--sf-space-xs);border-block-end:var(--sf-border-width-hairline) solid var(--sf-color-border--subtle)}}@layer slashed.forms;@layer slashed.layout{.sf-section{padding-block:var(--sf-section-pad)}.sf-section--xs{padding-block:var(--sf-section-pad--xs)}.sf-section--s{padding-block:var(--sf-section-pad--s)}.sf-section--m{padding-block:var(--sf-section-pad--m)}.sf-section--l{padding-block:var(--sf-section-pad--l)}.sf-section--xl{padding-block:var(--sf-section-pad--xl)}.sf-section--2xl{padding-block:var(--sf-section-pad--2xl)}.sf-section-group>.sf-section+.sf-section{padding-block-start:0}.sf-section--collapse:has(+.sf-section--collapse){padding-block-end:calc(var(--sf-section-pad) / 2)}.sf-section--collapse+.sf-section--collapse{padding-block-start:calc(var(--sf-section-pad) / 2)}.sf-divider{border:none;border-block-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);margin-block:var(--sf-space-m)}.sf-divider--vertical{border-block-start:none;border-inline-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);margin-block:0;margin-inline:var(--sf-space-m);align-self:stretch}.sf-container{width:100%;max-width:var(--sf-container-default);padding-inline:var(--sf-space-gutter);margin-inline:auto;container:sf-layout/inline-size}.sf-container--narrow{max-width:var(--sf-container-narrow)}.sf-container--prose{max-width:var(--sf-container-prose)}.sf-container--wide{max-width:var(--sf-container-wide)}.sf-container--full{max-width:var(--sf-container-full)}.sf-stack{gap:var(--sf-stack-gap);flex-direction:column;display:flex}.sf-stack--xs{gap:var(--sf-space-xs)}.sf-stack--s{gap:var(--sf-space-s)}.sf-stack--m{gap:var(--sf-space-m)}.sf-stack--l{gap:var(--sf-space-l)}.sf-stack--xl{gap:var(--sf-space-xl)}.sf-stack--2xl{gap:var(--sf-space-2xl)}.sf-stack--center{align-items:center}.sf-stack--end{align-items:flex-end}.sf-stack--stretch{align-items:stretch}.sf-box{padding:var(--sf-box-padding);outline:var(--sf-box-border-width) solid var(--sf-box-border-color)}.sf-center{box-sizing:content-box;max-inline-size:var(--sf-center-max);padding-inline:var(--sf-center-gutter);margin-inline:auto}.sf-center--intrinsic{flex-direction:column;align-items:center;display:flex}.sf-cluster{gap:var(--sf-cluster-gap);align-items:var(--sf-cluster-align);justify-content:var(--sf-cluster-justify);flex-wrap:wrap;display:flex}.sf-cluster--xs{gap:var(--sf-space-xs)}.sf-cluster--s{gap:var(--sf-space-s)}.sf-cluster--m{gap:var(--sf-space-m)}.sf-cluster--l{gap:var(--sf-space-l)}.sf-cluster--xl{gap:var(--sf-space-xl)}.sf-cluster--2xl{gap:var(--sf-space-2xl)}.sf-cluster--no-wrap{flex-wrap:nowrap}.sf-cluster--center{justify-content:center}.sf-cluster--end{justify-content:flex-end}.sf-cluster--between{justify-content:space-between}.sf-sidebar{gap:var(--sf-sidebar-gap);flex-wrap:wrap;display:flex}.sf-sidebar>:first-child{flex-basis:var(--sf-sidebar-width);flex-grow:1}.sf-sidebar>:last-child,.sf-sidebar--right>:first-child{min-inline-size:var(--sf-sidebar-min-width);flex-grow:999;flex-basis:0}.sf-sidebar--right>:last-child{flex-basis:var(--sf-sidebar-width);flex-grow:1}.sf-sidebar--narrow{--sf-sidebar-width:12rem}.sf-sidebar--wide{--sf-sidebar-width:26rem}.sf-switcher{gap:var(--sf-switcher-gap);flex-wrap:wrap;display:flex}.sf-switcher>*{flex-grow:1;flex-basis:calc((var(--sf-switcher-threshold) - 100%) * 999)}.sf-switcher--no-wrap{flex-wrap:nowrap;overflow-x:auto}.sf-switcher--vertical{flex-direction:column}.sf-grid{grid-template-columns:repeat(auto-fill, minmax(min(var(--sf-grid-min), 100%), 1fr));gap:var(--sf-grid-gap);display:grid}.sf-grid--fit{grid-template-columns:repeat(auto-fit, minmax(min(var(--sf-grid-min), 100%), 1fr))}.sf-grid--xs{--sf-grid-min:var(--sf-grid-min-xs)}.sf-grid--s{--sf-grid-min:var(--sf-grid-min-s)}.sf-grid--m{--sf-grid-min:var(--sf-grid-min-m)}.sf-grid--l{--sf-grid-min:var(--sf-grid-min-l)}.sf-grid--xl{--sf-grid-min:var(--sf-grid-min-xl)}.sf-grid--2xl{--sf-grid-min:var(--sf-grid-min-2xl)}.sf-grid--dense{grid-auto-flow:dense}.sf-icon{inline-size:var(--sf-icon-size,var(--sf-icon-m));block-size:var(--sf-icon-size,var(--sf-icon-m));vertical-align:text-bottom;fill:currentColor;flex-shrink:0;display:inline-block}.sf-icon--xs{--sf-icon-size:var(--sf-icon-xs)}.sf-icon--s{--sf-icon-size:var(--sf-icon-s)}.sf-icon--m{--sf-icon-size:var(--sf-icon-m)}.sf-icon--l{--sf-icon-size:var(--sf-icon-l)}.sf-icon--xl{--sf-icon-size:var(--sf-icon-xl)}.sf-icon--boxed{inline-size:calc(var(--sf-icon-size,var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);block-size:calc(var(--sf-icon-size,var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);padding:var(--sf-icon-box-pad);border:var(--sf-icon-box-border);border-radius:var(--sf-icon-box-radius);background:var(--sf-icon-box-bg);box-sizing:content-box}.sf-cover{min-height:var(--sf-cover-min-height);padding-block:var(--sf-cover-padding);flex-direction:column;display:flex}.sf-cover>.sf-cover__center{margin-block:auto}.sf-cover--min{min-height:50dvh}.sf-cover--max{min-height:0;max-height:100dvh}.sf-cover--padding-s{padding-block:var(--sf-space-xl)}.sf-cover--padding-l{padding-block:var(--sf-space-4xl)}.sf-frame{aspect-ratio:var(--sf-frame-ratio);justify-content:center;align-items:center;display:flex;overflow:hidden}.sf-frame>img,.sf-frame>video{object-fit:cover;width:100%;height:100%}.sf-frame--square{aspect-ratio:var(--sf-ratio-square)}.sf-frame--video{aspect-ratio:var(--sf-ratio-video)}.sf-frame--cinema{aspect-ratio:var(--sf-ratio-cinema)}.sf-frame--portrait{aspect-ratio:var(--sf-ratio-portrait)}.sf-frame--4-3{aspect-ratio:var(--sf-ratio-4-3)}.sf-frame--3-2{aspect-ratio:var(--sf-ratio-3-2)}.sf-frame--golden{aspect-ratio:var(--sf-ratio-golden)}.sf-reel{block-size:var(--sf-reel-height);gap:var(--sf-reel-gap);scroll-snap-type:x mandatory;scroll-padding-inline:var(--sf-reel-gap);overscroll-behavior-x:contain;scrollbar-width:thin;display:flex;overflow-x:auto}.sf-reel>*{flex:0 0 var(--sf-reel-item-width);scroll-snap-align:start}.sf-imposter{position:absolute;inset-block-start:50%;inset-inline-start:50%;translate:-50% -50%}.sf-imposter--fixed{position:fixed}.sf-imposter--contain{max-inline-size:calc(100% - var(--sf-imposter-margin) * 2);max-block-size:calc(100% - var(--sf-imposter-margin) * 2);overflow:auto}.sf-alternate{gap:var(--sf-alternate-gap);display:grid;container:sf-alternate/inline-size}.sf-alternate>*{gap:var(--sf-alternate-inner-gap);align-items:center;display:grid}@container sf-alternate (width>=48em){.sf-alternate>*{grid-template-columns:1fr 1fr}.sf-alternate>:nth-child(2n)>:first-child{order:2}.sf-alternate>:nth-child(2n)>:last-child{order:1}}.sf-pancake{grid-template-rows:auto 1fr auto;min-height:100dvh;display:grid}.sf-grid-1,.sf-grid-2,.sf-grid-3,.sf-grid-4,.sf-grid-6{gap:var(--sf-grid-gap);display:grid}@container (width>=30em){.sf-grid-4{grid-template-columns:repeat(2,1fr)}.sf-grid-6{grid-template-columns:repeat(3,1fr)}}@container (width>=48em){.sf-grid-2{grid-template-columns:repeat(2,1fr)}.sf-grid-3{grid-template-columns:repeat(3,1fr)}.sf-grid-4{grid-template-columns:repeat(4,1fr)}.sf-grid-6{grid-template-columns:repeat(6,1fr)}}.sf-grid-1-2,.sf-grid-2-1,.sf-grid-1-3,.sf-grid-3-1{gap:var(--sf-grid-gap);display:grid}@container (width>=48em){.sf-grid-1-2{grid-template-columns:1fr 2fr}.sf-grid-2-1{grid-template-columns:2fr 1fr}.sf-grid-1-3{grid-template-columns:1fr 3fr}.sf-grid-3-1{grid-template-columns:3fr 1fr}}.sf-content-grid{grid-template-columns:[full-start] minmax(var(--sf-space-gutter), 1fr) [breakout-start] minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2)) [content-start] min(var(--sf-content-width), 100% - var(--sf-space-gutter) * 2) [content-end] minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2)) [breakout-end] minmax(var(--sf-space-gutter), 1fr) [full-end];display:grid}.sf-content-grid>*{grid-column:content}.sf-content-grid>.sf-breakout{grid-column:breakout}.sf-content-grid>.sf-full-bleed{grid-column:full}.sf-bento{grid-template-columns:repeat(var(--sf-bento-cols,var(--sf-bento-cols-default)), 1fr);grid-auto-rows:minmax(var(--sf-bento-row,var(--sf-bento-row-default)), auto);grid-auto-flow:dense;gap:var(--sf-bento-gap);display:grid}@container (width>=30em) and (width<=47.99em){.sf-bento{grid-template-columns:repeat(2,1fr)}}.sf-bento--2{--sf-bento-cols:2}.sf-bento--4{--sf-bento-cols:4}.sf-bento--compact{--sf-bento-row:var(--sf-bento-row-compact)}.sf-bento--tall{--sf-bento-row:var(--sf-bento-row-tall)}.sf-subgrid{grid-template-columns:subgrid;display:grid}.sf-subgrid-rows{grid-template-rows:subgrid;display:grid}}@layer slashed.components;@layer slashed.macros{.sf-prose{max-width:var(--sf-container-prose);overflow-wrap:break-word}.sf-prose>*+*{margin-block-start:var(--sf-prose-paragraph)}.sf-prose :is(h2,h3,h4)+*{margin-block-start:var(--sf-space-s)}.sf-prose li+li{margin-block-start:var(--sf-space-xs)}.sf-prose :is(blockquote,pre){margin-block:var(--sf-space-m)}.sf-prose img{margin-block:var(--sf-space-m);border-radius:var(--sf-radius-m)}.sf-prose :is(ul,ol){padding-inline-start:1.25em}.sf-prose ul{list-style:outside}.sf-prose ol{list-style:decimal}.sf-prose ::marker{color:var(--sf-color-primary)}.sf-prose figure{margin-block:var(--sf-space-l)}.sf-prose figcaption{font-size:var(--sf-text-s);color:var(--sf-color-text--muted);margin-block-start:var(--sf-space-xs)}.sf-prose table{margin-block:var(--sf-space-m);max-width:100%;display:block;overflow-x:auto}.sf-prose video{margin-block:var(--sf-space-m);border-radius:var(--sf-radius-m)}.sf-prose .sf-not-prose>*+*,.sf-prose .sf-not-prose li+li{margin-block-start:0}.sf-prose .sf-not-prose :is(ul,ol){list-style:revert;padding-inline-start:revert}.sf-prose .sf-not-prose ::marker{color:inherit}.sf-prose .sf-not-prose img{border-radius:0;margin-block:0}.sf-prose .sf-not-prose figure{margin-block:0}.sf-prose .sf-not-prose figcaption{font-size:inherit;color:inherit;margin-block-start:0}.sf-prose .sf-not-prose table{display:revert;overflow-x:revert;max-width:revert;margin-block:0}.sf-prose .sf-not-prose video{border-radius:0;margin-block:0}.sf-flow>*+*{margin-block-start:var(--sf-flow-space)}.sf-truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.sf-line-clamp-2,.sf-line-clamp-3,.sf-line-clamp-N{-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.sf-line-clamp-2{-webkit-line-clamp:2;line-clamp:2}.sf-line-clamp-3{-webkit-line-clamp:3;line-clamp:3}.sf-line-clamp-N{-webkit-line-clamp:var(--sf-line-clamp);line-clamp:var(--sf-line-clamp)}.sf-equal-height{align-items:stretch;display:flex}.sf-equal-height>*{flex:auto;block-size:auto}.sf-aspect{aspect-ratio:var(--sf-aspect)}.sf-scroll-shadow{mask-image:linear-gradient(to bottom, transparent 0, black var(--sf-scroll-shadow-size), black calc(100% - var(--sf-scroll-shadow-size)), transparent 100%);overflow-y:auto}.sf-scroll-snap{scroll-snap-type:y mandatory;overscroll-behavior-y:contain;overflow-y:auto}.sf-scroll-snap>*{scroll-snap-align:start}.sf-overflow-fade{mask-image:linear-gradient(to right, black 0, black calc(100% - var(--sf-scroll-shadow-size)), transparent 100%);overflow:hidden}.sf-no-tap-highlight{-webkit-tap-highlight-color:transparent}.sf-surface--primary{background:var(--sf-color-primary);color:var(--sf-color-text--on-primary)}.sf-surface--secondary{background:var(--sf-color-secondary);color:var(--sf-color-text--on-secondary)}.sf-surface--tertiary{background:var(--sf-color-tertiary);color:var(--sf-color-text--on-tertiary)}.sf-surface--action{background:var(--sf-color-action);color:var(--sf-color-text--on-action)}.sf-surface--neutral{background:var(--sf-color-neutral);color:var(--sf-color-text--on-neutral)}.sf-surface--inverse{background:var(--sf-color-inverse);color:var(--sf-color-text--on-inverse)}.sf-surface--success{background:var(--sf-color-success);color:var(--sf-color-text--on-success)}.sf-surface--warning{background:var(--sf-color-warning);color:var(--sf-color-text--on-warning)}.sf-surface--error{background:var(--sf-color-error);color:var(--sf-color-text--on-error)}.sf-surface--info{background:var(--sf-color-info);color:var(--sf-color-text--on-info)}.sf-surface--danger{background:var(--sf-color-danger);color:var(--sf-color-text--on-danger)}.sf-text-gradient{background-image:var(--sf-gradient-primary);-webkit-background-clip:text;background-clip:text}@supports (background-clip:text){.sf-text-gradient{color:#0000}}.sf-link-external:after{content:var(--sf-link-external-marker);font-size:.85em;text-decoration:none;display:inline-block}}@layer slashed.utilities;@layer slashed.states{.is-hidden{display:none!important}.is-invisible{visibility:hidden}.is-visible{visibility:visible}.is-disabled{opacity:var(--sf-opacity-disabled);pointer-events:none;cursor:not-allowed;user-select:none}.is-readonly{pointer-events:none;user-select:none}.is-loading{pointer-events:none;position:relative;color:#0000!important}.is-loading:after{content:"";border:var(--sf-border-width-2) solid var(--sf-color-border--strong);border-block-start-color:var(--sf-color-action);border-radius:var(--sf-radius-full);width:1em;height:1em;animation:var(--sf-animation-spin);margin-block-start:-.5em;margin-inline-start:-.5em;position:absolute;inset-block-start:50%;inset-inline-start:50%}.is-busy{cursor:progress}.is-pending{opacity:var(--sf-state-pending-opacity);cursor:progress}.is-skeleton{background:linear-gradient(90deg, var(--sf-color-well) 25%, var(--sf-color-raised) 50%, var(--sf-color-well) 75%);animation:var(--sf-animation-shimmer);border-radius:var(--sf-radius-s);pointer-events:none;user-select:none;background-size:200% 100%;color:#0000!important}.is-active{--sf-is-active:1}.is-selected{background-color:var(--sf-color-bg--selected)}.is-current{--sf-is-current:1;font-weight:var(--sf-current-font-weight,var(--sf-font-weight-bold))}.is-highlighted{background-color:var(--sf-color-bg--focus)}.is-pressed{--sf-is-pressed:1;background-color:var(--sf-color-bg--selected)}.is-open{--sf-is-open:1}.is-collapsed{--sf-is-open:0}.is-expanded{--sf-is-open:1}.is-valid{--sf-field-border-color:var(--sf-color-success);--sf-field-text-color:var(--sf-color-success-strong)}.is-invalid{--sf-field-border-color:var(--sf-color-error);--sf-field-text-color:var(--sf-color-error-strong)}.is-warning{--sf-field-border-color:var(--sf-color-warning);--sf-field-text-color:var(--sf-color-warning-strong)}.is-success{--sf-field-border-color:var(--sf-color-success);--sf-field-text-color:var(--sf-color-success-strong)}.is-error{--sf-field-border-color:var(--sf-color-error);--sf-field-text-color:var(--sf-color-error-strong)}.is-info{--sf-field-border-color:var(--sf-color-info);--sf-field-text-color:var(--sf-color-info-strong)}.is-danger{--sf-field-border-color:var(--sf-color-danger);--sf-field-text-color:var(--sf-color-danger-strong)}.is-sticky{top:var(--sf-sticky-offset,0);z-index:var(--sf-z-low);position:sticky}.is-pinned{z-index:var(--sf-z-mid);position:sticky;top:0}.is-fixed{position:fixed}.is-fullscreen{z-index:var(--sf-z-max);position:fixed;inset:0}.is-resizable{resize:both;overflow:auto}.is-clipped{overflow:hidden!important}.is-scrollable{overscroll-behavior:contain;overflow:auto}.is-truncated{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.is-dragging{opacity:var(--sf-opacity-50);cursor:grabbing}.is-drop-target{outline:var(--sf-focus-ring-width) dashed var(--sf-color-action);outline-offset:var(--sf-focus-ring-offset)}.is-draggable{cursor:grab}.is-overlay{position:absolute;inset:0}.is-clickable{cursor:pointer}.is-unselectable{user-select:none}.is-focused{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-focus-ring-color);outline-offset:var(--sf-focus-ring-offset)}.is-empty:empty{display:none}}@layer slashed.themes{@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--sf-is-dark:1}}[data-theme=light]{color-scheme:light;--sf-is-dark:0}[data-theme=dark]{color-scheme:dark;--sf-is-dark:1}:root[data-lumlocker]{--sf-color-primary:light-dark(oklch(from var(--sf-color-primary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-primary-dark,var(--sf-color-primary-light)) var(--sf-lumlocker) c h));--sf-color-secondary:light-dark(oklch(from var(--sf-color-secondary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-secondary-dark,var(--sf-color-secondary-light)) var(--sf-lumlocker) c h));--sf-color-tertiary:light-dark(oklch(from var(--sf-color-tertiary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-tertiary-dark,var(--sf-color-tertiary-light)) var(--sf-lumlocker) c h));--sf-color-action:light-dark(oklch(from var(--sf-color-action-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-action-dark,var(--sf-color-action-light)) var(--sf-lumlocker) c h));--sf-color-neutral:light-dark(oklch(from var(--sf-color-neutral-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-neutral-dark,var(--sf-color-neutral-light)) var(--sf-lumlocker) c h))}}@layer slashed.motion{@media (prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}html:focus-within{scroll-behavior:auto}a,button,input,select,textarea,summary{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-duration:var(--sf-duration-fast);transition-timing-function:var(--sf-ease-out)}@supports (view-transition-name:none){::view-transition-old(root),::view-transition-new(root){animation-duration:var(--sf-duration-normal);animation-timing-function:var(--sf-ease-out)}}.sf-fade-in{animation:var(--sf-animation-fade-in)}.sf-fade-out{animation:var(--sf-animation-fade-out)}.sf-slide-in-up{animation:var(--sf-animation-slide-in-up)}.sf-slide-in-down{animation:var(--sf-animation-slide-in-down)}.sf-slide-in-left{animation:var(--sf-animation-slide-in-left)}.sf-slide-in-right{animation:var(--sf-animation-slide-in-right)}.sf-scale-up{animation:var(--sf-animation-scale-up)}.sf-scale-down{animation:var(--sf-animation-scale-down)}.sf-entrance--fade,.sf-entrance--fade-up,.sf-entrance--fade-down,.sf-entrance--fade-left,.sf-entrance--fade-right,.sf-entrance--scale-up{animation-duration:var(--sf-duration-slow);animation-timing-function:var(--sf-ease-out);animation-fill-mode:both}@supports (animation-timeline:view()){.sf-entrance--fade,.sf-entrance--fade-up,.sf-entrance--fade-down,.sf-entrance--fade-left,.sf-entrance--fade-right,.sf-entrance--scale-up{animation-timeline:view();animation-range:var(--sf-scroll-timeline-range-start,entry 0%) var(--sf-scroll-timeline-range-end,cover 30%)}}.sf-entrance--fade{animation-name:sf-fade-in}.sf-entrance--fade-up{animation-name:sf-slide-in-up}.sf-entrance--fade-down{animation-name:sf-slide-in-down}.sf-entrance--fade-left{animation-name:sf-slide-in-left}.sf-entrance--fade-right{animation-name:sf-slide-in-right}.sf-entrance--scale-up{animation-name:sf-scale-up}}@keyframes sf-fade-in{0%{opacity:0}to{opacity:1}}@keyframes sf-fade-out{0%{opacity:1}to{opacity:0}}@keyframes sf-slide-in-up{0%{opacity:0;translate:0 1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-down{0%{opacity:0;translate:0 -1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-left{0%{opacity:0;translate:1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-right{0%{opacity:0;translate:-1rem}to{opacity:1;translate:0}}@keyframes sf-scale-up{0%{opacity:0;scale:.92}to{opacity:1;scale:1}}@keyframes sf-scale-down{0%{opacity:1;scale:1}to{opacity:0;scale:.92}}@keyframes sf-spin{to{rotate:360deg}}@keyframes sf-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@keyframes sf-ping{75%,to{opacity:0;scale:2}}@keyframes sf-blink{0%,50%{opacity:1}50.01%,to{opacity:0}}@keyframes sf-float{0%,to{translate:0}50%{translate:0 -.5rem}}@keyframes sf-color-pulse{0%{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) l c h)}50%{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) calc(l + .25) c h)}to{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) l c h)}}@media (prefers-reduced-motion:no-preference){.sf-color-pulse{animation:var(--sf-animation-color-pulse)}}}@layer slashed.accessibility{:focus:not(:focus-visible){outline:none}:focus-visible{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-focus-ring-color)!important;outline-offset:var(--sf-focus-ring-offset)!important}@media (prefers-reduced-motion:reduce){:root{--sf-duration-instant:.01ms;--sf-duration-fast:.01ms;--sf-duration-normal:.01ms;--sf-duration-slow:.01ms;--sf-duration-slower:.01ms}*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.no-motion,.no-motion *,.no-motion :before,.no-motion :after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}@media (prefers-contrast:more){:root{--sf-focus-ring-width:3px;--sf-contrast-bias:.06}hr{border-block-start-width:var(--sf-border-width-2)}}@media (prefers-reduced-transparency:reduce){::backdrop{background-color:var(--sf-color-neutral);opacity:1}}@media (pointer:coarse){button,input[type=button],input[type=submit],input[type=reset],select,summary,a{min-height:var(--sf-touch-target);min-width:var(--sf-touch-target)}}[disabled],[aria-disabled=true]{cursor:not-allowed}.sr-only,.visually-hidden,.sr-only-focusable:not(:focus,:focus-within){clip-path:inset(50%)!important;white-space:nowrap!important;border:0!important;width:1px!important;height:1px!important;margin:-1px!important;padding:0!important;position:absolute!important;overflow:clip!important}.skip-link{top:-100%;left:var(--sf-space-m);z-index:var(--sf-z-max);padding:var(--sf-space-s) var(--sf-space-m);background:var(--sf-color-bg);color:var(--sf-color-text);border:var(--sf-border-width-1) solid var(--sf-color-border);border-radius:var(--sf-radius-m);transition:top var(--sf-duration-fast) var(--sf-ease-out);position:absolute}.skip-link:focus{top:var(--sf-space-m)}.focus-parent:focus-within{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}.sf-clickable-parent{--sf-clickable-overlay-z:1;cursor:pointer;position:relative}.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):after,.sf-clickable-parent [data-overlay-link]:after{content:"";z-index:var(--sf-clickable-overlay-z);pointer-events:auto;position:absolute;inset:0}.sf-clickable-parent :is(button,input,textarea,select,summary,video,audio,[role=button],[data-no-overlay]),.sf-clickable-parent:has([data-overlay-link]) a:where(:any-link):not([data-overlay-link]),.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):not(:first-of-type){z-index:calc(var(--sf-clickable-overlay-z) + 1);position:relative}.sf-clickable-parent:focus-within{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}@media (forced-colors:active){:root{--sf-focus-ring-color:Highlight}:focus-visible{outline-color:highlight!important}:where(*){box-shadow:none;text-shadow:none}input,select,textarea{border:var(--sf-border-width-1) solid}}}@layer slashed.print{.print-only{display:none}@media print{@page{size:var(--sf-print-page-size,a4);margin:var(--sf-print-page-margin,2cm)}body{font-size:var(--sf-print-base-size)}*,:before,:after{box-shadow:none;text-shadow:none}a[href]:after{content:" (" attr(href) ")";word-break:break-all;font-size:90%}a[href^=\#]:after,a[href^=javascript\: i]:after{content:""}abbr[title]:after{content:" (" attr(title) ")"}img,svg,video,canvas,figure,table,pre,blockquote{break-inside:avoid}thead{display:table-header-group}tr{break-inside:avoid}h1,h2,h3,h4,h5,h6{break-after:avoid;orphans:3;widows:3}p{orphans:3;widows:3}pre,blockquote{border:var(--sf-border-width-1) solid var(--sf-color-border--strong);padding:var(--sf-space-s)}nav,aside,button,input,select,textarea,audio,video,dialog,[popover],.no-print{display:none!important}.print-only{display:revert!important}details{display:block}details>summary{display:none!important}details:not([open])>:not(summary){display:block}.print-color-exact,.print-color-exact *{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}.print-no-color,.print-no-color *,.print-no-color :before,.print-no-color :after{color:canvastext!important;background:0 0!important}}}@layer slashed.legacy,slashed.overrides; +@layer slashed.tokens{@property --sf-color-primary-light{syntax:"";inherits:true;initial-value:oklch(45% .2 264)}@property --sf-color-secondary-light{syntax:"";inherits:true;initial-value:oklch(25% .03 260)}@property --sf-color-tertiary-light{syntax:"";inherits:true;initial-value:oklch(48% .14 310)}@property --sf-color-action-light{syntax:"";inherits:true;initial-value:oklch(60% .16 210)}@property --sf-color-neutral-light{syntax:"";inherits:true;initial-value:oklch(45% .02 260)}@property --sf-color-base-light{syntax:"";inherits:true;initial-value:oklch(98% .005 260)}@property --sf-color-success-light{syntax:"";inherits:true;initial-value:oklch(48% .17 150)}@property --sf-color-warning-light{syntax:"";inherits:true;initial-value:oklch(75% .17 80)}@property --sf-color-error-light{syntax:"";inherits:true;initial-value:oklch(62% .2 35)}@property --sf-color-info-light{syntax:"";inherits:true;initial-value:oklch(48% .15 240)}@property --sf-color-danger-light{syntax:"";inherits:true;initial-value:oklch(48% .24 12)}@property --sf-is-dark{syntax:"";inherits:true;initial-value:0}@property --sf-is-active{syntax:"";inherits:true;initial-value:0}@property --sf-is-current{syntax:"";inherits:true;initial-value:0}@property --sf-is-pressed{syntax:"";inherits:true;initial-value:0}@property --sf-is-open{syntax:"";inherits:true;initial-value:0}:root{--sf-color-scheme:light dark;--sf-lumlocker:.65;--sf-color-primary:light-dark(var(--sf-color-primary-light),var(--sf-color-primary-dark,oklch(from var(--sf-color-primary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-secondary:light-dark(var(--sf-color-secondary-light),var(--sf-color-secondary-dark,oklch(from var(--sf-color-secondary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-tertiary:light-dark(var(--sf-color-tertiary-light),var(--sf-color-tertiary-dark,oklch(from var(--sf-color-tertiary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-action:light-dark(var(--sf-color-action-light),var(--sf-color-action-dark,oklch(from var(--sf-color-action-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-neutral:light-dark(var(--sf-color-neutral-light),var(--sf-color-neutral-dark,oklch(from var(--sf-color-neutral-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-base:light-dark(var(--sf-color-base-light),var(--sf-color-base-dark,oklch(from var(--sf-color-base-light) clamp(.16, calc(1.18 - l), .24) calc(c * .5) h)));--sf-color-success:light-dark(var(--sf-color-success-light),var(--sf-color-success-dark,oklch(from var(--sf-color-success-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-warning:light-dark(var(--sf-color-warning-light),var(--sf-color-warning-dark,oklch(from var(--sf-color-warning-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-error:light-dark(var(--sf-color-error-light),var(--sf-color-error-dark,oklch(from var(--sf-color-error-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-info:light-dark(var(--sf-color-info-light),var(--sf-color-info-dark,oklch(from var(--sf-color-info-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-danger:light-dark(var(--sf-color-danger-light),var(--sf-color-danger-dark,oklch(from var(--sf-color-danger-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-bg:oklch(from var(--sf-color-base) calc(l + .02) c h);--sf-color-surface:var(--sf-color-base);--sf-color-well:oklch(from var(--sf-color-base) calc(l - .02) c h);--sf-color-raised:oklch(from var(--sf-color-base) calc(l + .04) c h);--sf-color-overlay:oklch(from var(--sf-color-base) l c h / .9);--sf-color-inverse:oklch(from var(--sf-color-base) calc(1 - l) c h);--sf-color-text:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.05, calc(l - .4 - var(--sf-contrast-bias)), .35) c h),oklch(from var(--sf-color-neutral) clamp(.7, calc(l + .25 + var(--sf-contrast-bias)), 1) c h));--sf-color-text--secondary:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.15, calc(l - .25 - var(--sf-contrast-bias)), .45) c h),oklch(from var(--sf-color-neutral) clamp(.55, calc(l + .1 + var(--sf-contrast-bias)), .9) c h));--sf-color-text--muted:var(--sf-color-neutral);--sf-color-text--placeholder:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.45, calc(l + .15), .75) c h),oklch(from var(--sf-color-neutral) clamp(.35, calc(l - .1), .65) c h));--sf-color-text--disabled:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.55, calc(l + .25), .82) c h),oklch(from var(--sf-color-neutral) clamp(.25, calc(l - .2), .55) c h));--sf-color-text--inverse:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.85, calc(l + .4), .98) c h),oklch(from var(--sf-color-neutral) clamp(.05, calc(l - .4), .35) c h));--sf-color-heading:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.05, calc(l - .4 - var(--sf-contrast-bias)), .35) c h),oklch(from var(--sf-color-neutral) clamp(.7, calc(l + .25 + var(--sf-contrast-bias)), 1) c h));--sf-color-code-text:oklch(from var(--sf-color-code-bg) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-primary:oklch(from var(--sf-color-primary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-secondary:oklch(from var(--sf-color-secondary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-tertiary:oklch(from var(--sf-color-tertiary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-action:oklch(from var(--sf-color-action) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-neutral:oklch(from var(--sf-color-neutral) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-base:var(--sf-color-text);--sf-color-text--on-inverse:var(--sf-color-text--inverse);--sf-color-text--on-success:oklch(from var(--sf-color-success) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-warning:oklch(from var(--sf-color-warning) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-error:oklch(from var(--sf-color-error) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-info:oklch(from var(--sf-color-info) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-danger:oklch(from var(--sf-color-danger) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-border:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.7, calc(l + .35), .95) .005 h),oklch(from var(--sf-color-neutral) clamp(.25, calc(l - .3), .55) .005 h));--sf-color-border--subtle:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.75, calc(l + .4), .97) .005 h),oklch(from var(--sf-color-neutral) clamp(.2, calc(l - .38), .45) .005 h));--sf-color-border--strong:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.55, calc(l + .1), .85) .02 h),oklch(from var(--sf-color-neutral) clamp(.38, calc(l - .1), .65) .02 h));--sf-color-border--focus:var(--sf-color-action);--sf-color-border--disabled:oklch(from var(--sf-color-border--subtle) l 0 h / .5);--sf-color-border--translucent:oklch(from var(--sf-color-neutral) l c h / .15);--sf-color-link:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .07, .48), 1) c h),oklch(from var(--sf-color-action) clamp(.68, l, 1) c h));--sf-color-link--hover:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .15, .4), 1) c h),oklch(from var(--sf-color-action) clamp(0, max(l + .1, .68), 1) c h));--sf-color-link--active:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .21, .34), 1) c h),oklch(from var(--sf-color-action) clamp(0, max(l + .15, .74), 1) c h));--sf-color-link--visited:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .07, .48), 1) c calc(h + 60)),oklch(from var(--sf-color-action) clamp(.68, l, 1) c calc(h + 60)));--sf-color-link--underline:oklch(from var(--sf-color-action) l c h / .3);--sf-color-link--disabled:var(--sf-color-text--disabled);--sf-color-bg--hover:oklch(from var(--sf-color-neutral) l c h / .08);--sf-color-bg--active:oklch(from var(--sf-color-neutral) l c h / .12);--sf-color-bg--selected:oklch(from var(--sf-color-action) l c h / .1);--sf-color-bg--focus:oklch(from var(--sf-color-action) l c h / .06);--sf-color-bg--disabled:var(--sf-color-well);--sf-color-code-bg:var(--sf-color-well);--sf-color-selection-bg:oklch(from var(--sf-color-action) l c h / .22);--sf-color-selection-text:inherit;--sf-color-mark-bg:oklch(from var(--sf-color-warning) l c h / .25);--sf-color-mark-text:inherit;--sf-color-dim:oklch(0% 0 0/.5);--sf-scrollbar-thumb:var(--sf-color-neutral);--sf-scrollbar-track:transparent;--sf-color-success-subtle:oklch(from var(--sf-color-success) l c h / .12);--sf-color-success-strong:light-dark(oklch(from var(--sf-color-success-light) calc(l - .15) c h),oklch(from var(--sf-color-success) clamp(.7, calc(l + .15), 1) c h));--sf-color-success-muted:oklch(from var(--sf-color-success) l c h / .3);--sf-color-warning-subtle:oklch(from var(--sf-color-warning) l c h / .12);--sf-color-warning-strong:light-dark(oklch(from var(--sf-color-warning-light) calc(l - .25) c h),oklch(from var(--sf-color-warning) clamp(.7, calc(l + .05), 1) c h));--sf-color-warning-muted:oklch(from var(--sf-color-warning) l c h / .3);--sf-color-error-subtle:oklch(from var(--sf-color-error) l c h / .1);--sf-color-error-strong:light-dark(oklch(from var(--sf-color-error-light) calc(l - .1) c h),oklch(from var(--sf-color-error) clamp(.7, calc(l + .15), 1) c h));--sf-color-error-muted:oklch(from var(--sf-color-error) l c h / .3);--sf-color-info-subtle:oklch(from var(--sf-color-info) l c h / .1);--sf-color-info-strong:light-dark(oklch(from var(--sf-color-info-light) calc(l - .1) c h),oklch(from var(--sf-color-info) clamp(.7, calc(l + .15), 1) c h));--sf-color-info-muted:oklch(from var(--sf-color-info) l c h / .3);--sf-color-danger-subtle:oklch(from var(--sf-color-danger) l c h / .1);--sf-color-danger-strong:light-dark(oklch(from var(--sf-color-danger-light) calc(l - .1) c h),oklch(from var(--sf-color-danger) clamp(.7, calc(l + .15), 1) c h));--sf-color-danger-muted:oklch(from var(--sf-color-danger) l c h / .3);--sf-gradient-primary:linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) calc(l - .08) c h));--sf-gradient-secondary:linear-gradient(135deg, var(--sf-color-secondary), oklch(from var(--sf-color-secondary) calc(l - .08) c h));--sf-gradient-tertiary:linear-gradient(135deg, var(--sf-color-tertiary), oklch(from var(--sf-color-tertiary) calc(l - .08) c h));--sf-gradient-brand:linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) l c calc(h + 30)));--sf-gradient-surface:linear-gradient(180deg, var(--sf-color-surface), var(--sf-color-bg));--sf-gradient-fade--r:linear-gradient(to right, transparent, var(--sf-color-bg));--sf-gradient-fade--l:linear-gradient(to left, transparent, var(--sf-color-bg));--sf-gradient-fade--t:linear-gradient(to top, transparent, var(--sf-color-bg));--sf-gradient-fade--b:linear-gradient(to bottom, transparent, var(--sf-color-bg));--sf-shadow-strength:calc(.08 + var(--sf-is-dark) * .17);--sf-shadow-glow-color:var(--sf-color-primary);--sf-shadow-glow:0 0 15px 2px oklch(from var(--sf-shadow-glow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-opacity-disabled:.45;--sf-state-pending-opacity:.7;--sf-focus-ring-color:var(--sf-color-action);--sf-focus-ring-shadow:0 0 0 var(--sf-focus-ring-offset) var(--sf-color-bg), 0 0 0 calc(var(--sf-focus-ring-offset) + var(--sf-focus-ring-width)) var(--sf-focus-ring-color);--sf-caret-color:var(--sf-color-action);--sf-space-scale:1;--sf-text-scale:1;--sf-text-display-scale:1;--sf-radius-scale:1;--sf-motion-scale:1;--sf-font-body:system-ui, -apple-system, sans-serif;--sf-font-heading:var(--sf-font-body);--sf-font-display:var(--sf-font-heading);--sf-font-mono:ui-monospace, monospace;--sf-font-humanist:"Seravek", "Gill Sans Nova", "Ubuntu", "Calibri", "DejaVu Sans", source-sans-pro, sans-serif;--sf-font-geometric:"Avenir", "Montserrat", "Corbel", "URW Gothic", source-sans-pro, sans-serif;--sf-font-slab:"Rockwell", "Rockwell Nova", "Roboto Slab", "DejaVu Serif", "Sitka Small", serif;--sf-font-features:normal;--sf-font-variation:normal;--sf-optical-sizing:auto;--sf-font-weight-thin:100;--sf-font-weight-extralight:200;--sf-font-weight-light:300;--sf-font-weight-normal:400;--sf-font-weight-medium:500;--sf-font-weight-semibold:600;--sf-font-weight-bold:700;--sf-font-weight-extrabold:800;--sf-font-weight-black:900;--sf-font-weight-body:var(--sf-font-weight-normal);--sf-font-weight-heading:var(--sf-font-weight-semibold);--sf-font-weight-display:var(--sf-font-weight-bold);--sf-current-font-weight:var(--sf-font-weight-bold);--sf-text-2xs:clamp(.51rem, calc(.000296296 * (100vw - 22.5rem) + .51rem), .53rem);--sf-text-xs:clamp(.64rem, calc(.000888889 * (100vw - 22.5rem) + .64rem), .7rem);--sf-text-s:clamp(.8rem, calc(.00207407 * (100vw - 22.5rem) + .8rem), .94rem);--sf-text-m:clamp(1rem, calc(.0037037 * (100vw - 22.5rem) + 1rem), 1.25rem);--sf-text-l:clamp(1.25rem, calc(.00622222 * (100vw - 22.5rem) + 1.25rem), 1.67rem);--sf-text-xl:clamp(1.56rem, calc(.00977778 * (100vw - 22.5rem) + 1.56rem), 2.22rem);--sf-text-2xl:clamp(1.95rem, calc(.014963 * (100vw - 22.5rem) + 1.95rem), 2.96rem);--sf-text-3xl:clamp(2.44rem, calc(.0223704 * (100vw - 22.5rem) + 2.44rem), 3.95rem);--sf-text-4xl:clamp(3.05rem, calc(.0327407 * (100vw - 22.5rem) + 3.05rem), 5.26rem);--sf-text-display-s:clamp(2.4rem, calc(.00888889 * (100vw - 22.5rem) + 2.4rem), 3rem);--sf-text-display-m:clamp(3rem, calc(.0148148 * (100vw - 22.5rem) + 3rem), 4rem);--sf-text-display-l:clamp(3.75rem, calc(.0234074 * (100vw - 22.5rem) + 3.75rem), 5.33rem);--sf-leading-tight:1.1;--sf-leading-snug:1.3;--sf-leading-normal:1.5;--sf-leading-relaxed:1.625;--sf-tracking-tight:-.025em;--sf-tracking-normal:0;--sf-tracking-wide:.025em;--sf-tracking-wider:.05em;--sf-tracking-widest:.1em;--sf-icon-xs:.875em;--sf-icon-s:1em;--sf-icon-m:1.5em;--sf-icon-l:2em;--sf-icon-xl:3em;--sf-space-none:0;--sf-space-px:1px;--sf-space-gutter:var(--sf-space-l);--sf-space-2xs:calc(clamp(.51rem, calc(.00488889 * (100vw - 22.5rem) + .51rem), .84rem) * var(--sf-space-scale));--sf-space-xs:calc(clamp(.64rem, calc(.00725926 * (100vw - 22.5rem) + .64rem), 1.13rem) * var(--sf-space-scale));--sf-space-s:calc(clamp(.8rem, calc(.0103704 * (100vw - 22.5rem) + .8rem), 1.5rem) * var(--sf-space-scale));--sf-space-m:calc(clamp(1rem, calc(.0148148 * (100vw - 22.5rem) + 1rem), 2rem) * var(--sf-space-scale));--sf-space-l:calc(clamp(1.25rem, calc(.021037 * (100vw - 22.5rem) + 1.25rem), 2.67rem) * var(--sf-space-scale));--sf-space-xl:calc(clamp(1.56rem, calc(.0294815 * (100vw - 22.5rem) + 1.56rem), 3.55rem) * var(--sf-space-scale));--sf-space-2xl:calc(clamp(1.95rem, calc(.0413333 * (100vw - 22.5rem) + 1.95rem), 4.74rem) * var(--sf-space-scale));--sf-space-3xl:calc(clamp(2.44rem, calc(.0573333 * (100vw - 22.5rem) + 2.44rem), 6.31rem) * var(--sf-space-scale));--sf-space-4xl:calc(clamp(3.05rem, calc(.0795556 * (100vw - 22.5rem) + 3.05rem), 8.42rem) * var(--sf-space-scale));--sf-size-xs:1.5rem;--sf-size-s:2rem;--sf-size-m:2.5rem;--sf-size-l:2.75rem;--sf-size-xl:3.5rem;--sf-container-narrow:38rem;--sf-container-prose:65ch;--sf-container-default:75rem;--sf-container-wide:90rem;--sf-container-full:100%;--sf-ratio-square:1;--sf-ratio-video:16 / 9;--sf-ratio-cinema:21 / 9;--sf-ratio-4-3:4 / 3;--sf-ratio-3-2:3 / 2;--sf-ratio-portrait:3 / 4;--sf-ratio-golden:1.618 / 1;--sf-border-width-hairline:.5px;--sf-border-width-1:1px;--sf-border-width-2:2px;--sf-border-width-3:3px;--sf-border-width-4:4px;--sf-border-style:solid;--sf-border-style-strong:solid;--sf-border-style-soft:dashed;--sf-border-style-dotted:dotted;--sf-divider-width:var(--sf-border-width-1);--sf-divider-style:solid;--sf-divider-color:var(--sf-color-border);--sf-radius-none:0;--sf-radius-xs:calc(2px * var(--sf-radius-scale));--sf-radius-s:calc(4px * var(--sf-radius-scale));--sf-radius-m:calc(8px * var(--sf-radius-scale));--sf-radius-l:calc(12px * var(--sf-radius-scale));--sf-radius-xl:calc(16px * var(--sf-radius-scale));--sf-radius-2xl:calc(24px * var(--sf-radius-scale));--sf-radius-3xl:calc(32px * var(--sf-radius-scale));--sf-radius-4xl:calc(48px * var(--sf-radius-scale));--sf-radius-full:9999px;--sf-radius-pill:var(--sf-radius-full);--sf-radius-outer:calc(var(--sf-radius-m) + var(--sf-component-pad));--sf-shadow-color:oklch(from var(--sf-color-neutral) .15 c h);--sf-shadow-none:none;--sf-shadow-xs:0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7));--sf-shadow-s:0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 2px 6px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, var(--sf-shadow-strength), .7));--sf-shadow-m:0 1px 3px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-shadow-l:0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 8px 24px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), .7)), 0 16px 48px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-shadow-xl:0 2px 8px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 12px 36px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3.5), .7)), 0 24px 72px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7));--sf-shadow-2xl:0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .6), .7)), 0 20px 60px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 4), .7)), 0 40px 100px -8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 5), .7));--sf-shadow-inner:inset 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-text-shadow-none:none;--sf-text-shadow-s:0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), .7));--sf-text-shadow-m:0 2px 4px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-text-shadow-l:0 4px 8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7));--sf-drop-shadow-s:drop-shadow(0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), .7)));--sf-drop-shadow-m:drop-shadow(0 4px 6px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7)));--sf-drop-shadow-l:drop-shadow(0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7)));--sf-blur-xs:4px;--sf-blur-s:8px;--sf-blur-m:16px;--sf-blur-l:32px;--sf-blur-xl:48px;--sf-perspective-near:500px;--sf-perspective-normal:1000px;--sf-perspective-far:2000px;--sf-opacity-0:0;--sf-opacity-10:.1;--sf-opacity-25:.25;--sf-opacity-50:.5;--sf-opacity-75:.75;--sf-opacity-100:1;--sf-duration-none:0s;--sf-duration-instant:calc(.1s * var(--sf-motion-scale));--sf-duration-fast:calc(.15s * var(--sf-motion-scale));--sf-duration-normal:calc(.25s * var(--sf-motion-scale));--sf-duration-slow:calc(.4s * var(--sf-motion-scale));--sf-duration-slower:calc(.6s * var(--sf-motion-scale));--sf-ease-linear:linear;--sf-ease-out:cubic-bezier(.25, 0, .15, 1);--sf-ease-in:cubic-bezier(.5, 0, .75, .25);--sf-ease-in-out:cubic-bezier(.4, 0, .2, 1);--sf-ease-spring:linear(0, .5, 1.1, .95, 1.02, 1);--sf-ease-elastic:linear(0, .3, 1.2, .9, 1.05, 1);--sf-ease-bounce:linear(0, .35 18%, 1 32%, .86 42%, 1.02 56%, .98 72%, 1);--sf-ease-overshoot:linear(0, .6 30%, 1.08 55%, .98 75%, 1);--sf-animation-fade-in:sf-fade-in var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-fade-out:sf-fade-out var(--sf-duration-normal) var(--sf-ease-in) both;--sf-animation-slide-in-up:sf-slide-in-up var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-down:sf-slide-in-down var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-left:sf-slide-in-left var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-right:sf-slide-in-right var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-scale-up:sf-scale-up var(--sf-duration-normal) var(--sf-ease-overshoot) both;--sf-animation-scale-down:sf-scale-down var(--sf-duration-normal) var(--sf-ease-in) both;--sf-animation-color-pulse:sf-color-pulse var(--sf-duration-slow) var(--sf-ease-in-out) infinite;--sf-animation-ping:sf-ping var(--sf-duration-slow) var(--sf-ease-out) infinite;--sf-animation-blink:sf-blink calc(1s * var(--sf-motion-scale)) steps(1, end) infinite;--sf-animation-float:sf-float calc(3s * var(--sf-motion-scale)) var(--sf-ease-in-out) infinite;--sf-animation-spin:sf-spin var(--sf-duration-slower) linear infinite;--sf-animation-shimmer:sf-shimmer calc(1.5s * var(--sf-motion-scale)) ease-in-out infinite;--sf-animation-delay-1:calc(75ms * var(--sf-motion-scale));--sf-animation-delay-2:calc(.15s * var(--sf-motion-scale));--sf-animation-delay-3:calc(.225s * var(--sf-motion-scale));--sf-animation-delay-4:calc(.3s * var(--sf-motion-scale));--sf-animation-delay-5:calc(.375s * var(--sf-motion-scale));--sf-scroll-timeline-range-start:0%;--sf-scroll-timeline-range-end:100%;--sf-mask-scrim-start:var(--sf-space-l);--sf-mask-scrim-end:var(--sf-space-l);--sf-z-below:-1;--sf-z-base:0;--sf-z-raised:1;--sf-z-low:10;--sf-z-mid:100;--sf-z-high:500;--sf-z-top:900;--sf-z-max:9999;--sf-header-height:8rem;--sf-sticky-offset:var(--sf-header-height);--sf-focus-ring-width:2px;--sf-focus-ring-offset:2px;--sf-focus-ring-style:solid;--sf-touch-target:var(--sf-size-l);--sf-contrast-bias:0;--sf-contrast-threshold:.6;--sf-safe-top:env(safe-area-inset-top,0px);--sf-safe-bottom:env(safe-area-inset-bottom,0px);--sf-safe-left:env(safe-area-inset-left,0px);--sf-safe-right:env(safe-area-inset-right,0px);--sf-print-page-margin:2cm;--sf-print-page-size:a4;--sf-print-base-size:11pt;--sf-stroke-thin:1px;--sf-stroke-regular:1.5px;--sf-stroke-bold:2px;--sf-stroke-heavy:3px;--sf-body-font-size:var(--sf-text-m);--sf-body-line-height:var(--sf-leading-normal);--sf-body-font-weight:var(--sf-font-weight-body);--sf-body-font-family:var(--sf-font-body);--sf-body-color:var(--sf-color-text);--sf-body-text-wrap:pretty;--sf-body-strong-weight:var(--sf-font-weight-bold);--sf-body-em-style:italic;--sf-code-font-size:.875em;--sf-heading-font-family:var(--sf-font-heading);--sf-heading-color:var(--sf-color-heading);--sf-heading-text-wrap:balance;--sf-h1-size:var(--sf-text-4xl);--sf-h1-line-height:var(--sf-leading-tight);--sf-h1-font-weight:var(--sf-font-weight-heading);--sf-h1-letter-spacing:var(--sf-tracking-tight);--sf-h2-size:var(--sf-text-3xl);--sf-h2-line-height:var(--sf-leading-tight);--sf-h2-font-weight:var(--sf-font-weight-heading);--sf-h2-letter-spacing:var(--sf-tracking-tight);--sf-h3-size:var(--sf-text-2xl);--sf-h3-line-height:var(--sf-leading-snug);--sf-h3-font-weight:var(--sf-font-weight-heading);--sf-h3-letter-spacing:var(--sf-tracking-normal);--sf-h4-size:var(--sf-text-xl);--sf-h4-line-height:var(--sf-leading-snug);--sf-h4-font-weight:var(--sf-font-weight-heading);--sf-h4-letter-spacing:var(--sf-tracking-normal);--sf-h5-size:var(--sf-text-l);--sf-h5-line-height:var(--sf-leading-normal);--sf-h5-font-weight:var(--sf-font-weight-heading);--sf-h5-letter-spacing:var(--sf-tracking-normal);--sf-h6-size:var(--sf-text-m);--sf-h6-line-height:var(--sf-leading-normal);--sf-h6-font-weight:var(--sf-font-weight-heading);--sf-h6-letter-spacing:var(--sf-tracking-wide);--sf-gap:var(--sf-space-m);--sf-content-gap:var(--sf-space-s);--sf-component-pad:var(--sf-space-m);--sf-field-block:var(--sf-space-l);--sf-field-required-marker:" *";--sf-link-external-marker:" ↗";--sf-section-pad:var(--sf-section-pad--m);--sf-section-pad--xs:var(--sf-space-xl);--sf-section-pad--s:var(--sf-space-2xl);--sf-section-pad--m:var(--sf-space-3xl);--sf-section-pad--l:var(--sf-space-4xl);--sf-section-pad--xl:calc(var(--sf-space-4xl) * 1.5);--sf-section-pad--2xl:calc(var(--sf-space-4xl) * 2);--sf-transition-all:all var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-colors:color var(--sf-duration-normal) var(--sf-ease-out), background-color var(--sf-duration-normal) var(--sf-ease-out), border-color var(--sf-duration-normal) var(--sf-ease-out), text-decoration-color var(--sf-duration-normal) var(--sf-ease-out), fill var(--sf-duration-normal) var(--sf-ease-out), stroke var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-transform:transform var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-opacity:opacity var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-shadow:box-shadow var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-fast:all var(--sf-duration-fast) var(--sf-ease-out);--sf-transition-slow:all var(--sf-duration-slow) var(--sf-ease-in-out);--sf-transition-enter:all var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-exit:all var(--sf-duration-fast) var(--sf-ease-in);--sf-space-gap:var(--sf-gap);--sf-space-content:var(--sf-content-gap);--sf-stack-gap:var(--sf-space-content);--sf-gap-size:var(--sf-space-gap);--sf-box-padding:var(--sf-space-m);--sf-box-border-width:0;--sf-box-border-color:var(--sf-color-border);--sf-center-max:var(--sf-container-default);--sf-center-gutter:var(--sf-space-gutter);--sf-cluster-gap:var(--sf-space-gap);--sf-cluster-align:center;--sf-cluster-justify:flex-start;--sf-sidebar-gap:var(--sf-space-gap);--sf-sidebar-min-width:50%;--sf-sidebar-width:18rem;--sf-switcher-threshold:30rem;--sf-switcher-gap:var(--sf-space-gap);--sf-grid-min:16rem;--sf-grid-gap:var(--sf-space-gap);--sf-grid-min-xs:10rem;--sf-grid-min-s:13rem;--sf-grid-min-m:16rem;--sf-grid-min-l:20rem;--sf-grid-min-xl:24rem;--sf-grid-min-2xl:28rem;--sf-equal-cols:2;--sf-equal-gap:var(--sf-space-gap);--sf-cover-min-height:100dvh;--sf-cover-padding:var(--sf-section-pad);--sf-frame-ratio:16 / 9;--sf-icon-box-pad:.5em;--sf-icon-box-radius:var(--sf-radius-s);--sf-icon-box-bg:var(--sf-color-well);--sf-icon-box-border:var(--sf-border-width-1) solid var(--sf-color-border);--sf-reel-item-width:max-content;--sf-reel-gap:var(--sf-space-gap);--sf-reel-height:auto;--sf-imposter-margin:var(--sf-space-m);--sf-bento-cols-default:3;--sf-bento-row-default:10rem;--sf-bento-row-compact:6rem;--sf-bento-row-tall:16rem;--sf-bento-gap:var(--sf-space-gap);--sf-breakout-width:var(--sf-container-wide);--sf-content-width:var(--sf-container-default);--sf-prose-paragraph:var(--sf-space-content);--sf-alternate-gap:var(--sf-space-content);--sf-alternate-inner-gap:var(--sf-space-gap);--sf-flow-space:var(--sf-space-content);--sf-line-clamp:3;--sf-truncate-suffix:"…";--sf-aspect:16 / 9;--sf-scroll-shadow-size:2rem}}@layer slashed.reset{*,:before,:after{box-sizing:border-box;margin:0;padding:0}html{text-size-adjust:100%;scroll-padding-top:var(--sf-header-height,3.5rem);scrollbar-gutter:stable;color-scheme:var(--sf-color-scheme,light dark);hanging-punctuation:first last}@supports (interpolate-size:allow-keywords){html{interpolate-size:allow-keywords}}body{min-height:100dvh;min-inline-size:320px}menu,ul,ol{list-style:none}img,picture,video,canvas,svg{max-width:100%;height:auto;display:block}iframe,embed,object{max-width:100%;height:unset;border:none;display:block}button,input,select,textarea{font:inherit;color:inherit;letter-spacing:inherit;color-scheme:inherit;line-height:inherit}button{text-transform:none}::file-selector-button{font:inherit}search{display:block}fieldset{border:none;min-width:0;padding:0}legend{padding:0}a,button,input,select,textarea,summary{touch-action:manipulation}summary{cursor:pointer;list-style:none}textarea{resize:vertical}button{cursor:pointer}table{border-collapse:collapse;border-spacing:0}[hidden]{display:none!important}dialog,[popover]{color:inherit;background:0 0;border:none;padding:0}[inert]{cursor:default}}@layer slashed.base{body{font-family:var(--sf-body-font-family,var(--sf-font-body));font-size:var(--sf-body-font-size,var(--sf-text-m));line-height:var(--sf-body-line-height,var(--sf-leading-normal));font-weight:var(--sf-body-font-weight,var(--sf-font-weight-body));color:var(--sf-body-color,var(--sf-color-text));background-color:var(--sf-color-bg,#fff);font-synthesis:none}h1,h2,h3,h4,h5,h6{font-family:var(--sf-heading-font-family,var(--sf-font-heading));line-height:var(--sf-leading-tight);color:var(--sf-heading-color,var(--sf-color-heading));text-wrap:var(--sf-heading-text-wrap);overflow-wrap:break-word;text-rendering:optimizelegibility;scroll-margin-top:calc(var(--sf-header-height,3.5rem) + var(--sf-space-m))}h1{font-size:var(--sf-h1-size);line-height:var(--sf-h1-line-height);font-weight:var(--sf-h1-font-weight);letter-spacing:var(--sf-h1-letter-spacing)}h2{font-size:var(--sf-h2-size);line-height:var(--sf-h2-line-height);font-weight:var(--sf-h2-font-weight);letter-spacing:var(--sf-h2-letter-spacing)}h3{font-size:var(--sf-h3-size);line-height:var(--sf-h3-line-height);font-weight:var(--sf-h3-font-weight);letter-spacing:var(--sf-h3-letter-spacing)}h4{font-size:var(--sf-h4-size);line-height:var(--sf-h4-line-height);font-weight:var(--sf-h4-font-weight);letter-spacing:var(--sf-h4-letter-spacing)}h5{font-size:var(--sf-h5-size);line-height:var(--sf-h5-line-height);font-weight:var(--sf-h5-font-weight);letter-spacing:var(--sf-h5-letter-spacing)}h6{font-size:var(--sf-h6-size);line-height:var(--sf-h6-line-height);font-weight:var(--sf-h6-font-weight);letter-spacing:var(--sf-h6-letter-spacing)}p{text-wrap:var(--sf-body-text-wrap);overflow-wrap:break-word;orphans:2;widows:2}b,strong{font-weight:var(--sf-body-strong-weight)}em{font-style:var(--sf-body-em-style)}small{font-size:var(--sf-text-s,80%)}mark{background-color:var(--sf-color-mark-bg,yellow);color:var(--sf-color-mark-text,black);border-radius:var(--sf-radius-xs);padding-inline:.2em}abbr[title]{cursor:help;text-decoration:underline dotted}kbd,samp{font-family:var(--sf-font-mono);font-size:inherit}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sup{top:-.5em}sub{bottom:-.25em}a:link{color:var(--sf-color-link);text-decoration-color:var(--sf-color-link--underline);text-underline-offset:.15em;text-underline-position:from-font;transition:color var(--sf-duration-fast) var(--sf-ease-out), text-decoration-color var(--sf-duration-fast) var(--sf-ease-out)}a:hover{color:var(--sf-color-link--hover);text-decoration-color:currentColor}a:visited{color:var(--sf-color-link--visited)}a:active{color:var(--sf-color-link--active)}a:not([class]){text-decoration-skip-ink:auto}a,code{overflow-wrap:break-word}code{font-family:var(--sf-font-mono);font-size:var(--sf-code-font-size);border-radius:var(--sf-radius-xs);background-color:var(--sf-color-code-bg);color:var(--sf-color-code-text,inherit);padding:.125em .3em}pre{font-family:var(--sf-font-mono);font-size:var(--sf-text-s);padding:var(--sf-space-m);border-radius:var(--sf-radius-m);background:var(--sf-color-code-block-bg,var(--sf-color-code-bg));color:var(--sf-color-code-block-text,inherit);tab-size:2;hyphens:none;overflow-x:auto}pre code{color:inherit;font-size:inherit;background:0 0;border:0;border-radius:0;padding:0}svg{fill:currentColor}hr{border:none;border-block-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color)}:target{scroll-margin-top:calc(var(--sf-header-height,3.5rem) + var(--sf-space-m))}::selection{background:var(--sf-color-selection-bg);color:var(--sf-color-selection-text);text-shadow:none}th,caption{text-align:start}address,cite,dfn,var{font-style:normal}input,textarea{caret-color:var(--sf-caret-color)}input,progress{accent-color:var(--sf-color-action)}::backdrop{background-color:var(--sf-color-dim)}optgroup{font:inherit}::file-selector-button{font:inherit}::placeholder{opacity:1;color:var(--sf-color-text--placeholder,currentcolor)}blockquote{border-inline-start:var(--sf-border-width-3) solid var(--sf-color-border);padding-inline-start:var(--sf-space-m);font-style:italic}dt{font-weight:var(--sf-font-weight-heading)}dd{margin-inline-start:var(--sf-space-m)}table{text-align:start;width:100%}th{font-weight:var(--sf-font-weight-heading)}td,th{padding:var(--sf-space-2xs) var(--sf-space-xs);border-block-end:var(--sf-border-width-hairline) solid var(--sf-color-border--subtle)}}@layer slashed.forms;@layer slashed.layout{.sf-section{padding-block:var(--sf-section-pad)}.sf-section--xs{padding-block:var(--sf-section-pad--xs)}.sf-section--s{padding-block:var(--sf-section-pad--s)}.sf-section--m{padding-block:var(--sf-section-pad--m)}.sf-section--l{padding-block:var(--sf-section-pad--l)}.sf-section--xl{padding-block:var(--sf-section-pad--xl)}.sf-section--2xl{padding-block:var(--sf-section-pad--2xl)}.sf-section-group>.sf-section+.sf-section{padding-block-start:0}.sf-section--collapse:has(+.sf-section--collapse){padding-block-end:calc(var(--sf-section-pad) / 2)}.sf-section--collapse+.sf-section--collapse{padding-block-start:calc(var(--sf-section-pad) / 2)}.sf-divider{border:none;border-block-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);margin-block:var(--sf-space-m)}.sf-divider--vertical{border-block-start:none;border-inline-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);margin-block:0;margin-inline:var(--sf-space-m);align-self:stretch}.sf-container{width:100%;max-width:var(--sf-container-default);padding-inline:var(--sf-space-gutter);margin-inline:auto;container:sf-layout/inline-size}.sf-container--narrow{max-width:var(--sf-container-narrow)}.sf-container--prose{max-width:var(--sf-container-prose)}.sf-container--wide{max-width:var(--sf-container-wide)}.sf-container--full{max-width:var(--sf-container-full)}.sf-stack{gap:var(--sf-stack-gap);flex-direction:column;display:flex}.sf-stack--xs{gap:var(--sf-space-xs)}.sf-stack--s{gap:var(--sf-space-s)}.sf-stack--m{gap:var(--sf-space-m)}.sf-stack--l{gap:var(--sf-space-l)}.sf-stack--xl{gap:var(--sf-space-xl)}.sf-stack--2xl{gap:var(--sf-space-2xl)}.sf-stack--center{align-items:center}.sf-stack--end{align-items:flex-end}.sf-stack--stretch{align-items:stretch}.sf-gap{gap:var(--sf-gap-size)}.sf-gap--xs{gap:var(--sf-space-xs)}.sf-gap--s{gap:var(--sf-space-s)}.sf-gap--m{gap:var(--sf-space-m)}.sf-gap--l{gap:var(--sf-space-l)}.sf-gap--xl{gap:var(--sf-space-xl)}.sf-gap--2xl{gap:var(--sf-space-2xl)}.sf-box{padding:var(--sf-box-padding);outline:var(--sf-box-border-width) solid var(--sf-box-border-color)}.sf-center{box-sizing:content-box;max-inline-size:var(--sf-center-max);padding-inline:var(--sf-center-gutter);margin-inline:auto}.sf-center--intrinsic{flex-direction:column;align-items:center;display:flex}.sf-cluster{gap:var(--sf-cluster-gap);align-items:var(--sf-cluster-align);justify-content:var(--sf-cluster-justify);flex-wrap:wrap;display:flex}.sf-cluster--xs{gap:var(--sf-space-xs)}.sf-cluster--s{gap:var(--sf-space-s)}.sf-cluster--m{gap:var(--sf-space-m)}.sf-cluster--l{gap:var(--sf-space-l)}.sf-cluster--xl{gap:var(--sf-space-xl)}.sf-cluster--2xl{gap:var(--sf-space-2xl)}.sf-cluster--no-wrap{flex-wrap:nowrap}.sf-cluster--center{justify-content:center}.sf-cluster--end{justify-content:flex-end}.sf-cluster--between{justify-content:space-between}.sf-sidebar{gap:var(--sf-sidebar-gap);flex-wrap:wrap;display:flex}.sf-sidebar>:first-child{flex-basis:var(--sf-sidebar-width);flex-grow:1}.sf-sidebar>:last-child,.sf-sidebar--right>:first-child{min-inline-size:var(--sf-sidebar-min-width);flex-grow:999;flex-basis:0}.sf-sidebar--right>:last-child{flex-basis:var(--sf-sidebar-width);flex-grow:1}.sf-sidebar--narrow{--sf-sidebar-width:12rem}.sf-sidebar--wide{--sf-sidebar-width:26rem}.sf-switcher{gap:var(--sf-switcher-gap);flex-wrap:wrap;display:flex}.sf-switcher>*{flex-grow:1;flex-basis:calc((var(--sf-switcher-threshold) - 100%) * 999)}.sf-switcher--no-wrap{flex-wrap:nowrap;overflow-x:auto}.sf-switcher--vertical{flex-direction:column}.sf-grid{grid-template-columns:repeat(auto-fill, minmax(min(var(--sf-grid-min), 100%), 1fr));gap:var(--sf-grid-gap);display:grid}.sf-grid--fit{grid-template-columns:repeat(auto-fit, minmax(min(var(--sf-grid-min), 100%), 1fr))}.sf-grid--xs{--sf-grid-min:var(--sf-grid-min-xs)}.sf-grid--s{--sf-grid-min:var(--sf-grid-min-s)}.sf-grid--m{--sf-grid-min:var(--sf-grid-min-m)}.sf-grid--l{--sf-grid-min:var(--sf-grid-min-l)}.sf-grid--xl{--sf-grid-min:var(--sf-grid-min-xl)}.sf-grid--2xl{--sf-grid-min:var(--sf-grid-min-2xl)}.sf-grid--dense{grid-auto-flow:dense}.sf-icon{inline-size:var(--sf-icon-size,var(--sf-icon-m));block-size:var(--sf-icon-size,var(--sf-icon-m));vertical-align:text-bottom;fill:currentColor;flex-shrink:0;display:inline-block}.sf-icon--xs{--sf-icon-size:var(--sf-icon-xs)}.sf-icon--s{--sf-icon-size:var(--sf-icon-s)}.sf-icon--m{--sf-icon-size:var(--sf-icon-m)}.sf-icon--l{--sf-icon-size:var(--sf-icon-l)}.sf-icon--xl{--sf-icon-size:var(--sf-icon-xl)}.sf-icon--boxed{inline-size:calc(var(--sf-icon-size,var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);block-size:calc(var(--sf-icon-size,var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);padding:var(--sf-icon-box-pad);border:var(--sf-icon-box-border);border-radius:var(--sf-icon-box-radius);background:var(--sf-icon-box-bg);box-sizing:content-box}.sf-cover{min-height:var(--sf-cover-min-height);padding-block:var(--sf-cover-padding);flex-direction:column;display:flex}.sf-cover>.sf-cover__center{margin-block:auto}.sf-cover--min{min-height:50dvh}.sf-cover--max{min-height:0;max-height:100dvh}.sf-cover--padding-s{padding-block:var(--sf-space-xl)}.sf-cover--padding-l{padding-block:var(--sf-space-4xl)}.sf-frame{aspect-ratio:var(--sf-frame-ratio);justify-content:center;align-items:center;display:flex;overflow:hidden}.sf-frame>img,.sf-frame>video{object-fit:cover;width:100%;height:100%}.sf-frame--square{aspect-ratio:var(--sf-ratio-square)}.sf-frame--video{aspect-ratio:var(--sf-ratio-video)}.sf-frame--cinema{aspect-ratio:var(--sf-ratio-cinema)}.sf-frame--portrait{aspect-ratio:var(--sf-ratio-portrait)}.sf-frame--4-3{aspect-ratio:var(--sf-ratio-4-3)}.sf-frame--3-2{aspect-ratio:var(--sf-ratio-3-2)}.sf-frame--golden{aspect-ratio:var(--sf-ratio-golden)}.sf-reel{block-size:var(--sf-reel-height);gap:var(--sf-reel-gap);scroll-snap-type:x mandatory;scroll-padding-inline:var(--sf-reel-gap);overscroll-behavior-x:contain;scrollbar-width:thin;display:flex;overflow-x:auto}.sf-reel>*{flex:0 0 var(--sf-reel-item-width);scroll-snap-align:start}.sf-imposter{position:absolute;inset-block-start:50%;inset-inline-start:50%;translate:-50% -50%}.sf-imposter--fixed{position:fixed}.sf-imposter--contain{max-inline-size:calc(100% - var(--sf-imposter-margin) * 2);max-block-size:calc(100% - var(--sf-imposter-margin) * 2);overflow:auto}.sf-alternate{gap:var(--sf-alternate-gap);display:grid;container:sf-alternate/inline-size}.sf-alternate>*{gap:var(--sf-alternate-inner-gap);align-items:center;display:grid}@container sf-alternate (width>=48em){.sf-alternate>*{grid-template-columns:1fr 1fr}.sf-alternate>:nth-child(2n)>:first-child{order:2}.sf-alternate>:nth-child(2n)>:last-child{order:1}}.sf-pancake{grid-template-rows:auto 1fr auto;min-height:100dvh;display:grid}.sf-grid-1,.sf-grid-2,.sf-grid-3,.sf-grid-4,.sf-grid-6{gap:var(--sf-grid-gap);display:grid}@container (width>=30em){.sf-grid-4{grid-template-columns:repeat(2,1fr)}.sf-grid-6{grid-template-columns:repeat(3,1fr)}}@container (width>=48em){.sf-grid-2{grid-template-columns:repeat(2,1fr)}.sf-grid-3{grid-template-columns:repeat(3,1fr)}.sf-grid-4{grid-template-columns:repeat(4,1fr)}.sf-grid-6{grid-template-columns:repeat(6,1fr)}}.sf-grid-1-2,.sf-grid-2-1,.sf-grid-1-3,.sf-grid-3-1{gap:var(--sf-grid-gap);display:grid}@container (width>=48em){.sf-grid-1-2{grid-template-columns:1fr 2fr}.sf-grid-2-1{grid-template-columns:2fr 1fr}.sf-grid-1-3{grid-template-columns:1fr 3fr}.sf-grid-3-1{grid-template-columns:3fr 1fr}}.sf-equal{grid-template-columns:repeat(var(--sf-equal-cols), 1fr);gap:var(--sf-equal-gap);display:grid}.sf-equal--2{--sf-equal-cols:2}.sf-equal--3{--sf-equal-cols:3}.sf-equal--4{--sf-equal-cols:4}.sf-equal--6{--sf-equal-cols:6}.sf-content-grid{grid-template-columns:[full-start] minmax(var(--sf-space-gutter), 1fr) [breakout-start] minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2)) [content-start] min(var(--sf-content-width), 100% - var(--sf-space-gutter) * 2) [content-end] minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2)) [breakout-end] minmax(var(--sf-space-gutter), 1fr) [full-end];display:grid}.sf-content-grid>*{grid-column:content}.sf-content-grid>.sf-breakout{grid-column:breakout}.sf-content-grid>.sf-full-bleed{grid-column:full}.sf-bento{grid-template-columns:repeat(var(--sf-bento-cols,var(--sf-bento-cols-default)), 1fr);grid-auto-rows:minmax(var(--sf-bento-row,var(--sf-bento-row-default)), auto);grid-auto-flow:dense;gap:var(--sf-bento-gap);display:grid}@container (width>=30em) and (width<=47.99em){.sf-bento{grid-template-columns:repeat(2,1fr)}}.sf-bento--2{--sf-bento-cols:2}.sf-bento--4{--sf-bento-cols:4}.sf-bento--compact{--sf-bento-row:var(--sf-bento-row-compact)}.sf-bento--tall{--sf-bento-row:var(--sf-bento-row-tall)}.sf-subgrid{grid-template-columns:subgrid;display:grid}.sf-subgrid-rows{grid-template-rows:subgrid;display:grid}}@layer slashed.components;@layer slashed.macros{.sf-prose{max-width:var(--sf-container-prose);overflow-wrap:break-word}.sf-prose>*+*{margin-block-start:var(--sf-prose-paragraph)}.sf-prose :is(h2,h3,h4)+*{margin-block-start:var(--sf-space-s)}.sf-prose li+li{margin-block-start:var(--sf-space-xs)}.sf-prose :is(blockquote,pre){margin-block:var(--sf-space-m)}.sf-prose img{margin-block:var(--sf-space-m);border-radius:var(--sf-radius-m)}.sf-prose :is(ul,ol){padding-inline-start:1.25em}.sf-prose ul{list-style:outside}.sf-prose ol{list-style:decimal}.sf-prose ::marker{color:var(--sf-color-primary)}.sf-prose figure{margin-block:var(--sf-space-l)}.sf-prose figcaption{font-size:var(--sf-text-s);color:var(--sf-color-text--muted);margin-block-start:var(--sf-space-xs)}.sf-prose table{margin-block:var(--sf-space-m);max-width:100%;display:block;overflow-x:auto}.sf-prose video{margin-block:var(--sf-space-m);border-radius:var(--sf-radius-m)}.sf-prose .sf-not-prose>*+*,.sf-prose .sf-not-prose li+li{margin-block-start:0}.sf-prose .sf-not-prose :is(ul,ol){list-style:revert;padding-inline-start:revert}.sf-prose .sf-not-prose ::marker{color:inherit}.sf-prose .sf-not-prose img{border-radius:0;margin-block:0}.sf-prose .sf-not-prose figure{margin-block:0}.sf-prose .sf-not-prose figcaption{font-size:inherit;color:inherit;margin-block-start:0}.sf-prose .sf-not-prose table{display:revert;overflow-x:revert;max-width:revert;margin-block:0}.sf-prose .sf-not-prose video{border-radius:0;margin-block:0}.sf-flow>*+*{margin-block-start:var(--sf-flow-space)}.sf-truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.sf-line-clamp-2,.sf-line-clamp-3,.sf-line-clamp-N{-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.sf-line-clamp-2{-webkit-line-clamp:2;line-clamp:2}.sf-line-clamp-3{-webkit-line-clamp:3;line-clamp:3}.sf-line-clamp-N{-webkit-line-clamp:var(--sf-line-clamp);line-clamp:var(--sf-line-clamp)}.sf-equal-height{align-items:stretch;display:flex}.sf-equal-height>*{flex:auto;block-size:auto}.sf-aspect{aspect-ratio:var(--sf-aspect)}.sf-scroll-shadow{mask-image:linear-gradient(to bottom, transparent 0, black var(--sf-scroll-shadow-size), black calc(100% - var(--sf-scroll-shadow-size)), transparent 100%);overflow-y:auto}.sf-scroll-snap{scroll-snap-type:y mandatory;overscroll-behavior-y:contain;overflow-y:auto}.sf-scroll-snap>*{scroll-snap-align:start}.sf-overflow-fade{mask-image:linear-gradient(to right, black 0, black calc(100% - var(--sf-scroll-shadow-size)), transparent 100%);overflow:hidden}.sf-no-tap-highlight{-webkit-tap-highlight-color:transparent}.sf-surface--primary{background:var(--sf-color-primary);color:var(--sf-color-text--on-primary)}.sf-surface--secondary{background:var(--sf-color-secondary);color:var(--sf-color-text--on-secondary)}.sf-surface--tertiary{background:var(--sf-color-tertiary);color:var(--sf-color-text--on-tertiary)}.sf-surface--action{background:var(--sf-color-action);color:var(--sf-color-text--on-action)}.sf-surface--neutral{background:var(--sf-color-neutral);color:var(--sf-color-text--on-neutral)}.sf-surface--inverse{background:var(--sf-color-inverse);color:var(--sf-color-text--on-inverse)}.sf-surface--success{background:var(--sf-color-success);color:var(--sf-color-text--on-success)}.sf-surface--warning{background:var(--sf-color-warning);color:var(--sf-color-text--on-warning)}.sf-surface--error{background:var(--sf-color-error);color:var(--sf-color-text--on-error)}.sf-surface--info{background:var(--sf-color-info);color:var(--sf-color-text--on-info)}.sf-surface--danger{background:var(--sf-color-danger);color:var(--sf-color-text--on-danger)}.sf-text-gradient{background-image:var(--sf-gradient-primary);-webkit-background-clip:text;background-clip:text}@supports (background-clip:text){.sf-text-gradient{color:#0000}}.sf-link-external:after{content:var(--sf-link-external-marker);font-size:.85em;text-decoration:none;display:inline-block}}@layer slashed.utilities;@layer slashed.states{.is-hidden{display:none!important}.is-invisible{visibility:hidden}.is-visible{visibility:visible}.is-disabled{opacity:var(--sf-opacity-disabled);pointer-events:none;cursor:not-allowed;user-select:none}.is-readonly{pointer-events:none;user-select:none}.is-loading{pointer-events:none;position:relative;color:#0000!important}.is-loading:after{content:"";border:var(--sf-border-width-2) solid var(--sf-color-border--strong);border-block-start-color:var(--sf-color-action);border-radius:var(--sf-radius-full);width:1em;height:1em;animation:var(--sf-animation-spin);margin-block-start:-.5em;margin-inline-start:-.5em;position:absolute;inset-block-start:50%;inset-inline-start:50%}.is-busy{cursor:progress}.is-pending{opacity:var(--sf-state-pending-opacity);cursor:progress}.is-skeleton{background:linear-gradient(90deg, var(--sf-color-well) 25%, var(--sf-color-raised) 50%, var(--sf-color-well) 75%);animation:var(--sf-animation-shimmer);border-radius:var(--sf-radius-s);pointer-events:none;user-select:none;background-size:200% 100%;color:#0000!important}.is-active{--sf-is-active:1}.is-selected{background-color:var(--sf-color-bg--selected)}.is-current{--sf-is-current:1;font-weight:var(--sf-current-font-weight,var(--sf-font-weight-bold))}.is-highlighted{background-color:var(--sf-color-bg--focus)}.is-pressed{--sf-is-pressed:1;background-color:var(--sf-color-bg--selected)}.is-open{--sf-is-open:1}.is-collapsed{--sf-is-open:0}.is-expanded{--sf-is-open:1}.is-valid{--sf-field-border-color:var(--sf-color-success);--sf-field-text-color:var(--sf-color-success-strong)}.is-invalid{--sf-field-border-color:var(--sf-color-error);--sf-field-text-color:var(--sf-color-error-strong)}.is-warning{--sf-field-border-color:var(--sf-color-warning);--sf-field-text-color:var(--sf-color-warning-strong)}.is-success{--sf-field-border-color:var(--sf-color-success);--sf-field-text-color:var(--sf-color-success-strong)}.is-error{--sf-field-border-color:var(--sf-color-error);--sf-field-text-color:var(--sf-color-error-strong)}.is-info{--sf-field-border-color:var(--sf-color-info);--sf-field-text-color:var(--sf-color-info-strong)}.is-danger{--sf-field-border-color:var(--sf-color-danger);--sf-field-text-color:var(--sf-color-danger-strong)}.is-sticky{top:var(--sf-sticky-offset,0);z-index:var(--sf-z-low);position:sticky}.is-pinned{z-index:var(--sf-z-mid);position:sticky;top:0}.is-fixed{position:fixed}.is-fullscreen{z-index:var(--sf-z-max);position:fixed;inset:0}.is-resizable{resize:both;overflow:auto}.is-clipped{overflow:hidden!important}.is-scrollable{overscroll-behavior:contain;overflow:auto}.is-truncated{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.is-dragging{opacity:var(--sf-opacity-50);cursor:grabbing}.is-drop-target{outline:var(--sf-focus-ring-width) dashed var(--sf-color-action);outline-offset:var(--sf-focus-ring-offset)}.is-draggable{cursor:grab}.is-overlay{position:absolute;inset:0}.is-clickable{cursor:pointer}.is-unselectable{user-select:none}.is-focused{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-focus-ring-color);outline-offset:var(--sf-focus-ring-offset)}.is-empty:empty{display:none}}@layer slashed.themes{@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--sf-is-dark:1}}[data-theme=light]{color-scheme:light;--sf-is-dark:0}[data-theme=dark]{color-scheme:dark;--sf-is-dark:1}:root[data-lumlocker]{--sf-color-primary:light-dark(oklch(from var(--sf-color-primary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-primary-dark,var(--sf-color-primary-light)) var(--sf-lumlocker) c h));--sf-color-secondary:light-dark(oklch(from var(--sf-color-secondary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-secondary-dark,var(--sf-color-secondary-light)) var(--sf-lumlocker) c h));--sf-color-tertiary:light-dark(oklch(from var(--sf-color-tertiary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-tertiary-dark,var(--sf-color-tertiary-light)) var(--sf-lumlocker) c h));--sf-color-action:light-dark(oklch(from var(--sf-color-action-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-action-dark,var(--sf-color-action-light)) var(--sf-lumlocker) c h));--sf-color-neutral:light-dark(oklch(from var(--sf-color-neutral-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-neutral-dark,var(--sf-color-neutral-light)) var(--sf-lumlocker) c h))}}@layer slashed.motion{@media (prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}html:focus-within{scroll-behavior:auto}a,button,input,select,textarea,summary{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-duration:var(--sf-duration-fast);transition-timing-function:var(--sf-ease-out)}@supports (view-transition-name:none){::view-transition-old(root),::view-transition-new(root){animation-duration:var(--sf-duration-normal);animation-timing-function:var(--sf-ease-out)}}.sf-fade-in{animation:var(--sf-animation-fade-in)}.sf-fade-out{animation:var(--sf-animation-fade-out)}.sf-slide-in-up{animation:var(--sf-animation-slide-in-up)}.sf-slide-in-down{animation:var(--sf-animation-slide-in-down)}.sf-slide-in-left{animation:var(--sf-animation-slide-in-left)}.sf-slide-in-right{animation:var(--sf-animation-slide-in-right)}.sf-scale-up{animation:var(--sf-animation-scale-up)}.sf-scale-down{animation:var(--sf-animation-scale-down)}.sf-entrance--fade,.sf-entrance--fade-up,.sf-entrance--fade-down,.sf-entrance--fade-left,.sf-entrance--fade-right,.sf-entrance--scale-up{animation-duration:var(--sf-duration-slow);animation-timing-function:var(--sf-ease-out);animation-fill-mode:both}@supports (animation-timeline:view()){.sf-entrance--fade,.sf-entrance--fade-up,.sf-entrance--fade-down,.sf-entrance--fade-left,.sf-entrance--fade-right,.sf-entrance--scale-up{animation-timeline:view();animation-range:var(--sf-scroll-timeline-range-start,entry 0%) var(--sf-scroll-timeline-range-end,cover 30%)}}.sf-entrance--fade{animation-name:sf-fade-in}.sf-entrance--fade-up{animation-name:sf-slide-in-up}.sf-entrance--fade-down{animation-name:sf-slide-in-down}.sf-entrance--fade-left{animation-name:sf-slide-in-left}.sf-entrance--fade-right{animation-name:sf-slide-in-right}.sf-entrance--scale-up{animation-name:sf-scale-up}}@keyframes sf-fade-in{0%{opacity:0}to{opacity:1}}@keyframes sf-fade-out{0%{opacity:1}to{opacity:0}}@keyframes sf-slide-in-up{0%{opacity:0;translate:0 1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-down{0%{opacity:0;translate:0 -1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-left{0%{opacity:0;translate:1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-right{0%{opacity:0;translate:-1rem}to{opacity:1;translate:0}}@keyframes sf-scale-up{0%{opacity:0;scale:.92}to{opacity:1;scale:1}}@keyframes sf-scale-down{0%{opacity:1;scale:1}to{opacity:0;scale:.92}}@keyframes sf-spin{to{rotate:360deg}}@keyframes sf-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@keyframes sf-ping{75%,to{opacity:0;scale:2}}@keyframes sf-blink{0%,50%{opacity:1}50.01%,to{opacity:0}}@keyframes sf-float{0%,to{translate:0}50%{translate:0 -.5rem}}@keyframes sf-color-pulse{0%{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) l c h)}50%{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) calc(l + .25) c h)}to{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) l c h)}}@media (prefers-reduced-motion:no-preference){.sf-color-pulse{animation:var(--sf-animation-color-pulse)}}}@layer slashed.accessibility{:focus:not(:focus-visible){outline:none}:focus-visible{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-focus-ring-color)!important;outline-offset:var(--sf-focus-ring-offset)!important}@media (prefers-reduced-motion:reduce){:root{--sf-duration-instant:.01ms;--sf-duration-fast:.01ms;--sf-duration-normal:.01ms;--sf-duration-slow:.01ms;--sf-duration-slower:.01ms}*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.no-motion,.no-motion *,.no-motion :before,.no-motion :after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}@media (prefers-contrast:more){:root{--sf-focus-ring-width:3px;--sf-contrast-bias:.06}hr{border-block-start-width:var(--sf-border-width-2)}}@media (prefers-reduced-transparency:reduce){::backdrop{background-color:var(--sf-color-neutral);opacity:1}}@media (pointer:coarse){button,input[type=button],input[type=submit],input[type=reset],select,summary,a{min-height:var(--sf-touch-target);min-width:var(--sf-touch-target)}}[disabled],[aria-disabled=true]{cursor:not-allowed}.sr-only,.visually-hidden,.sr-only-focusable:not(:focus,:focus-within){clip-path:inset(50%)!important;white-space:nowrap!important;border:0!important;width:1px!important;height:1px!important;margin:-1px!important;padding:0!important;position:absolute!important;overflow:clip!important}.skip-link{top:-100%;left:var(--sf-space-m);z-index:var(--sf-z-max);padding:var(--sf-space-s) var(--sf-space-m);background:var(--sf-color-bg);color:var(--sf-color-text);border:var(--sf-border-width-1) solid var(--sf-color-border);border-radius:var(--sf-radius-m);transition:top var(--sf-duration-fast) var(--sf-ease-out);position:absolute}.skip-link:focus{top:var(--sf-space-m)}.focus-parent:focus-within{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}.sf-clickable-parent{--sf-clickable-overlay-z:1;cursor:pointer;position:relative}.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):after,.sf-clickable-parent [data-overlay-link]:after{content:"";z-index:var(--sf-clickable-overlay-z);pointer-events:auto;position:absolute;inset:0}.sf-clickable-parent :is(button,input,textarea,select,summary,video,audio,[role=button],[data-no-overlay]),.sf-clickable-parent:has([data-overlay-link]) a:where(:any-link):not([data-overlay-link]),.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):not(:first-of-type){z-index:calc(var(--sf-clickable-overlay-z) + 1);position:relative}.sf-clickable-parent:focus-within{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}@media (forced-colors:active){:root{--sf-focus-ring-color:Highlight}:focus-visible{outline-color:highlight!important}:where(*){box-shadow:none;text-shadow:none}input,select,textarea{border:var(--sf-border-width-1) solid}}}@layer slashed.print{.print-only{display:none}@media print{@page{size:var(--sf-print-page-size,a4);margin:var(--sf-print-page-margin,2cm)}body{font-size:var(--sf-print-base-size)}*,:before,:after{box-shadow:none;text-shadow:none}a[href]:after{content:" (" attr(href) ")";word-break:break-all;font-size:90%}a[href^=\#]:after,a[href^=javascript\: i]:after{content:""}abbr[title]:after{content:" (" attr(title) ")"}img,svg,video,canvas,figure,table,pre,blockquote{break-inside:avoid}thead{display:table-header-group}tr{break-inside:avoid}h1,h2,h3,h4,h5,h6{break-after:avoid;orphans:3;widows:3}p{orphans:3;widows:3}pre,blockquote{border:var(--sf-border-width-1) solid var(--sf-color-border--strong);padding:var(--sf-space-s)}nav,aside,button,input,select,textarea,audio,video,dialog,[popover],.no-print{display:none!important}.print-only{display:revert!important}details{display:block}details>summary{display:none!important}details:not([open])>:not(summary){display:block}.print-color-exact,.print-color-exact *{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}.print-no-color,.print-no-color *,.print-no-color :before,.print-no-color :after{color:canvastext!important;background:0 0!important}}}@layer slashed.legacy,slashed.overrides; /*# sourceMappingURL=slashed.essential.min.css.map */ diff --git a/dist/slashed.essential.min.css.map b/dist/slashed.essential.min.css.map index 4c5a58f9..c52cae2d 100644 --- a/dist/slashed.essential.min.css.map +++ b/dist/slashed.essential.min.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":null,"mappings":"AAOA,sBA6JE,kGACA,qGACA,oGACA,kGACA,mGACA,iGAIA,mGACA,kGACA,+FACA,gGACA,iGAIA,wEAEA,0EACA,2EACA,2EACA,wEAOA,svyBA1LF,qBA6sCE,0DAMA,mLAaA,4CACE,sCAGF,6CAKA,2BAIA,sEAMA,0EAOA,wHAQA,2BAIA,oCAIA,qBAIA,2CAMA,iBAIA,iEAIA,uCAKA,yBAIA,sBAIA,gDAKA,gCAIA,oEAOA,wBAzzCF,oBA00CE,mWAUA,yVAUA,oJACA,oJACA,oJACA,oJACA,oJACA,oJAEA,iFAOA,kDAIA,sCAIA,qCAIA,iJAOA,yDAKA,2DAKA,8EAMA,cACA,kBAcA,oRASA,6EACA,8CACA,4CACA,6CAEA,gCAIA,+MASA,iRAWA,2FASA,sBAIA,0GAKA,mFAIA,0GAMA,4BAIA,uCAIA,iDAIA,mDAIA,gDAIA,sBACA,oCACA,6EAIA,8IAMA,6CAIA,yCAIA,kCAKA,6CAIA,4IAjhDF,qBAAA,sBA0oDE,gDACA,wDACA,sDACA,sDACA,sDACA,wDACA,0DAEA,gEAgBA,oGAGA,gGAQA,kJAKA,4MAkBA,wJAOA,2DACA,yDACA,uDACA,uDAOA,qEAKA,qCACA,mCACA,mCACA,mCACA,qCACA,uCAEA,qCACA,oCACA,uCAQA,0GAWA,iIAOA,4EAWA,gJAOA,uCACA,qCACA,qCACA,qCACA,uCACA,yCAEA,sCACA,2CACA,0CACA,mDAQA,kEAMA,wEAKA,+HAYA,8EAKA,6CACA,2CAQA,oEAMA,wFAKA,uDACA,6CASA,iIAMA,iGAIA,iDACA,+CACA,+CACA,+CACA,iDACA,mDAIA,qCAMA,0LAQA,8CACA,4CACA,4CACA,4CACA,8CAOA,uVAgBA,yHAOA,8CAIA,gCACA,8CACA,sDACA,uDAOA,oHAQA,sEAOA,sDACA,oDACA,sDACA,0DACA,gDACA,gDACA,sDAOA,uNAWA,sEAUA,gGAOA,mCAIA,yJAgBA,0FAMA,kFAMA,sCACE,8CAGA,kDACA,kDAOF,4EAUA,2FAKA,yBACE,+CACA,gDAGF,yBACE,+CACA,+CACA,+CACA,gDAOF,wFAMA,yBACE,2CACA,2CACA,2CACA,4CAQF,saAgBA,uCACA,mDACA,iDAOA,sOAQA,8CACE,+CAGF,+BACA,+BACA,8DACA,wDAMA,uDACA,0DAhnEF,0BAAA,sBAmpEE,uEAKA,2DACA,+DACA,sDACA,6DACA,8EACA,iDACA,gCACA,gCACA,iDACA,gDACA,wHACA,4FACA,gFAKA,+EAMA,iFACA,+CACA,2DACA,8CACA,wFACA,+FACA,6DAQA,qDAcA,uEAMA,mHAOA,mDACA,mDACA,yFAUA,kDAIA,6CAWA,yCAUA,8LAeA,2FAKA,0CASA,mJAeA,6DAiBA,+FACA,qGACA,kGACA,4FACA,+FACA,+FACA,+FACA,+FACA,yFACA,sFACA,4FAmBA,gHAMA,iCACE,+BAmBF,0HAj3EF,yBAAA,sBAq5EE,kCAIA,gCAIA,+BAQA,wGAOA,kDASA,wEAMA,gWAkBA,yBAIA,oEAOA,2RAmBA,4BAMA,2DAIA,mGASA,2DAIA,4EAeA,wBAIA,6BAIA,4BAgBA,+GAKA,6GAKA,iHAKA,iHAKA,2GAKA,wGAKA,8GASA,iFAMA,yDAMA,yBAIA,8DASA,wCASA,sCAIA,yDAKA,wEAUA,0DAKA,4HAKA,0BAQA,sCASA,6BAIA,kCAIA,sJAWA,8BAnrFF,sBAikDE,mCACE,0DAQF,qDACA,mDAuBA,+9BAlmDF,sBAstFE,8CAEE,4BAKA,uCAMA,+QASA,sCACE,mJAaF,kDACA,oDACA,0DACA,8DACA,8DACA,gEACA,oDACA,wDA4BA,0PAUA,sCACE,iRAWF,6CACA,oDACA,wDACA,wDACA,0DACA,mDASF,iDACA,kDACA,kFACA,qFACA,kFACA,oFACA,oEACA,sEAEA,qCACA,oFAMA,6CACA,0DACA,8DAMA,4QAMA,8CACE,4DAh2FJ,6BAq4FE,wCAIA,6KAWA,uCACE,0IAQA,8JAaF,yMAcA,+BACE,uDAOA,sDAQF,6CACE,+DAYF,wBACE,oJAeF,mDAUA,6RAmBA,mVAYA,uCAYA,wKA4CA,iFAYA,2NAgBA,kWAeA,+KAYA,8BACE,sCAIA,iDAIA,2CAQA,8DA9oGJ,qBAyrGE,yBAIA,aAEE,+EAKA,yCAMA,kDASA,6EAMA,2DAKA,+CAIA,oEAIA,iCAIA,sBAIA,uDAMA,qBAKA,8GASA,qGAMA,qCASA,sBAGA,uCAGA,gDASA,sHAUA,uIA1yGJ","sources":["slashed.essential.css"],"sourcesContent":["/* SLASHED v0.3.0 — slashed.essential.css */\n\n/* ─── core/layers.css ─── */\n/* SLASHED — core/layers.css\n CSS cascade layer order. Load this file first, always.\n ─────────────────────────────────────────────────── */\n\n@layer\n slashed.tokens,\n slashed.reset,\n slashed.base,\n slashed.forms,\n slashed.layout,\n slashed.components,\n slashed.macros,\n slashed.utilities,\n slashed.states,\n slashed.themes,\n slashed.motion,\n slashed.accessibility,\n slashed.print,\n slashed.legacy,\n slashed.overrides;\n\n/* ─── core/tokens.css ─── */\n/* ============================================================\n SLASHED — core/tokens.css\n @layer slashed.tokens\n\n ARCHITECTURE — 3 layers:\n 1. @property source tokens — animatable, 6 brand + 5 status (-light only); -dark is optional\n 2. Resolved semantic tokens — light-dark() with auto-derivation of dark from light (relative color syntax)\n 3. Derived tokens — relative color syntax (auto-contrast, tints/shades)\n\n THEMING — override just the 6 -light tokens. Dark mode auto-adapts via\n relative color syntax — zero extra work. For full control over dark mode,\n optionally override the -dark tokens (unregistered custom properties).\n\n Quick branding (6 tokens):\n :root {\n --sf-color-primary-light: oklch(0.45 0.20 264);\n --sf-color-secondary-light: oklch(0.25 0.03 260);\n --sf-color-tertiary-light: oklch(0.48 0.14 310);\n --sf-color-action-light: oklch(0.60 0.16 210);\n --sf-color-neutral-light: oklch(0.45 0.02 260);\n --sf-color-base-light: oklch(0.98 0.005 260);\n }\n Full per-mode control (add -dark overrides):\n :root {\n --sf-color-primary-dark: oklch(0.75 0.18 264);\n }\n\n BEM CONSUMER API — most tokens are intentionally unused by the\n framework itself. Tokens such as --sf-shadow-*, --sf-blur-*, --sf-gap,\n --sf-gradient-* etc. are meant for use in your own BEM classes:\n .card { box-shadow: var(--sf-shadow-m); gap: var(--sf-gap); }\n \"Not used internally\" does NOT mean dead code: every token is exercised\n in docs/demo.html and validated by the regression suite (tests/), so an\n unreferenced token is never untested or orphaned.\n\n RELATION TO tokens.layout.css — the spacing aliases in the \"Spacing\n aliases\" section are the source of truth for --sf-space-gap and\n --sf-space-content in tokens.layout.css. Overriding --sf-gap changes the\n gaps across all layout primitives (cluster, grid, sidebar, etc.).\n\n PUBLIC API vs INTERNAL (token-API freeze contract)\n ─────────────────────────────────────────────────\n Tokens are classified into three stability tiers (PUBLIC,\n PUBLIC-ADVANCED, INTERNAL).\n PUBLIC and PUBLIC-ADVANCED share the same SemVer guarantee\n (removal/rename = major bump after freeze). The distinction is\n documentation-level: ADVANCED tokens are powerful but niche — they\n live in a separate \"Advanced\" section in docs.\n\n PUBLIC — everyday knobs, prominently documented:\n · brand source tokens (--sf-color-{primary,secondary,tertiary,action,neutral,base}-light)\n · status source tokens (--sf-color-{success,warning,error,info,danger}-light)\n · resolved semantic colors (--sf-color-{primary,...,danger}, surfaces, text, borders, links, interactive bg, selection)\n · text-on-color (--sf-color-text--on-*)\n · code colors (--sf-color-code-bg, --sf-color-code-text)\n · status triplets (--sf-color-{success,...,danger}-{subtle,strong,muted})\n · gradients (--sf-gradient-*)\n · shadows (--sf-shadow-{none,xs,s,m,l,xl,2xl,inner})\n · text/drop shadows (--sf-text-shadow-*, --sf-drop-shadow-*)\n · blur (--sf-blur-*)\n · opacity (--sf-opacity-{0,10,25,50,75,100}, --sf-opacity-disabled, --sf-state-pending-opacity)\n · spacing (--sf-space-{none,px,gutter,2xs..4xl})\n · font families (--sf-font-{body,heading,display,mono,humanist,geometric,slab})\n · font weights (--sf-font-weight-{thin..black,body,heading,display})\n · font sizes (--sf-text-{2xs..4xl}, --sf-text-display-{s,m,l})\n · leading (--sf-leading-{tight,snug,normal,relaxed})\n · tracking (--sf-tracking-{tight,normal,wide,wider,widest})\n · icon sizes (--sf-icon-{xs,s,m,l,xl})\n · UI sizes (--sf-size-{xs,s,m,l,xl})\n · containers (--sf-container-{narrow,prose,default,wide,full})\n · ratios (--sf-ratio-{square,video,cinema,portrait,golden,4-3,3-2})\n · border widths (--sf-border-width-{hairline,1,2,3,4})\n · border styles (--sf-border-style, --strong, --soft, --dotted)\n · divider (--sf-divider-{width,style,color})\n · radius (--sf-radius-{none,xs,s,m,l,xl,2xl,3xl,4xl,full,pill})\n · duration (--sf-duration-{none,instant,fast,normal,slow,slower})\n · easing (--sf-ease-{linear,out,in,in-out,spring,elastic,bounce,overshoot})\n · transitions (--sf-transition-{all,colors,transform,opacity,shadow,fast,slow,enter,exit})\n · animations (--sf-animation-{fade-in,fade-out,slide-in-*,scale-*,color-pulse,ping,blink,float,spin,shimmer})\n · animation delays (--sf-animation-delay-{1..5})\n · z-index (--sf-z-{below,base,raised,low,mid,high,top,max})\n · header/sticky (--sf-header-height, --sf-sticky-offset)\n · touch target (--sf-touch-target)\n · focus ring (--sf-focus-ring-{width,offset,style,color})\n · caret (--sf-caret-color)\n · scrollbar (--sf-scrollbar-{thumb,track})\n · BEM aliases (--sf-gap, --sf-content-gap, --sf-component-pad, --sf-field-block,\n --sf-field-required-marker, --sf-link-external-marker)\n · section padding (--sf-section-pad, --sf-section-pad--{xs,s,m,l,xl,2xl})\n · typography aliases (--sf-body-*, --sf-code-font-size, --sf-heading-*, --sf-h{1..6}-*)\n · current-font-weight (--sf-current-font-weight)\n · color scheme (--sf-color-scheme)\n · stroke (--sf-stroke-{thin,regular,bold,heavy})\n · layout tokens — all of tokens.layout.css (per-primitive gaps, sidebar, grid, bento, etc.)\n · macro tokens — all of tokens.macros.css (flow-space, line-clamp, aspect, scroll-shadow-size)\n · palette tokens — all of optional/tokens.palette.css\n · scoped override tokens (--sf-icon-size, --sf-bento-cols, --sf-bento-row,\n --sf-color-code-block-bg, --sf-color-code-block-text,\n --sf-field-border-color, --sf-field-text-color)\n\n PUBLIC-ADVANCED — stable (same SemVer guarantee) but niche/powerful:\n · --sf-lumlocker (LumLocker lightness override)\n · --sf-contrast-bias, --sf-contrast-threshold (text-contrast tuning)\n · --sf-shadow-strength, --sf-shadow-color, --sf-shadow-glow-color (shadow internals)\n · --sf-font-features, --sf-font-variation, --sf-optical-sizing (OpenType)\n · --sf-space-scale, --sf-text-scale, --sf-text-display-scale,\n --sf-radius-scale, --sf-motion-scale (global multipliers)\n · --sf-scroll-timeline-range-{start,end} (scroll-driven animation)\n · --sf-mask-scrim-{start,end} (edge-fade masks)\n · --sf-safe-{top,bottom,left,right} (safe-area insets)\n · --sf-perspective-{near,normal,far} (3D transforms)\n · --sf-truncate-suffix (ellipsis character override)\n · --sf-radius-outer (concentric radius helper)\n · --sf-is-active, --sf-is-current, --sf-is-pressed, --sf-is-open (state flags)\n · --sf-focus-ring-shadow (composite focus shadow)\n · --sf-print-{page-margin,page-size,base-size} (print tokens)\n\n INTERNAL — implementation detail, may change without a major bump:\n · --sf-is-dark (mode flag — set via data-theme, not directly)\n\n CANONICAL-SOURCE ALIASES (intentional synonym hops, kept by design):\n · --sf-space-gap → --sf-gap (layout primitives read this name)\n · --sf-space-content → --sf-content-gap (stack / prose rhythm)\n · --sf-section-pad → --sf-section-pad--m (unsuffixed = the -m default)\n These are deliberate two-name contracts: override the canonical token\n (right side) to move everything downstream. The alias graph is otherwise\n ≤3 hops (per-primitive → layout-system → canonical source), giving three\n override levels: local (style=\"--sf-cluster-gap\"), all-primitives\n (--sf-space-gap), or global (--sf-gap).\n ============================================================ */\n\n@layer slashed.tokens {\n\n /* ============================================================\n @property — BRAND COLORS (-light source tokens, animatable)\n Override these 6 tokens to rebrand — dark mode auto-derives.\n Optionally set --sf-color-X-dark (no @property) for full control.\n ============================================================ */\n\n @property --sf-color-primary-light { syntax: \"\"; inherits: true; initial-value: oklch(0.45 0.20 264); }\n @property --sf-color-secondary-light { syntax: \"\"; inherits: true; initial-value: oklch(0.25 0.03 260); }\n @property --sf-color-tertiary-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.14 310); }\n @property --sf-color-action-light { syntax: \"\"; inherits: true; initial-value: oklch(0.60 0.16 210); }\n @property --sf-color-neutral-light { syntax: \"\"; inherits: true; initial-value: oklch(0.45 0.02 260); }\n @property --sf-color-base-light { syntax: \"\"; inherits: true; initial-value: oklch(0.98 0.005 260); }\n\n /* @property — STATUS COLORS (-light source tokens) */\n\n @property --sf-color-success-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.17 150); }\n @property --sf-color-warning-light { syntax: \"\"; inherits: true; initial-value: oklch(0.75 0.17 80); }\n @property --sf-color-error-light { syntax: \"\"; inherits: true; initial-value: oklch(0.62 0.20 35); }\n @property --sf-color-info-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.15 240); }\n @property --sf-color-danger-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.24 12); }\n\n /* Mode flag — drives formula direction for non-color dark overrides.\n Set by base.css via color-scheme switches. */\n @property --sf-is-dark { syntax: \"\"; inherits: true; initial-value: 0; }\n\n @property --sf-is-active { syntax: \"\"; inherits: true; initial-value: 0; }\n @property --sf-is-current { syntax: \"\"; inherits: true; initial-value: 0; }\n @property --sf-is-pressed { syntax: \"\"; inherits: true; initial-value: 0; }\n @property --sf-is-open { syntax: \"\"; inherits: true; initial-value: 0; }\n\n\n /* ============================================================\n DERIVED SEMANTIC TOKENS\n ============================================================ */\n\n :root {\n\n /* ----------------------------------------------------------\n Color scheme\n ---------------------------------------------------------- */\n --sf-color-scheme: light dark;\n\n /* ----------------------------------------------------------\n LumLocker — OKLCH L value used by the\n :root[data-lumlocker] override in core/themes.css.\n Locks all 5 brand colors (primary, secondary, tertiary,\n action, neutral) to one shared lightness while keeping their\n individual hue and chroma. Base is excluded — it must remain\n a light surface for page background.\n Override to change the locked lightness:\n :root { --sf-lumlocker: 0.55 }\n ---------------------------------------------------------- */\n --sf-lumlocker: 0.65;\n\n /* ----------------------------------------------------------\n Resolved color tokens — auto-switch via light-dark().\n Components reference these. Override the -light / -dark\n source tokens above to customise; or override these directly\n with your own light-dark() expression.\n ---------------------------------------------------------- */\n /* Dark auto-derivation formula (brand + status):\n clamp(0.65, 0.95 - l*0.5, 0.88) lightens dark-mode value relative to the light source.\n Base inverts: clamp(0.16, 1.18 - l, 0.24) — near-white flips to near-dark.\n Override any --sf-color-X-dark to take full per-mode control. */\n --sf-color-primary: light-dark(var(--sf-color-primary-light), var(--sf-color-primary-dark, oklch(from var(--sf-color-primary-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-secondary: light-dark(var(--sf-color-secondary-light), var(--sf-color-secondary-dark, oklch(from var(--sf-color-secondary-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-tertiary: light-dark(var(--sf-color-tertiary-light), var(--sf-color-tertiary-dark, oklch(from var(--sf-color-tertiary-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-action: light-dark(var(--sf-color-action-light), var(--sf-color-action-dark, oklch(from var(--sf-color-action-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-neutral: light-dark(var(--sf-color-neutral-light), var(--sf-color-neutral-dark, oklch(from var(--sf-color-neutral-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-base: light-dark(var(--sf-color-base-light), var(--sf-color-base-dark, oklch(from var(--sf-color-base-light) clamp(0.16, calc(1.18 - l), 0.24) calc(c * 0.5) h)));\n\n --sf-color-success: light-dark(var(--sf-color-success-light), var(--sf-color-success-dark, oklch(from var(--sf-color-success-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-warning: light-dark(var(--sf-color-warning-light), var(--sf-color-warning-dark, oklch(from var(--sf-color-warning-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-error: light-dark(var(--sf-color-error-light), var(--sf-color-error-dark, oklch(from var(--sf-color-error-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-info: light-dark(var(--sf-color-info-light), var(--sf-color-info-dark, oklch(from var(--sf-color-info-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-danger: light-dark(var(--sf-color-danger-light), var(--sf-color-danger-dark, oklch(from var(--sf-color-danger-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n\n\n /* ----------------------------------------------------------\n Surfaces — derived from --sf-color-base (auto-adapts)\n ---------------------------------------------------------- */\n --sf-color-bg: oklch(from var(--sf-color-base) calc(l + 0.02) c h);\n --sf-color-surface: var(--sf-color-base);\n --sf-color-well: oklch(from var(--sf-color-base) calc(l - 0.02) c h);\n --sf-color-raised: oklch(from var(--sf-color-base) calc(l + 0.04) c h);\n --sf-color-overlay: oklch(from var(--sf-color-base) l c h / 0.9);\n --sf-color-inverse: oklch(from var(--sf-color-base) calc(1 - l) c h);\n\n\n /* ----------------------------------------------------------\n Text — light and dark require opposite formula directions,\n so each side of light-dark() references its source token.\n ---------------------------------------------------------- */\n --sf-color-text: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h),\n oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1) c h)\n );\n --sf-color-text--secondary: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.15, calc(l - 0.25 - var(--sf-contrast-bias)), 0.45) c h),\n oklch(from var(--sf-color-neutral) clamp(0.55, calc(l + 0.1 + var(--sf-contrast-bias)), 0.90) c h)\n );\n --sf-color-text--muted: var(--sf-color-neutral);\n --sf-color-text--placeholder: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.45, calc(l + 0.15), 0.75) c h),\n oklch(from var(--sf-color-neutral) clamp(0.35, calc(l - 0.1), 0.65) c h)\n );\n --sf-color-text--disabled: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.55, calc(l + 0.25), 0.82) c h),\n oklch(from var(--sf-color-neutral) clamp(0.25, calc(l - 0.2), 0.55) c h)\n );\n --sf-color-text--inverse: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.85, calc(l + 0.4), 0.98) c h),\n oklch(from var(--sf-color-neutral) clamp(0.05, calc(l - 0.4), 0.35) c h)\n );\n --sf-color-heading: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h),\n oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1) c h)\n );\n /* Inline-code text colour. Auto-contrasts with --sf-color-code-bg using\n the same luminance formula as --sf-color-text--on-*. This prevents\n invisible text when code appears inside inverted-colour containers\n (e.g. sf-imposter with a dark background where inherited colour is\n near-white). Override with a tint value if desired. */\n --sf-color-code-text: oklch(from var(--sf-color-code-bg) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n\n\n /* ----------------------------------------------------------\n Text-on-color — sign(var(--sf-contrast-threshold) - l) * 999 is mode-agnostic:\n auto-selects light or dark text based on background luminance.\n Uses resolved tokens — works for any user-supplied color.\n\n CONTRAST NOTE: Guarantees ≥ 3:1 (WCAG AA Large Text / UI).\n Colors in the mid-luminance range (L ≈ 0.52–0.67) may not\n reach 4.5:1 (AA Normal Text) regardless of threshold chosen —\n this is a mathematical limitation of any binary black/white\n decision. The SLASHED configurator will warn users when their\n brand colors fall in this range. For body text on colored\n backgrounds, override --sf-color-text--on-* manually.\n ---------------------------------------------------------- */\n --sf-color-text--on-primary: oklch(from var(--sf-color-primary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-secondary: oklch(from var(--sf-color-secondary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-tertiary: oklch(from var(--sf-color-tertiary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-action: oklch(from var(--sf-color-action) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-neutral: oklch(from var(--sf-color-neutral) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-base: var(--sf-color-text);\n --sf-color-text--on-inverse: var(--sf-color-text--inverse);\n --sf-color-text--on-success: oklch(from var(--sf-color-success) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-warning: oklch(from var(--sf-color-warning) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-error: oklch(from var(--sf-color-error) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-info: oklch(from var(--sf-color-info) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-danger: oklch(from var(--sf-color-danger) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n\n\n /* ----------------------------------------------------------\n Borders — direction-dependent, reference source tokens.\n Light: borders are lighter than neutral (l + offset).\n Dark: borders are darker than neutral (l - offset).\n ---------------------------------------------------------- */\n --sf-color-border: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.70, calc(l + 0.35), 0.95) 0.005 h),\n oklch(from var(--sf-color-neutral) clamp(0.25, calc(l - 0.3), 0.55) 0.005 h)\n );\n --sf-color-border--subtle: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.75, calc(l + 0.4), 0.97) 0.005 h),\n oklch(from var(--sf-color-neutral) clamp(0.20, calc(l - 0.38), 0.45) 0.005 h)\n );\n --sf-color-border--strong: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.55, calc(l + 0.1), 0.85) 0.02 h),\n oklch(from var(--sf-color-neutral) clamp(0.38, calc(l - 0.1), 0.65) 0.02 h)\n );\n --sf-color-border--focus: var(--sf-color-action);\n --sf-color-border--disabled: oklch(from var(--sf-color-border--subtle) l 0 h / 0.5);\n --sf-color-border--translucent: oklch(from var(--sf-color-neutral) l c h / 0.15);\n\n\n /* ----------------------------------------------------------\n Links — derived from --sf-color-action (auto-adapts)\n ---------------------------------------------------------- */\n /* Links keep the action hue but CLAMP lightness toward a contrast-safe\n band — a ceiling in light mode, a floor in dark mode — so link text\n clears WCAG AA (4.5:1) on the page background for the default palette\n AND the large majority of brand overrides (a fixed offset alone only\n fixed the default). Lightness dominates OKLCH contrast but doesn't\n fully determine it: very high-chroma hues (saturated yellow/green) can\n still fall short even at a safe lightness — those remain the consumer's\n responsibility (see docs/theming.md → \"Link contrast\").\n\n NOTE: the consumer-facing :link rule in core/base.css uses\n `a:link` (not bare `a`) so its specificity matches WebKit's UA\n stylesheet `a:link { color: -webkit-link }`. Without that\n pseudo-class the framework's `--sf-color-link` value is\n silently ignored on unvisited anchors in WebKit and\n `-webkit-link` (≈ #00728f in dark mode) paints instead —\n regardless of how this token is computed. PR #73 and several\n follow-up attempts mistakenly chased the formula. */\n --sf-color-link: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.07, 0.48), 1) c h),\n oklch(from var(--sf-color-action) clamp(0.68, l, 1) c h)\n );\n --sf-color-link--hover: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.15, 0.40), 1) c h),\n oklch(from var(--sf-color-action) clamp(0, max(l + 0.10, 0.68), 1) c h)\n );\n --sf-color-link--active: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.21, 0.34), 1) c h),\n oklch(from var(--sf-color-action) clamp(0, max(l + 0.15, 0.74), 1) c h)\n );\n /* Visited link — 60° hue shift from action gives clearly distinct\n chroma without colliding with default tertiary (h ≈ 310). Same\n lightness clamp as the base link. If your brand puts action and\n tertiary close in hue, override directly:\n :root { --sf-color-link--visited: #6b46c1; } */\n --sf-color-link--visited: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.07, 0.48), 1) c calc(h + 60)),\n oklch(from var(--sf-color-action) clamp(0.68, l, 1) c calc(h + 60))\n );\n --sf-color-link--underline: oklch(from var(--sf-color-action) l c h / 0.3);\n --sf-color-link--disabled: var(--sf-color-text--disabled);\n\n\n /* ----------------------------------------------------------\n Interactive states — derived from --sf-color-neutral (auto-adapts)\n ---------------------------------------------------------- */\n --sf-color-bg--hover: oklch(from var(--sf-color-neutral) l c h / 0.08);\n --sf-color-bg--active: oklch(from var(--sf-color-neutral) l c h / 0.12);\n --sf-color-bg--selected: oklch(from var(--sf-color-action) l c h / 0.1);\n --sf-color-bg--focus: oklch(from var(--sf-color-action) l c h / 0.06);\n --sf-color-bg--disabled: var(--sf-color-well);\n --sf-color-code-bg: var(--sf-color-well);\n\n /* ----------------------------------------------------------\n Selection & backdrop\n ---------------------------------------------------------- */\n --sf-color-selection-bg: oklch(from var(--sf-color-action) l c h / 0.22);\n --sf-color-selection-text: inherit;\n --sf-color-mark-bg: oklch(from var(--sf-color-warning) l c h / 0.25);\n --sf-color-mark-text: inherit;\n --sf-color-dim: oklch(0 0 0 / 0.5);\n\n\n /* ----------------------------------------------------------\n Scrollbar\n ---------------------------------------------------------- */\n --sf-scrollbar-thumb: var(--sf-color-neutral);\n --sf-scrollbar-track: transparent;\n\n\n /* ----------------------------------------------------------\n Status triplets — bg/border use same formula both modes;\n text is direction-dependent so uses source tokens.\n error = form validation, input errors\n danger = destructive actions (delete, remove)\n ---------------------------------------------------------- */\n --sf-color-success-subtle: oklch(from var(--sf-color-success) l c h / 0.12);\n --sf-color-success-strong: light-dark(\n oklch(from var(--sf-color-success-light) calc(l - 0.15) c h),\n oklch(from var(--sf-color-success) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-success-muted: oklch(from var(--sf-color-success) l c h / 0.3);\n\n --sf-color-warning-subtle: oklch(from var(--sf-color-warning) l c h / 0.12);\n --sf-color-warning-strong: light-dark(\n oklch(from var(--sf-color-warning-light) calc(l - 0.25) c h),\n oklch(from var(--sf-color-warning) clamp(0.70, calc(l + 0.05), 1) c h)\n );\n --sf-color-warning-muted: oklch(from var(--sf-color-warning) l c h / 0.3);\n\n --sf-color-error-subtle: oklch(from var(--sf-color-error) l c h / 0.1);\n --sf-color-error-strong: light-dark(\n oklch(from var(--sf-color-error-light) calc(l - 0.1) c h),\n oklch(from var(--sf-color-error) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-error-muted: oklch(from var(--sf-color-error) l c h / 0.3);\n\n --sf-color-info-subtle: oklch(from var(--sf-color-info) l c h / 0.1);\n --sf-color-info-strong: light-dark(\n oklch(from var(--sf-color-info-light) calc(l - 0.1) c h),\n oklch(from var(--sf-color-info) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-info-muted: oklch(from var(--sf-color-info) l c h / 0.3);\n\n --sf-color-danger-subtle: oklch(from var(--sf-color-danger) l c h / 0.1);\n --sf-color-danger-strong: light-dark(\n oklch(from var(--sf-color-danger-light) calc(l - 0.1) c h),\n oklch(from var(--sf-color-danger) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-danger-muted: oklch(from var(--sf-color-danger) l c h / 0.3);\n\n\n /* ----------------------------------------------------------\n Gradients — derived from brand colors (auto-adapt)\n ---------------------------------------------------------- */\n --sf-gradient-primary: linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) calc(l - 0.08) c h));\n --sf-gradient-secondary: linear-gradient(135deg, var(--sf-color-secondary), oklch(from var(--sf-color-secondary) calc(l - 0.08) c h));\n --sf-gradient-tertiary: linear-gradient(135deg, var(--sf-color-tertiary), oklch(from var(--sf-color-tertiary) calc(l - 0.08) c h));\n --sf-gradient-brand: linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) l c calc(h + 30)));\n --sf-gradient-surface: linear-gradient(180deg, var(--sf-color-surface), var(--sf-color-bg));\n\n --sf-gradient-fade--r: linear-gradient(to right, transparent, var(--sf-color-bg));\n --sf-gradient-fade--l: linear-gradient(to left, transparent, var(--sf-color-bg));\n --sf-gradient-fade--t: linear-gradient(to top, transparent, var(--sf-color-bg));\n --sf-gradient-fade--b: linear-gradient(to bottom, transparent, var(--sf-color-bg));\n\n\n /* ----------------------------------------------------------\n Shadow — strength scales up in dark mode via --sf-is-dark\n ---------------------------------------------------------- */\n --sf-shadow-strength: calc(0.08 + var(--sf-is-dark) * 0.17);\n --sf-shadow-glow-color: var(--sf-color-primary);\n --sf-shadow-glow: 0 0 15px 2px oklch(from var(--sf-shadow-glow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-opacity-disabled: 0.45;\n --sf-state-pending-opacity: 0.7;\n\n\n /* ----------------------------------------------------------\n Focus / form colors\n ---------------------------------------------------------- */\n --sf-focus-ring-color: var(--sf-color-action);\n --sf-focus-ring-shadow: 0 0 0 var(--sf-focus-ring-offset) var(--sf-color-bg),\n 0 0 0 calc(var(--sf-focus-ring-offset) + var(--sf-focus-ring-width)) var(--sf-focus-ring-color);\n --sf-caret-color: var(--sf-color-action);\n\n }\n\n\n\n\n /* ============================================================\n NON-COLOR DESIGN TOKENS\n ============================================================ */\n\n :root {\n\n /* ----------------------------------------------------------\n Scale multipliers\n ---------------------------------------------------------- */\n --sf-space-scale: 1;\n --sf-text-scale: 1;\n --sf-text-display-scale: 1;\n --sf-radius-scale: 1;\n --sf-motion-scale: 1;\n\n\n /* ----------------------------------------------------------\n Font families\n ---------------------------------------------------------- */\n --sf-font-body: system-ui, -apple-system, sans-serif;\n --sf-font-heading: var(--sf-font-body);\n --sf-font-display: var(--sf-font-heading);\n --sf-font-mono: ui-monospace, monospace;\n\n /* Optional system-font stacks (zero-cost, no @import) */\n --sf-font-humanist: \"Seravek\", \"Gill Sans Nova\", \"Ubuntu\", \"Calibri\", \"DejaVu Sans\", source-sans-pro, sans-serif;\n --sf-font-geometric: \"Avenir\", \"Montserrat\", \"Corbel\", \"URW Gothic\", source-sans-pro, sans-serif;\n --sf-font-slab: \"Rockwell\", \"Rockwell Nova\", \"Roboto Slab\", \"DejaVu Serif\", \"Sitka Small\", serif;\n\n /* Opt-in OpenType / variable-font controls (token-only, not applied by the framework) */\n --sf-font-features: normal; /* e.g. \"cv11\", \"ss01\" */\n --sf-font-variation: normal; /* e.g. \"wght\" 450, \"opsz\" 32 */\n --sf-optical-sizing: auto;\n\n\n /* ----------------------------------------------------------\n Font weights\n ---------------------------------------------------------- */\n /* Numeric scale */\n --sf-font-weight-thin: 100;\n --sf-font-weight-extralight: 200;\n --sf-font-weight-light: 300;\n --sf-font-weight-normal: 400;\n --sf-font-weight-medium: 500;\n --sf-font-weight-semibold: 600;\n --sf-font-weight-bold: 700;\n --sf-font-weight-extrabold: 800;\n --sf-font-weight-black: 900;\n\n /* Semantic aliases — reference the numeric scale */\n --sf-font-weight-body: var(--sf-font-weight-normal);\n --sf-font-weight-heading: var(--sf-font-weight-semibold);\n --sf-font-weight-display: var(--sf-font-weight-bold);\n\n /* State token — consumed by .is-current in states.css.\n Override to match your nav design (e.g. semibold for thin fonts). */\n --sf-current-font-weight: var(--sf-font-weight-bold);\n\n\n /* ----------------------------------------------------------\n Font sizes — fluid\n ---------------------------------------------------------- */\n --sf-text-2xs: clamp(0.51rem, calc(0.00029629629629629656 * (100vw - 22.5rem) + 0.51rem), 0.53rem);\n --sf-text-xs: clamp(0.64rem, calc(0.0008888888888888881 * (100vw - 22.5rem) + 0.64rem), 0.7rem);\n --sf-text-s: clamp(0.8rem, calc(0.002074074074074073 * (100vw - 22.5rem) + 0.8rem), 0.94rem);\n --sf-text-m: clamp(1rem, calc(0.003703703703703704 * (100vw - 22.5rem) + 1rem), 1.25rem);\n --sf-text-l: clamp(1.25rem, calc(0.006222222222222221 * (100vw - 22.5rem) + 1.25rem), 1.67rem);\n --sf-text-xl: clamp(1.56rem, calc(0.00977777777777778 * (100vw - 22.5rem) + 1.56rem), 2.22rem);\n --sf-text-2xl: clamp(1.95rem, calc(0.014962962962962963 * (100vw - 22.5rem) + 1.95rem), 2.96rem);\n --sf-text-3xl: clamp(2.44rem, calc(0.022370370370370374 * (100vw - 22.5rem) + 2.44rem), 3.95rem);\n --sf-text-4xl: clamp(3.05rem, calc(0.03274074074074074 * (100vw - 22.5rem) + 3.05rem), 5.26rem);\n\n --sf-text-display-s: clamp(2.4rem, calc(0.00888888888888889 * (100vw - 22.5rem) + 2.4rem), 3rem);\n --sf-text-display-m: clamp(3rem, calc(0.014814814814814815 * (100vw - 22.5rem) + 3rem), 4rem);\n --sf-text-display-l: clamp(3.75rem, calc(0.023407407407407408 * (100vw - 22.5rem) + 3.75rem), 5.33rem);\n\n\n /* ----------------------------------------------------------\n Line heights & letter spacing\n ---------------------------------------------------------- */\n --sf-leading-tight: 1.1;\n --sf-leading-snug: 1.3;\n --sf-leading-normal: 1.5;\n --sf-leading-relaxed: 1.625;\n\n --sf-tracking-tight: -0.025em;\n --sf-tracking-normal: 0;\n --sf-tracking-wide: 0.025em;\n --sf-tracking-wider: 0.05em;\n --sf-tracking-widest: 0.1em;\n\n\n /* ----------------------------------------------------------\n Icon sizes — em-based by design.\n Icons are sized in em so they scale with the surrounding\n text: an icon in a button at --sf-text-m renders at 1rem;\n moved into a heading at --sf-text-2xl, the same icon scales\n up automatically.\n Global scaling flows transitively through --sf-text-scale\n (parent text grows → em-icons grow) — there is no separate\n --sf-icon-scale knob by design. clamp() is intentionally\n absent here: making icons fluid INDEPENDENTLY of context\n would break the relationship to surrounding text.\n Capped at --xl (3em ≈ 48px on default text); above that the\n element ceases to be an icon and becomes an illustration.\n ---------------------------------------------------------- */\n --sf-icon-xs: 0.875em;\n --sf-icon-s: 1em;\n --sf-icon-m: 1.5em;\n --sf-icon-l: 2em;\n --sf-icon-xl: 3em;\n\n\n /* ----------------------------------------------------------\n Spacing — fixed\n ---------------------------------------------------------- */\n --sf-space-none: 0;\n --sf-space-px: 1px;\n --sf-space-gutter: var(--sf-space-l);\n\n /* Spacing — fluid */\n --sf-space-2xs: calc(clamp(0.51rem, calc(0.004888888888888888 * (100vw - 22.5rem) + 0.51rem), 0.84rem) * var(--sf-space-scale));\n --sf-space-xs: calc(clamp(0.64rem, calc(0.007259259259259258 * (100vw - 22.5rem) + 0.64rem), 1.13rem) * var(--sf-space-scale));\n --sf-space-s: calc(clamp(0.8rem, calc(0.01037037037037037 * (100vw - 22.5rem) + 0.8rem), 1.5rem) * var(--sf-space-scale));\n --sf-space-m: calc(clamp(1rem, calc(0.014814814814814815 * (100vw - 22.5rem) + 1rem), 2rem) * var(--sf-space-scale));\n --sf-space-l: calc(clamp(1.25rem, calc(0.021037037037037035 * (100vw - 22.5rem) + 1.25rem), 2.67rem) * var(--sf-space-scale));\n --sf-space-xl: calc(clamp(1.56rem, calc(0.029481481481481477 * (100vw - 22.5rem) + 1.56rem), 3.55rem) * var(--sf-space-scale));\n --sf-space-2xl: calc(clamp(1.95rem, calc(0.04133333333333333 * (100vw - 22.5rem) + 1.95rem), 4.74rem) * var(--sf-space-scale));\n --sf-space-3xl: calc(clamp(2.44rem, calc(0.057333333333333326 * (100vw - 22.5rem) + 2.44rem), 6.31rem) * var(--sf-space-scale));\n --sf-space-4xl: calc(clamp(3.05rem, calc(0.07955555555555556 * (100vw - 22.5rem) + 3.05rem), 8.42rem) * var(--sf-space-scale));\n\n\n /* ----------------------------------------------------------\n UI sizes\n ---------------------------------------------------------- */\n --sf-size-xs: 1.5rem;\n --sf-size-s: 2rem;\n --sf-size-m: 2.5rem;\n --sf-size-l: 2.75rem;\n --sf-size-xl: 3.5rem;\n\n\n /* ----------------------------------------------------------\n Containers\n ---------------------------------------------------------- */\n --sf-container-narrow: 38rem;\n --sf-container-prose: 65ch;\n --sf-container-default: 75rem;\n --sf-container-wide: 90rem;\n --sf-container-full: 100%;\n\n\n /* ----------------------------------------------------------\n Aspect ratios\n ---------------------------------------------------------- */\n --sf-ratio-square: 1;\n --sf-ratio-video: 16 / 9;\n --sf-ratio-cinema: 21 / 9;\n --sf-ratio-4-3: 4 / 3;\n --sf-ratio-3-2: 3 / 2;\n --sf-ratio-portrait: 3 / 4;\n --sf-ratio-golden: 1.618 / 1;\n\n\n /* ----------------------------------------------------------\n Border widths\n --sf-border-width-hairline: device-pixel-dependent. On 1x DPR\n displays (Windows non-Retina) the browser will round 0.5px up\n to 1px, so it visually matches --sf-border-width-1. On 2x+\n DPR (macOS/iOS Retina, most modern phones) it renders as a\n true half-pixel hairline. Use it for subtle separators where\n 1px on low-DPR is acceptable.\n ---------------------------------------------------------- */\n --sf-border-width-hairline: 0.5px;\n --sf-border-width-1: 1px;\n --sf-border-width-2: 2px;\n --sf-border-width-3: 3px;\n --sf-border-width-4: 4px;\n\n /* ----------------------------------------------------------\n Border styles — paired with --sf-border-width-* and\n --sf-color-border*. Consumer BEM:\n .my-card { border: var(--sf-border-width-1) var(--sf-border-style) var(--sf-color-border); }\n Override per element to switch decorative styles without\n rewriting the rule.\n ---------------------------------------------------------- */\n --sf-border-style: solid; /* default */\n --sf-border-style-strong: solid; /* alias for emphasis */\n --sf-border-style-soft: dashed;\n --sf-border-style-dotted: dotted;\n\n /* ----------------------------------------------------------\n Divider — global separator system. Drives `hr` (base) and\n the `.sf-divider` layout primitive.\n ---------------------------------------------------------- */\n --sf-divider-width: var(--sf-border-width-1);\n --sf-divider-style: solid;\n --sf-divider-color: var(--sf-color-border);\n\n\n /* ----------------------------------------------------------\n Border radius\n --sf-radius-full: intentionally not scaled by --sf-radius-scale.\n A pill/circle is a topological constant, not a relative size:\n any --sf-radius-scale: 0 (\"sharp\" design system) should still\n render pill buttons as pills. 9999px is a magic-number convention\n large enough to clip to half the shorter edge for any realistic UI.\n ---------------------------------------------------------- */\n --sf-radius-none: 0;\n --sf-radius-xs: calc(2px * var(--sf-radius-scale));\n --sf-radius-s: calc(4px * var(--sf-radius-scale));\n --sf-radius-m: calc(8px * var(--sf-radius-scale));\n --sf-radius-l: calc(12px * var(--sf-radius-scale));\n --sf-radius-xl: calc(16px * var(--sf-radius-scale));\n --sf-radius-2xl: calc(24px * var(--sf-radius-scale));\n --sf-radius-3xl: calc(32px * var(--sf-radius-scale));\n --sf-radius-4xl: calc(48px * var(--sf-radius-scale));\n --sf-radius-full: 9999px;\n /* Pill — semantic alias of --sf-radius-full. Reach for this name\n on rectangles you want capsule-shaped (badges, tags, chips); use\n --sf-radius-full when you mean \"as round as topologically possible\". */\n --sf-radius-pill: var(--sf-radius-full);\n /* Concentric helper — outer radius for a container that wraps\n content padded by --sf-component-pad with inner radius m.\n Keeps inner elements visually proportional inside a padded box.\n Override per use:\n .my-card { border-radius: var(--sf-radius-outer); }\n Maths: outer = inner + padding ⇒ aligned curves at corners. */\n --sf-radius-outer: calc(var(--sf-radius-m) + var(--sf-component-pad));\n\n\n /* ----------------------------------------------------------\n Shadows — BEM consumer API\n Use in your own classes: .card { box-shadow: var(--sf-shadow-m); }\n ---------------------------------------------------------- */\n /* Shadow tint — near-black derived from the neutral; the tint simply\n inherits the neutral's own chroma/hue, so a colourless neutral yields\n colourless shadows. Forced dark in both modes; --sf-shadow-strength\n handles dark-mode intensity. Override to a brand colour for explicitly\n tinted shadows — keep it dark or you get bright slabs:\n oklch(from var(--sf-color-primary) 0.15 c h). */\n --sf-shadow-color: oklch(from var(--sf-color-neutral) 0.15 c h);\n\n --sf-shadow-none: none;\n --sf-shadow-xs: 0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7));\n --sf-shadow-s: 0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 2px 6px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, var(--sf-shadow-strength), 0.7));\n --sf-shadow-m: 0 1px 3px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-shadow-l: 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 8px 24px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), 0.7)),\n 0 16px 48px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-shadow-xl: 0 2px 8px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 12px 36px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3.5), 0.7)),\n 0 24px 72px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7));\n --sf-shadow-2xl: 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.6), 0.7)),\n 0 20px 60px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 4), 0.7)),\n 0 40px 100px -8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 5), 0.7));\n --sf-shadow-inner: inset 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n\n\n /* ----------------------------------------------------------\n Text shadows — share --sf-shadow-color / --sf-shadow-strength\n ---------------------------------------------------------- */\n --sf-text-shadow-none: none;\n --sf-text-shadow-s: 0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7));\n --sf-text-shadow-m: 0 2px 4px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-text-shadow-l: 0 4px 8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7));\n\n /* Drop shadows — for filter: drop-shadow() (respects alpha edges) */\n --sf-drop-shadow-s: drop-shadow(0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7)));\n --sf-drop-shadow-m: drop-shadow(0 4px 6px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7)));\n --sf-drop-shadow-l: drop-shadow(0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7)));\n\n\n /* ----------------------------------------------------------\n Blur & opacity\n ---------------------------------------------------------- */\n --sf-blur-xs: 4px;\n --sf-blur-s: 8px;\n --sf-blur-m: 16px;\n --sf-blur-l: 32px;\n --sf-blur-xl: 48px;\n\n /* 3D perspective */\n --sf-perspective-near: 500px;\n --sf-perspective-normal: 1000px;\n --sf-perspective-far: 2000px;\n\n --sf-opacity-0: 0;\n --sf-opacity-10: 0.1;\n --sf-opacity-25: 0.25;\n --sf-opacity-50: 0.5;\n --sf-opacity-75: 0.75;\n --sf-opacity-100: 1;\n\n\n /* ----------------------------------------------------------\n Motion & easing\n ---------------------------------------------------------- */\n --sf-duration-none: 0ms;\n --sf-duration-instant: calc(100ms * var(--sf-motion-scale));\n --sf-duration-fast: calc(150ms * var(--sf-motion-scale));\n --sf-duration-normal: calc(250ms * var(--sf-motion-scale));\n --sf-duration-slow: calc(400ms * var(--sf-motion-scale));\n --sf-duration-slower: calc(600ms * var(--sf-motion-scale));\n\n --sf-ease-linear: linear;\n --sf-ease-out: cubic-bezier(0.25, 0, 0.15, 1);\n --sf-ease-in: cubic-bezier(0.5, 0, 0.75, 0.25);\n --sf-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);\n --sf-ease-spring: linear(0, 0.5, 1.1, 0.95, 1.02, 1);\n --sf-ease-elastic: linear(0, 0.3, 1.2, 0.9, 1.05, 1);\n --sf-ease-bounce: linear(0, 0.35 18%, 1 32%, 0.86 42%, 1.02 56%, 0.98 72%, 1);\n --sf-ease-overshoot: linear(0, 0.6 30%, 1.08 55%, 0.98 75%, 1);\n\n\n /* ----------------------------------------------------------\n Animation presets — pair keyframes (core/motion.css) with\n duration + easing. Use directly: animation: var(--sf-animation-fade-in);\n ---------------------------------------------------------- */\n --sf-animation-fade-in: sf-fade-in var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-fade-out: sf-fade-out var(--sf-duration-normal) var(--sf-ease-in) both;\n --sf-animation-slide-in-up: sf-slide-in-up var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-slide-in-down: sf-slide-in-down var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-slide-in-left: sf-slide-in-left var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-slide-in-right: sf-slide-in-right var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-scale-up: sf-scale-up var(--sf-duration-normal) var(--sf-ease-overshoot) both;\n --sf-animation-scale-down: sf-scale-down var(--sf-duration-normal) var(--sf-ease-in) both;\n --sf-animation-color-pulse: sf-color-pulse var(--sf-duration-slow) var(--sf-ease-in-out) infinite;\n --sf-animation-ping: sf-ping var(--sf-duration-slow) var(--sf-ease-out) infinite;\n --sf-animation-blink: sf-blink calc(1s * var(--sf-motion-scale)) steps(1, end) infinite;\n --sf-animation-float: sf-float calc(3s * var(--sf-motion-scale)) var(--sf-ease-in-out) infinite;\n --sf-animation-spin: sf-spin var(--sf-duration-slower) linear infinite;\n --sf-animation-shimmer: sf-shimmer calc(1.5s * var(--sf-motion-scale)) ease-in-out infinite;\n\n /* Stagger delays — apply to successive items for entrance choreography\n (e.g. style=\"animation-delay: var(--sf-animation-delay-2)\"). Tokens\n only; SLASHED ships no .sf-stagger utility class (by design — apply via token presets or your own BEM rules). */\n --sf-animation-delay-1: calc(75ms * var(--sf-motion-scale));\n --sf-animation-delay-2: calc(150ms * var(--sf-motion-scale));\n --sf-animation-delay-3: calc(225ms * var(--sf-motion-scale));\n --sf-animation-delay-4: calc(300ms * var(--sf-motion-scale));\n --sf-animation-delay-5: calc(375ms * var(--sf-motion-scale));\n\n\n /* ----------------------------------------------------------\n Scroll-driven animation range — consumer API. Attach these\n to your own scroll-timeline via animation-range. Not consumed\n by the framework itself; intended for BEM component authors.\n ---------------------------------------------------------- */\n --sf-scroll-timeline-range-start: 0%;\n --sf-scroll-timeline-range-end: 100%;\n\n\n /* ----------------------------------------------------------\n Mask scrim — edge-fade stops for scroll reels / overflow.\n Use in a mask-image gradient to fade content near an edge:\n mask-image: linear-gradient(to right,\n transparent 0, #000 var(--sf-mask-scrim-start),\n #000 calc(100% - var(--sf-mask-scrim-end)), transparent 100%);\n ---------------------------------------------------------- */\n --sf-mask-scrim-start: var(--sf-space-l);\n --sf-mask-scrim-end: var(--sf-space-l);\n\n\n /* ----------------------------------------------------------\n Z-index\n ---------------------------------------------------------- */\n --sf-z-below: -1;\n --sf-z-base: 0;\n --sf-z-raised: 1;\n --sf-z-low: 10;\n --sf-z-mid: 100;\n --sf-z-high: 500;\n --sf-z-top: 900;\n --sf-z-max: 9999;\n\n\n /* ----------------------------------------------------------\n Layout & a11y\n ---------------------------------------------------------- */\n --sf-header-height: 8rem;\n --sf-sticky-offset: var(--sf-header-height);\n --sf-focus-ring-width: 2px;\n --sf-focus-ring-offset: 2px;\n --sf-focus-ring-style: solid;\n --sf-touch-target: var(--sf-size-l);\n /* Global text-contrast knob. Nudges derived reading-text colours\n toward the extremes (darker in light mode, lighter in dark mode).\n Positive = more contrast; 0 = no change. Consumed by\n --sf-color-text, --sf-color-text--secondary, --sf-color-heading. */\n --sf-contrast-bias: 0;\n /* Lightness crossover for text-on-color auto-contrast. Colours with\n L above this value get dark text; below get light text. Default 0.6\n guarantees ≥ 3:1 for most colours. Shift down (e.g. 0.55) if your\n brand sits in the 0.52-0.60 range and you prefer white text on it. */\n --sf-contrast-threshold: 0.6;\n\n --sf-safe-top: env(safe-area-inset-top, 0px);\n --sf-safe-bottom: env(safe-area-inset-bottom, 0px);\n --sf-safe-left: env(safe-area-inset-left, 0px);\n --sf-safe-right: env(safe-area-inset-right, 0px);\n\n\n /* ----------------------------------------------------------\n Print\n ---------------------------------------------------------- */\n --sf-print-page-margin: 2cm;\n --sf-print-page-size: a4;\n --sf-print-base-size: 11pt;\n\n\n /* ----------------------------------------------------------\n Stroke widths\n ---------------------------------------------------------- */\n --sf-stroke-thin: 1px;\n --sf-stroke-regular: 1.5px;\n --sf-stroke-bold: 2px;\n --sf-stroke-heavy: 3px;\n\n }\n\n\n /* ============================================================\n TYPOGRAPHY ALIASES\n ============================================================ */\n\n :root {\n --sf-body-font-size: var(--sf-text-m);\n --sf-body-line-height: var(--sf-leading-normal);\n --sf-body-font-weight: var(--sf-font-weight-body);\n --sf-body-font-family: var(--sf-font-body);\n --sf-body-color: var(--sf-color-text);\n --sf-body-text-wrap: pretty;\n --sf-body-strong-weight: var(--sf-font-weight-bold);\n --sf-body-em-style: italic;\n\n --sf-code-font-size: 0.875em;\n\n --sf-heading-font-family: var(--sf-font-heading);\n --sf-heading-color: var(--sf-color-heading);\n --sf-heading-text-wrap: balance;\n\n --sf-h1-size: var(--sf-text-4xl);\n --sf-h1-line-height: var(--sf-leading-tight);\n --sf-h1-font-weight: var(--sf-font-weight-heading);\n --sf-h1-letter-spacing: var(--sf-tracking-tight);\n\n --sf-h2-size: var(--sf-text-3xl);\n --sf-h2-line-height: var(--sf-leading-tight);\n --sf-h2-font-weight: var(--sf-font-weight-heading);\n --sf-h2-letter-spacing: var(--sf-tracking-tight);\n\n --sf-h3-size: var(--sf-text-2xl);\n --sf-h3-line-height: var(--sf-leading-snug);\n --sf-h3-font-weight: var(--sf-font-weight-heading);\n --sf-h3-letter-spacing: var(--sf-tracking-normal);\n\n --sf-h4-size: var(--sf-text-xl);\n --sf-h4-line-height: var(--sf-leading-snug);\n --sf-h4-font-weight: var(--sf-font-weight-heading);\n --sf-h4-letter-spacing: var(--sf-tracking-normal);\n\n --sf-h5-size: var(--sf-text-l);\n --sf-h5-line-height: var(--sf-leading-normal);\n --sf-h5-font-weight: var(--sf-font-weight-heading);\n --sf-h5-letter-spacing: var(--sf-tracking-normal);\n\n --sf-h6-size: var(--sf-text-m);\n --sf-h6-line-height: var(--sf-leading-normal);\n --sf-h6-font-weight: var(--sf-font-weight-heading);\n --sf-h6-letter-spacing: var(--sf-tracking-wide);\n\n\n /* ----------------------------------------------------------\n Spacing aliases — BEM consumer API\n Use in your own BEM classes: .nav { gap: var(--sf-gap); }\n Source of truth for tokens.layout.css:\n --sf-space-gap → var(--sf-gap) (all layout primitives)\n --sf-space-content → var(--sf-content-gap) (stack, prose)\n ---------------------------------------------------------- */\n --sf-gap: var(--sf-space-m); /* gap between BEM elements */\n --sf-content-gap: var(--sf-space-s); /* rhythm within a component */\n --sf-component-pad: var(--sf-space-m); /* standard component padding */\n --sf-field-block: var(--sf-space-l); /* form-field block spacing */\n --sf-field-required-marker: \" *\";\n /* Leading space is baked into the default so consumers can fully\n disable the indicator with `--sf-link-external-marker: \"\"` —\n without the space a bare \"\" still left a stray space after the\n link text. Override with your own glyph (include leading space\n if you want one): `:root { --sf-link-external-marker: \" →\" }`. */\n --sf-link-external-marker: \" \\2197\"; /* \" ↗\" — consumed by .sf-link-external */\n\n\n /* ----------------------------------------------------------\n Section padding\n ---------------------------------------------------------- */\n --sf-section-pad: var(--sf-section-pad--m);\n --sf-section-pad--xs: var(--sf-space-xl);\n --sf-section-pad--s: var(--sf-space-2xl);\n --sf-section-pad--m: var(--sf-space-3xl);\n --sf-section-pad--l: var(--sf-space-4xl);\n --sf-section-pad--xl: calc(var(--sf-space-4xl) * 1.5);\n --sf-section-pad--2xl: calc(var(--sf-space-4xl) * 2);\n\n\n /* ----------------------------------------------------------\n Transition shorthands\n\n --sf-transition-all transitions ALL properties — convenient but\n a performance footgun (forces the browser to watch every computed\n value). Prefer the granular tokens below for production UI.\n ---------------------------------------------------------- */\n --sf-transition-all: all var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-colors:\n color var(--sf-duration-normal) var(--sf-ease-out),\n background-color var(--sf-duration-normal) var(--sf-ease-out),\n border-color var(--sf-duration-normal) var(--sf-ease-out),\n text-decoration-color var(--sf-duration-normal) var(--sf-ease-out),\n fill var(--sf-duration-normal) var(--sf-ease-out),\n stroke var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-transform: transform var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-opacity: opacity var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-shadow: box-shadow var(--sf-duration-normal) var(--sf-ease-out);\n\n --sf-transition-fast: all var(--sf-duration-fast) var(--sf-ease-out);\n --sf-transition-slow: all var(--sf-duration-slow) var(--sf-ease-in-out);\n --sf-transition-enter: all var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-exit: all var(--sf-duration-fast) var(--sf-ease-in);\n }\n\n}\n\n/* ─── core/tokens.layout.css ─── */\n/* ============================================================\n SLASHED — core/tokens.layout.css\n Semantic tokens for layout primitives.\n Load before: core/layout.css\n Layer: slashed.tokens\n Prefix: --sf-\n\n DEPENDENCIES — requires tokens.css (loaded earlier).\n Spacing aliases inherit from the BEM consumer API in tokens.css:\n --sf-space-gap → var(--sf-gap) from tokens.css\n --sf-space-content → var(--sf-content-gap) from tokens.css\n Override --sf-gap or --sf-content-gap (the canonical source) to change\n the gaps across all layout primitives at once. --sf-space-gap and\n --sf-space-content are CANONICAL-SOURCE ALIASES — a second public name\n kept for layout-primitive readability, not a redundant indirection.\n\n PUBLIC API — every --sf-*-gap / --sf-*-padding / --sf-*-min / etc. here\n is a per-primitive override knob (e.g. style=\"--sf-cluster-gap: …\").\n No INTERNAL tokens live in this file.\n\n Each layout primitive (cluster, grid, sidebar…) has its own\n tokens (--sf-cluster-gap etc.) for local overrides.\n ============================================================ */\n\n@layer slashed.tokens {\n :root {\n\n /* ----------------------------------------------------------\n Shared spacing aliases\n ---------------------------------------------------------- */\n --sf-space-gap: var(--sf-gap); /* component-level gap */\n --sf-space-content: var(--sf-content-gap); /* within-component rhythm */\n\n /* ----------------------------------------------------------\n Stack\n ---------------------------------------------------------- */\n --sf-stack-gap: var(--sf-space-content);\n\n /* ----------------------------------------------------------\n Box\n ---------------------------------------------------------- */\n --sf-box-padding: var(--sf-space-m);\n --sf-box-border-width: 0;\n --sf-box-border-color: var(--sf-color-border);\n\n /* ----------------------------------------------------------\n Center\n ---------------------------------------------------------- */\n --sf-center-max: var(--sf-container-default);\n --sf-center-gutter: var(--sf-space-gutter);\n\n /* ----------------------------------------------------------\n Cluster\n ---------------------------------------------------------- */\n --sf-cluster-gap: var(--sf-space-gap);\n --sf-cluster-align: center;\n --sf-cluster-justify: flex-start;\n\n /* ----------------------------------------------------------\n Sidebar\n ---------------------------------------------------------- */\n --sf-sidebar-gap: var(--sf-space-gap);\n --sf-sidebar-min-width: 50%; /* content column minimum */\n --sf-sidebar-width: 18rem; /* sidebar panel default size */\n\n /* ----------------------------------------------------------\n Switcher\n ---------------------------------------------------------- */\n --sf-switcher-threshold: 30rem;\n --sf-switcher-gap: var(--sf-space-gap);\n\n /* ----------------------------------------------------------\n Grid (auto-fill, breakpoint-free)\n ---------------------------------------------------------- */\n --sf-grid-min: 16rem;\n --sf-grid-gap: var(--sf-space-gap);\n\n --sf-grid-min-xs: 10rem;\n --sf-grid-min-s: 13rem;\n --sf-grid-min-m: 16rem;\n --sf-grid-min-l: 20rem;\n --sf-grid-min-xl: 24rem;\n --sf-grid-min-2xl: 28rem;\n\n /* ----------------------------------------------------------\n Cover\n ---------------------------------------------------------- */\n --sf-cover-min-height: 100dvh;\n --sf-cover-padding: var(--sf-section-pad);\n\n /* ----------------------------------------------------------\n Frame\n ---------------------------------------------------------- */\n --sf-frame-ratio: 16 / 9; /* literal , valid in aspect-ratio */\n\n /* ----------------------------------------------------------\n Icon (boxed variant) — extras for .sf-icon--boxed.\n The naked .sf-icon sizing tokens (--sf-icon-xs..xl) live in\n core/tokens.css alongside the typography scale.\n ---------------------------------------------------------- */\n --sf-icon-box-pad: 0.5em;\n --sf-icon-box-radius: var(--sf-radius-s);\n --sf-icon-box-bg: var(--sf-color-well);\n --sf-icon-box-border: var(--sf-border-width-1) solid var(--sf-color-border);\n\n /* ----------------------------------------------------------\n Reel\n ---------------------------------------------------------- */\n --sf-reel-item-width: max-content;\n --sf-reel-gap: var(--sf-space-gap);\n --sf-reel-height: auto;\n\n /* ----------------------------------------------------------\n Imposter (centered absolute/fixed overlay)\n ---------------------------------------------------------- */\n --sf-imposter-margin: var(--sf-space-m);\n\n /* ----------------------------------------------------------\n Bento grid\n ---------------------------------------------------------- */\n --sf-bento-cols-default: 3;\n --sf-bento-row-default: 10rem;\n --sf-bento-row-compact: 6rem;\n --sf-bento-row-tall: 16rem;\n --sf-bento-gap: var(--sf-space-gap);\n\n /* ----------------------------------------------------------\n Content grid (breakout pattern)\n ---------------------------------------------------------- */\n --sf-breakout-width: var(--sf-container-wide);\n --sf-content-width: var(--sf-container-default);\n\n /* ----------------------------------------------------------\n Prose\n ---------------------------------------------------------- */\n --sf-prose-paragraph: var(--sf-space-content);\n\n /* ----------------------------------------------------------\n Alternate (zigzag)\n ---------------------------------------------------------- */\n --sf-alternate-gap: var(--sf-space-content);\n --sf-alternate-inner-gap: var(--sf-space-gap);\n\n }\n}\n\n/* ─── core/tokens.macros.css ─── */\n/* ============================================================\n SLASHED — core/tokens.macros.css\n Semantic tokens for macros (recipes / patterns).\n Load before: core/macros.css\n Layer: slashed.tokens\n Prefix: --sf-\n\n DEPENDENCIES — requires tokens.css (loaded earlier).\n Macro tokens reference --sf-space-* / --sf-radius-* / etc.\n\n PUBLIC API — every token here is a per-recipe override knob\n (e.g. style=\"--sf-line-clamp: 5\"). No INTERNAL tokens live in\n this file.\n\n Architectural parallel — these tokens stand alongside\n tokens.layout.css: same prefix, same shape, same status\n (essential, public, override-friendly).\n ============================================================ */\n\n@layer slashed.tokens {\n :root {\n\n /* ----------------------------------------------------------\n Flow — distance between flow children (lobotomized owl).\n Consumed by .sf-flow > * + *.\n ---------------------------------------------------------- */\n --sf-flow-space: var(--sf-space-content);\n\n /* ----------------------------------------------------------\n Line clamp — default line count for .sf-line-clamp-N.\n Override per element: style=\"--sf-line-clamp: 5\".\n The fixed-N variants .sf-line-clamp-2 / -3 don't read this\n token (they hardcode the value), so the default only\n applies to .sf-line-clamp-N callers.\n ---------------------------------------------------------- */\n --sf-line-clamp: 3;\n\n /* ----------------------------------------------------------\n Truncate — ellipsis character for .sf-truncate.\n Stays as a token so consumers can swap to ASCII \"...\" or a\n localised character without rewriting the rule. The CSS\n text-overflow: ellipsis property doesn't read it directly;\n it's exposed for consumer-built variants.\n ---------------------------------------------------------- */\n --sf-truncate-suffix: \"\\2026\";\n\n /* ----------------------------------------------------------\n Aspect ratio — default for .sf-aspect (generic ratio container,\n distinct from layout's .sf-frame which has its own --sf-frame-ratio).\n ---------------------------------------------------------- */\n --sf-aspect: 16 / 9;\n\n /* ----------------------------------------------------------\n Scroll shadow / overflow fade — mask gradient size.\n Used by .sf-scroll-shadow (top+bottom) and .sf-overflow-fade\n (end-edge horizontal fade).\n ---------------------------------------------------------- */\n --sf-scroll-shadow-size: 2rem;\n\n }\n}\n\n/* ─── core/reset.css ─── */\n/* SLASHED — core/reset.css\n @layer slashed.reset\n ─────────────────────────────────────────────────── */\n\n@layer slashed.reset {\n\n *, *::before, *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n html {\n text-size-adjust: 100%;\n scroll-padding-top: var(--sf-header-height, 3.5rem);\n scrollbar-gutter: stable;\n color-scheme: var(--sf-color-scheme, light dark);\n hanging-punctuation: first last;\n }\n\n /* interpolate-size enables animations to/from intrinsic sizing\n keywords (auto, min-content, max-content, fit-content). Shipped:\n Chrome 129 (Aug 2024), Safari 18 (Sep 2024). Firefox: behind a\n flag as of 2026-Q2. Wrapped in @supports so unsupporting engines\n get clean progressive enhancement, not an unknown-declaration. */\n @supports (interpolate-size: allow-keywords) {\n html { interpolate-size: allow-keywords; }\n }\n\n body {\n min-height: 100dvh;\n min-inline-size: 320px;\n }\n\n menu, ul, ol {\n list-style: none;\n }\n\n img, picture, video, canvas, svg {\n display: block;\n max-width: 100%;\n height: auto;\n }\n\n iframe, embed, object {\n display: block;\n max-width: 100%;\n height: unset;\n border: none;\n }\n\n button, input, select, textarea {\n font: inherit;\n color: inherit;\n letter-spacing: inherit;\n color-scheme: inherit;\n line-height: inherit;\n }\n\n button {\n text-transform: none;\n }\n\n ::file-selector-button {\n font: inherit;\n }\n\n search {\n display: block;\n }\n\n fieldset {\n border: none;\n padding: 0;\n min-width: 0;\n }\n\n legend {\n padding: 0;\n }\n\n a, button, input, select, textarea, summary {\n touch-action: manipulation;\n }\n\n summary {\n list-style: none;\n cursor: pointer;\n }\n\n textarea {\n resize: vertical;\n }\n\n button {\n cursor: pointer;\n }\n\n table {\n border-collapse: collapse;\n border-spacing: 0;\n }\n\n [hidden] {\n display: none !important;\n }\n\n dialog, [popover] {\n padding: 0;\n border: none;\n background: none;\n color: inherit;\n }\n\n [inert] {\n cursor: default;\n }\n\n}\n\n/* ─── core/base.css ─── */\n/* SLASHED — core/base.css\n @layer slashed.base\n ─────────────────────────────────────────────────── */\n\n@layer slashed.base {\n\n /* Dark/light mode lives in core/themes.css (slashed.themes layer).\n The baseline color-scheme: var(--sf-color-scheme, light dark) is\n set on html in core/reset.css. */\n\n body {\n font-family: var(--sf-body-font-family, var(--sf-font-body));\n font-size: var(--sf-body-font-size, var(--sf-text-m));\n line-height: var(--sf-body-line-height, var(--sf-leading-normal));\n font-weight: var(--sf-body-font-weight, var(--sf-font-weight-body));\n color: var(--sf-body-color, var(--sf-color-text));\n background-color: var(--sf-color-bg, #fff);\n font-synthesis: none;\n }\n\n h1, h2, h3, h4, h5, h6 {\n font-family: var(--sf-heading-font-family, var(--sf-font-heading));\n line-height: var(--sf-leading-tight);\n color: var(--sf-heading-color, var(--sf-color-heading));\n text-wrap: var(--sf-heading-text-wrap);\n overflow-wrap: break-word;\n text-rendering: optimizelegibility;\n scroll-margin-top: calc(var(--sf-header-height, 3.5rem) + var(--sf-space-m));\n }\n\n h1 { font-size: var(--sf-h1-size); line-height: var(--sf-h1-line-height); font-weight: var(--sf-h1-font-weight); letter-spacing: var(--sf-h1-letter-spacing); }\n h2 { font-size: var(--sf-h2-size); line-height: var(--sf-h2-line-height); font-weight: var(--sf-h2-font-weight); letter-spacing: var(--sf-h2-letter-spacing); }\n h3 { font-size: var(--sf-h3-size); line-height: var(--sf-h3-line-height); font-weight: var(--sf-h3-font-weight); letter-spacing: var(--sf-h3-letter-spacing); }\n h4 { font-size: var(--sf-h4-size); line-height: var(--sf-h4-line-height); font-weight: var(--sf-h4-font-weight); letter-spacing: var(--sf-h4-letter-spacing); }\n h5 { font-size: var(--sf-h5-size); line-height: var(--sf-h5-line-height); font-weight: var(--sf-h5-font-weight); letter-spacing: var(--sf-h5-letter-spacing); }\n h6 { font-size: var(--sf-h6-size); line-height: var(--sf-h6-line-height); font-weight: var(--sf-h6-font-weight); letter-spacing: var(--sf-h6-letter-spacing); }\n\n p {\n text-wrap: var(--sf-body-text-wrap);\n overflow-wrap: break-word;\n orphans: 2;\n widows: 2;\n }\n\n b, strong {\n font-weight: var(--sf-body-strong-weight);\n }\n\n em {\n font-style: var(--sf-body-em-style);\n }\n\n small {\n font-size: var(--sf-text-s, 80%);\n }\n\n mark {\n background-color: var(--sf-color-mark-bg, yellow);\n color: var(--sf-color-mark-text, black);\n border-radius: var(--sf-radius-xs);\n padding-inline: 0.2em;\n }\n\n abbr[title] {\n text-decoration: underline dotted;\n cursor: help;\n }\n\n kbd, samp {\n font-family: var(--sf-font-mono);\n font-size: inherit;\n }\n\n sub, sup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n }\n sup { top: -0.5em; }\n sub { bottom: -0.25em; }\n\n /* `a:link` (specificity 0,1,1) rather than bare `a` (0,0,1) so the\n author rule wins over WebKit's UA stylesheet rule\n `a:link { color: -webkit-link; }`. The bare-`a` form silently\n loses to the UA `:link` rule on unvisited anchors in WebKit,\n which paints `-webkit-link` (≈ #00728f in dark mode) regardless\n of whatever `--sf-color-link` resolves to — producing axe's\n 3.12 : 1 dark-mode contrast failure that survived four attempts\n to \"fix\" the link colour formula (PR #73, then three iterations\n of PR #76). The colour formula was never the problem; the\n selector specificity was. Hover / visited / active rules already\n used pseudo-classes so they had matching specificity and were\n never affected. */\n a:link {\n color: var(--sf-color-link);\n text-decoration-color: var(--sf-color-link--underline);\n text-underline-offset: 0.15em;\n text-underline-position: from-font;\n transition:\n color var(--sf-duration-fast) var(--sf-ease-out),\n text-decoration-color var(--sf-duration-fast) var(--sf-ease-out);\n }\n a:hover { color: var(--sf-color-link--hover); text-decoration-color: currentcolor; }\n a:visited { color: var(--sf-color-link--visited); }\n a:active { color: var(--sf-color-link--active); }\n a:not([class]) { text-decoration-skip-ink: auto; }\n\n a, code {\n overflow-wrap: break-word;\n }\n\n code {\n font-family: var(--sf-font-mono);\n font-size: var(--sf-code-font-size);\n padding: 0.125em 0.3em;\n border-radius: var(--sf-radius-xs);\n background-color: var(--sf-color-code-bg);\n color: var(--sf-color-code-text, inherit);\n }\n\n pre {\n font-family: var(--sf-font-mono);\n font-size: var(--sf-text-s);\n padding: var(--sf-space-m);\n border-radius: var(--sf-radius-m);\n background: var(--sf-color-code-block-bg, var(--sf-color-code-bg));\n color: var(--sf-color-code-block-text, inherit);\n overflow-x: auto;\n tab-size: 2;\n hyphens: none;\n }\n pre code {\n background: none;\n color: inherit;\n border: 0;\n padding: 0;\n font-size: inherit;\n border-radius: 0;\n }\n\n svg {\n fill: currentcolor;\n }\n\n hr {\n border: none;\n border-block-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);\n }\n\n :target {\n scroll-margin-top: calc(var(--sf-header-height, 3.5rem) + var(--sf-space-m));\n }\n\n ::selection {\n background: var(--sf-color-selection-bg);\n color: var(--sf-color-selection-text);\n text-shadow: none;\n }\n\n th, caption {\n text-align: start;\n }\n\n address, cite, dfn, var {\n font-style: normal;\n }\n\n input, textarea {\n caret-color: var(--sf-caret-color);\n }\n\n input, progress {\n accent-color: var(--sf-color-action);\n }\n\n ::backdrop {\n background-color: var(--sf-color-dim);\n }\n\n optgroup { font: inherit; }\n ::file-selector-button { font: inherit; }\n ::placeholder { opacity: 1; color: var(--sf-color-text--placeholder, currentcolor); }\n\n /* Minimal rich-block defaults — just enough to distinguish from

.\n Full styling lives in .sf-prose (core/layout.css). */\n blockquote {\n border-inline-start: var(--sf-border-width-3) solid var(--sf-color-border);\n padding-inline-start: var(--sf-space-m);\n font-style: italic;\n }\n\n dt {\n font-weight: var(--sf-font-weight-heading);\n }\n\n dd {\n margin-inline-start: var(--sf-space-m);\n }\n\n table {\n width: 100%;\n text-align: start;\n }\n\n th {\n font-weight: var(--sf-font-weight-heading);\n }\n\n td, th {\n padding: var(--sf-space-2xs) var(--sf-space-xs);\n border-block-end: var(--sf-border-width-hairline) solid var(--sf-color-border--subtle);\n }\n\n}\n\n/* ─── core/themes.css ─── */\n/* SLASHED — core/themes.css\n @layer slashed.themes\n\n Theme switching: token reassignments only. No new selectors,\n no new properties — strictly remaps existing tokens based on\n system preference and explicit data-theme attributes.\n\n Cascade position is significant. slashed.themes sits ABOVE\n slashed.{states, utilities, components, layout, base} (see\n core/layers.css), so a theme's token reassignment cannot be\n accidentally beaten by a component or utility rule of equal\n specificity. Only consumer code in slashed.overrides (or\n unlayered CSS) can override a theme.\n\n USAGE\n → follows OS preference\n → whole page dark\n

→ only this section dark\n
→ only this section light\n\n The base color-scheme: var(--sf-color-scheme, light dark)\n declaration lives in core/reset.css and provides the baseline.\n This layer flips it.\n\n MULTI-BRAND — scope a palette to any subtree by re-declaring the 6\n source tokens under a selector (e.g. [data-brand=\"x\"]). See\n optional/theme-example.css and docs/theming.md.\n\n THEME TRANSITION — toggling [data-theme] via document.startViewTransition()\n animates through the ::view-transition(root) rules in core/motion.css.\n For a plain cross-fade of colours, opt into a transition class on \n (example in optional/theme-example.css) — never transition every colour\n globally and permanently (perf footgun).\n ─────────────────────────────────────────────────── */\n\n@layer slashed.themes {\n\n /* OS preference — only when no data-theme is set on :root.\n A section-level [data-theme] still wins via the explicit\n selectors below (higher specificity within the same layer). */\n @media (prefers-color-scheme: dark) {\n :root:not([data-theme]) {\n color-scheme: dark;\n --sf-is-dark: 1;\n }\n }\n\n /* Explicit theme — works on :root (whole page) or any element\n (section-level theming). */\n [data-theme=\"light\"] { color-scheme: light; --sf-is-dark: 0; }\n [data-theme=\"dark\"] { color-scheme: dark; --sf-is-dark: 1; }\n\n\n /* LumLocker — opt-in via [data-lumlocker] on :root.\n Locks the 5 brand colors (primary, secondary, tertiary, action,\n neutral) to one shared OKLCH L value (--sf-lumlocker,\n default 0.65 in core/tokens.css), keeping each color's own hue\n and chroma. Useful when a brand picker produces colors with\n wildly different L values that look mismatched in weight.\n\n Base is excluded by design — base IS the page surface (L ~0.98 in\n light mode, ~0.20 in dark) and locking it would destroy the page\n background.\n\n Default state: OFF. Without the attribute, every brand keeps its\n own L. Add :root[data-lumlocker] (or set via JS) to enable.\n\n Per-color escape: override --sf-color-X under\n :root[data-lumlocker] to opt that one color out:\n :root[data-lumlocker] { --sf-color-primary: #yourColor; }\n\n The dark-side input falls back to -light when -dark isn't\n explicitly set, so this works even without per-mode overrides. */\n :root[data-lumlocker] {\n --sf-color-primary: light-dark(\n oklch(from var(--sf-color-primary-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-primary-dark, var(--sf-color-primary-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-secondary: light-dark(\n oklch(from var(--sf-color-secondary-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-secondary-dark, var(--sf-color-secondary-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-tertiary: light-dark(\n oklch(from var(--sf-color-tertiary-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-tertiary-dark, var(--sf-color-tertiary-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-action: light-dark(\n oklch(from var(--sf-color-action-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-action-dark, var(--sf-color-action-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-neutral: light-dark(\n oklch(from var(--sf-color-neutral-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-neutral-dark, var(--sf-color-neutral-light)) var(--sf-lumlocker) c h)\n );\n }\n\n}\n\n/* ─── core/layout.css ─── */\n/* ============================================================\n SLASHED — core/layout.css\n ★ de facto required for most projects\n Layout primitives + structural patterns\n Requires: core/tokens.css, core/tokens.layout.css\n ============================================================ */\n\n@layer slashed.layout {\n\n /* -- Section -------------------------------------------------\n Page section with vertical padding. Size modifiers\n override the scoped token.\n ---------------------------------------------------------- */\n\n .sf-section { padding-block: var(--sf-section-pad); }\n .sf-section--xs { padding-block: var(--sf-section-pad--xs); }\n .sf-section--s { padding-block: var(--sf-section-pad--s); }\n .sf-section--m { padding-block: var(--sf-section-pad--m); }\n .sf-section--l { padding-block: var(--sf-section-pad--l); }\n .sf-section--xl { padding-block: var(--sf-section-pad--xl); }\n .sf-section--2xl { padding-block: var(--sf-section-pad--2xl); }\n\n .sf-section-group > .sf-section + .sf-section { padding-block-start: 0; }\n\n /* Collapse modifier — when two adjacent sections share this modifier,\n they redistribute padding 50/50 across the boundary so total spacing\n stays one full padding gap (instead of two). Works regardless of\n background color: each section always has half its padding on\n the boundary side, so content never touches a color edge.\n\n Use when sections are siblings outside a .sf-section-group wrapper\n and may share OR differ in background.\n\n Note: uses --sf-section-pad (the canonical alias = m by default).\n For other sizes, set --sf-section-pad locally on each section:\n
\n ---------------------------------------------------------- */\n .sf-section--collapse:has(+ .sf-section--collapse) {\n padding-block-end: calc(var(--sf-section-pad) / 2);\n }\n .sf-section--collapse + .sf-section--collapse {\n padding-block-start: calc(var(--sf-section-pad) / 2);\n }\n\n /* -- Divider -------------------------------------------------\n Standalone separator primitive. Token-driven; pairs with the\n `--sf-divider-*` tokens and mirrors the `hr` element default.\n ---------------------------------------------------------- */\n .sf-divider {\n border: none;\n border-block-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);\n margin-block: var(--sf-space-m);\n }\n .sf-divider--vertical {\n border-block-start: none;\n border-inline-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);\n margin-block: 0;\n margin-inline: var(--sf-space-m);\n align-self: stretch;\n }\n\n /* -- Container -----------------------------------------------\n Max-width container, centered with side gutters. Establishes\n a named inline-size container `sf-layout` so children can\n target it precisely with `@container sf-layout (...)`.\n The framework's own `.sf-bento` and `.sf-grid-*` rules use\n anonymous `@container` queries on purpose — they react to\n the nearest inline-size ancestor, which lets a consumer\n embed them inside any container without rewiring.\n ---------------------------------------------------------- */\n\n .sf-container {\n container: sf-layout / inline-size;\n width: 100%;\n max-width: var(--sf-container-default);\n margin-inline: auto;\n padding-inline: var(--sf-space-gutter);\n }\n .sf-container--narrow { max-width: var(--sf-container-narrow); }\n .sf-container--prose { max-width: var(--sf-container-prose); }\n .sf-container--wide { max-width: var(--sf-container-wide); }\n .sf-container--full { max-width: var(--sf-container-full); }\n\n /* -- Stack ---------------------------------------------------\n Flex column with even vertical spacing between children.\n Override: style=\"--sf-stack-gap: 2rem\"\n ---------------------------------------------------------- */\n\n .sf-stack {\n display: flex;\n flex-direction: column;\n gap: var(--sf-stack-gap);\n }\n .sf-stack--xs { gap: var(--sf-space-xs); }\n .sf-stack--s { gap: var(--sf-space-s); }\n .sf-stack--m { gap: var(--sf-space-m); }\n .sf-stack--l { gap: var(--sf-space-l); }\n .sf-stack--xl { gap: var(--sf-space-xl); }\n .sf-stack--2xl { gap: var(--sf-space-2xl); }\n\n .sf-stack--center { align-items: center; }\n .sf-stack--end { align-items: flex-end; }\n .sf-stack--stretch { align-items: stretch; }\n\n /* -- Box -----------------------------------------------------\n Isolated unit with padding and an optional border.\n Outline instead of border — doesn't disturb the box model when toggled.\n Override: style=\"--sf-box-padding: 1rem; --sf-box-border-width: 1px\"\n ---------------------------------------------------------- */\n\n .sf-box {\n padding: var(--sf-box-padding);\n outline: var(--sf-box-border-width) solid var(--sf-box-border-color);\n }\n\n /* -- Center --------------------------------------------------\n Centers content with a max-inline-size and side gutters.\n content-box: max-inline-size applies to content only; padding is additive.\n Override: style=\"--sf-center-max: 60rem\"\n ---------------------------------------------------------- */\n\n .sf-center {\n box-sizing: content-box;\n margin-inline: auto;\n max-inline-size: var(--sf-center-max);\n padding-inline: var(--sf-center-gutter);\n }\n\n .sf-center--intrinsic {\n display: flex;\n flex-direction: column;\n align-items: center;\n }\n\n /* -- Cluster -------------------------------------------------\n Flex-wrap group of variable-width items.\n Override: style=\"--sf-cluster-gap: 0.5rem; --sf-cluster-justify: center\"\n ---------------------------------------------------------- */\n\n .sf-cluster {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sf-cluster-gap);\n align-items: var(--sf-cluster-align);\n justify-content: var(--sf-cluster-justify);\n }\n .sf-cluster--xs { gap: var(--sf-space-xs); }\n .sf-cluster--s { gap: var(--sf-space-s); }\n .sf-cluster--m { gap: var(--sf-space-m); }\n .sf-cluster--l { gap: var(--sf-space-l); }\n .sf-cluster--xl { gap: var(--sf-space-xl); }\n .sf-cluster--2xl { gap: var(--sf-space-2xl); }\n\n .sf-cluster--no-wrap { flex-wrap: nowrap; }\n .sf-cluster--center { justify-content: center; }\n .sf-cluster--end { justify-content: flex-end; }\n .sf-cluster--between { justify-content: space-between; }\n\n /* -- Sidebar -------------------------------------------------\n Two-column layout: first-child = sidebar, last-child = main content.\n Collapses when the viewport is too narrow (flex-basis trick).\n Override: style=\"--sf-sidebar-width: 20rem\"\n ---------------------------------------------------------- */\n\n .sf-sidebar {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sf-sidebar-gap);\n }\n\n .sf-sidebar > :first-child {\n flex-basis: var(--sf-sidebar-width);\n flex-grow: 1;\n }\n\n .sf-sidebar > :last-child {\n flex-basis: 0;\n flex-grow: 999;\n min-inline-size: var(--sf-sidebar-min-width);\n }\n\n .sf-sidebar--right > :first-child {\n flex-basis: 0;\n flex-grow: 999;\n min-inline-size: var(--sf-sidebar-min-width);\n }\n\n .sf-sidebar--right > :last-child {\n flex-basis: var(--sf-sidebar-width);\n flex-grow: 1;\n }\n\n .sf-sidebar--narrow { --sf-sidebar-width: 12rem; }\n .sf-sidebar--wide { --sf-sidebar-width: 26rem; }\n\n /* -- Switcher ------------------------------------------------\n Switches between a column (narrow viewport) and a row (wide).\n Uses calc((threshold - 100%) * 999) — zero media queries.\n Override: style=\"--sf-switcher-threshold: 40rem\"\n ---------------------------------------------------------- */\n\n .sf-switcher {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sf-switcher-gap);\n }\n\n .sf-switcher > * {\n flex-grow: 1;\n flex-basis: calc((var(--sf-switcher-threshold) - 100%) * 999);\n }\n\n .sf-switcher--no-wrap { flex-wrap: nowrap; overflow-x: auto; }\n .sf-switcher--vertical { flex-direction: column; }\n\n /* -- Grid ----------------------------------------------------\n Auto-fill CSS grid with no breakpoints. --fit switches to auto-fit.\n auto-fill: empty tracks preserved, last item doesn't stretch.\n auto-fit (--fit): empty tracks collapsed, items stretch to fill the gap.\n Override: style=\"--sf-grid-min: 12rem; --sf-grid-gap: 1rem\"\n ---------------------------------------------------------- */\n\n .sf-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(min(var(--sf-grid-min), 100%), 1fr));\n gap: var(--sf-grid-gap);\n }\n\n .sf-grid--fit {\n grid-template-columns: repeat(auto-fit, minmax(min(var(--sf-grid-min), 100%), 1fr));\n }\n\n .sf-grid--xs { --sf-grid-min: var(--sf-grid-min-xs); }\n .sf-grid--s { --sf-grid-min: var(--sf-grid-min-s); }\n .sf-grid--m { --sf-grid-min: var(--sf-grid-min-m); }\n .sf-grid--l { --sf-grid-min: var(--sf-grid-min-l); }\n .sf-grid--xl { --sf-grid-min: var(--sf-grid-min-xl); }\n .sf-grid--2xl { --sf-grid-min: var(--sf-grid-min-2xl); }\n\n /* Dense packing — let later items backfill earlier gaps (as .sf-bento\n does). Applies to any grid, including the fixed-column .sf-grid-N. */\n .sf-grid--dense { grid-auto-flow: dense; }\n\n /* -- Icon ----------------------------------------------------\n Inline icon sizing primitive. Uses em-based --sf-icon-* tokens\n so an icon scales with its surrounding text. Defaults to -m.\n ---------------------------------------------------------- */\n .sf-icon {\n display: inline-block;\n inline-size: var(--sf-icon-size, var(--sf-icon-m));\n block-size: var(--sf-icon-size, var(--sf-icon-m));\n flex-shrink: 0;\n vertical-align: text-bottom;\n fill: currentcolor;\n }\n .sf-icon--xs { --sf-icon-size: var(--sf-icon-xs); }\n .sf-icon--s { --sf-icon-size: var(--sf-icon-s); }\n .sf-icon--m { --sf-icon-size: var(--sf-icon-m); }\n .sf-icon--l { --sf-icon-size: var(--sf-icon-l); }\n .sf-icon--xl { --sf-icon-size: var(--sf-icon-xl); }\n\n /* Boxed variant — wraps the icon in a padded, bordered, optionally\n coloured frame. Adds inline-/block-size on top of the naked icon\n size so the visible glyph stays at --sf-icon-size and the box\n grows around it (content-box). Override per icon:\n */\n .sf-icon--boxed {\n inline-size: calc(var(--sf-icon-size, var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);\n block-size: calc(var(--sf-icon-size, var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);\n padding: var(--sf-icon-box-pad);\n border: var(--sf-icon-box-border);\n border-radius: var(--sf-icon-box-radius);\n background: var(--sf-icon-box-bg);\n box-sizing: content-box;\n }\n\n /* -- Cover ---------------------------------------------------\n Full-height flex column. A .sf-cover__center child is\n vertically centered via margin-block: auto.\n Override: style=\"--sf-cover-min-height: 50dvh\"\n ---------------------------------------------------------- */\n\n .sf-cover {\n display: flex;\n flex-direction: column;\n min-height: var(--sf-cover-min-height);\n padding-block: var(--sf-cover-padding);\n }\n\n .sf-cover > .sf-cover__center {\n margin-block: auto;\n }\n\n .sf-cover--min { min-height: 50dvh; }\n .sf-cover--max { min-height: 0; max-height: 100dvh; }\n .sf-cover--padding-s { padding-block: var(--sf-space-xl); }\n .sf-cover--padding-l { padding-block: var(--sf-space-4xl); }\n\n /* -- Frame ---------------------------------------------------\n Aspect-ratio container. Media children: fill + cover.\n Override: style=\"--sf-frame-ratio: 4 / 3\"\n ---------------------------------------------------------- */\n\n .sf-frame {\n aspect-ratio: var(--sf-frame-ratio);\n overflow: hidden;\n display: flex;\n justify-content: center;\n align-items: center;\n }\n\n .sf-frame > img,\n .sf-frame > video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n .sf-frame--square { aspect-ratio: var(--sf-ratio-square); }\n .sf-frame--video { aspect-ratio: var(--sf-ratio-video); }\n .sf-frame--cinema { aspect-ratio: var(--sf-ratio-cinema); }\n .sf-frame--portrait { aspect-ratio: var(--sf-ratio-portrait); }\n .sf-frame--4-3 { aspect-ratio: var(--sf-ratio-4-3); }\n .sf-frame--3-2 { aspect-ratio: var(--sf-ratio-3-2); }\n .sf-frame--golden { aspect-ratio: var(--sf-ratio-golden); }\n\n /* -- Reel ----------------------------------------------------\n Horizontal scroll with scroll-snap. Each child snaps to start.\n Override: style=\"--sf-reel-gap: 1rem; --sf-reel-item-width: 280px\"\n ---------------------------------------------------------- */\n\n .sf-reel {\n display: flex;\n block-size: var(--sf-reel-height);\n gap: var(--sf-reel-gap);\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n scroll-padding-inline: var(--sf-reel-gap);\n overscroll-behavior-x: contain;\n scrollbar-width: thin;\n }\n\n .sf-reel > * {\n flex: 0 0 var(--sf-reel-item-width);\n scroll-snap-align: start;\n }\n\n /* -- Imposter ------------------------------------------------\n Element centered absolutely within its parent.\n The parent must have position: relative.\n ---------------------------------------------------------- */\n\n .sf-imposter {\n position: absolute;\n inset-block-start: 50%;\n inset-inline-start: 50%;\n translate: -50% -50%;\n }\n\n .sf-imposter--fixed {\n position: fixed;\n }\n\n .sf-imposter--contain {\n max-inline-size: calc(100% - var(--sf-imposter-margin) * 2);\n max-block-size: calc(100% - var(--sf-imposter-margin) * 2);\n overflow: auto;\n }\n\n /* -- Alternate (zigzag / media-object) -----------------------\n Alternating two-column layouts (container-query responsive).\n Every other row reverses its children's order.\n\n Establishes a named container `sf-alternate` and binds the\n responsive query to that name — so a `.sf-alternate` nested\n inside another container (e.g. `.sf-bento`) still flips on\n its OWN width, not the outer container's.\n ---------------------------------------------------------- */\n\n .sf-alternate {\n display: grid;\n gap: var(--sf-alternate-gap);\n container: sf-alternate / inline-size;\n }\n\n .sf-alternate > * {\n display: grid;\n gap: var(--sf-alternate-inner-gap);\n align-items: center;\n }\n\n @container sf-alternate (min-width: 48em) {\n .sf-alternate > * {\n grid-template-columns: 1fr 1fr;\n }\n .sf-alternate > :nth-child(even) > :first-child { order: 2; }\n .sf-alternate > :nth-child(even) > :last-child { order: 1; }\n }\n\n /* -- Pancake (sticky footer) ---------------------------------\n Grid with three rows: header / main(1fr) / footer.\n ---------------------------------------------------------- */\n\n .sf-pancake {\n display: grid;\n grid-template-rows: auto 1fr auto;\n min-height: 100dvh;\n }\n\n /* -- Fixed column grids (container-query responsive) ---------\n Responsive fixed-column grids with no media queries.\n ---------------------------------------------------------- */\n\n .sf-grid-1, .sf-grid-2, .sf-grid-3, .sf-grid-4, .sf-grid-6 {\n display: grid;\n gap: var(--sf-grid-gap);\n }\n\n @container (min-width: 30em) {\n .sf-grid-4 { grid-template-columns: repeat(2, 1fr); }\n .sf-grid-6 { grid-template-columns: repeat(3, 1fr); }\n }\n\n @container (min-width: 48em) {\n .sf-grid-2 { grid-template-columns: repeat(2, 1fr); }\n .sf-grid-3 { grid-template-columns: repeat(3, 1fr); }\n .sf-grid-4 { grid-template-columns: repeat(4, 1fr); }\n .sf-grid-6 { grid-template-columns: repeat(6, 1fr); }\n }\n\n /* -- Ratio grids ---------------------------------------------\n Two-column ratio grids, container-query responsive.\n ---------------------------------------------------------- */\n\n .sf-grid-1-2, .sf-grid-2-1,\n .sf-grid-1-3, .sf-grid-3-1 {\n display: grid;\n gap: var(--sf-grid-gap);\n }\n\n @container (min-width: 48em) {\n .sf-grid-1-2 { grid-template-columns: 1fr 2fr; }\n .sf-grid-2-1 { grid-template-columns: 2fr 1fr; }\n .sf-grid-1-3 { grid-template-columns: 1fr 3fr; }\n .sf-grid-3-1 { grid-template-columns: 3fr 1fr; }\n }\n\n /* -- Content grid (breakout pattern) -------------------------\n Full-width grid that lets items break out to breakout or full\n width. Children default to the content column.\n ---------------------------------------------------------- */\n\n .sf-content-grid {\n display: grid;\n grid-template-columns:\n [full-start]\n minmax(var(--sf-space-gutter), 1fr)\n [breakout-start]\n minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2))\n [content-start]\n min(var(--sf-content-width), 100% - var(--sf-space-gutter) * 2)\n [content-end]\n minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2))\n [breakout-end]\n minmax(var(--sf-space-gutter), 1fr)\n [full-end];\n }\n\n .sf-content-grid > * { grid-column: content; }\n .sf-content-grid > .sf-breakout { grid-column: breakout; }\n .sf-content-grid > .sf-full-bleed { grid-column: full; }\n\n /* -- Bento grid ----------------------------------------------\n Free-form grid with auto-flow dense, responsive via CQ.\n Override: style=\"--sf-bento-cols: 4; --sf-bento-row: 12rem\"\n ---------------------------------------------------------- */\n\n .sf-bento {\n display: grid;\n grid-template-columns: repeat(var(--sf-bento-cols, var(--sf-bento-cols-default)), 1fr);\n grid-auto-rows: minmax(var(--sf-bento-row, var(--sf-bento-row-default)), auto);\n grid-auto-flow: dense;\n gap: var(--sf-bento-gap);\n }\n\n @container (min-width: 30em) and (max-width: 47.99em) {\n .sf-bento { grid-template-columns: repeat(2, 1fr); }\n }\n\n .sf-bento--2 { --sf-bento-cols: 2; }\n .sf-bento--4 { --sf-bento-cols: 4; }\n .sf-bento--compact { --sf-bento-row: var(--sf-bento-row-compact); }\n .sf-bento--tall { --sf-bento-row: var(--sf-bento-row-tall); }\n\n /* -- Subgrid -------------------------------------------------\n A grid child that inherits its parent's tracks.\n ---------------------------------------------------------- */\n\n .sf-subgrid { display: grid; grid-template-columns: subgrid; }\n .sf-subgrid-rows { display: grid; grid-template-rows: subgrid; }\n\n}\n\n/* ─── core/macros.css ─── */\n/* ============================================================\n SLASHED — core/macros.css\n ★ shipped in essential bundle\n Macro-classes (recipes / patterns).\n Requires: core/tokens.css, core/tokens.macros.css\n Layer: slashed.macros\n\n Macro-classes vs layout primitives:\n · Layout primitive = answers \"where do my children go?\"\n (e.g. .sf-stack, .sf-grid, .sf-cluster)\n · Macro-class = answers \"what does this element DO or\n LOOK LIKE?\" (e.g. .sf-prose, .sf-flow, .sf-truncate)\n\n Cascade position:\n layout → components → macros → utilities → states → …\n Macros may compose with primitives and components, but a\n single-property utility still wins on the same selector.\n\n PUBLIC API — every class here is part of the public surface.\n Tokens consumed by these classes are listed in\n core/tokens.macros.css.\n ============================================================ */\n\n@layer slashed.macros {\n\n /* -- Prose ---------------------------------------------------\n Long-form text column with automatic vertical rhythm.\n .sf-not-prose resets styles inside a prose block.\n ---------------------------------------------------------- */\n\n .sf-prose {\n max-width: var(--sf-container-prose);\n overflow-wrap: break-word;\n }\n\n .sf-prose > * + * { margin-block-start: var(--sf-prose-paragraph); }\n .sf-prose :is(h2, h3, h4) + * { margin-block-start: var(--sf-space-s); }\n .sf-prose li + li { margin-block-start: var(--sf-space-xs); }\n .sf-prose :is(blockquote, pre) { margin-block: var(--sf-space-m); }\n .sf-prose img { margin-block: var(--sf-space-m); border-radius: var(--sf-radius-m); }\n .sf-prose :is(ul, ol) { padding-inline-start: 1.25em; }\n .sf-prose ul { list-style: disc; }\n .sf-prose ol { list-style: decimal; }\n .sf-prose ::marker { color: var(--sf-color-primary); }\n .sf-prose figure { margin-block: var(--sf-space-l); }\n .sf-prose figcaption { font-size: var(--sf-text-s); color: var(--sf-color-text--muted); margin-block-start: var(--sf-space-xs); }\n .sf-prose table { margin-block: var(--sf-space-m); display: block; overflow-x: auto; max-width: 100%; }\n .sf-prose video { margin-block: var(--sf-space-m); border-radius: var(--sf-radius-m); }\n\n /* .sf-not-prose resets — specificity 0-2-1 intentionally exceeds the\n general 0-2-0 budget because these rules must override the .sf-prose\n child selectors above (also 0-2-0). This is a documented exception. */\n .sf-prose .sf-not-prose > * + * { margin-block-start: 0; }\n .sf-prose .sf-not-prose li + li { margin-block-start: 0; }\n /* `revert` in a cascade layer rolls back past all layers to the UA\n stylesheet — restoring browser-default list styling. In the flat\n bundle (layers stripped), `revert` falls back to the UA stylesheet\n directly. Both paths produce the same result: native list bullets. */\n .sf-prose .sf-not-prose :is(ul,ol) { list-style: revert; padding-inline-start: revert; }\n .sf-prose .sf-not-prose ::marker { color: inherit; }\n .sf-prose .sf-not-prose img { margin-block: 0; border-radius: 0; }\n .sf-prose .sf-not-prose figure { margin-block: 0; }\n .sf-prose .sf-not-prose figcaption { font-size: inherit; color: inherit; margin-block-start: 0; }\n .sf-prose .sf-not-prose table { margin-block: 0; display: revert; overflow-x: revert; max-width: revert; }\n .sf-prose .sf-not-prose video { margin-block: 0; border-radius: 0; }\n\n /* -- Flow ----------------------------------------------------\n Heydon Pickering's \"lobotomized owl\" — every flow child gets\n equal margin-block-start. Useful for content blocks where you\n don't know in advance which elements will be present.\n Override: style=\"--sf-flow-space: 2rem\"\n ---------------------------------------------------------- */\n .sf-flow > * + * {\n margin-block-start: var(--sf-flow-space);\n }\n\n /* -- Truncate / line-clamp -----------------------------------\n Single-line ellipsis (.sf-truncate) and N-line clamp\n (.sf-line-clamp-2 / -3 / -N). The fixed-count variants\n hardcode -webkit-line-clamp; -N reads --sf-line-clamp.\n\n Note: -webkit-line-clamp is not vendor-locked despite the\n prefix — every modern browser implements it as the de-facto\n standard. The unprefixed CSS Overflow 4 `line-clamp` exists\n but is not yet widely shipped.\n ---------------------------------------------------------- */\n .sf-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .sf-line-clamp-2,\n .sf-line-clamp-3,\n .sf-line-clamp-N {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n .sf-line-clamp-2 { -webkit-line-clamp: 2; line-clamp: 2; }\n .sf-line-clamp-3 { -webkit-line-clamp: 3; line-clamp: 3; }\n .sf-line-clamp-N {\n -webkit-line-clamp: var(--sf-line-clamp);\n line-clamp: var(--sf-line-clamp);\n }\n\n /* -- Equal height --------------------------------------------\n Force grid/flex children to share the tallest one's height.\n Works in flex (default `align-items: stretch`) and is\n re-asserted here for cases where a wrapper resets it.\n ---------------------------------------------------------- */\n .sf-equal-height {\n display: flex;\n align-items: stretch;\n }\n .sf-equal-height > * {\n block-size: auto;\n flex: 1 1 auto;\n }\n\n /* -- Aspect --------------------------------------------------\n Generic aspect-ratio container. Distinct from layout's\n .sf-frame which targets media (object-fit: cover) — .sf-aspect\n is content-agnostic.\n Override: style=\"--sf-aspect: 4 / 3\"\n ---------------------------------------------------------- */\n .sf-aspect {\n aspect-ratio: var(--sf-aspect);\n }\n\n /* -- Scroll shadow -------------------------------------------\n Top + bottom mask gradient that reveals when content is\n scrolled inside a vertical-scroll container. Pure CSS,\n no JS. Combine with overflow-y: auto on the same element.\n Override: style=\"--sf-scroll-shadow-size: 3rem\"\n ---------------------------------------------------------- */\n .sf-scroll-shadow {\n overflow-y: auto;\n mask-image:\n linear-gradient(to bottom,\n transparent 0,\n black var(--sf-scroll-shadow-size),\n black calc(100% - var(--sf-scroll-shadow-size)),\n transparent 100%);\n }\n\n /* -- Scroll snap ---------------------------------------------\n Vertical scroll-snap container for vertically stacked sections.\n Pairs with consumer-supplied scroll-snap-align on children.\n For horizontal snap, use the .sf-reel layout primitive.\n ---------------------------------------------------------- */\n .sf-scroll-snap {\n overflow-y: auto;\n scroll-snap-type: y mandatory;\n overscroll-behavior-y: contain;\n }\n .sf-scroll-snap > * {\n scroll-snap-align: start;\n }\n\n /* -- Overflow fade -------------------------------------------\n End-edge horizontal fade for clipped inline content\n (e.g. a row of tags overflowing a card). Pure mask, so it\n respects the element's actual background.\n ---------------------------------------------------------- */\n .sf-overflow-fade {\n overflow: hidden;\n mask-image: linear-gradient(\n to right,\n black 0,\n black calc(100% - var(--sf-scroll-shadow-size)),\n transparent 100%\n );\n }\n\n /* -- No tap highlight ----------------------------------------\n Suppresses the WebKit/Android grey tap-highlight overlay on\n interactive elements where it conflicts with the framework's\n own .is-active / hover treatment.\n ---------------------------------------------------------- */\n .sf-no-tap-highlight {\n -webkit-tap-highlight-color: transparent;\n }\n\n /* -- Surface -------------------------------------------------\n Sets background + auto-flipped foreground in one class.\n Combines --sf-color-X (resolved semantic, mode-aware) with\n the matching --sf-color-text--on-X (auto-contrast) so text\n legibility is automatic without consumer math.\n\n 11 variants cover all brand + status + inverse colors. Base is\n excluded — base IS the page surface; use plain\n background: var(--sf-color-bg) if you need it explicitly.\n\n Composes naturally with .sf-section / .sf-card / .sf-stack:\n
\n ---------------------------------------------------------- */\n .sf-surface--primary { background: var(--sf-color-primary); color: var(--sf-color-text--on-primary); }\n .sf-surface--secondary { background: var(--sf-color-secondary); color: var(--sf-color-text--on-secondary); }\n .sf-surface--tertiary { background: var(--sf-color-tertiary); color: var(--sf-color-text--on-tertiary); }\n .sf-surface--action { background: var(--sf-color-action); color: var(--sf-color-text--on-action); }\n .sf-surface--neutral { background: var(--sf-color-neutral); color: var(--sf-color-text--on-neutral); }\n .sf-surface--inverse { background: var(--sf-color-inverse); color: var(--sf-color-text--on-inverse); }\n .sf-surface--success { background: var(--sf-color-success); color: var(--sf-color-text--on-success); }\n .sf-surface--warning { background: var(--sf-color-warning); color: var(--sf-color-text--on-warning); }\n .sf-surface--error { background: var(--sf-color-error); color: var(--sf-color-text--on-error); }\n .sf-surface--info { background: var(--sf-color-info); color: var(--sf-color-text--on-info); }\n .sf-surface--danger { background: var(--sf-color-danger); color: var(--sf-color-text--on-danger); }\n\n /* -- Text gradient -------------------------------------------\n Fills text with a gradient. Defaults to --sf-gradient-primary\n (auto-adapts to brand + dark mode). Override the background-image\n per-instance for any other gradient or radial:\n

\n\n Selection on gradient text inherits the clipping — that's a\n known browser limitation across all engines, accepted as-is.\n\n The `color: transparent` declaration is gated behind a feature\n query for background-clip:text. Without that gate, an engine\n that ignored background-clip:text would render the text fully\n invisible (transparent over no clipped fill). All current\n evergreen browsers support either the standard or the\n -webkit- prefixed form, so the supports check is a defensive\n guard rather than a real-world fallback.\n ---------------------------------------------------------- */\n .sf-text-gradient {\n background-image: var(--sf-gradient-primary);\n /* stylelint-disable-next-line property-no-vendor-prefix -- required for Safari */\n -webkit-background-clip: text;\n background-clip: text;\n }\n @supports ((background-clip: text) or (-webkit-background-clip: text)) {\n .sf-text-gradient {\n color: transparent;\n }\n }\n\n /* -- External link indication --------------------------------\n Adds an external-link glyph after the link text. Opt-in per\n element via class — for global auto-detection, write your\n own rule in unlayered CSS:\n a[rel~=\"external\"]:not(.no-external)::after {\n content: var(--sf-link-external-marker);\n }\n\n Marker is tokenised so consumers can swap to \" →\", \" ⤴\", or\n unset (empty string fully disables it — the default token\n value bakes the leading space in, so `\"\"` leaves nothing\n after the link):\n :root { --sf-link-external-marker: \"\" }\n ---------------------------------------------------------- */\n .sf-link-external::after {\n content: var(--sf-link-external-marker);\n display: inline-block;\n font-size: 0.85em;\n text-decoration: none;\n }\n\n}\n\n/* ─── core/states.css ─── */\n/* SLASHED — core/states.css\n @layer slashed.states\n\n Stateful classes toggled by JS or ARIA attributes.\n Prefix: .is-* (exclusive to this layer — never used in utilities).\n\n These are NOT utility classes. They describe dynamic,\n runtime states that change in response to user interaction\n or application logic. Utilities describe static intent;\n states describe current condition.\n\n Design principles:\n ─ Generic states only — component-specific overrides belong\n in slashed.components (e.g. .sf-modal.is-open).\n ─ Tokens first — every value references a design token.\n ─ Low specificity — single class, no nesting.\n ─ Minimal property sets — states set the MINIMUM needed\n to communicate the state. Components layer on top.\n ─────────────────────────────────────────────────── */\n\n@layer slashed.states {\n\n /* ============================================================\n VISIBILITY\n ============================================================ */\n\n .is-hidden {\n display: none !important;\n }\n\n .is-invisible {\n visibility: hidden;\n }\n\n .is-visible {\n visibility: visible;\n }\n\n /* ============================================================\n INTERACTIVITY — DISABLED / READONLY\n ============================================================ */\n\n .is-disabled {\n opacity: var(--sf-opacity-disabled);\n pointer-events: none;\n cursor: not-allowed;\n user-select: none;\n }\n\n .is-readonly {\n pointer-events: none;\n user-select: none;\n }\n\n /* ============================================================\n LOADING / ASYNC FEEDBACK\n ============================================================ */\n\n .is-loading {\n color: transparent !important;\n pointer-events: none;\n position: relative;\n }\n\n .is-loading::after {\n content: \"\";\n position: absolute;\n inset-block-start: 50%;\n inset-inline-start: 50%;\n width: 1em;\n height: 1em;\n margin-block-start: -0.5em;\n margin-inline-start: -0.5em;\n border: var(--sf-border-width-2) solid var(--sf-color-border--strong);\n border-block-start-color: var(--sf-color-action);\n border-radius: var(--sf-radius-full);\n /* Reduced-motion: accessibility.css kills animation-duration via\n !important on *, *::before, *::after — so this spinner stops\n even without a local reduced-motion wrapper. */\n animation: var(--sf-animation-spin);\n }\n\n .is-busy {\n cursor: progress;\n }\n\n .is-pending {\n /* Optimistic UI — request in flight but content still visible/usable\n (unlike .is-loading, which masks content with a spinner). */\n opacity: var(--sf-state-pending-opacity);\n cursor: progress;\n }\n\n .is-skeleton {\n color: transparent !important;\n background: linear-gradient(\n 90deg,\n var(--sf-color-well) 25%,\n var(--sf-color-raised) 50%,\n var(--sf-color-well) 75%\n );\n background-size: 200% 100%;\n animation: var(--sf-animation-shimmer);\n border-radius: var(--sf-radius-s);\n pointer-events: none;\n user-select: none;\n }\n\n /* ============================================================\n ACTIVE / SELECTED / CURRENT — navigation & toggling\n ============================================================ */\n\n .is-active {\n /* Generic active indicator — components add visual specifics.\n Sets a scoped token for components to consume. */\n --sf-is-active: 1;\n }\n\n .is-selected {\n background-color: var(--sf-color-bg--selected);\n }\n\n .is-current {\n /* Navigation: marks the current page/route.\n Components style via .sf-nav .is-current etc.\n Weight is tokenised so consumers can override without specificity battles:\n :root { --sf-current-font-weight: var(--sf-font-weight-semibold); } */\n --sf-is-current: 1;\n font-weight: var(--sf-current-font-weight, var(--sf-font-weight-bold));\n }\n\n .is-highlighted {\n background-color: var(--sf-color-bg--focus);\n }\n\n .is-pressed {\n /* Toggle buttons (aria-pressed=\"true\"). Components add specifics. */\n --sf-is-pressed: 1;\n background-color: var(--sf-color-bg--selected);\n }\n\n /* ============================================================\n OPEN / CLOSED — disclosure widgets, modals, dropdowns\n\n .is-open / .is-collapsed — a thing is shown vs hidden (modal,\n dropdown, drawer). .is-expanded — maps to aria-expanded on a\n disclosure/accordion trigger. They share the --sf-is-open flag\n but signal different intent to consumers.\n ============================================================ */\n\n .is-open {\n --sf-is-open: 1;\n }\n\n .is-collapsed {\n --sf-is-open: 0;\n }\n\n .is-expanded {\n --sf-is-open: 1;\n }\n\n /* ============================================================\n VALIDATION / FEEDBACK\n\n Overlap semantics (see docs/states.md):\n · .is-invalid / .is-valid — form-field validation (maps to\n aria-invalid). Use on inputs.\n · .is-error / .is-success — general component feedback, not tied\n to a single field (a failed save, a completed step).\n · .is-danger — destructive-action context (a delete button), NOT\n a validation result. Distinct from .is-error.\n ============================================================ */\n\n .is-valid {\n --sf-field-border-color: var(--sf-color-success);\n --sf-field-text-color: var(--sf-color-success-strong);\n }\n\n .is-invalid {\n --sf-field-border-color: var(--sf-color-error);\n --sf-field-text-color: var(--sf-color-error-strong);\n }\n\n .is-warning {\n --sf-field-border-color: var(--sf-color-warning);\n --sf-field-text-color: var(--sf-color-warning-strong);\n }\n\n .is-success {\n --sf-field-border-color: var(--sf-color-success);\n --sf-field-text-color: var(--sf-color-success-strong);\n }\n\n .is-error {\n --sf-field-border-color: var(--sf-color-error);\n --sf-field-text-color: var(--sf-color-error-strong);\n }\n\n .is-info {\n --sf-field-border-color: var(--sf-color-info);\n --sf-field-text-color: var(--sf-color-info-strong);\n }\n\n .is-danger {\n --sf-field-border-color: var(--sf-color-danger);\n --sf-field-text-color: var(--sf-color-danger-strong);\n }\n\n /* ============================================================\n POSITION / STICKINESS\n ============================================================ */\n\n .is-sticky {\n position: sticky;\n top: var(--sf-sticky-offset, 0);\n z-index: var(--sf-z-low);\n }\n\n .is-pinned {\n position: sticky;\n top: 0;\n z-index: var(--sf-z-mid);\n }\n\n .is-fixed {\n position: fixed;\n }\n\n .is-fullscreen {\n /* App-managed fullscreen overlay. For the native Fullscreen API,\n prefer the `:fullscreen` pseudo-class; this is the JS-toggled\n equivalent for elements not entered via that API. */\n position: fixed;\n inset: 0;\n z-index: var(--sf-z-max);\n }\n\n .is-resizable {\n resize: both;\n overflow: auto;\n }\n\n /* ============================================================\n OVERFLOW / CLIPPING\n ============================================================ */\n\n .is-clipped {\n overflow: hidden !important;\n }\n\n .is-scrollable {\n overflow: auto;\n overscroll-behavior: contain;\n }\n\n .is-truncated {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* ============================================================\n DRAG & DROP\n ============================================================ */\n\n .is-dragging {\n opacity: var(--sf-opacity-50);\n cursor: grabbing;\n }\n\n .is-drop-target {\n outline: var(--sf-focus-ring-width) dashed var(--sf-color-action);\n outline-offset: var(--sf-focus-ring-offset);\n }\n\n .is-draggable {\n cursor: grab;\n }\n\n /* ============================================================\n OVERLAY / BACKDROP\n ============================================================ */\n\n .is-overlay {\n position: absolute;\n inset: 0;\n }\n\n /* ============================================================\n FOCUS / INTERACTION MODIFIERS\n ============================================================ */\n\n .is-clickable {\n cursor: pointer;\n }\n\n .is-unselectable {\n user-select: none;\n }\n\n .is-focused {\n /* Programmatic / JS-driven focus indicator, independent of\n :focus-visible. Normal weight so consumers can restyle. */\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-focus-ring-color);\n outline-offset: var(--sf-focus-ring-offset);\n }\n\n /* ============================================================\n EMPTY STATE\n ============================================================ */\n\n .is-empty:empty {\n display: none;\n }\n\n /* ============================================================\n KEYFRAMES (used by states above)\n\n Spinner and shimmer keyframes live in core/motion.css alongside\n the rest of the framework's @keyframes. They are referenced from\n here by name (sf-spin, sf-shimmer) and are inert until applied.\n If you load core/states.css without core/motion.css, define them\n in your own CSS or copy them from motion.css.\n ============================================================ */\n\n}\n\n/* ─── core/motion.css ─── */\n/* SLASHED — core/motion.css\n @layer slashed.motion\n\n Extends the base interactive transition (defined in base.css)\n with a broader property set and adds scroll/view-transition\n behaviour. Optional and independently loadable.\n\n Reduced-motion handling is intentionally NOT duplicated here:\n accessibility.css (a higher cascade layer, shipped in the\n 'essential' bundle) neutralises animation/transition via\n property-level !important. The no-preference wrapper below is\n a defensive opt-in stance — animations apply only when the\n user has not requested their absence — and remains correct\n even if motion.css is served without accessibility.css.\n ─────────────────────────────────────────────────── */\n\n@layer slashed.motion {\n\n @media (prefers-reduced-motion: no-preference) {\n\n html {\n scroll-behavior: smooth;\n }\n\n /* Keyboard navigation must not be hijacked by smooth scroll. */\n html:focus-within {\n scroll-behavior: auto;\n }\n\n /* Broadens the base.css link transition to all interactive\n elements and the full set of animatable UI properties. */\n a, button, input, select, textarea, summary {\n transition-property:\n color, background-color, border-color, text-decoration-color,\n fill, stroke, opacity, box-shadow, transform, filter,\n backdrop-filter;\n transition-duration: var(--sf-duration-fast);\n transition-timing-function: var(--sf-ease-out);\n }\n\n @supports (view-transition-name: none) {\n ::view-transition-old(root),\n ::view-transition-new(root) {\n animation-duration: var(--sf-duration-normal);\n animation-timing-function: var(--sf-ease-out);\n }\n }\n\n /* Animation presets — opt-in classes. Scoped to no-preference so\n reduced-motion users never trigger them (accessibility.css also\n neutralises animation-duration as a hard guarantee). These are\n DECORATIVE: under reduced motion they simply don't run, so never\n rely on one (e.g. .sf-fade-out) as the sole way to show/hide\n content — drive real state with hidden/display/JS instead. */\n .sf-fade-in { animation: var(--sf-animation-fade-in); }\n .sf-fade-out { animation: var(--sf-animation-fade-out); }\n .sf-slide-in-up { animation: var(--sf-animation-slide-in-up); }\n .sf-slide-in-down { animation: var(--sf-animation-slide-in-down); }\n .sf-slide-in-left { animation: var(--sf-animation-slide-in-left); }\n .sf-slide-in-right { animation: var(--sf-animation-slide-in-right); }\n .sf-scale-up { animation: var(--sf-animation-scale-up); }\n .sf-scale-down { animation: var(--sf-animation-scale-down); }\n\n /* Scroll-driven entrance effects — element animates into view\n as it enters the viewport. Uses CSS scroll-driven animations\n via animation-timeline: view(). Range tokens\n (--sf-scroll-timeline-range-{start,end}, declared in\n core/tokens.css) control where the animation starts and ends\n along the scroll progress.\n\n Example:\n
\n\n Reduced-motion: gated by the no-preference wrapper above, so\n these are inert when the user opts out. The core animation\n (translate/opacity) still composes correctly with reduced-motion\n neutralisation in accessibility.css if a consumer accidentally\n writes them outside the gate.\n\n Browser support — animation-timeline is shipping in Chrome 115+\n and Firefox 145+ (Nov 2026). Safari does not yet support it\n (tracked at WebKit bug 264057); on those engines the @supports\n block below is skipped, so the rule falls back to a one-shot\n time-driven animation playing once at --sf-duration-slow.\n animation-duration is set unconditionally because without it\n a non-supporting engine would default to 0s and snap to the\n end state instantly — there'd be no visible entrance at all.\n Where animation-timeline is supported the timeline drives\n progress and the duration is ignored. */\n .sf-entrance--fade,\n .sf-entrance--fade-up,\n .sf-entrance--fade-down,\n .sf-entrance--fade-left,\n .sf-entrance--fade-right,\n .sf-entrance--scale-up {\n animation-duration: var(--sf-duration-slow);\n animation-timing-function: var(--sf-ease-out);\n animation-fill-mode: both;\n }\n @supports (animation-timeline: view()) {\n .sf-entrance--fade,\n .sf-entrance--fade-up,\n .sf-entrance--fade-down,\n .sf-entrance--fade-left,\n .sf-entrance--fade-right,\n .sf-entrance--scale-up {\n animation-timeline: view();\n animation-range: var(--sf-scroll-timeline-range-start, entry 0%)\n var(--sf-scroll-timeline-range-end, cover 30%);\n }\n }\n .sf-entrance--fade { animation-name: sf-fade-in; }\n .sf-entrance--fade-up { animation-name: sf-slide-in-up; }\n .sf-entrance--fade-down { animation-name: sf-slide-in-down; }\n .sf-entrance--fade-left { animation-name: sf-slide-in-left; }\n .sf-entrance--fade-right { animation-name: sf-slide-in-right; }\n .sf-entrance--scale-up { animation-name: sf-scale-up; }\n\n }\n\n /* Keyframes — inert until referenced, so they live outside the\n no-preference wrapper (consumers may reference them at will).\n Spinner and shimmer are used by .is-loading / .is-skeleton in\n core/states.css; the rest power the .sf-* animation classes\n above. */\n @keyframes sf-fade-in { from { opacity: 0; } to { opacity: 1; } }\n @keyframes sf-fade-out { from { opacity: 1; } to { opacity: 0; } }\n @keyframes sf-slide-in-up { from { opacity: 0; translate: 0 1rem; } to { opacity: 1; translate: 0 0; } }\n @keyframes sf-slide-in-down { from { opacity: 0; translate: 0 -1rem; } to { opacity: 1; translate: 0 0; } }\n @keyframes sf-slide-in-left { from { opacity: 0; translate: 1rem 0; } to { opacity: 1; translate: 0 0; } }\n @keyframes sf-slide-in-right { from { opacity: 0; translate: -1rem 0; } to { opacity: 1; translate: 0 0; } }\n @keyframes sf-scale-up { from { opacity: 0; scale: 0.92; } to { opacity: 1; scale: 1; } }\n @keyframes sf-scale-down { from { opacity: 1; scale: 1; } to { opacity: 0; scale: 0.92; } }\n\n @keyframes sf-spin { to { rotate: 360deg; } }\n @keyframes sf-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }\n\n /* Notification ping (expanding fading ring), cursor blink, gentle float.\n Keyframes only — apply via your own BEM/component rules or the\n --sf-animation-ping / -blink / -float presets. No utility classes\n ship for these (by design). */\n @keyframes sf-ping { 75%, 100% { scale: 2; opacity: 0; } }\n @keyframes sf-blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }\n @keyframes sf-float { 0%, 100% { translate: 0 0; } 50% { translate: 0 -0.5rem; } }\n\n /* @property color interpolation — proves that registered custom\n properties animate smoothly in oklch. Because --sf-color-primary-light\n is registered with syntax:\"\", the browser interpolates the\n intermediate color values frame-by-frame. Opt-in via .sf-color-pulse. */\n @keyframes sf-color-pulse {\n 0% { --sf-color-primary-light: oklch(from var(--sf-color-primary-light) l c h); }\n 50% { --sf-color-primary-light: oklch(from var(--sf-color-primary-light) calc(l + 0.25) c h); }\n 100% { --sf-color-primary-light: oklch(from var(--sf-color-primary-light) l c h); }\n }\n\n @media (prefers-reduced-motion: no-preference) {\n .sf-color-pulse {\n animation: var(--sf-animation-color-pulse);\n }\n }\n\n /* @starting-style — entry animations for elements appearing in the\n top layer (dialog, [popover]) or via display:none → block. SLASHED\n ships no global @starting-style rules because the from-state is\n element/component specific. Consumers opt in per component, e.g.:\n dialog[open] { opacity: 1; @starting-style { opacity: 0; } }\n Tracked for the future components layer. */\n\n}\n\n/* ─── core/accessibility.css ─── */\n/* SLASHED — core/accessibility.css\n @layer slashed.accessibility\n\n Critical a11y rules use !important to survive unlayered CSS and\n third-party resets (unlayered normal beats any layered normal).\n Protection is SELECTIVE — only rules whose override is a genuine\n user-facing barrier are hardened. Rules that are legitimately\n customised (touch targets, skip-link position, contrast boost,\n cursor) are left normal on purpose.\n\n Audit note: reset.css/base.css contain no !important on outline,\n animation-* or transition-*, so the reversed important-layer order\n cannot let a lower layer defeat these rules.\n ─────────────────────────────────────────────────── */\n\n@layer slashed.accessibility {\n\n /* ----------------------------------------------------------\n Focus — keyboard visibility is WCAG 2.4.7, hard barrier.\n Ring definition lives here (not base.css) so all focus\n logic is in one high-priority layer. Hardened.\n ---------------------------------------------------------- */\n :focus:not(:focus-visible) {\n outline: none;\n }\n\n :focus-visible {\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-focus-ring-color) !important;\n outline-offset: var(--sf-focus-ring-offset) !important;\n }\n\n /* ----------------------------------------------------------\n Reduced motion. Token override helps components that read\n duration tokens in other contexts; the property-level\n !important block is the real guarantee and works\n independently of tokens (and of unlayered token overrides).\n ---------------------------------------------------------- */\n @media (prefers-reduced-motion: reduce) {\n :root {\n --sf-duration-instant: 0.01ms;\n --sf-duration-fast: 0.01ms;\n --sf-duration-normal: 0.01ms;\n --sf-duration-slow: 0.01ms;\n --sf-duration-slower: 0.01ms;\n }\n\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n scroll-behavior: auto !important;\n }\n }\n\n /* Manual motion opt-out — same suppression as the OS preference above,\n but consumer-toggled (e.g. a site-level \"reduce motion\" switch). Works\n regardless of the OS setting. */\n .no-motion,\n .no-motion *,\n .no-motion *::before,\n .no-motion *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n scroll-behavior: auto !important;\n }\n\n /* ----------------------------------------------------------\n High contrast — enhancement, not a barrier. Left normal:\n if overridden the focus ring still exists, just thinner.\n ---------------------------------------------------------- */\n @media (prefers-contrast: more) {\n :root {\n --sf-focus-ring-width: 3px;\n /* Push derived reading-text colours toward the extremes (see the\n --sf-contrast-bias knob in tokens.css). */\n --sf-contrast-bias: 0.06;\n }\n\n hr {\n border-block-start-width: var(--sf-border-width-2);\n }\n }\n\n /* ----------------------------------------------------------\n Reduced transparency.\n ---------------------------------------------------------- */\n @media (prefers-reduced-transparency: reduce) {\n ::backdrop {\n background-color: var(--sf-color-neutral);\n opacity: 1;\n }\n }\n\n /* ----------------------------------------------------------\n Minimum touch targets — WCAG 2.5.5 has documented\n exceptions; dense desktop-first UIs legitimately go below\n 44px with other hit-area strategies. Left normal ON PURPOSE\n so it stays a recommendation, not an unbreakable rule.\n ---------------------------------------------------------- */\n @media (pointer: coarse) {\n button,\n input[type=\"button\"],\n input[type=\"submit\"],\n input[type=\"reset\"],\n select,\n summary,\n a {\n min-height: var(--sf-touch-target);\n min-width: var(--sf-touch-target);\n }\n }\n\n /* ----------------------------------------------------------\n Disabled states — cosmetic, left normal.\n ---------------------------------------------------------- */\n [disabled],\n [aria-disabled=\"true\"] {\n cursor: not-allowed;\n }\n\n /* ----------------------------------------------------------\n Screen-reader-only. Atomic contract: a partial override\n either exposes hidden content visually or hides intended\n content. All-or-nothing, so the whole set is hardened.\n ---------------------------------------------------------- */\n .sr-only,\n .visually-hidden,\n .sr-only-focusable:not(:focus, :focus-within) {\n position: absolute !important;\n width: 1px !important;\n height: 1px !important;\n padding: 0 !important;\n margin: -1px !important;\n overflow: hidden !important; /* fallback for Safari <16 */\n overflow: clip !important;\n clip-path: inset(50%) !important;\n white-space: nowrap !important;\n border: 0 !important;\n }\n\n /* ----------------------------------------------------------\n Skip link — appearance belongs to the integrator's design.\n Left normal so position/colour can be themed freely.\n ---------------------------------------------------------- */\n .skip-link {\n position: absolute;\n top: -100%;\n left: var(--sf-space-m);\n z-index: var(--sf-z-max);\n padding: var(--sf-space-s) var(--sf-space-m);\n background: var(--sf-color-bg);\n color: var(--sf-color-text);\n border: var(--sf-border-width-1) solid var(--sf-color-border);\n border-radius: var(--sf-radius-m);\n transition: top var(--sf-duration-fast) var(--sf-ease-out);\n }\n .skip-link:focus {\n top: var(--sf-space-m);\n }\n\n /* ----------------------------------------------------------\n Focus-parent — an a11y *pattern*, not a state. Lives in the\n accessibility layer (was previously in states.css; moved in\n v0.3.0) so that all focus-related a11y rules share one home\n and survive third-party resets at the same priority as the\n focus-visible ring above. Cannot be hardened with !important\n because the outline shorthand is consumer-customisable.\n ---------------------------------------------------------- */\n .focus-parent:focus-within {\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-color-border--focus);\n outline-offset: var(--sf-focus-ring-offset);\n }\n\n /* ----------------------------------------------------------\n Clickable-parent — the card-with-link a11y pattern.\n Applied to the card container. Two modes:\n\n Automatic (single primary link):\n
\n

Title

\n

Description

\n \n
\n\n Explicit (multiple links — add [data-overlay-link] to the primary):\n
\n

Title

\n

Description

\n Category ← lifted automatically\n ← lifted automatically\n
\n\n Mechanic: the link's ::after fills the card via position:absolute;\n inset:0. It is contained by .sf-clickable-parent (position:relative)\n only if the link itself has no positioned ancestor between it and the\n card — so no element containing the overlay link should be given\n position:relative. Flow children are therefore intentionally left at\n position:static; they sit below the overlay in stacking order.\n\n Trade-off: card text is NOT user-selectable by mouse. A JavaScript\n click-delegate could restore selectability while preserving\n full-card navigation.\n\n Key features:\n • [data-overlay-link] explicit mode — reliable multi-link cards\n • `:focus-within` card ring — always included, no extra recipe\n • --sf-clickable-overlay-z — escape parent stacking contexts\n • Wider interactive-element exceptions (summary, video, audio…)\n\n Tune --sf-clickable-overlay-z to escape a parent stacking context.\n [data-no-overlay] opts any descendant above the overlay.\n ---------------------------------------------------------- */\n .sf-clickable-parent {\n --sf-clickable-overlay-z: 1;\n position: relative;\n cursor: pointer;\n }\n\n /* Overlay: the link's ::after fills the containing card.\n Automatic mode — every :any-link gets the overlay; works reliably\n for single-link cards. Secondary links that also receive ::after are\n lifted to position:relative (interactive rule below), which scopes\n their ::after to themselves and keeps them independently clickable.\n Explicit mode — only [data-overlay-link] fills the card. */\n .sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link)::after,\n .sf-clickable-parent [data-overlay-link]::after {\n content: \"\";\n position: absolute;\n inset: 0;\n z-index: var(--sf-clickable-overlay-z);\n pointer-events: auto;\n }\n\n /* Interactive descendants beat the overlay and stay functional.\n Explicit mode: every non-designated :any-link is lifted — robust at\n any nesting depth, no :first-of-type fragility.\n Automatic mode: links that are :not(:first-of-type) among siblings\n are lifted. Sibling-scoped — use explicit mode for cards where\n secondary links live at different nesting depths.\n [data-no-overlay] is the per-element consumer escape hatch. */\n .sf-clickable-parent :is(\n button, input, textarea, select,\n summary, video, audio,\n [role=\"button\"], [data-no-overlay]\n ),\n .sf-clickable-parent:has([data-overlay-link]) a:where(:any-link):not([data-overlay-link]),\n .sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):not(:first-of-type) {\n position: relative;\n z-index: calc(var(--sf-clickable-overlay-z) + 1);\n }\n\n /* Card-level focus ring — keyboard users see which card is active.\n The focus ring is always included without requiring a separate\n recipe. Suppress with outline:none\n if the card design provides its own focus indicator. */\n .sf-clickable-parent:focus-within {\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-color-border--focus);\n outline-offset: var(--sf-focus-ring-offset);\n }\n\n /* ----------------------------------------------------------\n Forced colors (Windows High Contrast). Outline-based focus\n already survives; we only (a) adopt the system Highlight\n colour for the ring and (b) drop decorative shadows that\n forced-colors would otherwise flatten into solid slabs.\n Shadow removal is zero-specificity so any rule can win it back.\n ---------------------------------------------------------- */\n @media (forced-colors: active) {\n :root {\n --sf-focus-ring-color: Highlight;\n }\n\n :focus-visible {\n outline-color: Highlight !important;\n }\n\n :where(*) {\n box-shadow: none;\n text-shadow: none;\n }\n\n /* Form controls otherwise lose their visible boundary in forced-colors\n (their token-driven border colour is replaced by the system canvas).\n Re-assert a system-coloured border so fields stay distinguishable. */\n input,\n select,\n textarea {\n border: var(--sf-border-width-1) solid;\n }\n }\n\n}\n\n/* ─── core/print.css ─── */\n/* SLASHED — core/print.css\n @layer slashed.print\n\n Print rules and @page definition. Print tokens (--sf-print-*)\n are declared in core/tokens.css.\n\n Color contract (Phase 3):\n authored colour is preserved by default. Browsers apply their own\n ink-saving heuristics via the default `print-color-adjust: economy`,\n so semantic colours (badges, status pills, , syntax highlighting)\n reach paper intact. Two opt-in classes give consumers control:\n\n .print-color-exact → force every descendant to print colours\n verbatim (for colour-coded data that loses\n meaning when the browser flattens it).\n .print-no-color → force everything to ink-on-paper:\n transparent backgrounds + system text colour.\n The legacy blanket reset, now opt-in.\n\n `!important` is reserved for selectors whose semantics require\n defeating consumer-authored CSS:\n - `.no-print` and the hide-list (nav, aside, button, …) — these\n are content negative-space and must vanish from print regardless\n of authored display rules.\n - `details > summary` — disclosure widget carries no print value.\n - The two opt-in colour classes above — when the consumer marks a\n region with them they are explicitly asking to override authored\n colour, so `!important` is the contract.\n ─────────────────────────────────────────────────── */\n\n@layer slashed.print {\n\n /* Screen default: print-only content is hidden until the page is printed. */\n .print-only {\n display: none;\n }\n\n @media print {\n\n @page {\n size: var(--sf-print-page-size, a4);\n margin: var(--sf-print-page-margin, 2cm);\n }\n\n body {\n font-size: var(--sf-print-base-size);\n }\n\n /* Decorative shadows are paper-noise. No `!important` — an authored\n shadow that explicitly survives into print is the consumer's call. */\n *,\n *::before,\n *::after {\n box-shadow: none;\n text-shadow: none;\n }\n\n /* Expand link URLs after the link text so paper-readers can follow\n them. Anchors and javascript: pseudo-links are excluded. */\n a[href]::after {\n content: \" (\" attr(href) \")\";\n font-size: 90%;\n word-break: break-all;\n }\n\n a[href^=\"#\"]::after,\n a[href^=\"javascript:\" i]::after {\n content: \"\";\n }\n\n abbr[title]::after {\n content: \" (\" attr(title) \")\";\n }\n\n img, svg, video, canvas, figure, table, pre, blockquote {\n break-inside: avoid;\n }\n\n thead {\n display: table-header-group;\n }\n\n tr {\n break-inside: avoid;\n }\n\n h1, h2, h3, h4, h5, h6 {\n break-after: avoid;\n orphans: 3;\n widows: 3;\n }\n\n p {\n orphans: 3;\n widows: 3;\n }\n\n pre, blockquote {\n border: var(--sf-border-width-1) solid var(--sf-color-border--strong);\n padding: var(--sf-space-s);\n }\n\n /* Hide non-essential interactive and decorative elements.\n `!important` is justified: these elements are content negative-space\n in print, and consumer-authored display rules must not bring them\n back accidentally. */\n nav, aside, button, input, select, textarea,\n audio, video, dialog, [popover], .no-print {\n display: none !important;\n }\n\n /* Reveal print-only content (overrides the screen default above). */\n .print-only {\n display: revert !important;\n }\n\n /* Preserve
content regardless of open state. Only\n `summary` keeps `!important` — the disclosure chevron carries no\n print value and the consumer should not be able to bring it back\n inadvertently. The other two rules win against the UA stylesheet\n on layered-author > UA precedence alone. */\n details {\n display: block;\n }\n details > summary {\n display: none !important;\n }\n details:not([open]) > :not(summary) {\n display: block;\n }\n\n /* ── Opt-in colour treatment ─────────────────────────────────── */\n\n /* Force colour-faithful rendering. Use on regions where a tinted\n background or coloured text encodes meaning (status pills,\n severity callouts, syntax highlighting, charts). */\n .print-color-exact,\n .print-color-exact * {\n print-color-adjust: exact !important;\n -webkit-print-color-adjust: exact !important;\n }\n\n /* Opt back into the legacy blanket reset: transparent backgrounds\n and system text colour everywhere inside the marked subtree.\n Use on regions where ink-saving is the contract (corporate\n boilerplate forms, append-to-existing-document templates). */\n .print-no-color,\n .print-no-color *,\n .print-no-color *::before,\n .print-no-color *::after {\n background: transparent !important;\n color: CanvasText !important;\n }\n\n }\n\n}\n"],"names":[]} \ No newline at end of file +{"version":3,"sourceRoot":null,"mappings":"AAOA,sBA6JE,kGACA,qGACA,oGACA,kGACA,mGACA,iGAIA,mGACA,kGACA,+FACA,gGACA,iGAIA,wEAEA,0EACA,2EACA,2EACA,wEAOA,60yBA1LF,qBAwtCE,0DAMA,mLAaA,4CACE,sCAGF,6CAKA,2BAIA,sEAMA,0EAOA,wHAQA,2BAIA,oCAIA,qBAIA,2CAMA,iBAIA,iEAIA,uCAKA,yBAIA,sBAIA,gDAKA,gCAIA,oEAOA,wBAp0CF,oBAq1CE,mWAUA,yVAUA,oJACA,oJACA,oJACA,oJACA,oJACA,oJAEA,iFAOA,kDAIA,sCAIA,qCAIA,iJAOA,yDAKA,2DAKA,8EAMA,cACA,kBAcA,oRASA,6EACA,8CACA,4CACA,6CAEA,gCAIA,+MASA,iRAWA,2FASA,sBAIA,0GAKA,mFAIA,0GAMA,4BAIA,uCAIA,iDAIA,mDAIA,gDAIA,sBACA,oCACA,6EAIA,8IAMA,6CAIA,yCAIA,kCAKA,6CAIA,4IA5hDF,qBAAA,sBAqpDE,gDACA,wDACA,sDACA,sDACA,sDACA,wDACA,0DAEA,gEAgBA,oGAGA,gGAQA,kJAKA,4MAkBA,wJAOA,2DACA,yDACA,uDACA,uDAOA,qEAKA,qCACA,mCACA,mCACA,mCACA,qCACA,uCAEA,qCACA,oCACA,uCAUA,+BACA,mCACA,iCACA,iCACA,iCACA,mCACA,qCAQA,0GAWA,iIAOA,4EAWA,gJAOA,uCACA,qCACA,qCACA,qCACA,uCACA,yCAEA,sCACA,2CACA,0CACA,mDAQA,kEAMA,wEAKA,+HAYA,8EAKA,6CACA,2CAQA,oEAMA,wFAKA,uDACA,6CASA,iIAMA,iGAIA,iDACA,+CACA,+CACA,+CACA,iDACA,mDAIA,qCAMA,0LAQA,8CACA,4CACA,4CACA,4CACA,8CAOA,uVAgBA,yHAOA,8CAIA,gCACA,8CACA,sDACA,uDAOA,oHAQA,sEAOA,sDACA,oDACA,sDACA,0DACA,gDACA,gDACA,sDAOA,uNAWA,sEAUA,gGAOA,mCAIA,yJAgBA,0FAMA,kFAMA,sCACE,8CAGA,kDACA,kDAOF,4EAUA,2FAKA,yBACE,+CACA,gDAGF,yBACE,+CACA,+CACA,+CACA,gDAOF,wFAMA,yBACE,2CACA,2CACA,2CACA,4CAUF,uGAKA,+BACA,+BACA,+BACA,+BAOA,saAgBA,uCACA,mDACA,iDAOA,sOAQA,8CACE,+CAGF,+BACA,+BACA,8DACA,wDAMA,uDACA,0DA5pEF,0BAAA,sBA+rEE,uEAKA,2DACA,+DACA,sDACA,6DACA,8EACA,iDACA,gCACA,gCACA,iDACA,gDACA,wHACA,4FACA,gFAKA,+EAMA,iFACA,+CACA,2DACA,8CACA,wFACA,+FACA,6DAQA,qDAcA,uEAMA,mHAOA,mDACA,mDACA,yFAUA,kDAIA,6CAWA,yCAUA,8LAeA,2FAKA,0CASA,mJAeA,6DAiBA,+FACA,qGACA,kGACA,4FACA,+FACA,+FACA,+FACA,+FACA,yFACA,sFACA,4FAmBA,gHAMA,iCACE,+BAmBF,0HA75EF,yBAAA,sBAi8EE,kCAIA,gCAIA,+BAQA,wGAOA,kDASA,wEAMA,gWAkBA,yBAIA,oEAOA,2RAmBA,4BAMA,2DAIA,mGASA,2DAIA,4EAeA,wBAIA,6BAIA,4BAgBA,+GAKA,6GAKA,iHAKA,iHAKA,2GAKA,wGAKA,8GASA,iFAMA,yDAMA,yBAIA,8DASA,wCASA,sCAIA,yDAKA,wEAUA,0DAKA,4HAKA,0BAQA,sCASA,6BAIA,kCAIA,sJAWA,8BA/tFF,sBA4kDE,mCACE,0DAQF,qDACA,mDAuBA,+9BA7mDF,sBAkwFE,8CAEE,4BAKA,uCAMA,+QASA,sCACE,mJAaF,kDACA,oDACA,0DACA,8DACA,8DACA,gEACA,oDACA,wDA4BA,0PAUA,sCACE,iRAWF,6CACA,oDACA,wDACA,wDACA,0DACA,mDASF,iDACA,kDACA,kFACA,qFACA,kFACA,oFACA,oEACA,sEAEA,qCACA,oFAMA,6CACA,0DACA,8DAMA,4QAMA,8CACE,4DA54FJ,6BAi7FE,wCAIA,6KAWA,uCACE,0IAQA,8JAaF,yMAcA,+BACE,uDAOA,sDAQF,6CACE,+DAYF,wBACE,oJAeF,mDAUA,6RAmBA,mVAYA,uCAYA,wKA4CA,iFAYA,2NAgBA,kWAeA,+KAYA,8BACE,sCAIA,iDAIA,2CAQA,8DA1rGJ,qBAquGE,yBAIA,aAEE,+EAKA,yCAMA,kDASA,6EAMA,2DAKA,+CAIA,oEAIA,iCAIA,sBAIA,uDAMA,qBAKA,8GASA,qGAMA,qCASA,sBAGA,uCAGA,gDASA,sHAUA,uIAt1GJ","sources":["slashed.essential.css"],"sourcesContent":["/* SLASHED v0.3.0 — slashed.essential.css */\n\n/* ─── core/layers.css ─── */\n/* SLASHED — core/layers.css\n CSS cascade layer order. Load this file first, always.\n ─────────────────────────────────────────────────── */\n\n@layer\n slashed.tokens,\n slashed.reset,\n slashed.base,\n slashed.forms,\n slashed.layout,\n slashed.components,\n slashed.macros,\n slashed.utilities,\n slashed.states,\n slashed.themes,\n slashed.motion,\n slashed.accessibility,\n slashed.print,\n slashed.legacy,\n slashed.overrides;\n\n/* ─── core/tokens.css ─── */\n/* ============================================================\n SLASHED — core/tokens.css\n @layer slashed.tokens\n\n ARCHITECTURE — 3 layers:\n 1. @property source tokens — animatable, 6 brand + 5 status (-light only); -dark is optional\n 2. Resolved semantic tokens — light-dark() with auto-derivation of dark from light (relative color syntax)\n 3. Derived tokens — relative color syntax (auto-contrast, tints/shades)\n\n THEMING — override just the 6 -light tokens. Dark mode auto-adapts via\n relative color syntax — zero extra work. For full control over dark mode,\n optionally override the -dark tokens (unregistered custom properties).\n\n Quick branding (6 tokens):\n :root {\n --sf-color-primary-light: oklch(0.45 0.20 264);\n --sf-color-secondary-light: oklch(0.25 0.03 260);\n --sf-color-tertiary-light: oklch(0.48 0.14 310);\n --sf-color-action-light: oklch(0.60 0.16 210);\n --sf-color-neutral-light: oklch(0.45 0.02 260);\n --sf-color-base-light: oklch(0.98 0.005 260);\n }\n Full per-mode control (add -dark overrides):\n :root {\n --sf-color-primary-dark: oklch(0.75 0.18 264);\n }\n\n BEM CONSUMER API — most tokens are intentionally unused by the\n framework itself. Tokens such as --sf-shadow-*, --sf-blur-*, --sf-gap,\n --sf-gradient-* etc. are meant for use in your own BEM classes:\n .card { box-shadow: var(--sf-shadow-m); gap: var(--sf-gap); }\n \"Not used internally\" does NOT mean dead code: every token is exercised\n in docs/demo.html and validated by the regression suite (tests/), so an\n unreferenced token is never untested or orphaned.\n\n RELATION TO tokens.layout.css — the spacing aliases in the \"Spacing\n aliases\" section are the source of truth for --sf-space-gap and\n --sf-space-content in tokens.layout.css. Overriding --sf-gap changes the\n gaps across all layout primitives (cluster, grid, sidebar, etc.).\n\n PUBLIC API vs INTERNAL (token-API freeze contract)\n ─────────────────────────────────────────────────\n Tokens are classified into three stability tiers (PUBLIC,\n PUBLIC-ADVANCED, INTERNAL).\n PUBLIC and PUBLIC-ADVANCED share the same SemVer guarantee\n (removal/rename = major bump after freeze). The distinction is\n documentation-level: ADVANCED tokens are powerful but niche — they\n live in a separate \"Advanced\" section in docs.\n\n PUBLIC — everyday knobs, prominently documented:\n · brand source tokens (--sf-color-{primary,secondary,tertiary,action,neutral,base}-light)\n · status source tokens (--sf-color-{success,warning,error,info,danger}-light)\n · resolved semantic colors (--sf-color-{primary,...,danger}, surfaces, text, borders, links, interactive bg, selection)\n · text-on-color (--sf-color-text--on-*)\n · code colors (--sf-color-code-bg, --sf-color-code-text)\n · status triplets (--sf-color-{success,...,danger}-{subtle,strong,muted})\n · gradients (--sf-gradient-*)\n · shadows (--sf-shadow-{none,xs,s,m,l,xl,2xl,inner})\n · text/drop shadows (--sf-text-shadow-*, --sf-drop-shadow-*)\n · blur (--sf-blur-*)\n · opacity (--sf-opacity-{0,10,25,50,75,100}, --sf-opacity-disabled, --sf-state-pending-opacity)\n · spacing (--sf-space-{none,px,gutter,2xs..4xl})\n · font families (--sf-font-{body,heading,display,mono,humanist,geometric,slab})\n · font weights (--sf-font-weight-{thin..black,body,heading,display})\n · font sizes (--sf-text-{2xs..4xl}, --sf-text-display-{s,m,l})\n · leading (--sf-leading-{tight,snug,normal,relaxed})\n · tracking (--sf-tracking-{tight,normal,wide,wider,widest})\n · icon sizes (--sf-icon-{xs,s,m,l,xl})\n · UI sizes (--sf-size-{xs,s,m,l,xl})\n · containers (--sf-container-{narrow,prose,default,wide,full})\n · ratios (--sf-ratio-{square,video,cinema,portrait,golden,4-3,3-2})\n · border widths (--sf-border-width-{hairline,1,2,3,4})\n · border styles (--sf-border-style, --strong, --soft, --dotted)\n · divider (--sf-divider-{width,style,color})\n · radius (--sf-radius-{none,xs,s,m,l,xl,2xl,3xl,4xl,full,pill})\n · duration (--sf-duration-{none,instant,fast,normal,slow,slower})\n · easing (--sf-ease-{linear,out,in,in-out,spring,elastic,bounce,overshoot})\n · transitions (--sf-transition-{all,colors,transform,opacity,shadow,fast,slow,enter,exit})\n · animations (--sf-animation-{fade-in,fade-out,slide-in-*,scale-*,color-pulse,ping,blink,float,spin,shimmer})\n · animation delays (--sf-animation-delay-{1..5})\n · z-index (--sf-z-{below,base,raised,low,mid,high,top,max})\n · header/sticky (--sf-header-height, --sf-sticky-offset)\n · touch target (--sf-touch-target)\n · focus ring (--sf-focus-ring-{width,offset,style,color})\n · caret (--sf-caret-color)\n · scrollbar (--sf-scrollbar-{thumb,track})\n · BEM aliases (--sf-gap, --sf-content-gap, --sf-component-pad, --sf-field-block,\n --sf-field-required-marker, --sf-link-external-marker)\n · section padding (--sf-section-pad, --sf-section-pad--{xs,s,m,l,xl,2xl})\n · typography aliases (--sf-body-*, --sf-code-font-size, --sf-heading-*, --sf-h{1..6}-*)\n · current-font-weight (--sf-current-font-weight)\n · color scheme (--sf-color-scheme)\n · stroke (--sf-stroke-{thin,regular,bold,heavy})\n · layout tokens — all of tokens.layout.css (per-primitive gaps, sidebar, grid, bento, etc.)\n · macro tokens — all of tokens.macros.css (flow-space, line-clamp, aspect, scroll-shadow-size)\n · palette tokens — all of optional/tokens.palette.css\n · scoped override tokens (--sf-icon-size, --sf-bento-cols, --sf-bento-row,\n --sf-color-code-block-bg, --sf-color-code-block-text,\n --sf-field-border-color, --sf-field-text-color)\n\n PUBLIC-ADVANCED — stable (same SemVer guarantee) but niche/powerful:\n · --sf-lumlocker (LumLocker lightness override)\n · --sf-contrast-bias, --sf-contrast-threshold (text-contrast tuning)\n · --sf-shadow-strength, --sf-shadow-color, --sf-shadow-glow-color (shadow internals)\n · --sf-font-features, --sf-font-variation, --sf-optical-sizing (OpenType)\n · --sf-space-scale, --sf-text-scale, --sf-text-display-scale,\n --sf-radius-scale, --sf-motion-scale (global multipliers)\n · --sf-scroll-timeline-range-{start,end} (scroll-driven animation)\n · --sf-mask-scrim-{start,end} (edge-fade masks)\n · --sf-safe-{top,bottom,left,right} (safe-area insets)\n · --sf-perspective-{near,normal,far} (3D transforms)\n · --sf-truncate-suffix (ellipsis character override)\n · --sf-radius-outer (concentric radius helper)\n · --sf-is-active, --sf-is-current, --sf-is-pressed, --sf-is-open (state flags)\n · --sf-focus-ring-shadow (composite focus shadow)\n · --sf-print-{page-margin,page-size,base-size} (print tokens)\n\n INTERNAL — implementation detail, may change without a major bump:\n · --sf-is-dark (mode flag — set via data-theme, not directly)\n\n CANONICAL-SOURCE ALIASES (intentional synonym hops, kept by design):\n · --sf-space-gap → --sf-gap (layout primitives read this name)\n · --sf-space-content → --sf-content-gap (stack / prose rhythm)\n · --sf-section-pad → --sf-section-pad--m (unsuffixed = the -m default)\n These are deliberate two-name contracts: override the canonical token\n (right side) to move everything downstream. The alias graph is otherwise\n ≤3 hops (per-primitive → layout-system → canonical source), giving three\n override levels: local (style=\"--sf-cluster-gap\"), all-primitives\n (--sf-space-gap), or global (--sf-gap).\n ============================================================ */\n\n@layer slashed.tokens {\n\n /* ============================================================\n @property — BRAND COLORS (-light source tokens, animatable)\n Override these 6 tokens to rebrand — dark mode auto-derives.\n Optionally set --sf-color-X-dark (no @property) for full control.\n ============================================================ */\n\n @property --sf-color-primary-light { syntax: \"\"; inherits: true; initial-value: oklch(0.45 0.20 264); }\n @property --sf-color-secondary-light { syntax: \"\"; inherits: true; initial-value: oklch(0.25 0.03 260); }\n @property --sf-color-tertiary-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.14 310); }\n @property --sf-color-action-light { syntax: \"\"; inherits: true; initial-value: oklch(0.60 0.16 210); }\n @property --sf-color-neutral-light { syntax: \"\"; inherits: true; initial-value: oklch(0.45 0.02 260); }\n @property --sf-color-base-light { syntax: \"\"; inherits: true; initial-value: oklch(0.98 0.005 260); }\n\n /* @property — STATUS COLORS (-light source tokens) */\n\n @property --sf-color-success-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.17 150); }\n @property --sf-color-warning-light { syntax: \"\"; inherits: true; initial-value: oklch(0.75 0.17 80); }\n @property --sf-color-error-light { syntax: \"\"; inherits: true; initial-value: oklch(0.62 0.20 35); }\n @property --sf-color-info-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.15 240); }\n @property --sf-color-danger-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.24 12); }\n\n /* Mode flag — drives formula direction for non-color dark overrides.\n Set by base.css via color-scheme switches. */\n @property --sf-is-dark { syntax: \"\"; inherits: true; initial-value: 0; }\n\n @property --sf-is-active { syntax: \"\"; inherits: true; initial-value: 0; }\n @property --sf-is-current { syntax: \"\"; inherits: true; initial-value: 0; }\n @property --sf-is-pressed { syntax: \"\"; inherits: true; initial-value: 0; }\n @property --sf-is-open { syntax: \"\"; inherits: true; initial-value: 0; }\n\n\n /* ============================================================\n DERIVED SEMANTIC TOKENS\n ============================================================ */\n\n :root {\n\n /* ----------------------------------------------------------\n Color scheme\n ---------------------------------------------------------- */\n --sf-color-scheme: light dark;\n\n /* ----------------------------------------------------------\n LumLocker — OKLCH L value used by the\n :root[data-lumlocker] override in core/themes.css.\n Locks all 5 brand colors (primary, secondary, tertiary,\n action, neutral) to one shared lightness while keeping their\n individual hue and chroma. Base is excluded — it must remain\n a light surface for page background.\n Override to change the locked lightness:\n :root { --sf-lumlocker: 0.55 }\n ---------------------------------------------------------- */\n --sf-lumlocker: 0.65;\n\n /* ----------------------------------------------------------\n Resolved color tokens — auto-switch via light-dark().\n Components reference these. Override the -light / -dark\n source tokens above to customise; or override these directly\n with your own light-dark() expression.\n ---------------------------------------------------------- */\n /* Dark auto-derivation formula (brand + status):\n clamp(0.65, 0.95 - l*0.5, 0.88) lightens dark-mode value relative to the light source.\n Base inverts: clamp(0.16, 1.18 - l, 0.24) — near-white flips to near-dark.\n Override any --sf-color-X-dark to take full per-mode control. */\n --sf-color-primary: light-dark(var(--sf-color-primary-light), var(--sf-color-primary-dark, oklch(from var(--sf-color-primary-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-secondary: light-dark(var(--sf-color-secondary-light), var(--sf-color-secondary-dark, oklch(from var(--sf-color-secondary-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-tertiary: light-dark(var(--sf-color-tertiary-light), var(--sf-color-tertiary-dark, oklch(from var(--sf-color-tertiary-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-action: light-dark(var(--sf-color-action-light), var(--sf-color-action-dark, oklch(from var(--sf-color-action-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-neutral: light-dark(var(--sf-color-neutral-light), var(--sf-color-neutral-dark, oklch(from var(--sf-color-neutral-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-base: light-dark(var(--sf-color-base-light), var(--sf-color-base-dark, oklch(from var(--sf-color-base-light) clamp(0.16, calc(1.18 - l), 0.24) calc(c * 0.5) h)));\n\n --sf-color-success: light-dark(var(--sf-color-success-light), var(--sf-color-success-dark, oklch(from var(--sf-color-success-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-warning: light-dark(var(--sf-color-warning-light), var(--sf-color-warning-dark, oklch(from var(--sf-color-warning-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-error: light-dark(var(--sf-color-error-light), var(--sf-color-error-dark, oklch(from var(--sf-color-error-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-info: light-dark(var(--sf-color-info-light), var(--sf-color-info-dark, oklch(from var(--sf-color-info-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-danger: light-dark(var(--sf-color-danger-light), var(--sf-color-danger-dark, oklch(from var(--sf-color-danger-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n\n\n /* ----------------------------------------------------------\n Surfaces — derived from --sf-color-base (auto-adapts)\n ---------------------------------------------------------- */\n --sf-color-bg: oklch(from var(--sf-color-base) calc(l + 0.02) c h);\n --sf-color-surface: var(--sf-color-base);\n --sf-color-well: oklch(from var(--sf-color-base) calc(l - 0.02) c h);\n --sf-color-raised: oklch(from var(--sf-color-base) calc(l + 0.04) c h);\n --sf-color-overlay: oklch(from var(--sf-color-base) l c h / 0.9);\n --sf-color-inverse: oklch(from var(--sf-color-base) calc(1 - l) c h);\n\n\n /* ----------------------------------------------------------\n Text — light and dark require opposite formula directions,\n so each side of light-dark() references its source token.\n ---------------------------------------------------------- */\n --sf-color-text: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h),\n oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1) c h)\n );\n --sf-color-text--secondary: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.15, calc(l - 0.25 - var(--sf-contrast-bias)), 0.45) c h),\n oklch(from var(--sf-color-neutral) clamp(0.55, calc(l + 0.1 + var(--sf-contrast-bias)), 0.90) c h)\n );\n --sf-color-text--muted: var(--sf-color-neutral);\n --sf-color-text--placeholder: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.45, calc(l + 0.15), 0.75) c h),\n oklch(from var(--sf-color-neutral) clamp(0.35, calc(l - 0.1), 0.65) c h)\n );\n --sf-color-text--disabled: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.55, calc(l + 0.25), 0.82) c h),\n oklch(from var(--sf-color-neutral) clamp(0.25, calc(l - 0.2), 0.55) c h)\n );\n --sf-color-text--inverse: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.85, calc(l + 0.4), 0.98) c h),\n oklch(from var(--sf-color-neutral) clamp(0.05, calc(l - 0.4), 0.35) c h)\n );\n --sf-color-heading: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h),\n oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1) c h)\n );\n /* Inline-code text colour. Auto-contrasts with --sf-color-code-bg using\n the same luminance formula as --sf-color-text--on-*. This prevents\n invisible text when code appears inside inverted-colour containers\n (e.g. sf-imposter with a dark background where inherited colour is\n near-white). Override with a tint value if desired. */\n --sf-color-code-text: oklch(from var(--sf-color-code-bg) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n\n\n /* ----------------------------------------------------------\n Text-on-color — sign(var(--sf-contrast-threshold) - l) * 999 is mode-agnostic:\n auto-selects light or dark text based on background luminance.\n Uses resolved tokens — works for any user-supplied color.\n\n CONTRAST NOTE: Guarantees ≥ 3:1 (WCAG AA Large Text / UI).\n Colors in the mid-luminance range (L ≈ 0.52–0.67) may not\n reach 4.5:1 (AA Normal Text) regardless of threshold chosen —\n this is a mathematical limitation of any binary black/white\n decision. The SLASHED configurator will warn users when their\n brand colors fall in this range. For body text on colored\n backgrounds, override --sf-color-text--on-* manually.\n ---------------------------------------------------------- */\n --sf-color-text--on-primary: oklch(from var(--sf-color-primary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-secondary: oklch(from var(--sf-color-secondary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-tertiary: oklch(from var(--sf-color-tertiary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-action: oklch(from var(--sf-color-action) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-neutral: oklch(from var(--sf-color-neutral) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-base: var(--sf-color-text);\n --sf-color-text--on-inverse: var(--sf-color-text--inverse);\n --sf-color-text--on-success: oklch(from var(--sf-color-success) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-warning: oklch(from var(--sf-color-warning) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-error: oklch(from var(--sf-color-error) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-info: oklch(from var(--sf-color-info) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-danger: oklch(from var(--sf-color-danger) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n\n\n /* ----------------------------------------------------------\n Borders — direction-dependent, reference source tokens.\n Light: borders are lighter than neutral (l + offset).\n Dark: borders are darker than neutral (l - offset).\n ---------------------------------------------------------- */\n --sf-color-border: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.70, calc(l + 0.35), 0.95) 0.005 h),\n oklch(from var(--sf-color-neutral) clamp(0.25, calc(l - 0.3), 0.55) 0.005 h)\n );\n --sf-color-border--subtle: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.75, calc(l + 0.4), 0.97) 0.005 h),\n oklch(from var(--sf-color-neutral) clamp(0.20, calc(l - 0.38), 0.45) 0.005 h)\n );\n --sf-color-border--strong: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.55, calc(l + 0.1), 0.85) 0.02 h),\n oklch(from var(--sf-color-neutral) clamp(0.38, calc(l - 0.1), 0.65) 0.02 h)\n );\n --sf-color-border--focus: var(--sf-color-action);\n --sf-color-border--disabled: oklch(from var(--sf-color-border--subtle) l 0 h / 0.5);\n --sf-color-border--translucent: oklch(from var(--sf-color-neutral) l c h / 0.15);\n\n\n /* ----------------------------------------------------------\n Links — derived from --sf-color-action (auto-adapts)\n ---------------------------------------------------------- */\n /* Links keep the action hue but CLAMP lightness toward a contrast-safe\n band — a ceiling in light mode, a floor in dark mode — so link text\n clears WCAG AA (4.5:1) on the page background for the default palette\n AND the large majority of brand overrides (a fixed offset alone only\n fixed the default). Lightness dominates OKLCH contrast but doesn't\n fully determine it: very high-chroma hues (saturated yellow/green) can\n still fall short even at a safe lightness — those remain the consumer's\n responsibility (see docs/theming.md → \"Link contrast\").\n\n NOTE: the consumer-facing :link rule in core/base.css uses\n `a:link` (not bare `a`) so its specificity matches WebKit's UA\n stylesheet `a:link { color: -webkit-link }`. Without that\n pseudo-class the framework's `--sf-color-link` value is\n silently ignored on unvisited anchors in WebKit and\n `-webkit-link` (≈ #00728f in dark mode) paints instead —\n regardless of how this token is computed. PR #73 and several\n follow-up attempts mistakenly chased the formula. */\n --sf-color-link: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.07, 0.48), 1) c h),\n oklch(from var(--sf-color-action) clamp(0.68, l, 1) c h)\n );\n --sf-color-link--hover: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.15, 0.40), 1) c h),\n oklch(from var(--sf-color-action) clamp(0, max(l + 0.10, 0.68), 1) c h)\n );\n --sf-color-link--active: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.21, 0.34), 1) c h),\n oklch(from var(--sf-color-action) clamp(0, max(l + 0.15, 0.74), 1) c h)\n );\n /* Visited link — 60° hue shift from action gives clearly distinct\n chroma without colliding with default tertiary (h ≈ 310). Same\n lightness clamp as the base link. If your brand puts action and\n tertiary close in hue, override directly:\n :root { --sf-color-link--visited: #6b46c1; } */\n --sf-color-link--visited: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.07, 0.48), 1) c calc(h + 60)),\n oklch(from var(--sf-color-action) clamp(0.68, l, 1) c calc(h + 60))\n );\n --sf-color-link--underline: oklch(from var(--sf-color-action) l c h / 0.3);\n --sf-color-link--disabled: var(--sf-color-text--disabled);\n\n\n /* ----------------------------------------------------------\n Interactive states — derived from --sf-color-neutral (auto-adapts)\n ---------------------------------------------------------- */\n --sf-color-bg--hover: oklch(from var(--sf-color-neutral) l c h / 0.08);\n --sf-color-bg--active: oklch(from var(--sf-color-neutral) l c h / 0.12);\n --sf-color-bg--selected: oklch(from var(--sf-color-action) l c h / 0.1);\n --sf-color-bg--focus: oklch(from var(--sf-color-action) l c h / 0.06);\n --sf-color-bg--disabled: var(--sf-color-well);\n --sf-color-code-bg: var(--sf-color-well);\n\n /* ----------------------------------------------------------\n Selection & backdrop\n ---------------------------------------------------------- */\n --sf-color-selection-bg: oklch(from var(--sf-color-action) l c h / 0.22);\n --sf-color-selection-text: inherit;\n --sf-color-mark-bg: oklch(from var(--sf-color-warning) l c h / 0.25);\n --sf-color-mark-text: inherit;\n --sf-color-dim: oklch(0 0 0 / 0.5);\n\n\n /* ----------------------------------------------------------\n Scrollbar\n ---------------------------------------------------------- */\n --sf-scrollbar-thumb: var(--sf-color-neutral);\n --sf-scrollbar-track: transparent;\n\n\n /* ----------------------------------------------------------\n Status triplets — bg/border use same formula both modes;\n text is direction-dependent so uses source tokens.\n error = form validation, input errors\n danger = destructive actions (delete, remove)\n ---------------------------------------------------------- */\n --sf-color-success-subtle: oklch(from var(--sf-color-success) l c h / 0.12);\n --sf-color-success-strong: light-dark(\n oklch(from var(--sf-color-success-light) calc(l - 0.15) c h),\n oklch(from var(--sf-color-success) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-success-muted: oklch(from var(--sf-color-success) l c h / 0.3);\n\n --sf-color-warning-subtle: oklch(from var(--sf-color-warning) l c h / 0.12);\n --sf-color-warning-strong: light-dark(\n oklch(from var(--sf-color-warning-light) calc(l - 0.25) c h),\n oklch(from var(--sf-color-warning) clamp(0.70, calc(l + 0.05), 1) c h)\n );\n --sf-color-warning-muted: oklch(from var(--sf-color-warning) l c h / 0.3);\n\n --sf-color-error-subtle: oklch(from var(--sf-color-error) l c h / 0.1);\n --sf-color-error-strong: light-dark(\n oklch(from var(--sf-color-error-light) calc(l - 0.1) c h),\n oklch(from var(--sf-color-error) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-error-muted: oklch(from var(--sf-color-error) l c h / 0.3);\n\n --sf-color-info-subtle: oklch(from var(--sf-color-info) l c h / 0.1);\n --sf-color-info-strong: light-dark(\n oklch(from var(--sf-color-info-light) calc(l - 0.1) c h),\n oklch(from var(--sf-color-info) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-info-muted: oklch(from var(--sf-color-info) l c h / 0.3);\n\n --sf-color-danger-subtle: oklch(from var(--sf-color-danger) l c h / 0.1);\n --sf-color-danger-strong: light-dark(\n oklch(from var(--sf-color-danger-light) calc(l - 0.1) c h),\n oklch(from var(--sf-color-danger) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-danger-muted: oklch(from var(--sf-color-danger) l c h / 0.3);\n\n\n /* ----------------------------------------------------------\n Gradients — derived from brand colors (auto-adapt)\n ---------------------------------------------------------- */\n --sf-gradient-primary: linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) calc(l - 0.08) c h));\n --sf-gradient-secondary: linear-gradient(135deg, var(--sf-color-secondary), oklch(from var(--sf-color-secondary) calc(l - 0.08) c h));\n --sf-gradient-tertiary: linear-gradient(135deg, var(--sf-color-tertiary), oklch(from var(--sf-color-tertiary) calc(l - 0.08) c h));\n --sf-gradient-brand: linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) l c calc(h + 30)));\n --sf-gradient-surface: linear-gradient(180deg, var(--sf-color-surface), var(--sf-color-bg));\n\n --sf-gradient-fade--r: linear-gradient(to right, transparent, var(--sf-color-bg));\n --sf-gradient-fade--l: linear-gradient(to left, transparent, var(--sf-color-bg));\n --sf-gradient-fade--t: linear-gradient(to top, transparent, var(--sf-color-bg));\n --sf-gradient-fade--b: linear-gradient(to bottom, transparent, var(--sf-color-bg));\n\n\n /* ----------------------------------------------------------\n Shadow — strength scales up in dark mode via --sf-is-dark\n ---------------------------------------------------------- */\n --sf-shadow-strength: calc(0.08 + var(--sf-is-dark) * 0.17);\n --sf-shadow-glow-color: var(--sf-color-primary);\n --sf-shadow-glow: 0 0 15px 2px oklch(from var(--sf-shadow-glow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-opacity-disabled: 0.45;\n --sf-state-pending-opacity: 0.7;\n\n\n /* ----------------------------------------------------------\n Focus / form colors\n ---------------------------------------------------------- */\n --sf-focus-ring-color: var(--sf-color-action);\n --sf-focus-ring-shadow: 0 0 0 var(--sf-focus-ring-offset) var(--sf-color-bg),\n 0 0 0 calc(var(--sf-focus-ring-offset) + var(--sf-focus-ring-width)) var(--sf-focus-ring-color);\n --sf-caret-color: var(--sf-color-action);\n\n }\n\n\n\n\n /* ============================================================\n NON-COLOR DESIGN TOKENS\n ============================================================ */\n\n :root {\n\n /* ----------------------------------------------------------\n Scale multipliers\n ---------------------------------------------------------- */\n --sf-space-scale: 1;\n --sf-text-scale: 1;\n --sf-text-display-scale: 1;\n --sf-radius-scale: 1;\n --sf-motion-scale: 1;\n\n\n /* ----------------------------------------------------------\n Font families\n ---------------------------------------------------------- */\n --sf-font-body: system-ui, -apple-system, sans-serif;\n --sf-font-heading: var(--sf-font-body);\n --sf-font-display: var(--sf-font-heading);\n --sf-font-mono: ui-monospace, monospace;\n\n /* Optional system-font stacks (zero-cost, no @import) */\n --sf-font-humanist: \"Seravek\", \"Gill Sans Nova\", \"Ubuntu\", \"Calibri\", \"DejaVu Sans\", source-sans-pro, sans-serif;\n --sf-font-geometric: \"Avenir\", \"Montserrat\", \"Corbel\", \"URW Gothic\", source-sans-pro, sans-serif;\n --sf-font-slab: \"Rockwell\", \"Rockwell Nova\", \"Roboto Slab\", \"DejaVu Serif\", \"Sitka Small\", serif;\n\n /* Opt-in OpenType / variable-font controls (token-only, not applied by the framework) */\n --sf-font-features: normal; /* e.g. \"cv11\", \"ss01\" */\n --sf-font-variation: normal; /* e.g. \"wght\" 450, \"opsz\" 32 */\n --sf-optical-sizing: auto;\n\n\n /* ----------------------------------------------------------\n Font weights\n ---------------------------------------------------------- */\n /* Numeric scale */\n --sf-font-weight-thin: 100;\n --sf-font-weight-extralight: 200;\n --sf-font-weight-light: 300;\n --sf-font-weight-normal: 400;\n --sf-font-weight-medium: 500;\n --sf-font-weight-semibold: 600;\n --sf-font-weight-bold: 700;\n --sf-font-weight-extrabold: 800;\n --sf-font-weight-black: 900;\n\n /* Semantic aliases — reference the numeric scale */\n --sf-font-weight-body: var(--sf-font-weight-normal);\n --sf-font-weight-heading: var(--sf-font-weight-semibold);\n --sf-font-weight-display: var(--sf-font-weight-bold);\n\n /* State token — consumed by .is-current in states.css.\n Override to match your nav design (e.g. semibold for thin fonts). */\n --sf-current-font-weight: var(--sf-font-weight-bold);\n\n\n /* ----------------------------------------------------------\n Font sizes — fluid\n ---------------------------------------------------------- */\n --sf-text-2xs: clamp(0.51rem, calc(0.00029629629629629656 * (100vw - 22.5rem) + 0.51rem), 0.53rem);\n --sf-text-xs: clamp(0.64rem, calc(0.0008888888888888881 * (100vw - 22.5rem) + 0.64rem), 0.7rem);\n --sf-text-s: clamp(0.8rem, calc(0.002074074074074073 * (100vw - 22.5rem) + 0.8rem), 0.94rem);\n --sf-text-m: clamp(1rem, calc(0.003703703703703704 * (100vw - 22.5rem) + 1rem), 1.25rem);\n --sf-text-l: clamp(1.25rem, calc(0.006222222222222221 * (100vw - 22.5rem) + 1.25rem), 1.67rem);\n --sf-text-xl: clamp(1.56rem, calc(0.00977777777777778 * (100vw - 22.5rem) + 1.56rem), 2.22rem);\n --sf-text-2xl: clamp(1.95rem, calc(0.014962962962962963 * (100vw - 22.5rem) + 1.95rem), 2.96rem);\n --sf-text-3xl: clamp(2.44rem, calc(0.022370370370370374 * (100vw - 22.5rem) + 2.44rem), 3.95rem);\n --sf-text-4xl: clamp(3.05rem, calc(0.03274074074074074 * (100vw - 22.5rem) + 3.05rem), 5.26rem);\n\n --sf-text-display-s: clamp(2.4rem, calc(0.00888888888888889 * (100vw - 22.5rem) + 2.4rem), 3rem);\n --sf-text-display-m: clamp(3rem, calc(0.014814814814814815 * (100vw - 22.5rem) + 3rem), 4rem);\n --sf-text-display-l: clamp(3.75rem, calc(0.023407407407407408 * (100vw - 22.5rem) + 3.75rem), 5.33rem);\n\n\n /* ----------------------------------------------------------\n Line heights & letter spacing\n ---------------------------------------------------------- */\n --sf-leading-tight: 1.1;\n --sf-leading-snug: 1.3;\n --sf-leading-normal: 1.5;\n --sf-leading-relaxed: 1.625;\n\n --sf-tracking-tight: -0.025em;\n --sf-tracking-normal: 0;\n --sf-tracking-wide: 0.025em;\n --sf-tracking-wider: 0.05em;\n --sf-tracking-widest: 0.1em;\n\n\n /* ----------------------------------------------------------\n Icon sizes — em-based by design.\n Icons are sized in em so they scale with the surrounding\n text: an icon in a button at --sf-text-m renders at 1rem;\n moved into a heading at --sf-text-2xl, the same icon scales\n up automatically.\n Global scaling flows transitively through --sf-text-scale\n (parent text grows → em-icons grow) — there is no separate\n --sf-icon-scale knob by design. clamp() is intentionally\n absent here: making icons fluid INDEPENDENTLY of context\n would break the relationship to surrounding text.\n Capped at --xl (3em ≈ 48px on default text); above that the\n element ceases to be an icon and becomes an illustration.\n ---------------------------------------------------------- */\n --sf-icon-xs: 0.875em;\n --sf-icon-s: 1em;\n --sf-icon-m: 1.5em;\n --sf-icon-l: 2em;\n --sf-icon-xl: 3em;\n\n\n /* ----------------------------------------------------------\n Spacing — fixed\n ---------------------------------------------------------- */\n --sf-space-none: 0;\n --sf-space-px: 1px;\n --sf-space-gutter: var(--sf-space-l);\n\n /* Spacing — fluid */\n --sf-space-2xs: calc(clamp(0.51rem, calc(0.004888888888888888 * (100vw - 22.5rem) + 0.51rem), 0.84rem) * var(--sf-space-scale));\n --sf-space-xs: calc(clamp(0.64rem, calc(0.007259259259259258 * (100vw - 22.5rem) + 0.64rem), 1.13rem) * var(--sf-space-scale));\n --sf-space-s: calc(clamp(0.8rem, calc(0.01037037037037037 * (100vw - 22.5rem) + 0.8rem), 1.5rem) * var(--sf-space-scale));\n --sf-space-m: calc(clamp(1rem, calc(0.014814814814814815 * (100vw - 22.5rem) + 1rem), 2rem) * var(--sf-space-scale));\n --sf-space-l: calc(clamp(1.25rem, calc(0.021037037037037035 * (100vw - 22.5rem) + 1.25rem), 2.67rem) * var(--sf-space-scale));\n --sf-space-xl: calc(clamp(1.56rem, calc(0.029481481481481477 * (100vw - 22.5rem) + 1.56rem), 3.55rem) * var(--sf-space-scale));\n --sf-space-2xl: calc(clamp(1.95rem, calc(0.04133333333333333 * (100vw - 22.5rem) + 1.95rem), 4.74rem) * var(--sf-space-scale));\n --sf-space-3xl: calc(clamp(2.44rem, calc(0.057333333333333326 * (100vw - 22.5rem) + 2.44rem), 6.31rem) * var(--sf-space-scale));\n --sf-space-4xl: calc(clamp(3.05rem, calc(0.07955555555555556 * (100vw - 22.5rem) + 3.05rem), 8.42rem) * var(--sf-space-scale));\n\n\n /* ----------------------------------------------------------\n UI sizes\n ---------------------------------------------------------- */\n --sf-size-xs: 1.5rem;\n --sf-size-s: 2rem;\n --sf-size-m: 2.5rem;\n --sf-size-l: 2.75rem;\n --sf-size-xl: 3.5rem;\n\n\n /* ----------------------------------------------------------\n Containers\n ---------------------------------------------------------- */\n --sf-container-narrow: 38rem;\n --sf-container-prose: 65ch;\n --sf-container-default: 75rem;\n --sf-container-wide: 90rem;\n --sf-container-full: 100%;\n\n\n /* ----------------------------------------------------------\n Aspect ratios\n ---------------------------------------------------------- */\n --sf-ratio-square: 1;\n --sf-ratio-video: 16 / 9;\n --sf-ratio-cinema: 21 / 9;\n --sf-ratio-4-3: 4 / 3;\n --sf-ratio-3-2: 3 / 2;\n --sf-ratio-portrait: 3 / 4;\n --sf-ratio-golden: 1.618 / 1;\n\n\n /* ----------------------------------------------------------\n Border widths\n --sf-border-width-hairline: device-pixel-dependent. On 1x DPR\n displays (Windows non-Retina) the browser will round 0.5px up\n to 1px, so it visually matches --sf-border-width-1. On 2x+\n DPR (macOS/iOS Retina, most modern phones) it renders as a\n true half-pixel hairline. Use it for subtle separators where\n 1px on low-DPR is acceptable.\n ---------------------------------------------------------- */\n --sf-border-width-hairline: 0.5px;\n --sf-border-width-1: 1px;\n --sf-border-width-2: 2px;\n --sf-border-width-3: 3px;\n --sf-border-width-4: 4px;\n\n /* ----------------------------------------------------------\n Border styles — paired with --sf-border-width-* and\n --sf-color-border*. Consumer BEM:\n .my-card { border: var(--sf-border-width-1) var(--sf-border-style) var(--sf-color-border); }\n Override per element to switch decorative styles without\n rewriting the rule.\n ---------------------------------------------------------- */\n --sf-border-style: solid; /* default */\n --sf-border-style-strong: solid; /* alias for emphasis */\n --sf-border-style-soft: dashed;\n --sf-border-style-dotted: dotted;\n\n /* ----------------------------------------------------------\n Divider — global separator system. Drives `hr` (base) and\n the `.sf-divider` layout primitive.\n ---------------------------------------------------------- */\n --sf-divider-width: var(--sf-border-width-1);\n --sf-divider-style: solid;\n --sf-divider-color: var(--sf-color-border);\n\n\n /* ----------------------------------------------------------\n Border radius\n --sf-radius-full: intentionally not scaled by --sf-radius-scale.\n A pill/circle is a topological constant, not a relative size:\n any --sf-radius-scale: 0 (\"sharp\" design system) should still\n render pill buttons as pills. 9999px is a magic-number convention\n large enough to clip to half the shorter edge for any realistic UI.\n ---------------------------------------------------------- */\n --sf-radius-none: 0;\n --sf-radius-xs: calc(2px * var(--sf-radius-scale));\n --sf-radius-s: calc(4px * var(--sf-radius-scale));\n --sf-radius-m: calc(8px * var(--sf-radius-scale));\n --sf-radius-l: calc(12px * var(--sf-radius-scale));\n --sf-radius-xl: calc(16px * var(--sf-radius-scale));\n --sf-radius-2xl: calc(24px * var(--sf-radius-scale));\n --sf-radius-3xl: calc(32px * var(--sf-radius-scale));\n --sf-radius-4xl: calc(48px * var(--sf-radius-scale));\n --sf-radius-full: 9999px;\n /* Pill — semantic alias of --sf-radius-full. Reach for this name\n on rectangles you want capsule-shaped (badges, tags, chips); use\n --sf-radius-full when you mean \"as round as topologically possible\". */\n --sf-radius-pill: var(--sf-radius-full);\n /* Concentric helper — outer radius for a container that wraps\n content padded by --sf-component-pad with inner radius m.\n Keeps inner elements visually proportional inside a padded box.\n Override per use:\n .my-card { border-radius: var(--sf-radius-outer); }\n Maths: outer = inner + padding ⇒ aligned curves at corners. */\n --sf-radius-outer: calc(var(--sf-radius-m) + var(--sf-component-pad));\n\n\n /* ----------------------------------------------------------\n Shadows — BEM consumer API\n Use in your own classes: .card { box-shadow: var(--sf-shadow-m); }\n ---------------------------------------------------------- */\n /* Shadow tint — near-black derived from the neutral; the tint simply\n inherits the neutral's own chroma/hue, so a colourless neutral yields\n colourless shadows. Forced dark in both modes; --sf-shadow-strength\n handles dark-mode intensity. Override to a brand colour for explicitly\n tinted shadows — keep it dark or you get bright slabs:\n oklch(from var(--sf-color-primary) 0.15 c h). */\n --sf-shadow-color: oklch(from var(--sf-color-neutral) 0.15 c h);\n\n --sf-shadow-none: none;\n --sf-shadow-xs: 0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7));\n --sf-shadow-s: 0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 2px 6px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, var(--sf-shadow-strength), 0.7));\n --sf-shadow-m: 0 1px 3px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-shadow-l: 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 8px 24px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), 0.7)),\n 0 16px 48px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-shadow-xl: 0 2px 8px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 12px 36px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3.5), 0.7)),\n 0 24px 72px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7));\n --sf-shadow-2xl: 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.6), 0.7)),\n 0 20px 60px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 4), 0.7)),\n 0 40px 100px -8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 5), 0.7));\n --sf-shadow-inner: inset 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n\n\n /* ----------------------------------------------------------\n Text shadows — share --sf-shadow-color / --sf-shadow-strength\n ---------------------------------------------------------- */\n --sf-text-shadow-none: none;\n --sf-text-shadow-s: 0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7));\n --sf-text-shadow-m: 0 2px 4px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-text-shadow-l: 0 4px 8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7));\n\n /* Drop shadows — for filter: drop-shadow() (respects alpha edges) */\n --sf-drop-shadow-s: drop-shadow(0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7)));\n --sf-drop-shadow-m: drop-shadow(0 4px 6px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7)));\n --sf-drop-shadow-l: drop-shadow(0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7)));\n\n\n /* ----------------------------------------------------------\n Blur & opacity\n ---------------------------------------------------------- */\n --sf-blur-xs: 4px;\n --sf-blur-s: 8px;\n --sf-blur-m: 16px;\n --sf-blur-l: 32px;\n --sf-blur-xl: 48px;\n\n /* 3D perspective */\n --sf-perspective-near: 500px;\n --sf-perspective-normal: 1000px;\n --sf-perspective-far: 2000px;\n\n --sf-opacity-0: 0;\n --sf-opacity-10: 0.1;\n --sf-opacity-25: 0.25;\n --sf-opacity-50: 0.5;\n --sf-opacity-75: 0.75;\n --sf-opacity-100: 1;\n\n\n /* ----------------------------------------------------------\n Motion & easing\n ---------------------------------------------------------- */\n --sf-duration-none: 0ms;\n --sf-duration-instant: calc(100ms * var(--sf-motion-scale));\n --sf-duration-fast: calc(150ms * var(--sf-motion-scale));\n --sf-duration-normal: calc(250ms * var(--sf-motion-scale));\n --sf-duration-slow: calc(400ms * var(--sf-motion-scale));\n --sf-duration-slower: calc(600ms * var(--sf-motion-scale));\n\n --sf-ease-linear: linear;\n --sf-ease-out: cubic-bezier(0.25, 0, 0.15, 1);\n --sf-ease-in: cubic-bezier(0.5, 0, 0.75, 0.25);\n --sf-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);\n --sf-ease-spring: linear(0, 0.5, 1.1, 0.95, 1.02, 1);\n --sf-ease-elastic: linear(0, 0.3, 1.2, 0.9, 1.05, 1);\n --sf-ease-bounce: linear(0, 0.35 18%, 1 32%, 0.86 42%, 1.02 56%, 0.98 72%, 1);\n --sf-ease-overshoot: linear(0, 0.6 30%, 1.08 55%, 0.98 75%, 1);\n\n\n /* ----------------------------------------------------------\n Animation presets — pair keyframes (core/motion.css) with\n duration + easing. Use directly: animation: var(--sf-animation-fade-in);\n ---------------------------------------------------------- */\n --sf-animation-fade-in: sf-fade-in var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-fade-out: sf-fade-out var(--sf-duration-normal) var(--sf-ease-in) both;\n --sf-animation-slide-in-up: sf-slide-in-up var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-slide-in-down: sf-slide-in-down var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-slide-in-left: sf-slide-in-left var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-slide-in-right: sf-slide-in-right var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-scale-up: sf-scale-up var(--sf-duration-normal) var(--sf-ease-overshoot) both;\n --sf-animation-scale-down: sf-scale-down var(--sf-duration-normal) var(--sf-ease-in) both;\n --sf-animation-color-pulse: sf-color-pulse var(--sf-duration-slow) var(--sf-ease-in-out) infinite;\n --sf-animation-ping: sf-ping var(--sf-duration-slow) var(--sf-ease-out) infinite;\n --sf-animation-blink: sf-blink calc(1s * var(--sf-motion-scale)) steps(1, end) infinite;\n --sf-animation-float: sf-float calc(3s * var(--sf-motion-scale)) var(--sf-ease-in-out) infinite;\n --sf-animation-spin: sf-spin var(--sf-duration-slower) linear infinite;\n --sf-animation-shimmer: sf-shimmer calc(1.5s * var(--sf-motion-scale)) ease-in-out infinite;\n\n /* Stagger delays — apply to successive items for entrance choreography\n (e.g. style=\"animation-delay: var(--sf-animation-delay-2)\"). Tokens\n only; SLASHED ships no .sf-stagger utility class (by design — apply via token presets or your own BEM rules). */\n --sf-animation-delay-1: calc(75ms * var(--sf-motion-scale));\n --sf-animation-delay-2: calc(150ms * var(--sf-motion-scale));\n --sf-animation-delay-3: calc(225ms * var(--sf-motion-scale));\n --sf-animation-delay-4: calc(300ms * var(--sf-motion-scale));\n --sf-animation-delay-5: calc(375ms * var(--sf-motion-scale));\n\n\n /* ----------------------------------------------------------\n Scroll-driven animation range — consumer API. Attach these\n to your own scroll-timeline via animation-range. Not consumed\n by the framework itself; intended for BEM component authors.\n ---------------------------------------------------------- */\n --sf-scroll-timeline-range-start: 0%;\n --sf-scroll-timeline-range-end: 100%;\n\n\n /* ----------------------------------------------------------\n Mask scrim — edge-fade stops for scroll reels / overflow.\n Use in a mask-image gradient to fade content near an edge:\n mask-image: linear-gradient(to right,\n transparent 0, #000 var(--sf-mask-scrim-start),\n #000 calc(100% - var(--sf-mask-scrim-end)), transparent 100%);\n ---------------------------------------------------------- */\n --sf-mask-scrim-start: var(--sf-space-l);\n --sf-mask-scrim-end: var(--sf-space-l);\n\n\n /* ----------------------------------------------------------\n Z-index\n ---------------------------------------------------------- */\n --sf-z-below: -1;\n --sf-z-base: 0;\n --sf-z-raised: 1;\n --sf-z-low: 10;\n --sf-z-mid: 100;\n --sf-z-high: 500;\n --sf-z-top: 900;\n --sf-z-max: 9999;\n\n\n /* ----------------------------------------------------------\n Layout & a11y\n ---------------------------------------------------------- */\n --sf-header-height: 8rem;\n --sf-sticky-offset: var(--sf-header-height);\n --sf-focus-ring-width: 2px;\n --sf-focus-ring-offset: 2px;\n --sf-focus-ring-style: solid;\n --sf-touch-target: var(--sf-size-l);\n /* Global text-contrast knob. Nudges derived reading-text colours\n toward the extremes (darker in light mode, lighter in dark mode).\n Positive = more contrast; 0 = no change. Consumed by\n --sf-color-text, --sf-color-text--secondary, --sf-color-heading. */\n --sf-contrast-bias: 0;\n /* Lightness crossover for text-on-color auto-contrast. Colours with\n L above this value get dark text; below get light text. Default 0.6\n guarantees ≥ 3:1 for most colours. Shift down (e.g. 0.55) if your\n brand sits in the 0.52-0.60 range and you prefer white text on it. */\n --sf-contrast-threshold: 0.6;\n\n --sf-safe-top: env(safe-area-inset-top, 0px);\n --sf-safe-bottom: env(safe-area-inset-bottom, 0px);\n --sf-safe-left: env(safe-area-inset-left, 0px);\n --sf-safe-right: env(safe-area-inset-right, 0px);\n\n\n /* ----------------------------------------------------------\n Print\n ---------------------------------------------------------- */\n --sf-print-page-margin: 2cm;\n --sf-print-page-size: a4;\n --sf-print-base-size: 11pt;\n\n\n /* ----------------------------------------------------------\n Stroke widths\n ---------------------------------------------------------- */\n --sf-stroke-thin: 1px;\n --sf-stroke-regular: 1.5px;\n --sf-stroke-bold: 2px;\n --sf-stroke-heavy: 3px;\n\n }\n\n\n /* ============================================================\n TYPOGRAPHY ALIASES\n ============================================================ */\n\n :root {\n --sf-body-font-size: var(--sf-text-m);\n --sf-body-line-height: var(--sf-leading-normal);\n --sf-body-font-weight: var(--sf-font-weight-body);\n --sf-body-font-family: var(--sf-font-body);\n --sf-body-color: var(--sf-color-text);\n --sf-body-text-wrap: pretty;\n --sf-body-strong-weight: var(--sf-font-weight-bold);\n --sf-body-em-style: italic;\n\n --sf-code-font-size: 0.875em;\n\n --sf-heading-font-family: var(--sf-font-heading);\n --sf-heading-color: var(--sf-color-heading);\n --sf-heading-text-wrap: balance;\n\n --sf-h1-size: var(--sf-text-4xl);\n --sf-h1-line-height: var(--sf-leading-tight);\n --sf-h1-font-weight: var(--sf-font-weight-heading);\n --sf-h1-letter-spacing: var(--sf-tracking-tight);\n\n --sf-h2-size: var(--sf-text-3xl);\n --sf-h2-line-height: var(--sf-leading-tight);\n --sf-h2-font-weight: var(--sf-font-weight-heading);\n --sf-h2-letter-spacing: var(--sf-tracking-tight);\n\n --sf-h3-size: var(--sf-text-2xl);\n --sf-h3-line-height: var(--sf-leading-snug);\n --sf-h3-font-weight: var(--sf-font-weight-heading);\n --sf-h3-letter-spacing: var(--sf-tracking-normal);\n\n --sf-h4-size: var(--sf-text-xl);\n --sf-h4-line-height: var(--sf-leading-snug);\n --sf-h4-font-weight: var(--sf-font-weight-heading);\n --sf-h4-letter-spacing: var(--sf-tracking-normal);\n\n --sf-h5-size: var(--sf-text-l);\n --sf-h5-line-height: var(--sf-leading-normal);\n --sf-h5-font-weight: var(--sf-font-weight-heading);\n --sf-h5-letter-spacing: var(--sf-tracking-normal);\n\n --sf-h6-size: var(--sf-text-m);\n --sf-h6-line-height: var(--sf-leading-normal);\n --sf-h6-font-weight: var(--sf-font-weight-heading);\n --sf-h6-letter-spacing: var(--sf-tracking-wide);\n\n\n /* ----------------------------------------------------------\n Spacing aliases — BEM consumer API\n Use in your own BEM classes: .nav { gap: var(--sf-gap); }\n Source of truth for tokens.layout.css:\n --sf-space-gap → var(--sf-gap) (all layout primitives)\n --sf-space-content → var(--sf-content-gap) (stack, prose)\n ---------------------------------------------------------- */\n --sf-gap: var(--sf-space-m); /* gap between BEM elements */\n --sf-content-gap: var(--sf-space-s); /* rhythm within a component */\n --sf-component-pad: var(--sf-space-m); /* standard component padding */\n --sf-field-block: var(--sf-space-l); /* form-field block spacing */\n --sf-field-required-marker: \" *\";\n /* Leading space is baked into the default so consumers can fully\n disable the indicator with `--sf-link-external-marker: \"\"` —\n without the space a bare \"\" still left a stray space after the\n link text. Override with your own glyph (include leading space\n if you want one): `:root { --sf-link-external-marker: \" →\" }`. */\n --sf-link-external-marker: \" \\2197\"; /* \" ↗\" — consumed by .sf-link-external */\n\n\n /* ----------------------------------------------------------\n Section padding\n ---------------------------------------------------------- */\n --sf-section-pad: var(--sf-section-pad--m);\n --sf-section-pad--xs: var(--sf-space-xl);\n --sf-section-pad--s: var(--sf-space-2xl);\n --sf-section-pad--m: var(--sf-space-3xl);\n --sf-section-pad--l: var(--sf-space-4xl);\n --sf-section-pad--xl: calc(var(--sf-space-4xl) * 1.5);\n --sf-section-pad--2xl: calc(var(--sf-space-4xl) * 2);\n\n\n /* ----------------------------------------------------------\n Transition shorthands\n\n --sf-transition-all transitions ALL properties — convenient but\n a performance footgun (forces the browser to watch every computed\n value). Prefer the granular tokens below for production UI.\n ---------------------------------------------------------- */\n --sf-transition-all: all var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-colors:\n color var(--sf-duration-normal) var(--sf-ease-out),\n background-color var(--sf-duration-normal) var(--sf-ease-out),\n border-color var(--sf-duration-normal) var(--sf-ease-out),\n text-decoration-color var(--sf-duration-normal) var(--sf-ease-out),\n fill var(--sf-duration-normal) var(--sf-ease-out),\n stroke var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-transform: transform var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-opacity: opacity var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-shadow: box-shadow var(--sf-duration-normal) var(--sf-ease-out);\n\n --sf-transition-fast: all var(--sf-duration-fast) var(--sf-ease-out);\n --sf-transition-slow: all var(--sf-duration-slow) var(--sf-ease-in-out);\n --sf-transition-enter: all var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-exit: all var(--sf-duration-fast) var(--sf-ease-in);\n }\n\n}\n\n/* ─── core/tokens.layout.css ─── */\n/* ============================================================\n SLASHED — core/tokens.layout.css\n Semantic tokens for layout primitives.\n Load before: core/layout.css\n Layer: slashed.tokens\n Prefix: --sf-\n\n DEPENDENCIES — requires tokens.css (loaded earlier).\n Spacing aliases inherit from the BEM consumer API in tokens.css:\n --sf-space-gap → var(--sf-gap) from tokens.css\n --sf-space-content → var(--sf-content-gap) from tokens.css\n Override --sf-gap or --sf-content-gap (the canonical source) to change\n the gaps across all layout primitives at once. --sf-space-gap and\n --sf-space-content are CANONICAL-SOURCE ALIASES — a second public name\n kept for layout-primitive readability, not a redundant indirection.\n\n PUBLIC API — every --sf-*-gap / --sf-*-padding / --sf-*-min / etc. here\n is a per-primitive override knob (e.g. style=\"--sf-cluster-gap: …\").\n No INTERNAL tokens live in this file.\n\n Each layout primitive (cluster, grid, sidebar…) has its own\n tokens (--sf-cluster-gap etc.) for local overrides.\n ============================================================ */\n\n@layer slashed.tokens {\n :root {\n\n /* ----------------------------------------------------------\n Shared spacing aliases\n ---------------------------------------------------------- */\n --sf-space-gap: var(--sf-gap); /* component-level gap */\n --sf-space-content: var(--sf-content-gap); /* within-component rhythm */\n\n /* ----------------------------------------------------------\n Stack\n ---------------------------------------------------------- */\n --sf-stack-gap: var(--sf-space-content);\n\n /* ----------------------------------------------------------\n Gap\n ---------------------------------------------------------- */\n --sf-gap-size: var(--sf-space-gap);\n\n /* ----------------------------------------------------------\n Box\n ---------------------------------------------------------- */\n --sf-box-padding: var(--sf-space-m);\n --sf-box-border-width: 0;\n --sf-box-border-color: var(--sf-color-border);\n\n /* ----------------------------------------------------------\n Center\n ---------------------------------------------------------- */\n --sf-center-max: var(--sf-container-default);\n --sf-center-gutter: var(--sf-space-gutter);\n\n /* ----------------------------------------------------------\n Cluster\n ---------------------------------------------------------- */\n --sf-cluster-gap: var(--sf-space-gap);\n --sf-cluster-align: center;\n --sf-cluster-justify: flex-start;\n\n /* ----------------------------------------------------------\n Sidebar\n ---------------------------------------------------------- */\n --sf-sidebar-gap: var(--sf-space-gap);\n --sf-sidebar-min-width: 50%; /* content column minimum */\n --sf-sidebar-width: 18rem; /* sidebar panel default size */\n\n /* ----------------------------------------------------------\n Switcher\n ---------------------------------------------------------- */\n --sf-switcher-threshold: 30rem;\n --sf-switcher-gap: var(--sf-space-gap);\n\n /* ----------------------------------------------------------\n Grid (auto-fill, breakpoint-free)\n ---------------------------------------------------------- */\n --sf-grid-min: 16rem;\n --sf-grid-gap: var(--sf-space-gap);\n\n --sf-grid-min-xs: 10rem;\n --sf-grid-min-s: 13rem;\n --sf-grid-min-m: 16rem;\n --sf-grid-min-l: 20rem;\n --sf-grid-min-xl: 24rem;\n --sf-grid-min-2xl: 28rem;\n\n /* ----------------------------------------------------------\n Equal columns (fixed, non-responsive)\n ---------------------------------------------------------- */\n --sf-equal-cols: 2;\n --sf-equal-gap: var(--sf-space-gap);\n\n /* ----------------------------------------------------------\n Cover\n ---------------------------------------------------------- */\n --sf-cover-min-height: 100dvh;\n --sf-cover-padding: var(--sf-section-pad);\n\n /* ----------------------------------------------------------\n Frame\n ---------------------------------------------------------- */\n --sf-frame-ratio: 16 / 9; /* literal , valid in aspect-ratio */\n\n /* ----------------------------------------------------------\n Icon (boxed variant) — extras for .sf-icon--boxed.\n The naked .sf-icon sizing tokens (--sf-icon-xs..xl) live in\n core/tokens.css alongside the typography scale.\n ---------------------------------------------------------- */\n --sf-icon-box-pad: 0.5em;\n --sf-icon-box-radius: var(--sf-radius-s);\n --sf-icon-box-bg: var(--sf-color-well);\n --sf-icon-box-border: var(--sf-border-width-1) solid var(--sf-color-border);\n\n /* ----------------------------------------------------------\n Reel\n ---------------------------------------------------------- */\n --sf-reel-item-width: max-content;\n --sf-reel-gap: var(--sf-space-gap);\n --sf-reel-height: auto;\n\n /* ----------------------------------------------------------\n Imposter (centered absolute/fixed overlay)\n ---------------------------------------------------------- */\n --sf-imposter-margin: var(--sf-space-m);\n\n /* ----------------------------------------------------------\n Bento grid\n ---------------------------------------------------------- */\n --sf-bento-cols-default: 3;\n --sf-bento-row-default: 10rem;\n --sf-bento-row-compact: 6rem;\n --sf-bento-row-tall: 16rem;\n --sf-bento-gap: var(--sf-space-gap);\n\n /* ----------------------------------------------------------\n Content grid (breakout pattern)\n ---------------------------------------------------------- */\n --sf-breakout-width: var(--sf-container-wide);\n --sf-content-width: var(--sf-container-default);\n\n /* ----------------------------------------------------------\n Prose\n ---------------------------------------------------------- */\n --sf-prose-paragraph: var(--sf-space-content);\n\n /* ----------------------------------------------------------\n Alternate (zigzag)\n ---------------------------------------------------------- */\n --sf-alternate-gap: var(--sf-space-content);\n --sf-alternate-inner-gap: var(--sf-space-gap);\n\n }\n}\n\n/* ─── core/tokens.macros.css ─── */\n/* ============================================================\n SLASHED — core/tokens.macros.css\n Semantic tokens for macros (recipes / patterns).\n Load before: core/macros.css\n Layer: slashed.tokens\n Prefix: --sf-\n\n DEPENDENCIES — requires tokens.css (loaded earlier).\n Macro tokens reference --sf-space-* / --sf-radius-* / etc.\n\n PUBLIC API — every token here is a per-recipe override knob\n (e.g. style=\"--sf-line-clamp: 5\"). No INTERNAL tokens live in\n this file.\n\n Architectural parallel — these tokens stand alongside\n tokens.layout.css: same prefix, same shape, same status\n (essential, public, override-friendly).\n ============================================================ */\n\n@layer slashed.tokens {\n :root {\n\n /* ----------------------------------------------------------\n Flow — distance between flow children (lobotomized owl).\n Consumed by .sf-flow > * + *.\n ---------------------------------------------------------- */\n --sf-flow-space: var(--sf-space-content);\n\n /* ----------------------------------------------------------\n Line clamp — default line count for .sf-line-clamp-N.\n Override per element: style=\"--sf-line-clamp: 5\".\n The fixed-N variants .sf-line-clamp-2 / -3 don't read this\n token (they hardcode the value), so the default only\n applies to .sf-line-clamp-N callers.\n ---------------------------------------------------------- */\n --sf-line-clamp: 3;\n\n /* ----------------------------------------------------------\n Truncate — ellipsis character for .sf-truncate.\n Stays as a token so consumers can swap to ASCII \"...\" or a\n localised character without rewriting the rule. The CSS\n text-overflow: ellipsis property doesn't read it directly;\n it's exposed for consumer-built variants.\n ---------------------------------------------------------- */\n --sf-truncate-suffix: \"\\2026\";\n\n /* ----------------------------------------------------------\n Aspect ratio — default for .sf-aspect (generic ratio container,\n distinct from layout's .sf-frame which has its own --sf-frame-ratio).\n ---------------------------------------------------------- */\n --sf-aspect: 16 / 9;\n\n /* ----------------------------------------------------------\n Scroll shadow / overflow fade — mask gradient size.\n Used by .sf-scroll-shadow (top+bottom) and .sf-overflow-fade\n (end-edge horizontal fade).\n ---------------------------------------------------------- */\n --sf-scroll-shadow-size: 2rem;\n\n }\n}\n\n/* ─── core/reset.css ─── */\n/* SLASHED — core/reset.css\n @layer slashed.reset\n ─────────────────────────────────────────────────── */\n\n@layer slashed.reset {\n\n *, *::before, *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n }\n\n html {\n text-size-adjust: 100%;\n scroll-padding-top: var(--sf-header-height, 3.5rem);\n scrollbar-gutter: stable;\n color-scheme: var(--sf-color-scheme, light dark);\n hanging-punctuation: first last;\n }\n\n /* interpolate-size enables animations to/from intrinsic sizing\n keywords (auto, min-content, max-content, fit-content). Shipped:\n Chrome 129 (Aug 2024), Safari 18 (Sep 2024). Firefox: behind a\n flag as of 2026-Q2. Wrapped in @supports so unsupporting engines\n get clean progressive enhancement, not an unknown-declaration. */\n @supports (interpolate-size: allow-keywords) {\n html { interpolate-size: allow-keywords; }\n }\n\n body {\n min-height: 100dvh;\n min-inline-size: 320px;\n }\n\n menu, ul, ol {\n list-style: none;\n }\n\n img, picture, video, canvas, svg {\n display: block;\n max-width: 100%;\n height: auto;\n }\n\n iframe, embed, object {\n display: block;\n max-width: 100%;\n height: unset;\n border: none;\n }\n\n button, input, select, textarea {\n font: inherit;\n color: inherit;\n letter-spacing: inherit;\n color-scheme: inherit;\n line-height: inherit;\n }\n\n button {\n text-transform: none;\n }\n\n ::file-selector-button {\n font: inherit;\n }\n\n search {\n display: block;\n }\n\n fieldset {\n border: none;\n padding: 0;\n min-width: 0;\n }\n\n legend {\n padding: 0;\n }\n\n a, button, input, select, textarea, summary {\n touch-action: manipulation;\n }\n\n summary {\n list-style: none;\n cursor: pointer;\n }\n\n textarea {\n resize: vertical;\n }\n\n button {\n cursor: pointer;\n }\n\n table {\n border-collapse: collapse;\n border-spacing: 0;\n }\n\n [hidden] {\n display: none !important;\n }\n\n dialog, [popover] {\n padding: 0;\n border: none;\n background: none;\n color: inherit;\n }\n\n [inert] {\n cursor: default;\n }\n\n}\n\n/* ─── core/base.css ─── */\n/* SLASHED — core/base.css\n @layer slashed.base\n ─────────────────────────────────────────────────── */\n\n@layer slashed.base {\n\n /* Dark/light mode lives in core/themes.css (slashed.themes layer).\n The baseline color-scheme: var(--sf-color-scheme, light dark) is\n set on html in core/reset.css. */\n\n body {\n font-family: var(--sf-body-font-family, var(--sf-font-body));\n font-size: var(--sf-body-font-size, var(--sf-text-m));\n line-height: var(--sf-body-line-height, var(--sf-leading-normal));\n font-weight: var(--sf-body-font-weight, var(--sf-font-weight-body));\n color: var(--sf-body-color, var(--sf-color-text));\n background-color: var(--sf-color-bg, #fff);\n font-synthesis: none;\n }\n\n h1, h2, h3, h4, h5, h6 {\n font-family: var(--sf-heading-font-family, var(--sf-font-heading));\n line-height: var(--sf-leading-tight);\n color: var(--sf-heading-color, var(--sf-color-heading));\n text-wrap: var(--sf-heading-text-wrap);\n overflow-wrap: break-word;\n text-rendering: optimizelegibility;\n scroll-margin-top: calc(var(--sf-header-height, 3.5rem) + var(--sf-space-m));\n }\n\n h1 { font-size: var(--sf-h1-size); line-height: var(--sf-h1-line-height); font-weight: var(--sf-h1-font-weight); letter-spacing: var(--sf-h1-letter-spacing); }\n h2 { font-size: var(--sf-h2-size); line-height: var(--sf-h2-line-height); font-weight: var(--sf-h2-font-weight); letter-spacing: var(--sf-h2-letter-spacing); }\n h3 { font-size: var(--sf-h3-size); line-height: var(--sf-h3-line-height); font-weight: var(--sf-h3-font-weight); letter-spacing: var(--sf-h3-letter-spacing); }\n h4 { font-size: var(--sf-h4-size); line-height: var(--sf-h4-line-height); font-weight: var(--sf-h4-font-weight); letter-spacing: var(--sf-h4-letter-spacing); }\n h5 { font-size: var(--sf-h5-size); line-height: var(--sf-h5-line-height); font-weight: var(--sf-h5-font-weight); letter-spacing: var(--sf-h5-letter-spacing); }\n h6 { font-size: var(--sf-h6-size); line-height: var(--sf-h6-line-height); font-weight: var(--sf-h6-font-weight); letter-spacing: var(--sf-h6-letter-spacing); }\n\n p {\n text-wrap: var(--sf-body-text-wrap);\n overflow-wrap: break-word;\n orphans: 2;\n widows: 2;\n }\n\n b, strong {\n font-weight: var(--sf-body-strong-weight);\n }\n\n em {\n font-style: var(--sf-body-em-style);\n }\n\n small {\n font-size: var(--sf-text-s, 80%);\n }\n\n mark {\n background-color: var(--sf-color-mark-bg, yellow);\n color: var(--sf-color-mark-text, black);\n border-radius: var(--sf-radius-xs);\n padding-inline: 0.2em;\n }\n\n abbr[title] {\n text-decoration: underline dotted;\n cursor: help;\n }\n\n kbd, samp {\n font-family: var(--sf-font-mono);\n font-size: inherit;\n }\n\n sub, sup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n }\n sup { top: -0.5em; }\n sub { bottom: -0.25em; }\n\n /* `a:link` (specificity 0,1,1) rather than bare `a` (0,0,1) so the\n author rule wins over WebKit's UA stylesheet rule\n `a:link { color: -webkit-link; }`. The bare-`a` form silently\n loses to the UA `:link` rule on unvisited anchors in WebKit,\n which paints `-webkit-link` (≈ #00728f in dark mode) regardless\n of whatever `--sf-color-link` resolves to — producing axe's\n 3.12 : 1 dark-mode contrast failure that survived four attempts\n to \"fix\" the link colour formula (PR #73, then three iterations\n of PR #76). The colour formula was never the problem; the\n selector specificity was. Hover / visited / active rules already\n used pseudo-classes so they had matching specificity and were\n never affected. */\n a:link {\n color: var(--sf-color-link);\n text-decoration-color: var(--sf-color-link--underline);\n text-underline-offset: 0.15em;\n text-underline-position: from-font;\n transition:\n color var(--sf-duration-fast) var(--sf-ease-out),\n text-decoration-color var(--sf-duration-fast) var(--sf-ease-out);\n }\n a:hover { color: var(--sf-color-link--hover); text-decoration-color: currentcolor; }\n a:visited { color: var(--sf-color-link--visited); }\n a:active { color: var(--sf-color-link--active); }\n a:not([class]) { text-decoration-skip-ink: auto; }\n\n a, code {\n overflow-wrap: break-word;\n }\n\n code {\n font-family: var(--sf-font-mono);\n font-size: var(--sf-code-font-size);\n padding: 0.125em 0.3em;\n border-radius: var(--sf-radius-xs);\n background-color: var(--sf-color-code-bg);\n color: var(--sf-color-code-text, inherit);\n }\n\n pre {\n font-family: var(--sf-font-mono);\n font-size: var(--sf-text-s);\n padding: var(--sf-space-m);\n border-radius: var(--sf-radius-m);\n background: var(--sf-color-code-block-bg, var(--sf-color-code-bg));\n color: var(--sf-color-code-block-text, inherit);\n overflow-x: auto;\n tab-size: 2;\n hyphens: none;\n }\n pre code {\n background: none;\n color: inherit;\n border: 0;\n padding: 0;\n font-size: inherit;\n border-radius: 0;\n }\n\n svg {\n fill: currentcolor;\n }\n\n hr {\n border: none;\n border-block-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);\n }\n\n :target {\n scroll-margin-top: calc(var(--sf-header-height, 3.5rem) + var(--sf-space-m));\n }\n\n ::selection {\n background: var(--sf-color-selection-bg);\n color: var(--sf-color-selection-text);\n text-shadow: none;\n }\n\n th, caption {\n text-align: start;\n }\n\n address, cite, dfn, var {\n font-style: normal;\n }\n\n input, textarea {\n caret-color: var(--sf-caret-color);\n }\n\n input, progress {\n accent-color: var(--sf-color-action);\n }\n\n ::backdrop {\n background-color: var(--sf-color-dim);\n }\n\n optgroup { font: inherit; }\n ::file-selector-button { font: inherit; }\n ::placeholder { opacity: 1; color: var(--sf-color-text--placeholder, currentcolor); }\n\n /* Minimal rich-block defaults — just enough to distinguish from

.\n Full styling lives in .sf-prose (core/layout.css). */\n blockquote {\n border-inline-start: var(--sf-border-width-3) solid var(--sf-color-border);\n padding-inline-start: var(--sf-space-m);\n font-style: italic;\n }\n\n dt {\n font-weight: var(--sf-font-weight-heading);\n }\n\n dd {\n margin-inline-start: var(--sf-space-m);\n }\n\n table {\n width: 100%;\n text-align: start;\n }\n\n th {\n font-weight: var(--sf-font-weight-heading);\n }\n\n td, th {\n padding: var(--sf-space-2xs) var(--sf-space-xs);\n border-block-end: var(--sf-border-width-hairline) solid var(--sf-color-border--subtle);\n }\n\n}\n\n/* ─── core/themes.css ─── */\n/* SLASHED — core/themes.css\n @layer slashed.themes\n\n Theme switching: token reassignments only. No new selectors,\n no new properties — strictly remaps existing tokens based on\n system preference and explicit data-theme attributes.\n\n Cascade position is significant. slashed.themes sits ABOVE\n slashed.{states, utilities, components, layout, base} (see\n core/layers.css), so a theme's token reassignment cannot be\n accidentally beaten by a component or utility rule of equal\n specificity. Only consumer code in slashed.overrides (or\n unlayered CSS) can override a theme.\n\n USAGE\n → follows OS preference\n → whole page dark\n

→ only this section dark\n
→ only this section light\n\n The base color-scheme: var(--sf-color-scheme, light dark)\n declaration lives in core/reset.css and provides the baseline.\n This layer flips it.\n\n MULTI-BRAND — scope a palette to any subtree by re-declaring the 6\n source tokens under a selector (e.g. [data-brand=\"x\"]). See\n optional/theme-example.css and docs/theming.md.\n\n THEME TRANSITION — toggling [data-theme] via document.startViewTransition()\n animates through the ::view-transition(root) rules in core/motion.css.\n For a plain cross-fade of colours, opt into a transition class on \n (example in optional/theme-example.css) — never transition every colour\n globally and permanently (perf footgun).\n ─────────────────────────────────────────────────── */\n\n@layer slashed.themes {\n\n /* OS preference — only when no data-theme is set on :root.\n A section-level [data-theme] still wins via the explicit\n selectors below (higher specificity within the same layer). */\n @media (prefers-color-scheme: dark) {\n :root:not([data-theme]) {\n color-scheme: dark;\n --sf-is-dark: 1;\n }\n }\n\n /* Explicit theme — works on :root (whole page) or any element\n (section-level theming). */\n [data-theme=\"light\"] { color-scheme: light; --sf-is-dark: 0; }\n [data-theme=\"dark\"] { color-scheme: dark; --sf-is-dark: 1; }\n\n\n /* LumLocker — opt-in via [data-lumlocker] on :root.\n Locks the 5 brand colors (primary, secondary, tertiary, action,\n neutral) to one shared OKLCH L value (--sf-lumlocker,\n default 0.65 in core/tokens.css), keeping each color's own hue\n and chroma. Useful when a brand picker produces colors with\n wildly different L values that look mismatched in weight.\n\n Base is excluded by design — base IS the page surface (L ~0.98 in\n light mode, ~0.20 in dark) and locking it would destroy the page\n background.\n\n Default state: OFF. Without the attribute, every brand keeps its\n own L. Add :root[data-lumlocker] (or set via JS) to enable.\n\n Per-color escape: override --sf-color-X under\n :root[data-lumlocker] to opt that one color out:\n :root[data-lumlocker] { --sf-color-primary: #yourColor; }\n\n The dark-side input falls back to -light when -dark isn't\n explicitly set, so this works even without per-mode overrides. */\n :root[data-lumlocker] {\n --sf-color-primary: light-dark(\n oklch(from var(--sf-color-primary-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-primary-dark, var(--sf-color-primary-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-secondary: light-dark(\n oklch(from var(--sf-color-secondary-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-secondary-dark, var(--sf-color-secondary-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-tertiary: light-dark(\n oklch(from var(--sf-color-tertiary-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-tertiary-dark, var(--sf-color-tertiary-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-action: light-dark(\n oklch(from var(--sf-color-action-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-action-dark, var(--sf-color-action-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-neutral: light-dark(\n oklch(from var(--sf-color-neutral-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-neutral-dark, var(--sf-color-neutral-light)) var(--sf-lumlocker) c h)\n );\n }\n\n}\n\n/* ─── core/layout.css ─── */\n/* ============================================================\n SLASHED — core/layout.css\n ★ de facto required for most projects\n Layout primitives + structural patterns\n Requires: core/tokens.css, core/tokens.layout.css\n ============================================================ */\n\n@layer slashed.layout {\n\n /* -- Section -------------------------------------------------\n Page section with vertical padding. Size modifiers\n override the scoped token.\n ---------------------------------------------------------- */\n\n .sf-section { padding-block: var(--sf-section-pad); }\n .sf-section--xs { padding-block: var(--sf-section-pad--xs); }\n .sf-section--s { padding-block: var(--sf-section-pad--s); }\n .sf-section--m { padding-block: var(--sf-section-pad--m); }\n .sf-section--l { padding-block: var(--sf-section-pad--l); }\n .sf-section--xl { padding-block: var(--sf-section-pad--xl); }\n .sf-section--2xl { padding-block: var(--sf-section-pad--2xl); }\n\n .sf-section-group > .sf-section + .sf-section { padding-block-start: 0; }\n\n /* Collapse modifier — when two adjacent sections share this modifier,\n they redistribute padding 50/50 across the boundary so total spacing\n stays one full padding gap (instead of two). Works regardless of\n background color: each section always has half its padding on\n the boundary side, so content never touches a color edge.\n\n Use when sections are siblings outside a .sf-section-group wrapper\n and may share OR differ in background.\n\n Note: uses --sf-section-pad (the canonical alias = m by default).\n For other sizes, set --sf-section-pad locally on each section:\n
\n ---------------------------------------------------------- */\n .sf-section--collapse:has(+ .sf-section--collapse) {\n padding-block-end: calc(var(--sf-section-pad) / 2);\n }\n .sf-section--collapse + .sf-section--collapse {\n padding-block-start: calc(var(--sf-section-pad) / 2);\n }\n\n /* -- Divider -------------------------------------------------\n Standalone separator primitive. Token-driven; pairs with the\n `--sf-divider-*` tokens and mirrors the `hr` element default.\n ---------------------------------------------------------- */\n .sf-divider {\n border: none;\n border-block-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);\n margin-block: var(--sf-space-m);\n }\n .sf-divider--vertical {\n border-block-start: none;\n border-inline-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);\n margin-block: 0;\n margin-inline: var(--sf-space-m);\n align-self: stretch;\n }\n\n /* -- Container -----------------------------------------------\n Max-width container, centered with side gutters. Establishes\n a named inline-size container `sf-layout` so children can\n target it precisely with `@container sf-layout (...)`.\n The framework's own `.sf-bento` and `.sf-grid-*` rules use\n anonymous `@container` queries on purpose — they react to\n the nearest inline-size ancestor, which lets a consumer\n embed them inside any container without rewiring.\n ---------------------------------------------------------- */\n\n .sf-container {\n container: sf-layout / inline-size;\n width: 100%;\n max-width: var(--sf-container-default);\n margin-inline: auto;\n padding-inline: var(--sf-space-gutter);\n }\n .sf-container--narrow { max-width: var(--sf-container-narrow); }\n .sf-container--prose { max-width: var(--sf-container-prose); }\n .sf-container--wide { max-width: var(--sf-container-wide); }\n .sf-container--full { max-width: var(--sf-container-full); }\n\n /* -- Stack ---------------------------------------------------\n Flex column with even vertical spacing between children.\n Override: style=\"--sf-stack-gap: 2rem\"\n ---------------------------------------------------------- */\n\n .sf-stack {\n display: flex;\n flex-direction: column;\n gap: var(--sf-stack-gap);\n }\n .sf-stack--xs { gap: var(--sf-space-xs); }\n .sf-stack--s { gap: var(--sf-space-s); }\n .sf-stack--m { gap: var(--sf-space-m); }\n .sf-stack--l { gap: var(--sf-space-l); }\n .sf-stack--xl { gap: var(--sf-space-xl); }\n .sf-stack--2xl { gap: var(--sf-space-2xl); }\n\n .sf-stack--center { align-items: center; }\n .sf-stack--end { align-items: flex-end; }\n .sf-stack--stretch { align-items: stretch; }\n\n /* -- Gap ----------------------------------------------------\n Injects gap into any existing flex or grid container without\n imposing a display type or axis. Unlike .sf-stack (flex\n column) or .sf-cluster (flex-wrap row), .sf-gap is layout-\n agnostic — use it on elements that already have their own\n display context.\n Override: style=\"--sf-gap-size: 2rem\"\n ---------------------------------------------------------- */\n .sf-gap { gap: var(--sf-gap-size); }\n .sf-gap--xs { gap: var(--sf-space-xs); }\n .sf-gap--s { gap: var(--sf-space-s); }\n .sf-gap--m { gap: var(--sf-space-m); }\n .sf-gap--l { gap: var(--sf-space-l); }\n .sf-gap--xl { gap: var(--sf-space-xl); }\n .sf-gap--2xl { gap: var(--sf-space-2xl); }\n\n /* -- Box -----------------------------------------------------\n Isolated unit with padding and an optional border.\n Outline instead of border — doesn't disturb the box model when toggled.\n Override: style=\"--sf-box-padding: 1rem; --sf-box-border-width: 1px\"\n ---------------------------------------------------------- */\n\n .sf-box {\n padding: var(--sf-box-padding);\n outline: var(--sf-box-border-width) solid var(--sf-box-border-color);\n }\n\n /* -- Center --------------------------------------------------\n Centers content with a max-inline-size and side gutters.\n content-box: max-inline-size applies to content only; padding is additive.\n Override: style=\"--sf-center-max: 60rem\"\n ---------------------------------------------------------- */\n\n .sf-center {\n box-sizing: content-box;\n margin-inline: auto;\n max-inline-size: var(--sf-center-max);\n padding-inline: var(--sf-center-gutter);\n }\n\n .sf-center--intrinsic {\n display: flex;\n flex-direction: column;\n align-items: center;\n }\n\n /* -- Cluster -------------------------------------------------\n Flex-wrap group of variable-width items.\n Override: style=\"--sf-cluster-gap: 0.5rem; --sf-cluster-justify: center\"\n ---------------------------------------------------------- */\n\n .sf-cluster {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sf-cluster-gap);\n align-items: var(--sf-cluster-align);\n justify-content: var(--sf-cluster-justify);\n }\n .sf-cluster--xs { gap: var(--sf-space-xs); }\n .sf-cluster--s { gap: var(--sf-space-s); }\n .sf-cluster--m { gap: var(--sf-space-m); }\n .sf-cluster--l { gap: var(--sf-space-l); }\n .sf-cluster--xl { gap: var(--sf-space-xl); }\n .sf-cluster--2xl { gap: var(--sf-space-2xl); }\n\n .sf-cluster--no-wrap { flex-wrap: nowrap; }\n .sf-cluster--center { justify-content: center; }\n .sf-cluster--end { justify-content: flex-end; }\n .sf-cluster--between { justify-content: space-between; }\n\n /* -- Sidebar -------------------------------------------------\n Two-column layout: first-child = sidebar, last-child = main content.\n Collapses when the viewport is too narrow (flex-basis trick).\n Override: style=\"--sf-sidebar-width: 20rem\"\n ---------------------------------------------------------- */\n\n .sf-sidebar {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sf-sidebar-gap);\n }\n\n .sf-sidebar > :first-child {\n flex-basis: var(--sf-sidebar-width);\n flex-grow: 1;\n }\n\n .sf-sidebar > :last-child {\n flex-basis: 0;\n flex-grow: 999;\n min-inline-size: var(--sf-sidebar-min-width);\n }\n\n .sf-sidebar--right > :first-child {\n flex-basis: 0;\n flex-grow: 999;\n min-inline-size: var(--sf-sidebar-min-width);\n }\n\n .sf-sidebar--right > :last-child {\n flex-basis: var(--sf-sidebar-width);\n flex-grow: 1;\n }\n\n .sf-sidebar--narrow { --sf-sidebar-width: 12rem; }\n .sf-sidebar--wide { --sf-sidebar-width: 26rem; }\n\n /* -- Switcher ------------------------------------------------\n Switches between a column (narrow viewport) and a row (wide).\n Uses calc((threshold - 100%) * 999) — zero media queries.\n Override: style=\"--sf-switcher-threshold: 40rem\"\n ---------------------------------------------------------- */\n\n .sf-switcher {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sf-switcher-gap);\n }\n\n .sf-switcher > * {\n flex-grow: 1;\n flex-basis: calc((var(--sf-switcher-threshold) - 100%) * 999);\n }\n\n .sf-switcher--no-wrap { flex-wrap: nowrap; overflow-x: auto; }\n .sf-switcher--vertical { flex-direction: column; }\n\n /* -- Grid ----------------------------------------------------\n Auto-fill CSS grid with no breakpoints. --fit switches to auto-fit.\n auto-fill: empty tracks preserved, last item doesn't stretch.\n auto-fit (--fit): empty tracks collapsed, items stretch to fill the gap.\n Override: style=\"--sf-grid-min: 12rem; --sf-grid-gap: 1rem\"\n ---------------------------------------------------------- */\n\n .sf-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(min(var(--sf-grid-min), 100%), 1fr));\n gap: var(--sf-grid-gap);\n }\n\n .sf-grid--fit {\n grid-template-columns: repeat(auto-fit, minmax(min(var(--sf-grid-min), 100%), 1fr));\n }\n\n .sf-grid--xs { --sf-grid-min: var(--sf-grid-min-xs); }\n .sf-grid--s { --sf-grid-min: var(--sf-grid-min-s); }\n .sf-grid--m { --sf-grid-min: var(--sf-grid-min-m); }\n .sf-grid--l { --sf-grid-min: var(--sf-grid-min-l); }\n .sf-grid--xl { --sf-grid-min: var(--sf-grid-min-xl); }\n .sf-grid--2xl { --sf-grid-min: var(--sf-grid-min-2xl); }\n\n /* Dense packing — let later items backfill earlier gaps (as .sf-bento\n does). Applies to any grid, including the fixed-column .sf-grid-N. */\n .sf-grid--dense { grid-auto-flow: dense; }\n\n /* -- Icon ----------------------------------------------------\n Inline icon sizing primitive. Uses em-based --sf-icon-* tokens\n so an icon scales with its surrounding text. Defaults to -m.\n ---------------------------------------------------------- */\n .sf-icon {\n display: inline-block;\n inline-size: var(--sf-icon-size, var(--sf-icon-m));\n block-size: var(--sf-icon-size, var(--sf-icon-m));\n flex-shrink: 0;\n vertical-align: text-bottom;\n fill: currentcolor;\n }\n .sf-icon--xs { --sf-icon-size: var(--sf-icon-xs); }\n .sf-icon--s { --sf-icon-size: var(--sf-icon-s); }\n .sf-icon--m { --sf-icon-size: var(--sf-icon-m); }\n .sf-icon--l { --sf-icon-size: var(--sf-icon-l); }\n .sf-icon--xl { --sf-icon-size: var(--sf-icon-xl); }\n\n /* Boxed variant — wraps the icon in a padded, bordered, optionally\n coloured frame. Adds inline-/block-size on top of the naked icon\n size so the visible glyph stays at --sf-icon-size and the box\n grows around it (content-box). Override per icon:\n */\n .sf-icon--boxed {\n inline-size: calc(var(--sf-icon-size, var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);\n block-size: calc(var(--sf-icon-size, var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);\n padding: var(--sf-icon-box-pad);\n border: var(--sf-icon-box-border);\n border-radius: var(--sf-icon-box-radius);\n background: var(--sf-icon-box-bg);\n box-sizing: content-box;\n }\n\n /* -- Cover ---------------------------------------------------\n Full-height flex column. A .sf-cover__center child is\n vertically centered via margin-block: auto.\n Override: style=\"--sf-cover-min-height: 50dvh\"\n ---------------------------------------------------------- */\n\n .sf-cover {\n display: flex;\n flex-direction: column;\n min-height: var(--sf-cover-min-height);\n padding-block: var(--sf-cover-padding);\n }\n\n .sf-cover > .sf-cover__center {\n margin-block: auto;\n }\n\n .sf-cover--min { min-height: 50dvh; }\n .sf-cover--max { min-height: 0; max-height: 100dvh; }\n .sf-cover--padding-s { padding-block: var(--sf-space-xl); }\n .sf-cover--padding-l { padding-block: var(--sf-space-4xl); }\n\n /* -- Frame ---------------------------------------------------\n Aspect-ratio container. Media children: fill + cover.\n Override: style=\"--sf-frame-ratio: 4 / 3\"\n ---------------------------------------------------------- */\n\n .sf-frame {\n aspect-ratio: var(--sf-frame-ratio);\n overflow: hidden;\n display: flex;\n justify-content: center;\n align-items: center;\n }\n\n .sf-frame > img,\n .sf-frame > video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n }\n\n .sf-frame--square { aspect-ratio: var(--sf-ratio-square); }\n .sf-frame--video { aspect-ratio: var(--sf-ratio-video); }\n .sf-frame--cinema { aspect-ratio: var(--sf-ratio-cinema); }\n .sf-frame--portrait { aspect-ratio: var(--sf-ratio-portrait); }\n .sf-frame--4-3 { aspect-ratio: var(--sf-ratio-4-3); }\n .sf-frame--3-2 { aspect-ratio: var(--sf-ratio-3-2); }\n .sf-frame--golden { aspect-ratio: var(--sf-ratio-golden); }\n\n /* -- Reel ----------------------------------------------------\n Horizontal scroll with scroll-snap. Each child snaps to start.\n Override: style=\"--sf-reel-gap: 1rem; --sf-reel-item-width: 280px\"\n ---------------------------------------------------------- */\n\n .sf-reel {\n display: flex;\n block-size: var(--sf-reel-height);\n gap: var(--sf-reel-gap);\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n scroll-padding-inline: var(--sf-reel-gap);\n overscroll-behavior-x: contain;\n scrollbar-width: thin;\n }\n\n .sf-reel > * {\n flex: 0 0 var(--sf-reel-item-width);\n scroll-snap-align: start;\n }\n\n /* -- Imposter ------------------------------------------------\n Element centered absolutely within its parent.\n The parent must have position: relative.\n ---------------------------------------------------------- */\n\n .sf-imposter {\n position: absolute;\n inset-block-start: 50%;\n inset-inline-start: 50%;\n translate: -50% -50%;\n }\n\n .sf-imposter--fixed {\n position: fixed;\n }\n\n .sf-imposter--contain {\n max-inline-size: calc(100% - var(--sf-imposter-margin) * 2);\n max-block-size: calc(100% - var(--sf-imposter-margin) * 2);\n overflow: auto;\n }\n\n /* -- Alternate (zigzag / media-object) -----------------------\n Alternating two-column layouts (container-query responsive).\n Every other row reverses its children's order.\n\n Establishes a named container `sf-alternate` and binds the\n responsive query to that name — so a `.sf-alternate` nested\n inside another container (e.g. `.sf-bento`) still flips on\n its OWN width, not the outer container's.\n ---------------------------------------------------------- */\n\n .sf-alternate {\n display: grid;\n gap: var(--sf-alternate-gap);\n container: sf-alternate / inline-size;\n }\n\n .sf-alternate > * {\n display: grid;\n gap: var(--sf-alternate-inner-gap);\n align-items: center;\n }\n\n @container sf-alternate (min-width: 48em) {\n .sf-alternate > * {\n grid-template-columns: 1fr 1fr;\n }\n .sf-alternate > :nth-child(even) > :first-child { order: 2; }\n .sf-alternate > :nth-child(even) > :last-child { order: 1; }\n }\n\n /* -- Pancake (sticky footer) ---------------------------------\n Grid with three rows: header / main(1fr) / footer.\n ---------------------------------------------------------- */\n\n .sf-pancake {\n display: grid;\n grid-template-rows: auto 1fr auto;\n min-height: 100dvh;\n }\n\n /* -- Fixed column grids (container-query responsive) ---------\n Responsive fixed-column grids with no media queries.\n ---------------------------------------------------------- */\n\n .sf-grid-1, .sf-grid-2, .sf-grid-3, .sf-grid-4, .sf-grid-6 {\n display: grid;\n gap: var(--sf-grid-gap);\n }\n\n @container (min-width: 30em) {\n .sf-grid-4 { grid-template-columns: repeat(2, 1fr); }\n .sf-grid-6 { grid-template-columns: repeat(3, 1fr); }\n }\n\n @container (min-width: 48em) {\n .sf-grid-2 { grid-template-columns: repeat(2, 1fr); }\n .sf-grid-3 { grid-template-columns: repeat(3, 1fr); }\n .sf-grid-4 { grid-template-columns: repeat(4, 1fr); }\n .sf-grid-6 { grid-template-columns: repeat(6, 1fr); }\n }\n\n /* -- Ratio grids ---------------------------------------------\n Two-column ratio grids, container-query responsive.\n ---------------------------------------------------------- */\n\n .sf-grid-1-2, .sf-grid-2-1,\n .sf-grid-1-3, .sf-grid-3-1 {\n display: grid;\n gap: var(--sf-grid-gap);\n }\n\n @container (min-width: 48em) {\n .sf-grid-1-2 { grid-template-columns: 1fr 2fr; }\n .sf-grid-2-1 { grid-template-columns: 2fr 1fr; }\n .sf-grid-1-3 { grid-template-columns: 1fr 3fr; }\n .sf-grid-3-1 { grid-template-columns: 3fr 1fr; }\n }\n\n /* -- Equal columns ------------------------------------------\n Fixed N-column grid. Column count NEVER collapses — unlike\n .sf-grid-N which uses container queries to stack at small\n sizes. Use when the layout must remain N columns at all\n widths; the caller is responsible for overflow handling.\n Override: style=\"--sf-equal-cols: 3\"\n ---------------------------------------------------------- */\n .sf-equal {\n display: grid;\n grid-template-columns: repeat(var(--sf-equal-cols), 1fr);\n gap: var(--sf-equal-gap);\n }\n .sf-equal--2 { --sf-equal-cols: 2; }\n .sf-equal--3 { --sf-equal-cols: 3; }\n .sf-equal--4 { --sf-equal-cols: 4; }\n .sf-equal--6 { --sf-equal-cols: 6; }\n\n /* -- Content grid (breakout pattern) -------------------------\n Full-width grid that lets items break out to breakout or full\n width. Children default to the content column.\n ---------------------------------------------------------- */\n\n .sf-content-grid {\n display: grid;\n grid-template-columns:\n [full-start]\n minmax(var(--sf-space-gutter), 1fr)\n [breakout-start]\n minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2))\n [content-start]\n min(var(--sf-content-width), 100% - var(--sf-space-gutter) * 2)\n [content-end]\n minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2))\n [breakout-end]\n minmax(var(--sf-space-gutter), 1fr)\n [full-end];\n }\n\n .sf-content-grid > * { grid-column: content; }\n .sf-content-grid > .sf-breakout { grid-column: breakout; }\n .sf-content-grid > .sf-full-bleed { grid-column: full; }\n\n /* -- Bento grid ----------------------------------------------\n Free-form grid with auto-flow dense, responsive via CQ.\n Override: style=\"--sf-bento-cols: 4; --sf-bento-row: 12rem\"\n ---------------------------------------------------------- */\n\n .sf-bento {\n display: grid;\n grid-template-columns: repeat(var(--sf-bento-cols, var(--sf-bento-cols-default)), 1fr);\n grid-auto-rows: minmax(var(--sf-bento-row, var(--sf-bento-row-default)), auto);\n grid-auto-flow: dense;\n gap: var(--sf-bento-gap);\n }\n\n @container (min-width: 30em) and (max-width: 47.99em) {\n .sf-bento { grid-template-columns: repeat(2, 1fr); }\n }\n\n .sf-bento--2 { --sf-bento-cols: 2; }\n .sf-bento--4 { --sf-bento-cols: 4; }\n .sf-bento--compact { --sf-bento-row: var(--sf-bento-row-compact); }\n .sf-bento--tall { --sf-bento-row: var(--sf-bento-row-tall); }\n\n /* -- Subgrid -------------------------------------------------\n A grid child that inherits its parent's tracks.\n ---------------------------------------------------------- */\n\n .sf-subgrid { display: grid; grid-template-columns: subgrid; }\n .sf-subgrid-rows { display: grid; grid-template-rows: subgrid; }\n\n}\n\n/* ─── core/macros.css ─── */\n/* ============================================================\n SLASHED — core/macros.css\n ★ shipped in essential bundle\n Macro-classes (recipes / patterns).\n Requires: core/tokens.css, core/tokens.macros.css\n Layer: slashed.macros\n\n Macro-classes vs layout primitives:\n · Layout primitive = answers \"where do my children go?\"\n (e.g. .sf-stack, .sf-grid, .sf-cluster)\n · Macro-class = answers \"what does this element DO or\n LOOK LIKE?\" (e.g. .sf-prose, .sf-flow, .sf-truncate)\n\n Cascade position:\n layout → components → macros → utilities → states → …\n Macros may compose with primitives and components, but a\n single-property utility still wins on the same selector.\n\n PUBLIC API — every class here is part of the public surface.\n Tokens consumed by these classes are listed in\n core/tokens.macros.css.\n ============================================================ */\n\n@layer slashed.macros {\n\n /* -- Prose ---------------------------------------------------\n Long-form text column with automatic vertical rhythm.\n .sf-not-prose resets styles inside a prose block.\n ---------------------------------------------------------- */\n\n .sf-prose {\n max-width: var(--sf-container-prose);\n overflow-wrap: break-word;\n }\n\n .sf-prose > * + * { margin-block-start: var(--sf-prose-paragraph); }\n .sf-prose :is(h2, h3, h4) + * { margin-block-start: var(--sf-space-s); }\n .sf-prose li + li { margin-block-start: var(--sf-space-xs); }\n .sf-prose :is(blockquote, pre) { margin-block: var(--sf-space-m); }\n .sf-prose img { margin-block: var(--sf-space-m); border-radius: var(--sf-radius-m); }\n .sf-prose :is(ul, ol) { padding-inline-start: 1.25em; }\n .sf-prose ul { list-style: disc; }\n .sf-prose ol { list-style: decimal; }\n .sf-prose ::marker { color: var(--sf-color-primary); }\n .sf-prose figure { margin-block: var(--sf-space-l); }\n .sf-prose figcaption { font-size: var(--sf-text-s); color: var(--sf-color-text--muted); margin-block-start: var(--sf-space-xs); }\n .sf-prose table { margin-block: var(--sf-space-m); display: block; overflow-x: auto; max-width: 100%; }\n .sf-prose video { margin-block: var(--sf-space-m); border-radius: var(--sf-radius-m); }\n\n /* .sf-not-prose resets — specificity 0-2-1 intentionally exceeds the\n general 0-2-0 budget because these rules must override the .sf-prose\n child selectors above (also 0-2-0). This is a documented exception. */\n .sf-prose .sf-not-prose > * + * { margin-block-start: 0; }\n .sf-prose .sf-not-prose li + li { margin-block-start: 0; }\n /* `revert` in a cascade layer rolls back past all layers to the UA\n stylesheet — restoring browser-default list styling. In the flat\n bundle (layers stripped), `revert` falls back to the UA stylesheet\n directly. Both paths produce the same result: native list bullets. */\n .sf-prose .sf-not-prose :is(ul,ol) { list-style: revert; padding-inline-start: revert; }\n .sf-prose .sf-not-prose ::marker { color: inherit; }\n .sf-prose .sf-not-prose img { margin-block: 0; border-radius: 0; }\n .sf-prose .sf-not-prose figure { margin-block: 0; }\n .sf-prose .sf-not-prose figcaption { font-size: inherit; color: inherit; margin-block-start: 0; }\n .sf-prose .sf-not-prose table { margin-block: 0; display: revert; overflow-x: revert; max-width: revert; }\n .sf-prose .sf-not-prose video { margin-block: 0; border-radius: 0; }\n\n /* -- Flow ----------------------------------------------------\n Heydon Pickering's \"lobotomized owl\" — every flow child gets\n equal margin-block-start. Useful for content blocks where you\n don't know in advance which elements will be present.\n Override: style=\"--sf-flow-space: 2rem\"\n ---------------------------------------------------------- */\n .sf-flow > * + * {\n margin-block-start: var(--sf-flow-space);\n }\n\n /* -- Truncate / line-clamp -----------------------------------\n Single-line ellipsis (.sf-truncate) and N-line clamp\n (.sf-line-clamp-2 / -3 / -N). The fixed-count variants\n hardcode -webkit-line-clamp; -N reads --sf-line-clamp.\n\n Note: -webkit-line-clamp is not vendor-locked despite the\n prefix — every modern browser implements it as the de-facto\n standard. The unprefixed CSS Overflow 4 `line-clamp` exists\n but is not yet widely shipped.\n ---------------------------------------------------------- */\n .sf-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .sf-line-clamp-2,\n .sf-line-clamp-3,\n .sf-line-clamp-N {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n overflow: hidden;\n }\n .sf-line-clamp-2 { -webkit-line-clamp: 2; line-clamp: 2; }\n .sf-line-clamp-3 { -webkit-line-clamp: 3; line-clamp: 3; }\n .sf-line-clamp-N {\n -webkit-line-clamp: var(--sf-line-clamp);\n line-clamp: var(--sf-line-clamp);\n }\n\n /* -- Equal height --------------------------------------------\n Force grid/flex children to share the tallest one's height.\n Works in flex (default `align-items: stretch`) and is\n re-asserted here for cases where a wrapper resets it.\n ---------------------------------------------------------- */\n .sf-equal-height {\n display: flex;\n align-items: stretch;\n }\n .sf-equal-height > * {\n block-size: auto;\n flex: 1 1 auto;\n }\n\n /* -- Aspect --------------------------------------------------\n Generic aspect-ratio container. Distinct from layout's\n .sf-frame which targets media (object-fit: cover) — .sf-aspect\n is content-agnostic.\n Override: style=\"--sf-aspect: 4 / 3\"\n ---------------------------------------------------------- */\n .sf-aspect {\n aspect-ratio: var(--sf-aspect);\n }\n\n /* -- Scroll shadow -------------------------------------------\n Top + bottom mask gradient that reveals when content is\n scrolled inside a vertical-scroll container. Pure CSS,\n no JS. Combine with overflow-y: auto on the same element.\n Override: style=\"--sf-scroll-shadow-size: 3rem\"\n ---------------------------------------------------------- */\n .sf-scroll-shadow {\n overflow-y: auto;\n mask-image:\n linear-gradient(to bottom,\n transparent 0,\n black var(--sf-scroll-shadow-size),\n black calc(100% - var(--sf-scroll-shadow-size)),\n transparent 100%);\n }\n\n /* -- Scroll snap ---------------------------------------------\n Vertical scroll-snap container for vertically stacked sections.\n Pairs with consumer-supplied scroll-snap-align on children.\n For horizontal snap, use the .sf-reel layout primitive.\n ---------------------------------------------------------- */\n .sf-scroll-snap {\n overflow-y: auto;\n scroll-snap-type: y mandatory;\n overscroll-behavior-y: contain;\n }\n .sf-scroll-snap > * {\n scroll-snap-align: start;\n }\n\n /* -- Overflow fade -------------------------------------------\n End-edge horizontal fade for clipped inline content\n (e.g. a row of tags overflowing a card). Pure mask, so it\n respects the element's actual background.\n ---------------------------------------------------------- */\n .sf-overflow-fade {\n overflow: hidden;\n mask-image: linear-gradient(\n to right,\n black 0,\n black calc(100% - var(--sf-scroll-shadow-size)),\n transparent 100%\n );\n }\n\n /* -- No tap highlight ----------------------------------------\n Suppresses the WebKit/Android grey tap-highlight overlay on\n interactive elements where it conflicts with the framework's\n own .is-active / hover treatment.\n ---------------------------------------------------------- */\n .sf-no-tap-highlight {\n -webkit-tap-highlight-color: transparent;\n }\n\n /* -- Surface -------------------------------------------------\n Sets background + auto-flipped foreground in one class.\n Combines --sf-color-X (resolved semantic, mode-aware) with\n the matching --sf-color-text--on-X (auto-contrast) so text\n legibility is automatic without consumer math.\n\n 11 variants cover all brand + status + inverse colors. Base is\n excluded — base IS the page surface; use plain\n background: var(--sf-color-bg) if you need it explicitly.\n\n Composes naturally with .sf-section / .sf-card / .sf-stack:\n
\n ---------------------------------------------------------- */\n .sf-surface--primary { background: var(--sf-color-primary); color: var(--sf-color-text--on-primary); }\n .sf-surface--secondary { background: var(--sf-color-secondary); color: var(--sf-color-text--on-secondary); }\n .sf-surface--tertiary { background: var(--sf-color-tertiary); color: var(--sf-color-text--on-tertiary); }\n .sf-surface--action { background: var(--sf-color-action); color: var(--sf-color-text--on-action); }\n .sf-surface--neutral { background: var(--sf-color-neutral); color: var(--sf-color-text--on-neutral); }\n .sf-surface--inverse { background: var(--sf-color-inverse); color: var(--sf-color-text--on-inverse); }\n .sf-surface--success { background: var(--sf-color-success); color: var(--sf-color-text--on-success); }\n .sf-surface--warning { background: var(--sf-color-warning); color: var(--sf-color-text--on-warning); }\n .sf-surface--error { background: var(--sf-color-error); color: var(--sf-color-text--on-error); }\n .sf-surface--info { background: var(--sf-color-info); color: var(--sf-color-text--on-info); }\n .sf-surface--danger { background: var(--sf-color-danger); color: var(--sf-color-text--on-danger); }\n\n /* -- Text gradient -------------------------------------------\n Fills text with a gradient. Defaults to --sf-gradient-primary\n (auto-adapts to brand + dark mode). Override the background-image\n per-instance for any other gradient or radial:\n

\n\n Selection on gradient text inherits the clipping — that's a\n known browser limitation across all engines, accepted as-is.\n\n The `color: transparent` declaration is gated behind a feature\n query for background-clip:text. Without that gate, an engine\n that ignored background-clip:text would render the text fully\n invisible (transparent over no clipped fill). All current\n evergreen browsers support either the standard or the\n -webkit- prefixed form, so the supports check is a defensive\n guard rather than a real-world fallback.\n ---------------------------------------------------------- */\n .sf-text-gradient {\n background-image: var(--sf-gradient-primary);\n /* stylelint-disable-next-line property-no-vendor-prefix -- required for Safari */\n -webkit-background-clip: text;\n background-clip: text;\n }\n @supports ((background-clip: text) or (-webkit-background-clip: text)) {\n .sf-text-gradient {\n color: transparent;\n }\n }\n\n /* -- External link indication --------------------------------\n Adds an external-link glyph after the link text. Opt-in per\n element via class — for global auto-detection, write your\n own rule in unlayered CSS:\n a[rel~=\"external\"]:not(.no-external)::after {\n content: var(--sf-link-external-marker);\n }\n\n Marker is tokenised so consumers can swap to \" →\", \" ⤴\", or\n unset (empty string fully disables it — the default token\n value bakes the leading space in, so `\"\"` leaves nothing\n after the link):\n :root { --sf-link-external-marker: \"\" }\n ---------------------------------------------------------- */\n .sf-link-external::after {\n content: var(--sf-link-external-marker);\n display: inline-block;\n font-size: 0.85em;\n text-decoration: none;\n }\n\n}\n\n/* ─── core/states.css ─── */\n/* SLASHED — core/states.css\n @layer slashed.states\n\n Stateful classes toggled by JS or ARIA attributes.\n Prefix: .is-* (exclusive to this layer — never used in utilities).\n\n These are NOT utility classes. They describe dynamic,\n runtime states that change in response to user interaction\n or application logic. Utilities describe static intent;\n states describe current condition.\n\n Design principles:\n ─ Generic states only — component-specific overrides belong\n in slashed.components (e.g. .sf-modal.is-open).\n ─ Tokens first — every value references a design token.\n ─ Low specificity — single class, no nesting.\n ─ Minimal property sets — states set the MINIMUM needed\n to communicate the state. Components layer on top.\n ─────────────────────────────────────────────────── */\n\n@layer slashed.states {\n\n /* ============================================================\n VISIBILITY\n ============================================================ */\n\n .is-hidden {\n display: none !important;\n }\n\n .is-invisible {\n visibility: hidden;\n }\n\n .is-visible {\n visibility: visible;\n }\n\n /* ============================================================\n INTERACTIVITY — DISABLED / READONLY\n ============================================================ */\n\n .is-disabled {\n opacity: var(--sf-opacity-disabled);\n pointer-events: none;\n cursor: not-allowed;\n user-select: none;\n }\n\n .is-readonly {\n pointer-events: none;\n user-select: none;\n }\n\n /* ============================================================\n LOADING / ASYNC FEEDBACK\n ============================================================ */\n\n .is-loading {\n color: transparent !important;\n pointer-events: none;\n position: relative;\n }\n\n .is-loading::after {\n content: \"\";\n position: absolute;\n inset-block-start: 50%;\n inset-inline-start: 50%;\n width: 1em;\n height: 1em;\n margin-block-start: -0.5em;\n margin-inline-start: -0.5em;\n border: var(--sf-border-width-2) solid var(--sf-color-border--strong);\n border-block-start-color: var(--sf-color-action);\n border-radius: var(--sf-radius-full);\n /* Reduced-motion: accessibility.css kills animation-duration via\n !important on *, *::before, *::after — so this spinner stops\n even without a local reduced-motion wrapper. */\n animation: var(--sf-animation-spin);\n }\n\n .is-busy {\n cursor: progress;\n }\n\n .is-pending {\n /* Optimistic UI — request in flight but content still visible/usable\n (unlike .is-loading, which masks content with a spinner). */\n opacity: var(--sf-state-pending-opacity);\n cursor: progress;\n }\n\n .is-skeleton {\n color: transparent !important;\n background: linear-gradient(\n 90deg,\n var(--sf-color-well) 25%,\n var(--sf-color-raised) 50%,\n var(--sf-color-well) 75%\n );\n background-size: 200% 100%;\n animation: var(--sf-animation-shimmer);\n border-radius: var(--sf-radius-s);\n pointer-events: none;\n user-select: none;\n }\n\n /* ============================================================\n ACTIVE / SELECTED / CURRENT — navigation & toggling\n ============================================================ */\n\n .is-active {\n /* Generic active indicator — components add visual specifics.\n Sets a scoped token for components to consume. */\n --sf-is-active: 1;\n }\n\n .is-selected {\n background-color: var(--sf-color-bg--selected);\n }\n\n .is-current {\n /* Navigation: marks the current page/route.\n Components style via .sf-nav .is-current etc.\n Weight is tokenised so consumers can override without specificity battles:\n :root { --sf-current-font-weight: var(--sf-font-weight-semibold); } */\n --sf-is-current: 1;\n font-weight: var(--sf-current-font-weight, var(--sf-font-weight-bold));\n }\n\n .is-highlighted {\n background-color: var(--sf-color-bg--focus);\n }\n\n .is-pressed {\n /* Toggle buttons (aria-pressed=\"true\"). Components add specifics. */\n --sf-is-pressed: 1;\n background-color: var(--sf-color-bg--selected);\n }\n\n /* ============================================================\n OPEN / CLOSED — disclosure widgets, modals, dropdowns\n\n .is-open / .is-collapsed — a thing is shown vs hidden (modal,\n dropdown, drawer). .is-expanded — maps to aria-expanded on a\n disclosure/accordion trigger. They share the --sf-is-open flag\n but signal different intent to consumers.\n ============================================================ */\n\n .is-open {\n --sf-is-open: 1;\n }\n\n .is-collapsed {\n --sf-is-open: 0;\n }\n\n .is-expanded {\n --sf-is-open: 1;\n }\n\n /* ============================================================\n VALIDATION / FEEDBACK\n\n Overlap semantics (see docs/states.md):\n · .is-invalid / .is-valid — form-field validation (maps to\n aria-invalid). Use on inputs.\n · .is-error / .is-success — general component feedback, not tied\n to a single field (a failed save, a completed step).\n · .is-danger — destructive-action context (a delete button), NOT\n a validation result. Distinct from .is-error.\n ============================================================ */\n\n .is-valid {\n --sf-field-border-color: var(--sf-color-success);\n --sf-field-text-color: var(--sf-color-success-strong);\n }\n\n .is-invalid {\n --sf-field-border-color: var(--sf-color-error);\n --sf-field-text-color: var(--sf-color-error-strong);\n }\n\n .is-warning {\n --sf-field-border-color: var(--sf-color-warning);\n --sf-field-text-color: var(--sf-color-warning-strong);\n }\n\n .is-success {\n --sf-field-border-color: var(--sf-color-success);\n --sf-field-text-color: var(--sf-color-success-strong);\n }\n\n .is-error {\n --sf-field-border-color: var(--sf-color-error);\n --sf-field-text-color: var(--sf-color-error-strong);\n }\n\n .is-info {\n --sf-field-border-color: var(--sf-color-info);\n --sf-field-text-color: var(--sf-color-info-strong);\n }\n\n .is-danger {\n --sf-field-border-color: var(--sf-color-danger);\n --sf-field-text-color: var(--sf-color-danger-strong);\n }\n\n /* ============================================================\n POSITION / STICKINESS\n ============================================================ */\n\n .is-sticky {\n position: sticky;\n top: var(--sf-sticky-offset, 0);\n z-index: var(--sf-z-low);\n }\n\n .is-pinned {\n position: sticky;\n top: 0;\n z-index: var(--sf-z-mid);\n }\n\n .is-fixed {\n position: fixed;\n }\n\n .is-fullscreen {\n /* App-managed fullscreen overlay. For the native Fullscreen API,\n prefer the `:fullscreen` pseudo-class; this is the JS-toggled\n equivalent for elements not entered via that API. */\n position: fixed;\n inset: 0;\n z-index: var(--sf-z-max);\n }\n\n .is-resizable {\n resize: both;\n overflow: auto;\n }\n\n /* ============================================================\n OVERFLOW / CLIPPING\n ============================================================ */\n\n .is-clipped {\n overflow: hidden !important;\n }\n\n .is-scrollable {\n overflow: auto;\n overscroll-behavior: contain;\n }\n\n .is-truncated {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n /* ============================================================\n DRAG & DROP\n ============================================================ */\n\n .is-dragging {\n opacity: var(--sf-opacity-50);\n cursor: grabbing;\n }\n\n .is-drop-target {\n outline: var(--sf-focus-ring-width) dashed var(--sf-color-action);\n outline-offset: var(--sf-focus-ring-offset);\n }\n\n .is-draggable {\n cursor: grab;\n }\n\n /* ============================================================\n OVERLAY / BACKDROP\n ============================================================ */\n\n .is-overlay {\n position: absolute;\n inset: 0;\n }\n\n /* ============================================================\n FOCUS / INTERACTION MODIFIERS\n ============================================================ */\n\n .is-clickable {\n cursor: pointer;\n }\n\n .is-unselectable {\n user-select: none;\n }\n\n .is-focused {\n /* Programmatic / JS-driven focus indicator, independent of\n :focus-visible. Normal weight so consumers can restyle. */\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-focus-ring-color);\n outline-offset: var(--sf-focus-ring-offset);\n }\n\n /* ============================================================\n EMPTY STATE\n ============================================================ */\n\n .is-empty:empty {\n display: none;\n }\n\n /* ============================================================\n KEYFRAMES (used by states above)\n\n Spinner and shimmer keyframes live in core/motion.css alongside\n the rest of the framework's @keyframes. They are referenced from\n here by name (sf-spin, sf-shimmer) and are inert until applied.\n If you load core/states.css without core/motion.css, define them\n in your own CSS or copy them from motion.css.\n ============================================================ */\n\n}\n\n/* ─── core/motion.css ─── */\n/* SLASHED — core/motion.css\n @layer slashed.motion\n\n Extends the base interactive transition (defined in base.css)\n with a broader property set and adds scroll/view-transition\n behaviour. Optional and independently loadable.\n\n Reduced-motion handling is intentionally NOT duplicated here:\n accessibility.css (a higher cascade layer, shipped in the\n 'essential' bundle) neutralises animation/transition via\n property-level !important. The no-preference wrapper below is\n a defensive opt-in stance — animations apply only when the\n user has not requested their absence — and remains correct\n even if motion.css is served without accessibility.css.\n ─────────────────────────────────────────────────── */\n\n@layer slashed.motion {\n\n @media (prefers-reduced-motion: no-preference) {\n\n html {\n scroll-behavior: smooth;\n }\n\n /* Keyboard navigation must not be hijacked by smooth scroll. */\n html:focus-within {\n scroll-behavior: auto;\n }\n\n /* Broadens the base.css link transition to all interactive\n elements and the full set of animatable UI properties. */\n a, button, input, select, textarea, summary {\n transition-property:\n color, background-color, border-color, text-decoration-color,\n fill, stroke, opacity, box-shadow, transform, filter,\n backdrop-filter;\n transition-duration: var(--sf-duration-fast);\n transition-timing-function: var(--sf-ease-out);\n }\n\n @supports (view-transition-name: none) {\n ::view-transition-old(root),\n ::view-transition-new(root) {\n animation-duration: var(--sf-duration-normal);\n animation-timing-function: var(--sf-ease-out);\n }\n }\n\n /* Animation presets — opt-in classes. Scoped to no-preference so\n reduced-motion users never trigger them (accessibility.css also\n neutralises animation-duration as a hard guarantee). These are\n DECORATIVE: under reduced motion they simply don't run, so never\n rely on one (e.g. .sf-fade-out) as the sole way to show/hide\n content — drive real state with hidden/display/JS instead. */\n .sf-fade-in { animation: var(--sf-animation-fade-in); }\n .sf-fade-out { animation: var(--sf-animation-fade-out); }\n .sf-slide-in-up { animation: var(--sf-animation-slide-in-up); }\n .sf-slide-in-down { animation: var(--sf-animation-slide-in-down); }\n .sf-slide-in-left { animation: var(--sf-animation-slide-in-left); }\n .sf-slide-in-right { animation: var(--sf-animation-slide-in-right); }\n .sf-scale-up { animation: var(--sf-animation-scale-up); }\n .sf-scale-down { animation: var(--sf-animation-scale-down); }\n\n /* Scroll-driven entrance effects — element animates into view\n as it enters the viewport. Uses CSS scroll-driven animations\n via animation-timeline: view(). Range tokens\n (--sf-scroll-timeline-range-{start,end}, declared in\n core/tokens.css) control where the animation starts and ends\n along the scroll progress.\n\n Example:\n
\n\n Reduced-motion: gated by the no-preference wrapper above, so\n these are inert when the user opts out. The core animation\n (translate/opacity) still composes correctly with reduced-motion\n neutralisation in accessibility.css if a consumer accidentally\n writes them outside the gate.\n\n Browser support — animation-timeline is shipping in Chrome 115+\n and Firefox 145+ (Nov 2026). Safari does not yet support it\n (tracked at WebKit bug 264057); on those engines the @supports\n block below is skipped, so the rule falls back to a one-shot\n time-driven animation playing once at --sf-duration-slow.\n animation-duration is set unconditionally because without it\n a non-supporting engine would default to 0s and snap to the\n end state instantly — there'd be no visible entrance at all.\n Where animation-timeline is supported the timeline drives\n progress and the duration is ignored. */\n .sf-entrance--fade,\n .sf-entrance--fade-up,\n .sf-entrance--fade-down,\n .sf-entrance--fade-left,\n .sf-entrance--fade-right,\n .sf-entrance--scale-up {\n animation-duration: var(--sf-duration-slow);\n animation-timing-function: var(--sf-ease-out);\n animation-fill-mode: both;\n }\n @supports (animation-timeline: view()) {\n .sf-entrance--fade,\n .sf-entrance--fade-up,\n .sf-entrance--fade-down,\n .sf-entrance--fade-left,\n .sf-entrance--fade-right,\n .sf-entrance--scale-up {\n animation-timeline: view();\n animation-range: var(--sf-scroll-timeline-range-start, entry 0%)\n var(--sf-scroll-timeline-range-end, cover 30%);\n }\n }\n .sf-entrance--fade { animation-name: sf-fade-in; }\n .sf-entrance--fade-up { animation-name: sf-slide-in-up; }\n .sf-entrance--fade-down { animation-name: sf-slide-in-down; }\n .sf-entrance--fade-left { animation-name: sf-slide-in-left; }\n .sf-entrance--fade-right { animation-name: sf-slide-in-right; }\n .sf-entrance--scale-up { animation-name: sf-scale-up; }\n\n }\n\n /* Keyframes — inert until referenced, so they live outside the\n no-preference wrapper (consumers may reference them at will).\n Spinner and shimmer are used by .is-loading / .is-skeleton in\n core/states.css; the rest power the .sf-* animation classes\n above. */\n @keyframes sf-fade-in { from { opacity: 0; } to { opacity: 1; } }\n @keyframes sf-fade-out { from { opacity: 1; } to { opacity: 0; } }\n @keyframes sf-slide-in-up { from { opacity: 0; translate: 0 1rem; } to { opacity: 1; translate: 0 0; } }\n @keyframes sf-slide-in-down { from { opacity: 0; translate: 0 -1rem; } to { opacity: 1; translate: 0 0; } }\n @keyframes sf-slide-in-left { from { opacity: 0; translate: 1rem 0; } to { opacity: 1; translate: 0 0; } }\n @keyframes sf-slide-in-right { from { opacity: 0; translate: -1rem 0; } to { opacity: 1; translate: 0 0; } }\n @keyframes sf-scale-up { from { opacity: 0; scale: 0.92; } to { opacity: 1; scale: 1; } }\n @keyframes sf-scale-down { from { opacity: 1; scale: 1; } to { opacity: 0; scale: 0.92; } }\n\n @keyframes sf-spin { to { rotate: 360deg; } }\n @keyframes sf-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }\n\n /* Notification ping (expanding fading ring), cursor blink, gentle float.\n Keyframes only — apply via your own BEM/component rules or the\n --sf-animation-ping / -blink / -float presets. No utility classes\n ship for these (by design). */\n @keyframes sf-ping { 75%, 100% { scale: 2; opacity: 0; } }\n @keyframes sf-blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }\n @keyframes sf-float { 0%, 100% { translate: 0 0; } 50% { translate: 0 -0.5rem; } }\n\n /* @property color interpolation — proves that registered custom\n properties animate smoothly in oklch. Because --sf-color-primary-light\n is registered with syntax:\"\", the browser interpolates the\n intermediate color values frame-by-frame. Opt-in via .sf-color-pulse. */\n @keyframes sf-color-pulse {\n 0% { --sf-color-primary-light: oklch(from var(--sf-color-primary-light) l c h); }\n 50% { --sf-color-primary-light: oklch(from var(--sf-color-primary-light) calc(l + 0.25) c h); }\n 100% { --sf-color-primary-light: oklch(from var(--sf-color-primary-light) l c h); }\n }\n\n @media (prefers-reduced-motion: no-preference) {\n .sf-color-pulse {\n animation: var(--sf-animation-color-pulse);\n }\n }\n\n /* @starting-style — entry animations for elements appearing in the\n top layer (dialog, [popover]) or via display:none → block. SLASHED\n ships no global @starting-style rules because the from-state is\n element/component specific. Consumers opt in per component, e.g.:\n dialog[open] { opacity: 1; @starting-style { opacity: 0; } }\n Tracked for the future components layer. */\n\n}\n\n/* ─── core/accessibility.css ─── */\n/* SLASHED — core/accessibility.css\n @layer slashed.accessibility\n\n Critical a11y rules use !important to survive unlayered CSS and\n third-party resets (unlayered normal beats any layered normal).\n Protection is SELECTIVE — only rules whose override is a genuine\n user-facing barrier are hardened. Rules that are legitimately\n customised (touch targets, skip-link position, contrast boost,\n cursor) are left normal on purpose.\n\n Audit note: reset.css/base.css contain no !important on outline,\n animation-* or transition-*, so the reversed important-layer order\n cannot let a lower layer defeat these rules.\n ─────────────────────────────────────────────────── */\n\n@layer slashed.accessibility {\n\n /* ----------------------------------------------------------\n Focus — keyboard visibility is WCAG 2.4.7, hard barrier.\n Ring definition lives here (not base.css) so all focus\n logic is in one high-priority layer. Hardened.\n ---------------------------------------------------------- */\n :focus:not(:focus-visible) {\n outline: none;\n }\n\n :focus-visible {\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-focus-ring-color) !important;\n outline-offset: var(--sf-focus-ring-offset) !important;\n }\n\n /* ----------------------------------------------------------\n Reduced motion. Token override helps components that read\n duration tokens in other contexts; the property-level\n !important block is the real guarantee and works\n independently of tokens (and of unlayered token overrides).\n ---------------------------------------------------------- */\n @media (prefers-reduced-motion: reduce) {\n :root {\n --sf-duration-instant: 0.01ms;\n --sf-duration-fast: 0.01ms;\n --sf-duration-normal: 0.01ms;\n --sf-duration-slow: 0.01ms;\n --sf-duration-slower: 0.01ms;\n }\n\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n scroll-behavior: auto !important;\n }\n }\n\n /* Manual motion opt-out — same suppression as the OS preference above,\n but consumer-toggled (e.g. a site-level \"reduce motion\" switch). Works\n regardless of the OS setting. */\n .no-motion,\n .no-motion *,\n .no-motion *::before,\n .no-motion *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n scroll-behavior: auto !important;\n }\n\n /* ----------------------------------------------------------\n High contrast — enhancement, not a barrier. Left normal:\n if overridden the focus ring still exists, just thinner.\n ---------------------------------------------------------- */\n @media (prefers-contrast: more) {\n :root {\n --sf-focus-ring-width: 3px;\n /* Push derived reading-text colours toward the extremes (see the\n --sf-contrast-bias knob in tokens.css). */\n --sf-contrast-bias: 0.06;\n }\n\n hr {\n border-block-start-width: var(--sf-border-width-2);\n }\n }\n\n /* ----------------------------------------------------------\n Reduced transparency.\n ---------------------------------------------------------- */\n @media (prefers-reduced-transparency: reduce) {\n ::backdrop {\n background-color: var(--sf-color-neutral);\n opacity: 1;\n }\n }\n\n /* ----------------------------------------------------------\n Minimum touch targets — WCAG 2.5.5 has documented\n exceptions; dense desktop-first UIs legitimately go below\n 44px with other hit-area strategies. Left normal ON PURPOSE\n so it stays a recommendation, not an unbreakable rule.\n ---------------------------------------------------------- */\n @media (pointer: coarse) {\n button,\n input[type=\"button\"],\n input[type=\"submit\"],\n input[type=\"reset\"],\n select,\n summary,\n a {\n min-height: var(--sf-touch-target);\n min-width: var(--sf-touch-target);\n }\n }\n\n /* ----------------------------------------------------------\n Disabled states — cosmetic, left normal.\n ---------------------------------------------------------- */\n [disabled],\n [aria-disabled=\"true\"] {\n cursor: not-allowed;\n }\n\n /* ----------------------------------------------------------\n Screen-reader-only. Atomic contract: a partial override\n either exposes hidden content visually or hides intended\n content. All-or-nothing, so the whole set is hardened.\n ---------------------------------------------------------- */\n .sr-only,\n .visually-hidden,\n .sr-only-focusable:not(:focus, :focus-within) {\n position: absolute !important;\n width: 1px !important;\n height: 1px !important;\n padding: 0 !important;\n margin: -1px !important;\n overflow: hidden !important; /* fallback for Safari <16 */\n overflow: clip !important;\n clip-path: inset(50%) !important;\n white-space: nowrap !important;\n border: 0 !important;\n }\n\n /* ----------------------------------------------------------\n Skip link — appearance belongs to the integrator's design.\n Left normal so position/colour can be themed freely.\n ---------------------------------------------------------- */\n .skip-link {\n position: absolute;\n top: -100%;\n left: var(--sf-space-m);\n z-index: var(--sf-z-max);\n padding: var(--sf-space-s) var(--sf-space-m);\n background: var(--sf-color-bg);\n color: var(--sf-color-text);\n border: var(--sf-border-width-1) solid var(--sf-color-border);\n border-radius: var(--sf-radius-m);\n transition: top var(--sf-duration-fast) var(--sf-ease-out);\n }\n .skip-link:focus {\n top: var(--sf-space-m);\n }\n\n /* ----------------------------------------------------------\n Focus-parent — an a11y *pattern*, not a state. Lives in the\n accessibility layer (was previously in states.css; moved in\n v0.3.0) so that all focus-related a11y rules share one home\n and survive third-party resets at the same priority as the\n focus-visible ring above. Cannot be hardened with !important\n because the outline shorthand is consumer-customisable.\n ---------------------------------------------------------- */\n .focus-parent:focus-within {\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-color-border--focus);\n outline-offset: var(--sf-focus-ring-offset);\n }\n\n /* ----------------------------------------------------------\n Clickable-parent — the card-with-link a11y pattern.\n Applied to the card container. Two modes:\n\n Automatic (single primary link):\n
\n

Title

\n

Description

\n \n
\n\n Explicit (multiple links — add [data-overlay-link] to the primary):\n
\n

Title

\n

Description

\n Category ← lifted automatically\n ← lifted automatically\n
\n\n Mechanic: the link's ::after fills the card via position:absolute;\n inset:0. It is contained by .sf-clickable-parent (position:relative)\n only if the link itself has no positioned ancestor between it and the\n card — so no element containing the overlay link should be given\n position:relative. Flow children are therefore intentionally left at\n position:static; they sit below the overlay in stacking order.\n\n Trade-off: card text is NOT user-selectable by mouse. A JavaScript\n click-delegate could restore selectability while preserving\n full-card navigation.\n\n Key features:\n • [data-overlay-link] explicit mode — reliable multi-link cards\n • `:focus-within` card ring — always included, no extra recipe\n • --sf-clickable-overlay-z — escape parent stacking contexts\n • Wider interactive-element exceptions (summary, video, audio…)\n\n Tune --sf-clickable-overlay-z to escape a parent stacking context.\n [data-no-overlay] opts any descendant above the overlay.\n ---------------------------------------------------------- */\n .sf-clickable-parent {\n --sf-clickable-overlay-z: 1;\n position: relative;\n cursor: pointer;\n }\n\n /* Overlay: the link's ::after fills the containing card.\n Automatic mode — every :any-link gets the overlay; works reliably\n for single-link cards. Secondary links that also receive ::after are\n lifted to position:relative (interactive rule below), which scopes\n their ::after to themselves and keeps them independently clickable.\n Explicit mode — only [data-overlay-link] fills the card. */\n .sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link)::after,\n .sf-clickable-parent [data-overlay-link]::after {\n content: \"\";\n position: absolute;\n inset: 0;\n z-index: var(--sf-clickable-overlay-z);\n pointer-events: auto;\n }\n\n /* Interactive descendants beat the overlay and stay functional.\n Explicit mode: every non-designated :any-link is lifted — robust at\n any nesting depth, no :first-of-type fragility.\n Automatic mode: links that are :not(:first-of-type) among siblings\n are lifted. Sibling-scoped — use explicit mode for cards where\n secondary links live at different nesting depths.\n [data-no-overlay] is the per-element consumer escape hatch. */\n .sf-clickable-parent :is(\n button, input, textarea, select,\n summary, video, audio,\n [role=\"button\"], [data-no-overlay]\n ),\n .sf-clickable-parent:has([data-overlay-link]) a:where(:any-link):not([data-overlay-link]),\n .sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):not(:first-of-type) {\n position: relative;\n z-index: calc(var(--sf-clickable-overlay-z) + 1);\n }\n\n /* Card-level focus ring — keyboard users see which card is active.\n The focus ring is always included without requiring a separate\n recipe. Suppress with outline:none\n if the card design provides its own focus indicator. */\n .sf-clickable-parent:focus-within {\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-color-border--focus);\n outline-offset: var(--sf-focus-ring-offset);\n }\n\n /* ----------------------------------------------------------\n Forced colors (Windows High Contrast). Outline-based focus\n already survives; we only (a) adopt the system Highlight\n colour for the ring and (b) drop decorative shadows that\n forced-colors would otherwise flatten into solid slabs.\n Shadow removal is zero-specificity so any rule can win it back.\n ---------------------------------------------------------- */\n @media (forced-colors: active) {\n :root {\n --sf-focus-ring-color: Highlight;\n }\n\n :focus-visible {\n outline-color: Highlight !important;\n }\n\n :where(*) {\n box-shadow: none;\n text-shadow: none;\n }\n\n /* Form controls otherwise lose their visible boundary in forced-colors\n (their token-driven border colour is replaced by the system canvas).\n Re-assert a system-coloured border so fields stay distinguishable. */\n input,\n select,\n textarea {\n border: var(--sf-border-width-1) solid;\n }\n }\n\n}\n\n/* ─── core/print.css ─── */\n/* SLASHED — core/print.css\n @layer slashed.print\n\n Print rules and @page definition. Print tokens (--sf-print-*)\n are declared in core/tokens.css.\n\n Color contract (Phase 3):\n authored colour is preserved by default. Browsers apply their own\n ink-saving heuristics via the default `print-color-adjust: economy`,\n so semantic colours (badges, status pills, , syntax highlighting)\n reach paper intact. Two opt-in classes give consumers control:\n\n .print-color-exact → force every descendant to print colours\n verbatim (for colour-coded data that loses\n meaning when the browser flattens it).\n .print-no-color → force everything to ink-on-paper:\n transparent backgrounds + system text colour.\n The legacy blanket reset, now opt-in.\n\n `!important` is reserved for selectors whose semantics require\n defeating consumer-authored CSS:\n - `.no-print` and the hide-list (nav, aside, button, …) — these\n are content negative-space and must vanish from print regardless\n of authored display rules.\n - `details > summary` — disclosure widget carries no print value.\n - The two opt-in colour classes above — when the consumer marks a\n region with them they are explicitly asking to override authored\n colour, so `!important` is the contract.\n ─────────────────────────────────────────────────── */\n\n@layer slashed.print {\n\n /* Screen default: print-only content is hidden until the page is printed. */\n .print-only {\n display: none;\n }\n\n @media print {\n\n @page {\n size: var(--sf-print-page-size, a4);\n margin: var(--sf-print-page-margin, 2cm);\n }\n\n body {\n font-size: var(--sf-print-base-size);\n }\n\n /* Decorative shadows are paper-noise. No `!important` — an authored\n shadow that explicitly survives into print is the consumer's call. */\n *,\n *::before,\n *::after {\n box-shadow: none;\n text-shadow: none;\n }\n\n /* Expand link URLs after the link text so paper-readers can follow\n them. Anchors and javascript: pseudo-links are excluded. */\n a[href]::after {\n content: \" (\" attr(href) \")\";\n font-size: 90%;\n word-break: break-all;\n }\n\n a[href^=\"#\"]::after,\n a[href^=\"javascript:\" i]::after {\n content: \"\";\n }\n\n abbr[title]::after {\n content: \" (\" attr(title) \")\";\n }\n\n img, svg, video, canvas, figure, table, pre, blockquote {\n break-inside: avoid;\n }\n\n thead {\n display: table-header-group;\n }\n\n tr {\n break-inside: avoid;\n }\n\n h1, h2, h3, h4, h5, h6 {\n break-after: avoid;\n orphans: 3;\n widows: 3;\n }\n\n p {\n orphans: 3;\n widows: 3;\n }\n\n pre, blockquote {\n border: var(--sf-border-width-1) solid var(--sf-color-border--strong);\n padding: var(--sf-space-s);\n }\n\n /* Hide non-essential interactive and decorative elements.\n `!important` is justified: these elements are content negative-space\n in print, and consumer-authored display rules must not bring them\n back accidentally. */\n nav, aside, button, input, select, textarea,\n audio, video, dialog, [popover], .no-print {\n display: none !important;\n }\n\n /* Reveal print-only content (overrides the screen default above). */\n .print-only {\n display: revert !important;\n }\n\n /* Preserve
content regardless of open state. Only\n `summary` keeps `!important` — the disclosure chevron carries no\n print value and the consumer should not be able to bring it back\n inadvertently. The other two rules win against the UA stylesheet\n on layered-author > UA precedence alone. */\n details {\n display: block;\n }\n details > summary {\n display: none !important;\n }\n details:not([open]) > :not(summary) {\n display: block;\n }\n\n /* ── Opt-in colour treatment ─────────────────────────────────── */\n\n /* Force colour-faithful rendering. Use on regions where a tinted\n background or coloured text encodes meaning (status pills,\n severity callouts, syntax highlighting, charts). */\n .print-color-exact,\n .print-color-exact * {\n print-color-adjust: exact !important;\n -webkit-print-color-adjust: exact !important;\n }\n\n /* Opt back into the legacy blanket reset: transparent backgrounds\n and system text colour everywhere inside the marked subtree.\n Use on regions where ink-saving is the contract (corporate\n boilerplate forms, append-to-existing-document templates). */\n .print-no-color,\n .print-no-color *,\n .print-no-color *::before,\n .print-no-color *::after {\n background: transparent !important;\n color: CanvasText !important;\n }\n\n }\n\n}\n"],"names":[]} \ No newline at end of file diff --git a/dist/slashed.full.css b/dist/slashed.full.css index 7e63de59..1422aff0 100644 --- a/dist/slashed.full.css +++ b/dist/slashed.full.css @@ -1056,6 +1056,11 @@ ---------------------------------------------------------- */ --sf-stack-gap: var(--sf-space-content); + /* ---------------------------------------------------------- + Gap + ---------------------------------------------------------- */ + --sf-gap-size: var(--sf-space-gap); + /* ---------------------------------------------------------- Box ---------------------------------------------------------- */ @@ -1102,6 +1107,12 @@ --sf-grid-min-xl: 24rem; --sf-grid-min-2xl: 28rem; + /* ---------------------------------------------------------- + Equal columns (fixed, non-responsive) + ---------------------------------------------------------- */ + --sf-equal-cols: 2; + --sf-equal-gap: var(--sf-space-gap); + /* ---------------------------------------------------------- Cover ---------------------------------------------------------- */ @@ -1770,6 +1781,22 @@ .sf-stack--end { align-items: flex-end; } .sf-stack--stretch { align-items: stretch; } + /* -- Gap ---------------------------------------------------- + Injects gap into any existing flex or grid container without + imposing a display type or axis. Unlike .sf-stack (flex + column) or .sf-cluster (flex-wrap row), .sf-gap is layout- + agnostic — use it on elements that already have their own + display context. + Override: style="--sf-gap-size: 2rem" + ---------------------------------------------------------- */ + .sf-gap { gap: var(--sf-gap-size); } + .sf-gap--xs { gap: var(--sf-space-xs); } + .sf-gap--s { gap: var(--sf-space-s); } + .sf-gap--m { gap: var(--sf-space-m); } + .sf-gap--l { gap: var(--sf-space-l); } + .sf-gap--xl { gap: var(--sf-space-xl); } + .sf-gap--2xl { gap: var(--sf-space-2xl); } + /* -- Box ----------------------------------------------------- Isolated unit with padding and an optional border. Outline instead of border — doesn't disturb the box model when toggled. @@ -2113,6 +2140,23 @@ .sf-grid-3-1 { grid-template-columns: 3fr 1fr; } } + /* -- Equal columns ------------------------------------------ + Fixed N-column grid. Column count NEVER collapses — unlike + .sf-grid-N which uses container queries to stack at small + sizes. Use when the layout must remain N columns at all + widths; the caller is responsible for overflow handling. + Override: style="--sf-equal-cols: 3" + ---------------------------------------------------------- */ + .sf-equal { + display: grid; + grid-template-columns: repeat(var(--sf-equal-cols), 1fr); + gap: var(--sf-equal-gap); + } + .sf-equal--2 { --sf-equal-cols: 2; } + .sf-equal--3 { --sf-equal-cols: 3; } + .sf-equal--4 { --sf-equal-cols: 4; } + .sf-equal--6 { --sf-equal-cols: 6; } + /* -- Content grid (breakout pattern) ------------------------- Full-width grid that lets items break out to breakout or full width. Children default to the content column. diff --git a/dist/slashed.full.flat.css b/dist/slashed.full.flat.css index 42cb01f5..c353ebea 100644 --- a/dist/slashed.full.flat.css +++ b/dist/slashed.full.flat.css @@ -1035,6 +1035,11 @@ ---------------------------------------------------------- */ --sf-stack-gap: var(--sf-space-content); + /* ---------------------------------------------------------- + Gap + ---------------------------------------------------------- */ + --sf-gap-size: var(--sf-space-gap); + /* ---------------------------------------------------------- Box ---------------------------------------------------------- */ @@ -1081,6 +1086,12 @@ --sf-grid-min-xl: 24rem; --sf-grid-min-2xl: 28rem; + /* ---------------------------------------------------------- + Equal columns (fixed, non-responsive) + ---------------------------------------------------------- */ + --sf-equal-cols: 2; + --sf-equal-gap: var(--sf-space-gap); + /* ---------------------------------------------------------- Cover ---------------------------------------------------------- */ @@ -1736,6 +1747,22 @@ td, th { .sf-stack--end { align-items: flex-end; } .sf-stack--stretch { align-items: stretch; } +/* -- Gap ---------------------------------------------------- + Injects gap into any existing flex or grid container without + imposing a display type or axis. Unlike .sf-stack (flex + column) or .sf-cluster (flex-wrap row), .sf-gap is layout- + agnostic — use it on elements that already have their own + display context. + Override: style="--sf-gap-size: 2rem" + ---------------------------------------------------------- */ +.sf-gap { gap: var(--sf-gap-size); } +.sf-gap--xs { gap: var(--sf-space-xs); } +.sf-gap--s { gap: var(--sf-space-s); } +.sf-gap--m { gap: var(--sf-space-m); } +.sf-gap--l { gap: var(--sf-space-l); } +.sf-gap--xl { gap: var(--sf-space-xl); } +.sf-gap--2xl { gap: var(--sf-space-2xl); } + /* -- Box ----------------------------------------------------- Isolated unit with padding and an optional border. Outline instead of border — doesn't disturb the box model when toggled. @@ -2079,6 +2106,23 @@ td, th { .sf-grid-3-1 { grid-template-columns: 3fr 1fr; } } +/* -- Equal columns ------------------------------------------ + Fixed N-column grid. Column count NEVER collapses — unlike + .sf-grid-N which uses container queries to stack at small + sizes. Use when the layout must remain N columns at all + widths; the caller is responsible for overflow handling. + Override: style="--sf-equal-cols: 3" + ---------------------------------------------------------- */ +.sf-equal { + display: grid; + grid-template-columns: repeat(var(--sf-equal-cols), 1fr); + gap: var(--sf-equal-gap); +} +.sf-equal--2 { --sf-equal-cols: 2; } +.sf-equal--3 { --sf-equal-cols: 3; } +.sf-equal--4 { --sf-equal-cols: 4; } +.sf-equal--6 { --sf-equal-cols: 6; } + /* -- Content grid (breakout pattern) ------------------------- Full-width grid that lets items break out to breakout or full width. Children default to the content column. diff --git a/dist/slashed.full.flat.min.css b/dist/slashed.full.flat.min.css index 54bfd2b8..d5e09c88 100644 --- a/dist/slashed.full.flat.min.css +++ b/dist/slashed.full.flat.min.css @@ -1,2 +1,2 @@ -@property --sf-color-primary-light{syntax:"";inherits:true;initial-value:oklch(45% .2 264)}@property --sf-color-secondary-light{syntax:"";inherits:true;initial-value:oklch(25% .03 260)}@property --sf-color-tertiary-light{syntax:"";inherits:true;initial-value:oklch(48% .14 310)}@property --sf-color-action-light{syntax:"";inherits:true;initial-value:oklch(60% .16 210)}@property --sf-color-neutral-light{syntax:"";inherits:true;initial-value:oklch(45% .02 260)}@property --sf-color-base-light{syntax:"";inherits:true;initial-value:oklch(98% .005 260)}@property --sf-color-success-light{syntax:"";inherits:true;initial-value:oklch(48% .17 150)}@property --sf-color-warning-light{syntax:"";inherits:true;initial-value:oklch(75% .17 80)}@property --sf-color-error-light{syntax:"";inherits:true;initial-value:oklch(62% .2 35)}@property --sf-color-info-light{syntax:"";inherits:true;initial-value:oklch(48% .15 240)}@property --sf-color-danger-light{syntax:"";inherits:true;initial-value:oklch(48% .24 12)}@property --sf-is-dark{syntax:"";inherits:true;initial-value:0}@property --sf-is-active{syntax:"";inherits:true;initial-value:0}@property --sf-is-current{syntax:"";inherits:true;initial-value:0}@property --sf-is-pressed{syntax:"";inherits:true;initial-value:0}@property --sf-is-open{syntax:"";inherits:true;initial-value:0}:root{--sf-color-scheme:light dark;--sf-lumlocker:.65;--sf-color-primary:light-dark(var(--sf-color-primary-light),var(--sf-color-primary-dark,oklch(from var(--sf-color-primary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-secondary:light-dark(var(--sf-color-secondary-light),var(--sf-color-secondary-dark,oklch(from var(--sf-color-secondary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-tertiary:light-dark(var(--sf-color-tertiary-light),var(--sf-color-tertiary-dark,oklch(from var(--sf-color-tertiary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-action:light-dark(var(--sf-color-action-light),var(--sf-color-action-dark,oklch(from var(--sf-color-action-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-neutral:light-dark(var(--sf-color-neutral-light),var(--sf-color-neutral-dark,oklch(from var(--sf-color-neutral-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-base:light-dark(var(--sf-color-base-light),var(--sf-color-base-dark,oklch(from var(--sf-color-base-light) clamp(.16, calc(1.18 - l), .24) calc(c * .5) h)));--sf-color-success:light-dark(var(--sf-color-success-light),var(--sf-color-success-dark,oklch(from var(--sf-color-success-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-warning:light-dark(var(--sf-color-warning-light),var(--sf-color-warning-dark,oklch(from var(--sf-color-warning-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-error:light-dark(var(--sf-color-error-light),var(--sf-color-error-dark,oklch(from var(--sf-color-error-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-info:light-dark(var(--sf-color-info-light),var(--sf-color-info-dark,oklch(from var(--sf-color-info-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-danger:light-dark(var(--sf-color-danger-light),var(--sf-color-danger-dark,oklch(from var(--sf-color-danger-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-bg:oklch(from var(--sf-color-base) calc(l + .02) c h);--sf-color-surface:var(--sf-color-base);--sf-color-well:oklch(from var(--sf-color-base) calc(l - .02) c h);--sf-color-raised:oklch(from var(--sf-color-base) calc(l + .04) c h);--sf-color-overlay:oklch(from var(--sf-color-base) l c h / .9);--sf-color-inverse:oklch(from var(--sf-color-base) calc(1 - l) c h);--sf-color-text:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.05, calc(l - .4 - var(--sf-contrast-bias)), .35) c h),oklch(from var(--sf-color-neutral) clamp(.7, calc(l + .25 + var(--sf-contrast-bias)), 1) c h));--sf-color-text--secondary:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.15, calc(l - .25 - var(--sf-contrast-bias)), .45) c h),oklch(from var(--sf-color-neutral) clamp(.55, calc(l + .1 + var(--sf-contrast-bias)), .9) c h));--sf-color-text--muted:var(--sf-color-neutral);--sf-color-text--placeholder:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.45, calc(l + .15), .75) c h),oklch(from var(--sf-color-neutral) clamp(.35, calc(l - .1), .65) c h));--sf-color-text--disabled:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.55, calc(l + .25), .82) c h),oklch(from var(--sf-color-neutral) clamp(.25, calc(l - .2), .55) c h));--sf-color-text--inverse:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.85, calc(l + .4), .98) c h),oklch(from var(--sf-color-neutral) clamp(.05, calc(l - .4), .35) c h));--sf-color-heading:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.05, calc(l - .4 - var(--sf-contrast-bias)), .35) c h),oklch(from var(--sf-color-neutral) clamp(.7, calc(l + .25 + var(--sf-contrast-bias)), 1) c h));--sf-color-code-text:oklch(from var(--sf-color-code-bg) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-primary:oklch(from var(--sf-color-primary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-secondary:oklch(from var(--sf-color-secondary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-tertiary:oklch(from var(--sf-color-tertiary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-action:oklch(from var(--sf-color-action) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-neutral:oklch(from var(--sf-color-neutral) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-base:var(--sf-color-text);--sf-color-text--on-inverse:var(--sf-color-text--inverse);--sf-color-text--on-success:oklch(from var(--sf-color-success) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-warning:oklch(from var(--sf-color-warning) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-error:oklch(from var(--sf-color-error) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-info:oklch(from var(--sf-color-info) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-danger:oklch(from var(--sf-color-danger) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-border:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.7, calc(l + .35), .95) .005 h),oklch(from var(--sf-color-neutral) clamp(.25, calc(l - .3), .55) .005 h));--sf-color-border--subtle:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.75, calc(l + .4), .97) .005 h),oklch(from var(--sf-color-neutral) clamp(.2, calc(l - .38), .45) .005 h));--sf-color-border--strong:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.55, calc(l + .1), .85) .02 h),oklch(from var(--sf-color-neutral) clamp(.38, calc(l - .1), .65) .02 h));--sf-color-border--focus:var(--sf-color-action);--sf-color-border--disabled:oklch(from var(--sf-color-border--subtle) l 0 h / .5);--sf-color-border--translucent:oklch(from var(--sf-color-neutral) l c h / .15);--sf-color-link:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .07, .48), 1) c h),oklch(from var(--sf-color-action) clamp(.68, l, 1) c h));--sf-color-link--hover:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .15, .4), 1) c h),oklch(from var(--sf-color-action) clamp(0, max(l + .1, .68), 1) c h));--sf-color-link--active:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .21, .34), 1) c h),oklch(from var(--sf-color-action) clamp(0, max(l + .15, .74), 1) c h));--sf-color-link--visited:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .07, .48), 1) c calc(h + 60)),oklch(from var(--sf-color-action) clamp(.68, l, 1) c calc(h + 60)));--sf-color-link--underline:oklch(from var(--sf-color-action) l c h / .3);--sf-color-link--disabled:var(--sf-color-text--disabled);--sf-color-bg--hover:oklch(from var(--sf-color-neutral) l c h / .08);--sf-color-bg--active:oklch(from var(--sf-color-neutral) l c h / .12);--sf-color-bg--selected:oklch(from var(--sf-color-action) l c h / .1);--sf-color-bg--focus:oklch(from var(--sf-color-action) l c h / .06);--sf-color-bg--disabled:var(--sf-color-well);--sf-color-code-bg:var(--sf-color-well);--sf-color-selection-bg:oklch(from var(--sf-color-action) l c h / .22);--sf-color-selection-text:inherit;--sf-color-mark-bg:oklch(from var(--sf-color-warning) l c h / .25);--sf-color-mark-text:inherit;--sf-color-dim:oklch(0% 0 0/.5);--sf-scrollbar-thumb:var(--sf-color-neutral);--sf-scrollbar-track:transparent;--sf-color-success-subtle:oklch(from var(--sf-color-success) l c h / .12);--sf-color-success-strong:light-dark(oklch(from var(--sf-color-success-light) calc(l - .15) c h),oklch(from var(--sf-color-success) clamp(.7, calc(l + .15), 1) c h));--sf-color-success-muted:oklch(from var(--sf-color-success) l c h / .3);--sf-color-warning-subtle:oklch(from var(--sf-color-warning) l c h / .12);--sf-color-warning-strong:light-dark(oklch(from var(--sf-color-warning-light) calc(l - .25) c h),oklch(from var(--sf-color-warning) clamp(.7, calc(l + .05), 1) c h));--sf-color-warning-muted:oklch(from var(--sf-color-warning) l c h / .3);--sf-color-error-subtle:oklch(from var(--sf-color-error) l c h / .1);--sf-color-error-strong:light-dark(oklch(from var(--sf-color-error-light) calc(l - .1) c h),oklch(from var(--sf-color-error) clamp(.7, calc(l + .15), 1) c h));--sf-color-error-muted:oklch(from var(--sf-color-error) l c h / .3);--sf-color-info-subtle:oklch(from var(--sf-color-info) l c h / .1);--sf-color-info-strong:light-dark(oklch(from var(--sf-color-info-light) calc(l - .1) c h),oklch(from var(--sf-color-info) clamp(.7, calc(l + .15), 1) c h));--sf-color-info-muted:oklch(from var(--sf-color-info) l c h / .3);--sf-color-danger-subtle:oklch(from var(--sf-color-danger) l c h / .1);--sf-color-danger-strong:light-dark(oklch(from var(--sf-color-danger-light) calc(l - .1) c h),oklch(from var(--sf-color-danger) clamp(.7, calc(l + .15), 1) c h));--sf-color-danger-muted:oklch(from var(--sf-color-danger) l c h / .3);--sf-gradient-primary:linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) calc(l - .08) c h));--sf-gradient-secondary:linear-gradient(135deg, var(--sf-color-secondary), oklch(from var(--sf-color-secondary) calc(l - .08) c h));--sf-gradient-tertiary:linear-gradient(135deg, var(--sf-color-tertiary), oklch(from var(--sf-color-tertiary) calc(l - .08) c h));--sf-gradient-brand:linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) l c calc(h + 30)));--sf-gradient-surface:linear-gradient(180deg, var(--sf-color-surface), var(--sf-color-bg));--sf-gradient-fade--r:linear-gradient(to right, transparent, var(--sf-color-bg));--sf-gradient-fade--l:linear-gradient(to left, transparent, var(--sf-color-bg));--sf-gradient-fade--t:linear-gradient(to top, transparent, var(--sf-color-bg));--sf-gradient-fade--b:linear-gradient(to bottom, transparent, var(--sf-color-bg));--sf-shadow-strength:calc(.08 + var(--sf-is-dark) * .17);--sf-shadow-glow-color:var(--sf-color-primary);--sf-shadow-glow:0 0 15px 2px oklch(from var(--sf-shadow-glow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-opacity-disabled:.45;--sf-state-pending-opacity:.7;--sf-focus-ring-color:var(--sf-color-action);--sf-focus-ring-shadow:0 0 0 var(--sf-focus-ring-offset) var(--sf-color-bg), 0 0 0 calc(var(--sf-focus-ring-offset) + var(--sf-focus-ring-width)) var(--sf-focus-ring-color);--sf-caret-color:var(--sf-color-action);--sf-space-scale:1;--sf-text-scale:1;--sf-text-display-scale:1;--sf-radius-scale:1;--sf-motion-scale:1;--sf-font-body:system-ui, -apple-system, sans-serif;--sf-font-heading:var(--sf-font-body);--sf-font-display:var(--sf-font-heading);--sf-font-mono:ui-monospace, monospace;--sf-font-humanist:"Seravek", "Gill Sans Nova", "Ubuntu", "Calibri", "DejaVu Sans", source-sans-pro, sans-serif;--sf-font-geometric:"Avenir", "Montserrat", "Corbel", "URW Gothic", source-sans-pro, sans-serif;--sf-font-slab:"Rockwell", "Rockwell Nova", "Roboto Slab", "DejaVu Serif", "Sitka Small", serif;--sf-font-features:normal;--sf-font-variation:normal;--sf-optical-sizing:auto;--sf-font-weight-thin:100;--sf-font-weight-extralight:200;--sf-font-weight-light:300;--sf-font-weight-normal:400;--sf-font-weight-medium:500;--sf-font-weight-semibold:600;--sf-font-weight-bold:700;--sf-font-weight-extrabold:800;--sf-font-weight-black:900;--sf-font-weight-body:var(--sf-font-weight-normal);--sf-font-weight-heading:var(--sf-font-weight-semibold);--sf-font-weight-display:var(--sf-font-weight-bold);--sf-current-font-weight:var(--sf-font-weight-bold);--sf-text-2xs:clamp(.51rem, calc(.000296296 * (100vw - 22.5rem) + .51rem), .53rem);--sf-text-xs:clamp(.64rem, calc(.000888889 * (100vw - 22.5rem) + .64rem), .7rem);--sf-text-s:clamp(.8rem, calc(.00207407 * (100vw - 22.5rem) + .8rem), .94rem);--sf-text-m:clamp(1rem, calc(.0037037 * (100vw - 22.5rem) + 1rem), 1.25rem);--sf-text-l:clamp(1.25rem, calc(.00622222 * (100vw - 22.5rem) + 1.25rem), 1.67rem);--sf-text-xl:clamp(1.56rem, calc(.00977778 * (100vw - 22.5rem) + 1.56rem), 2.22rem);--sf-text-2xl:clamp(1.95rem, calc(.014963 * (100vw - 22.5rem) + 1.95rem), 2.96rem);--sf-text-3xl:clamp(2.44rem, calc(.0223704 * (100vw - 22.5rem) + 2.44rem), 3.95rem);--sf-text-4xl:clamp(3.05rem, calc(.0327407 * (100vw - 22.5rem) + 3.05rem), 5.26rem);--sf-text-display-s:clamp(2.4rem, calc(.00888889 * (100vw - 22.5rem) + 2.4rem), 3rem);--sf-text-display-m:clamp(3rem, calc(.0148148 * (100vw - 22.5rem) + 3rem), 4rem);--sf-text-display-l:clamp(3.75rem, calc(.0234074 * (100vw - 22.5rem) + 3.75rem), 5.33rem);--sf-leading-tight:1.1;--sf-leading-snug:1.3;--sf-leading-normal:1.5;--sf-leading-relaxed:1.625;--sf-tracking-tight:-.025em;--sf-tracking-normal:0;--sf-tracking-wide:.025em;--sf-tracking-wider:.05em;--sf-tracking-widest:.1em;--sf-icon-xs:.875em;--sf-icon-s:1em;--sf-icon-m:1.5em;--sf-icon-l:2em;--sf-icon-xl:3em;--sf-space-none:0;--sf-space-px:1px;--sf-space-gutter:var(--sf-space-l);--sf-space-2xs:calc(clamp(.51rem, calc(.00488889 * (100vw - 22.5rem) + .51rem), .84rem) * var(--sf-space-scale));--sf-space-xs:calc(clamp(.64rem, calc(.00725926 * (100vw - 22.5rem) + .64rem), 1.13rem) * var(--sf-space-scale));--sf-space-s:calc(clamp(.8rem, calc(.0103704 * (100vw - 22.5rem) + .8rem), 1.5rem) * var(--sf-space-scale));--sf-space-m:calc(clamp(1rem, calc(.0148148 * (100vw - 22.5rem) + 1rem), 2rem) * var(--sf-space-scale));--sf-space-l:calc(clamp(1.25rem, calc(.021037 * (100vw - 22.5rem) + 1.25rem), 2.67rem) * var(--sf-space-scale));--sf-space-xl:calc(clamp(1.56rem, calc(.0294815 * (100vw - 22.5rem) + 1.56rem), 3.55rem) * var(--sf-space-scale));--sf-space-2xl:calc(clamp(1.95rem, calc(.0413333 * (100vw - 22.5rem) + 1.95rem), 4.74rem) * var(--sf-space-scale));--sf-space-3xl:calc(clamp(2.44rem, calc(.0573333 * (100vw - 22.5rem) + 2.44rem), 6.31rem) * var(--sf-space-scale));--sf-space-4xl:calc(clamp(3.05rem, calc(.0795556 * (100vw - 22.5rem) + 3.05rem), 8.42rem) * var(--sf-space-scale));--sf-size-xs:1.5rem;--sf-size-s:2rem;--sf-size-m:2.5rem;--sf-size-l:2.75rem;--sf-size-xl:3.5rem;--sf-container-narrow:38rem;--sf-container-prose:65ch;--sf-container-default:75rem;--sf-container-wide:90rem;--sf-container-full:100%;--sf-ratio-square:1;--sf-ratio-video:16 / 9;--sf-ratio-cinema:21 / 9;--sf-ratio-4-3:4 / 3;--sf-ratio-3-2:3 / 2;--sf-ratio-portrait:3 / 4;--sf-ratio-golden:1.618 / 1;--sf-border-width-hairline:.5px;--sf-border-width-1:1px;--sf-border-width-2:2px;--sf-border-width-3:3px;--sf-border-width-4:4px;--sf-border-style:solid;--sf-border-style-strong:solid;--sf-border-style-soft:dashed;--sf-border-style-dotted:dotted;--sf-divider-width:var(--sf-border-width-1);--sf-divider-style:solid;--sf-divider-color:var(--sf-color-border);--sf-radius-none:0;--sf-radius-xs:calc(2px * var(--sf-radius-scale));--sf-radius-s:calc(4px * var(--sf-radius-scale));--sf-radius-m:calc(8px * var(--sf-radius-scale));--sf-radius-l:calc(12px * var(--sf-radius-scale));--sf-radius-xl:calc(16px * var(--sf-radius-scale));--sf-radius-2xl:calc(24px * var(--sf-radius-scale));--sf-radius-3xl:calc(32px * var(--sf-radius-scale));--sf-radius-4xl:calc(48px * var(--sf-radius-scale));--sf-radius-full:9999px;--sf-radius-pill:var(--sf-radius-full);--sf-radius-outer:calc(var(--sf-radius-m) + var(--sf-component-pad));--sf-shadow-color:oklch(from var(--sf-color-neutral) .15 c h);--sf-shadow-none:none;--sf-shadow-xs:0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7));--sf-shadow-s:0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 2px 6px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, var(--sf-shadow-strength), .7));--sf-shadow-m:0 1px 3px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-shadow-l:0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 8px 24px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), .7)), 0 16px 48px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-shadow-xl:0 2px 8px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 12px 36px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3.5), .7)), 0 24px 72px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7));--sf-shadow-2xl:0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .6), .7)), 0 20px 60px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 4), .7)), 0 40px 100px -8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 5), .7));--sf-shadow-inner:inset 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-text-shadow-none:none;--sf-text-shadow-s:0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), .7));--sf-text-shadow-m:0 2px 4px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-text-shadow-l:0 4px 8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7));--sf-drop-shadow-s:drop-shadow(0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), .7)));--sf-drop-shadow-m:drop-shadow(0 4px 6px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7)));--sf-drop-shadow-l:drop-shadow(0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7)));--sf-blur-xs:4px;--sf-blur-s:8px;--sf-blur-m:16px;--sf-blur-l:32px;--sf-blur-xl:48px;--sf-perspective-near:500px;--sf-perspective-normal:1000px;--sf-perspective-far:2000px;--sf-opacity-0:0;--sf-opacity-10:.1;--sf-opacity-25:.25;--sf-opacity-50:.5;--sf-opacity-75:.75;--sf-opacity-100:1;--sf-duration-none:0s;--sf-duration-instant:calc(.1s * var(--sf-motion-scale));--sf-duration-fast:calc(.15s * var(--sf-motion-scale));--sf-duration-normal:calc(.25s * var(--sf-motion-scale));--sf-duration-slow:calc(.4s * var(--sf-motion-scale));--sf-duration-slower:calc(.6s * var(--sf-motion-scale));--sf-ease-linear:linear;--sf-ease-out:cubic-bezier(.25, 0, .15, 1);--sf-ease-in:cubic-bezier(.5, 0, .75, .25);--sf-ease-in-out:cubic-bezier(.4, 0, .2, 1);--sf-ease-spring:linear(0, .5, 1.1, .95, 1.02, 1);--sf-ease-elastic:linear(0, .3, 1.2, .9, 1.05, 1);--sf-ease-bounce:linear(0, .35 18%, 1 32%, .86 42%, 1.02 56%, .98 72%, 1);--sf-ease-overshoot:linear(0, .6 30%, 1.08 55%, .98 75%, 1);--sf-animation-fade-in:sf-fade-in var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-fade-out:sf-fade-out var(--sf-duration-normal) var(--sf-ease-in) both;--sf-animation-slide-in-up:sf-slide-in-up var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-down:sf-slide-in-down var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-left:sf-slide-in-left var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-right:sf-slide-in-right var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-scale-up:sf-scale-up var(--sf-duration-normal) var(--sf-ease-overshoot) both;--sf-animation-scale-down:sf-scale-down var(--sf-duration-normal) var(--sf-ease-in) both;--sf-animation-color-pulse:sf-color-pulse var(--sf-duration-slow) var(--sf-ease-in-out) infinite;--sf-animation-ping:sf-ping var(--sf-duration-slow) var(--sf-ease-out) infinite;--sf-animation-blink:sf-blink calc(1s * var(--sf-motion-scale)) steps(1, end) infinite;--sf-animation-float:sf-float calc(3s * var(--sf-motion-scale)) var(--sf-ease-in-out) infinite;--sf-animation-spin:sf-spin var(--sf-duration-slower) linear infinite;--sf-animation-shimmer:sf-shimmer calc(1.5s * var(--sf-motion-scale)) ease-in-out infinite;--sf-animation-delay-1:calc(75ms * var(--sf-motion-scale));--sf-animation-delay-2:calc(.15s * var(--sf-motion-scale));--sf-animation-delay-3:calc(.225s * var(--sf-motion-scale));--sf-animation-delay-4:calc(.3s * var(--sf-motion-scale));--sf-animation-delay-5:calc(.375s * var(--sf-motion-scale));--sf-scroll-timeline-range-start:0%;--sf-scroll-timeline-range-end:100%;--sf-mask-scrim-start:var(--sf-space-l);--sf-mask-scrim-end:var(--sf-space-l);--sf-z-below:-1;--sf-z-base:0;--sf-z-raised:1;--sf-z-low:10;--sf-z-mid:100;--sf-z-high:500;--sf-z-top:900;--sf-z-max:9999;--sf-header-height:8rem;--sf-sticky-offset:var(--sf-header-height);--sf-focus-ring-width:2px;--sf-focus-ring-offset:2px;--sf-focus-ring-style:solid;--sf-touch-target:var(--sf-size-l);--sf-contrast-bias:0;--sf-contrast-threshold:.6;--sf-safe-top:env(safe-area-inset-top,0px);--sf-safe-bottom:env(safe-area-inset-bottom,0px);--sf-safe-left:env(safe-area-inset-left,0px);--sf-safe-right:env(safe-area-inset-right,0px);--sf-print-page-margin:2cm;--sf-print-page-size:a4;--sf-print-base-size:11pt;--sf-stroke-thin:1px;--sf-stroke-regular:1.5px;--sf-stroke-bold:2px;--sf-stroke-heavy:3px;--sf-body-font-size:var(--sf-text-m);--sf-body-line-height:var(--sf-leading-normal);--sf-body-font-weight:var(--sf-font-weight-body);--sf-body-font-family:var(--sf-font-body);--sf-body-color:var(--sf-color-text);--sf-body-text-wrap:pretty;--sf-body-strong-weight:var(--sf-font-weight-bold);--sf-body-em-style:italic;--sf-code-font-size:.875em;--sf-heading-font-family:var(--sf-font-heading);--sf-heading-color:var(--sf-color-heading);--sf-heading-text-wrap:balance;--sf-h1-size:var(--sf-text-4xl);--sf-h1-line-height:var(--sf-leading-tight);--sf-h1-font-weight:var(--sf-font-weight-heading);--sf-h1-letter-spacing:var(--sf-tracking-tight);--sf-h2-size:var(--sf-text-3xl);--sf-h2-line-height:var(--sf-leading-tight);--sf-h2-font-weight:var(--sf-font-weight-heading);--sf-h2-letter-spacing:var(--sf-tracking-tight);--sf-h3-size:var(--sf-text-2xl);--sf-h3-line-height:var(--sf-leading-snug);--sf-h3-font-weight:var(--sf-font-weight-heading);--sf-h3-letter-spacing:var(--sf-tracking-normal);--sf-h4-size:var(--sf-text-xl);--sf-h4-line-height:var(--sf-leading-snug);--sf-h4-font-weight:var(--sf-font-weight-heading);--sf-h4-letter-spacing:var(--sf-tracking-normal);--sf-h5-size:var(--sf-text-l);--sf-h5-line-height:var(--sf-leading-normal);--sf-h5-font-weight:var(--sf-font-weight-heading);--sf-h5-letter-spacing:var(--sf-tracking-normal);--sf-h6-size:var(--sf-text-m);--sf-h6-line-height:var(--sf-leading-normal);--sf-h6-font-weight:var(--sf-font-weight-heading);--sf-h6-letter-spacing:var(--sf-tracking-wide);--sf-gap:var(--sf-space-m);--sf-content-gap:var(--sf-space-s);--sf-component-pad:var(--sf-space-m);--sf-field-block:var(--sf-space-l);--sf-field-required-marker:" *";--sf-link-external-marker:" ↗";--sf-section-pad:var(--sf-section-pad--m);--sf-section-pad--xs:var(--sf-space-xl);--sf-section-pad--s:var(--sf-space-2xl);--sf-section-pad--m:var(--sf-space-3xl);--sf-section-pad--l:var(--sf-space-4xl);--sf-section-pad--xl:calc(var(--sf-space-4xl) * 1.5);--sf-section-pad--2xl:calc(var(--sf-space-4xl) * 2);--sf-transition-all:all var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-colors:color var(--sf-duration-normal) var(--sf-ease-out), background-color var(--sf-duration-normal) var(--sf-ease-out), border-color var(--sf-duration-normal) var(--sf-ease-out), text-decoration-color var(--sf-duration-normal) var(--sf-ease-out), fill var(--sf-duration-normal) var(--sf-ease-out), stroke var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-transform:transform var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-opacity:opacity var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-shadow:box-shadow var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-fast:all var(--sf-duration-fast) var(--sf-ease-out);--sf-transition-slow:all var(--sf-duration-slow) var(--sf-ease-in-out);--sf-transition-enter:all var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-exit:all var(--sf-duration-fast) var(--sf-ease-in);--sf-space-gap:var(--sf-gap);--sf-space-content:var(--sf-content-gap);--sf-stack-gap:var(--sf-space-content);--sf-box-padding:var(--sf-space-m);--sf-box-border-width:0;--sf-box-border-color:var(--sf-color-border);--sf-center-max:var(--sf-container-default);--sf-center-gutter:var(--sf-space-gutter);--sf-cluster-gap:var(--sf-space-gap);--sf-cluster-align:center;--sf-cluster-justify:flex-start;--sf-sidebar-gap:var(--sf-space-gap);--sf-sidebar-min-width:50%;--sf-sidebar-width:18rem;--sf-switcher-threshold:30rem;--sf-switcher-gap:var(--sf-space-gap);--sf-grid-min:16rem;--sf-grid-gap:var(--sf-space-gap);--sf-grid-min-xs:10rem;--sf-grid-min-s:13rem;--sf-grid-min-m:16rem;--sf-grid-min-l:20rem;--sf-grid-min-xl:24rem;--sf-grid-min-2xl:28rem;--sf-cover-min-height:100dvh;--sf-cover-padding:var(--sf-section-pad);--sf-frame-ratio:16 / 9;--sf-icon-box-pad:.5em;--sf-icon-box-radius:var(--sf-radius-s);--sf-icon-box-bg:var(--sf-color-well);--sf-icon-box-border:var(--sf-border-width-1) solid var(--sf-color-border);--sf-reel-item-width:max-content;--sf-reel-gap:var(--sf-space-gap);--sf-reel-height:auto;--sf-imposter-margin:var(--sf-space-m);--sf-bento-cols-default:3;--sf-bento-row-default:10rem;--sf-bento-row-compact:6rem;--sf-bento-row-tall:16rem;--sf-bento-gap:var(--sf-space-gap);--sf-breakout-width:var(--sf-container-wide);--sf-content-width:var(--sf-container-default);--sf-prose-paragraph:var(--sf-space-content);--sf-alternate-gap:var(--sf-space-content);--sf-alternate-inner-gap:var(--sf-space-gap);--sf-flow-space:var(--sf-space-content);--sf-line-clamp:3;--sf-truncate-suffix:"…";--sf-aspect:16 / 9;--sf-scroll-shadow-size:2rem}*,:before,:after{box-sizing:border-box;margin:0;padding:0}html{text-size-adjust:100%;scroll-padding-top:var(--sf-header-height,3.5rem);scrollbar-gutter:stable;color-scheme:var(--sf-color-scheme,light dark);hanging-punctuation:first last}@supports (interpolate-size:allow-keywords){html{interpolate-size:allow-keywords}}body{min-height:100dvh;min-inline-size:320px}menu,ul,ol{list-style:none}img,picture,video,canvas,svg{max-width:100%;height:auto;display:block}iframe,embed,object{max-width:100%;height:unset;border:none;display:block}button,input,select,textarea{font:inherit;color:inherit;letter-spacing:inherit;color-scheme:inherit;line-height:inherit}button{text-transform:none}search{display:block}fieldset{border:none;min-width:0;padding:0}legend{padding:0}a,button,input,select,textarea,summary{touch-action:manipulation}summary{cursor:pointer;list-style:none}textarea{resize:vertical}button{cursor:pointer}table{border-collapse:collapse;border-spacing:0}[hidden]{display:none!important}dialog,[popover]{color:inherit;background:0 0;border:none;padding:0}[inert]{cursor:default}body{font-family:var(--sf-body-font-family,var(--sf-font-body));font-size:var(--sf-body-font-size,var(--sf-text-m));line-height:var(--sf-body-line-height,var(--sf-leading-normal));font-weight:var(--sf-body-font-weight,var(--sf-font-weight-body));color:var(--sf-body-color,var(--sf-color-text));background-color:var(--sf-color-bg,#fff);font-synthesis:none}h1,h2,h3,h4,h5,h6{font-family:var(--sf-heading-font-family,var(--sf-font-heading));line-height:var(--sf-leading-tight);color:var(--sf-heading-color,var(--sf-color-heading));text-wrap:var(--sf-heading-text-wrap);overflow-wrap:break-word;text-rendering:optimizelegibility;scroll-margin-top:calc(var(--sf-header-height,3.5rem) + var(--sf-space-m))}h1{font-size:var(--sf-h1-size);line-height:var(--sf-h1-line-height);font-weight:var(--sf-h1-font-weight);letter-spacing:var(--sf-h1-letter-spacing)}h2{font-size:var(--sf-h2-size);line-height:var(--sf-h2-line-height);font-weight:var(--sf-h2-font-weight);letter-spacing:var(--sf-h2-letter-spacing)}h3{font-size:var(--sf-h3-size);line-height:var(--sf-h3-line-height);font-weight:var(--sf-h3-font-weight);letter-spacing:var(--sf-h3-letter-spacing)}h4{font-size:var(--sf-h4-size);line-height:var(--sf-h4-line-height);font-weight:var(--sf-h4-font-weight);letter-spacing:var(--sf-h4-letter-spacing)}h5{font-size:var(--sf-h5-size);line-height:var(--sf-h5-line-height);font-weight:var(--sf-h5-font-weight);letter-spacing:var(--sf-h5-letter-spacing)}h6{font-size:var(--sf-h6-size);line-height:var(--sf-h6-line-height);font-weight:var(--sf-h6-font-weight);letter-spacing:var(--sf-h6-letter-spacing)}p{text-wrap:var(--sf-body-text-wrap);overflow-wrap:break-word;orphans:2;widows:2}b,strong{font-weight:var(--sf-body-strong-weight)}em{font-style:var(--sf-body-em-style)}small{font-size:var(--sf-text-s,80%)}mark{background-color:var(--sf-color-mark-bg,yellow);color:var(--sf-color-mark-text,black);border-radius:var(--sf-radius-xs);padding-inline:.2em}abbr[title]{cursor:help;text-decoration:underline dotted}kbd,samp{font-family:var(--sf-font-mono);font-size:inherit}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sup{top:-.5em}sub{bottom:-.25em}a:link{color:var(--sf-color-link);text-decoration-color:var(--sf-color-link--underline);text-underline-offset:.15em;text-underline-position:from-font;transition:color var(--sf-duration-fast) var(--sf-ease-out), text-decoration-color var(--sf-duration-fast) var(--sf-ease-out)}a:hover{color:var(--sf-color-link--hover);text-decoration-color:currentColor}a:visited{color:var(--sf-color-link--visited)}a:active{color:var(--sf-color-link--active)}a:not([class]){text-decoration-skip-ink:auto}a,code{overflow-wrap:break-word}code{font-family:var(--sf-font-mono);font-size:var(--sf-code-font-size);border-radius:var(--sf-radius-xs);background-color:var(--sf-color-code-bg);color:var(--sf-color-code-text,inherit);padding:.125em .3em}pre{font-family:var(--sf-font-mono);font-size:var(--sf-text-s);padding:var(--sf-space-m);border-radius:var(--sf-radius-m);background:var(--sf-color-code-block-bg,var(--sf-color-code-bg));color:var(--sf-color-code-block-text,inherit);tab-size:2;hyphens:none;overflow-x:auto}pre code{color:inherit;font-size:inherit;background:0 0;border:0;border-radius:0;padding:0}svg{fill:currentColor}hr{border:none;border-block-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color)}:target{scroll-margin-top:calc(var(--sf-header-height,3.5rem) + var(--sf-space-m))}::selection{background:var(--sf-color-selection-bg);color:var(--sf-color-selection-text);text-shadow:none}th,caption{text-align:start}address,cite,dfn,var{font-style:normal}input,textarea{caret-color:var(--sf-caret-color)}input,progress{accent-color:var(--sf-color-action)}::backdrop{background-color:var(--sf-color-dim)}optgroup{font:inherit}::file-selector-button{font:inherit}::placeholder{opacity:1;color:var(--sf-color-text--placeholder,currentcolor)}blockquote{border-inline-start:var(--sf-border-width-3) solid var(--sf-color-border);padding-inline-start:var(--sf-space-m);font-style:italic}dt{font-weight:var(--sf-font-weight-heading)}dd{margin-inline-start:var(--sf-space-m)}table{text-align:start;width:100%}th{font-weight:var(--sf-font-weight-heading)}td,th{padding:var(--sf-space-2xs) var(--sf-space-xs);border-block-end:var(--sf-border-width-hairline) solid var(--sf-color-border--subtle)}@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--sf-is-dark:1}}[data-theme=light]{color-scheme:light;--sf-is-dark:0}[data-theme=dark]{color-scheme:dark;--sf-is-dark:1}:root[data-lumlocker]{--sf-color-primary:light-dark(oklch(from var(--sf-color-primary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-primary-dark,var(--sf-color-primary-light)) var(--sf-lumlocker) c h));--sf-color-secondary:light-dark(oklch(from var(--sf-color-secondary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-secondary-dark,var(--sf-color-secondary-light)) var(--sf-lumlocker) c h));--sf-color-tertiary:light-dark(oklch(from var(--sf-color-tertiary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-tertiary-dark,var(--sf-color-tertiary-light)) var(--sf-lumlocker) c h));--sf-color-action:light-dark(oklch(from var(--sf-color-action-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-action-dark,var(--sf-color-action-light)) var(--sf-lumlocker) c h));--sf-color-neutral:light-dark(oklch(from var(--sf-color-neutral-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-neutral-dark,var(--sf-color-neutral-light)) var(--sf-lumlocker) c h))}.sf-section{padding-block:var(--sf-section-pad)}.sf-section--xs{padding-block:var(--sf-section-pad--xs)}.sf-section--s{padding-block:var(--sf-section-pad--s)}.sf-section--m{padding-block:var(--sf-section-pad--m)}.sf-section--l{padding-block:var(--sf-section-pad--l)}.sf-section--xl{padding-block:var(--sf-section-pad--xl)}.sf-section--2xl{padding-block:var(--sf-section-pad--2xl)}.sf-section-group>.sf-section+.sf-section{padding-block-start:0}.sf-section--collapse:has(+.sf-section--collapse){padding-block-end:calc(var(--sf-section-pad) / 2)}.sf-section--collapse+.sf-section--collapse{padding-block-start:calc(var(--sf-section-pad) / 2)}.sf-divider{border:none;border-block-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);margin-block:var(--sf-space-m)}.sf-divider--vertical{border-block-start:none;border-inline-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);margin-block:0;margin-inline:var(--sf-space-m);align-self:stretch}.sf-container{width:100%;max-width:var(--sf-container-default);padding-inline:var(--sf-space-gutter);margin-inline:auto;container:sf-layout/inline-size}.sf-container--narrow{max-width:var(--sf-container-narrow)}.sf-container--prose{max-width:var(--sf-container-prose)}.sf-container--wide{max-width:var(--sf-container-wide)}.sf-container--full{max-width:var(--sf-container-full)}.sf-stack{gap:var(--sf-stack-gap);flex-direction:column;display:flex}.sf-stack--xs{gap:var(--sf-space-xs)}.sf-stack--s{gap:var(--sf-space-s)}.sf-stack--m{gap:var(--sf-space-m)}.sf-stack--l{gap:var(--sf-space-l)}.sf-stack--xl{gap:var(--sf-space-xl)}.sf-stack--2xl{gap:var(--sf-space-2xl)}.sf-stack--center{align-items:center}.sf-stack--end{align-items:flex-end}.sf-stack--stretch{align-items:stretch}.sf-box{padding:var(--sf-box-padding);outline:var(--sf-box-border-width) solid var(--sf-box-border-color)}.sf-center{box-sizing:content-box;max-inline-size:var(--sf-center-max);padding-inline:var(--sf-center-gutter);margin-inline:auto}.sf-center--intrinsic{flex-direction:column;align-items:center;display:flex}.sf-cluster{gap:var(--sf-cluster-gap);align-items:var(--sf-cluster-align);justify-content:var(--sf-cluster-justify);flex-wrap:wrap;display:flex}.sf-cluster--xs{gap:var(--sf-space-xs)}.sf-cluster--s{gap:var(--sf-space-s)}.sf-cluster--m{gap:var(--sf-space-m)}.sf-cluster--l{gap:var(--sf-space-l)}.sf-cluster--xl{gap:var(--sf-space-xl)}.sf-cluster--2xl{gap:var(--sf-space-2xl)}.sf-cluster--no-wrap{flex-wrap:nowrap}.sf-cluster--center{justify-content:center}.sf-cluster--end{justify-content:flex-end}.sf-cluster--between{justify-content:space-between}.sf-sidebar{gap:var(--sf-sidebar-gap);flex-wrap:wrap;display:flex}.sf-sidebar>:first-child{flex-basis:var(--sf-sidebar-width);flex-grow:1}.sf-sidebar>:last-child,.sf-sidebar--right>:first-child{min-inline-size:var(--sf-sidebar-min-width);flex-grow:999;flex-basis:0}.sf-sidebar--right>:last-child{flex-basis:var(--sf-sidebar-width);flex-grow:1}.sf-sidebar--narrow{--sf-sidebar-width:12rem}.sf-sidebar--wide{--sf-sidebar-width:26rem}.sf-switcher{gap:var(--sf-switcher-gap);flex-wrap:wrap;display:flex}.sf-switcher>*{flex-grow:1;flex-basis:calc((var(--sf-switcher-threshold) - 100%) * 999)}.sf-switcher--no-wrap{flex-wrap:nowrap;overflow-x:auto}.sf-switcher--vertical{flex-direction:column}.sf-grid{grid-template-columns:repeat(auto-fill, minmax(min(var(--sf-grid-min), 100%), 1fr));gap:var(--sf-grid-gap);display:grid}.sf-grid--fit{grid-template-columns:repeat(auto-fit, minmax(min(var(--sf-grid-min), 100%), 1fr))}.sf-grid--xs{--sf-grid-min:var(--sf-grid-min-xs)}.sf-grid--s{--sf-grid-min:var(--sf-grid-min-s)}.sf-grid--m{--sf-grid-min:var(--sf-grid-min-m)}.sf-grid--l{--sf-grid-min:var(--sf-grid-min-l)}.sf-grid--xl{--sf-grid-min:var(--sf-grid-min-xl)}.sf-grid--2xl{--sf-grid-min:var(--sf-grid-min-2xl)}.sf-grid--dense{grid-auto-flow:dense}.sf-icon{inline-size:var(--sf-icon-size,var(--sf-icon-m));block-size:var(--sf-icon-size,var(--sf-icon-m));vertical-align:text-bottom;fill:currentColor;flex-shrink:0;display:inline-block}.sf-icon--xs{--sf-icon-size:var(--sf-icon-xs)}.sf-icon--s{--sf-icon-size:var(--sf-icon-s)}.sf-icon--m{--sf-icon-size:var(--sf-icon-m)}.sf-icon--l{--sf-icon-size:var(--sf-icon-l)}.sf-icon--xl{--sf-icon-size:var(--sf-icon-xl)}.sf-icon--boxed{inline-size:calc(var(--sf-icon-size,var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);block-size:calc(var(--sf-icon-size,var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);padding:var(--sf-icon-box-pad);border:var(--sf-icon-box-border);border-radius:var(--sf-icon-box-radius);background:var(--sf-icon-box-bg);box-sizing:content-box}.sf-cover{min-height:var(--sf-cover-min-height);padding-block:var(--sf-cover-padding);flex-direction:column;display:flex}.sf-cover>.sf-cover__center{margin-block:auto}.sf-cover--min{min-height:50dvh}.sf-cover--max{min-height:0;max-height:100dvh}.sf-cover--padding-s{padding-block:var(--sf-space-xl)}.sf-cover--padding-l{padding-block:var(--sf-space-4xl)}.sf-frame{aspect-ratio:var(--sf-frame-ratio);justify-content:center;align-items:center;display:flex;overflow:hidden}.sf-frame>img,.sf-frame>video{object-fit:cover;width:100%;height:100%}.sf-frame--square{aspect-ratio:var(--sf-ratio-square)}.sf-frame--video{aspect-ratio:var(--sf-ratio-video)}.sf-frame--cinema{aspect-ratio:var(--sf-ratio-cinema)}.sf-frame--portrait{aspect-ratio:var(--sf-ratio-portrait)}.sf-frame--4-3{aspect-ratio:var(--sf-ratio-4-3)}.sf-frame--3-2{aspect-ratio:var(--sf-ratio-3-2)}.sf-frame--golden{aspect-ratio:var(--sf-ratio-golden)}.sf-reel{block-size:var(--sf-reel-height);gap:var(--sf-reel-gap);scroll-snap-type:x mandatory;scroll-padding-inline:var(--sf-reel-gap);overscroll-behavior-x:contain;scrollbar-width:thin;display:flex;overflow-x:auto}.sf-reel>*{flex:0 0 var(--sf-reel-item-width);scroll-snap-align:start}.sf-imposter{position:absolute;inset-block-start:50%;inset-inline-start:50%;translate:-50% -50%}.sf-imposter--fixed{position:fixed}.sf-imposter--contain{max-inline-size:calc(100% - var(--sf-imposter-margin) * 2);max-block-size:calc(100% - var(--sf-imposter-margin) * 2);overflow:auto}.sf-alternate{gap:var(--sf-alternate-gap);display:grid;container:sf-alternate/inline-size}.sf-alternate>*{gap:var(--sf-alternate-inner-gap);align-items:center;display:grid}@container sf-alternate (width>=48em){.sf-alternate>*{grid-template-columns:1fr 1fr}.sf-alternate>:nth-child(2n)>:first-child{order:2}.sf-alternate>:nth-child(2n)>:last-child{order:1}}.sf-pancake{grid-template-rows:auto 1fr auto;min-height:100dvh;display:grid}.sf-grid-1,.sf-grid-2,.sf-grid-3,.sf-grid-4,.sf-grid-6{gap:var(--sf-grid-gap);display:grid}@container (width>=30em){.sf-grid-4{grid-template-columns:repeat(2,1fr)}.sf-grid-6{grid-template-columns:repeat(3,1fr)}}@container (width>=48em){.sf-grid-2{grid-template-columns:repeat(2,1fr)}.sf-grid-3{grid-template-columns:repeat(3,1fr)}.sf-grid-4{grid-template-columns:repeat(4,1fr)}.sf-grid-6{grid-template-columns:repeat(6,1fr)}}.sf-grid-1-2,.sf-grid-2-1,.sf-grid-1-3,.sf-grid-3-1{gap:var(--sf-grid-gap);display:grid}@container (width>=48em){.sf-grid-1-2{grid-template-columns:1fr 2fr}.sf-grid-2-1{grid-template-columns:2fr 1fr}.sf-grid-1-3{grid-template-columns:1fr 3fr}.sf-grid-3-1{grid-template-columns:3fr 1fr}}.sf-content-grid{grid-template-columns:[full-start] minmax(var(--sf-space-gutter), 1fr) [breakout-start] minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2)) [content-start] min(var(--sf-content-width), 100% - var(--sf-space-gutter) * 2) [content-end] minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2)) [breakout-end] minmax(var(--sf-space-gutter), 1fr) [full-end];display:grid}.sf-content-grid>*{grid-column:content}.sf-content-grid>.sf-breakout{grid-column:breakout}.sf-content-grid>.sf-full-bleed{grid-column:full}.sf-bento{grid-template-columns:repeat(var(--sf-bento-cols,var(--sf-bento-cols-default)), 1fr);grid-auto-rows:minmax(var(--sf-bento-row,var(--sf-bento-row-default)), auto);grid-auto-flow:dense;gap:var(--sf-bento-gap);display:grid}@container (width>=30em) and (width<=47.99em){.sf-bento{grid-template-columns:repeat(2,1fr)}}.sf-bento--2{--sf-bento-cols:2}.sf-bento--4{--sf-bento-cols:4}.sf-bento--compact{--sf-bento-row:var(--sf-bento-row-compact)}.sf-bento--tall{--sf-bento-row:var(--sf-bento-row-tall)}.sf-subgrid{grid-template-columns:subgrid;display:grid}.sf-subgrid-rows{grid-template-rows:subgrid;display:grid}.sf-prose{max-width:var(--sf-container-prose);overflow-wrap:break-word}.sf-prose>*+*{margin-block-start:var(--sf-prose-paragraph)}.sf-prose :is(h2,h3,h4)+*{margin-block-start:var(--sf-space-s)}.sf-prose li+li{margin-block-start:var(--sf-space-xs)}.sf-prose :is(blockquote,pre){margin-block:var(--sf-space-m)}.sf-prose img{margin-block:var(--sf-space-m);border-radius:var(--sf-radius-m)}.sf-prose :is(ul,ol){padding-inline-start:1.25em}.sf-prose ul{list-style:outside}.sf-prose ol{list-style:decimal}.sf-prose ::marker{color:var(--sf-color-primary)}.sf-prose figure{margin-block:var(--sf-space-l)}.sf-prose figcaption{font-size:var(--sf-text-s);color:var(--sf-color-text--muted);margin-block-start:var(--sf-space-xs)}.sf-prose table{margin-block:var(--sf-space-m);max-width:100%;display:block;overflow-x:auto}.sf-prose video{margin-block:var(--sf-space-m);border-radius:var(--sf-radius-m)}.sf-prose .sf-not-prose>*+*,.sf-prose .sf-not-prose li+li{margin-block-start:0}.sf-prose .sf-not-prose :is(ul,ol){list-style:revert;padding-inline-start:revert}.sf-prose .sf-not-prose ::marker{color:inherit}.sf-prose .sf-not-prose img{border-radius:0;margin-block:0}.sf-prose .sf-not-prose figure{margin-block:0}.sf-prose .sf-not-prose figcaption{font-size:inherit;color:inherit;margin-block-start:0}.sf-prose .sf-not-prose table{display:revert;overflow-x:revert;max-width:revert;margin-block:0}.sf-prose .sf-not-prose video{border-radius:0;margin-block:0}.sf-flow>*+*{margin-block-start:var(--sf-flow-space)}.sf-truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.sf-line-clamp-2,.sf-line-clamp-3,.sf-line-clamp-N{-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.sf-line-clamp-2{-webkit-line-clamp:2;line-clamp:2}.sf-line-clamp-3{-webkit-line-clamp:3;line-clamp:3}.sf-line-clamp-N{-webkit-line-clamp:var(--sf-line-clamp);line-clamp:var(--sf-line-clamp)}.sf-equal-height{align-items:stretch;display:flex}.sf-equal-height>*{flex:auto;block-size:auto}.sf-aspect{aspect-ratio:var(--sf-aspect)}.sf-scroll-shadow{mask-image:linear-gradient(to bottom, transparent 0, black var(--sf-scroll-shadow-size), black calc(100% - var(--sf-scroll-shadow-size)), transparent 100%);overflow-y:auto}.sf-scroll-snap{scroll-snap-type:y mandatory;overscroll-behavior-y:contain;overflow-y:auto}.sf-scroll-snap>*{scroll-snap-align:start}.sf-overflow-fade{mask-image:linear-gradient(to right, black 0, black calc(100% - var(--sf-scroll-shadow-size)), transparent 100%);overflow:hidden}.sf-no-tap-highlight{-webkit-tap-highlight-color:transparent}.sf-surface--primary{background:var(--sf-color-primary);color:var(--sf-color-text--on-primary)}.sf-surface--secondary{background:var(--sf-color-secondary);color:var(--sf-color-text--on-secondary)}.sf-surface--tertiary{background:var(--sf-color-tertiary);color:var(--sf-color-text--on-tertiary)}.sf-surface--action{background:var(--sf-color-action);color:var(--sf-color-text--on-action)}.sf-surface--neutral{background:var(--sf-color-neutral);color:var(--sf-color-text--on-neutral)}.sf-surface--inverse{background:var(--sf-color-inverse);color:var(--sf-color-text--on-inverse)}.sf-surface--success{background:var(--sf-color-success);color:var(--sf-color-text--on-success)}.sf-surface--warning{background:var(--sf-color-warning);color:var(--sf-color-text--on-warning)}.sf-surface--error{background:var(--sf-color-error);color:var(--sf-color-text--on-error)}.sf-surface--info{background:var(--sf-color-info);color:var(--sf-color-text--on-info)}.sf-surface--danger{background:var(--sf-color-danger);color:var(--sf-color-text--on-danger)}.sf-text-gradient{background-image:var(--sf-gradient-primary);-webkit-background-clip:text;background-clip:text}@supports (background-clip:text){.sf-text-gradient{color:#0000}}.sf-link-external:after{content:var(--sf-link-external-marker);font-size:.85em;text-decoration:none;display:inline-block}.is-hidden{display:none!important}.is-invisible{visibility:hidden}.is-visible{visibility:visible}.is-disabled{opacity:var(--sf-opacity-disabled);pointer-events:none;cursor:not-allowed;user-select:none}.is-readonly{pointer-events:none;user-select:none}.is-loading{pointer-events:none;position:relative;color:#0000!important}.is-loading:after{content:"";border:var(--sf-border-width-2) solid var(--sf-color-border--strong);border-block-start-color:var(--sf-color-action);border-radius:var(--sf-radius-full);width:1em;height:1em;animation:var(--sf-animation-spin);margin-block-start:-.5em;margin-inline-start:-.5em;position:absolute;inset-block-start:50%;inset-inline-start:50%}.is-busy{cursor:progress}.is-pending{opacity:var(--sf-state-pending-opacity);cursor:progress}.is-skeleton{background:linear-gradient(90deg, var(--sf-color-well) 25%, var(--sf-color-raised) 50%, var(--sf-color-well) 75%);animation:var(--sf-animation-shimmer);border-radius:var(--sf-radius-s);pointer-events:none;user-select:none;background-size:200% 100%;color:#0000!important}.is-active{--sf-is-active:1}.is-selected{background-color:var(--sf-color-bg--selected)}.is-current{--sf-is-current:1;font-weight:var(--sf-current-font-weight,var(--sf-font-weight-bold))}.is-highlighted{background-color:var(--sf-color-bg--focus)}.is-pressed{--sf-is-pressed:1;background-color:var(--sf-color-bg--selected)}.is-open{--sf-is-open:1}.is-collapsed{--sf-is-open:0}.is-expanded{--sf-is-open:1}.is-valid{--sf-field-border-color:var(--sf-color-success);--sf-field-text-color:var(--sf-color-success-strong)}.is-invalid{--sf-field-border-color:var(--sf-color-error);--sf-field-text-color:var(--sf-color-error-strong)}.is-warning{--sf-field-border-color:var(--sf-color-warning);--sf-field-text-color:var(--sf-color-warning-strong)}.is-success{--sf-field-border-color:var(--sf-color-success);--sf-field-text-color:var(--sf-color-success-strong)}.is-error{--sf-field-border-color:var(--sf-color-error);--sf-field-text-color:var(--sf-color-error-strong)}.is-info{--sf-field-border-color:var(--sf-color-info);--sf-field-text-color:var(--sf-color-info-strong)}.is-danger{--sf-field-border-color:var(--sf-color-danger);--sf-field-text-color:var(--sf-color-danger-strong)}.is-sticky{top:var(--sf-sticky-offset,0);z-index:var(--sf-z-low);position:sticky}.is-pinned{z-index:var(--sf-z-mid);position:sticky;top:0}.is-fixed{position:fixed}.is-fullscreen{z-index:var(--sf-z-max);position:fixed;inset:0}.is-resizable{resize:both;overflow:auto}.is-clipped{overflow:hidden!important}.is-scrollable{overscroll-behavior:contain;overflow:auto}.is-truncated{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.is-dragging{opacity:var(--sf-opacity-50);cursor:grabbing}.is-drop-target{outline:var(--sf-focus-ring-width) dashed var(--sf-color-action);outline-offset:var(--sf-focus-ring-offset)}.is-draggable{cursor:grab}.is-overlay{position:absolute;inset:0}.is-clickable{cursor:pointer}.is-unselectable{user-select:none}.is-focused{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-focus-ring-color);outline-offset:var(--sf-focus-ring-offset)}.is-empty:empty{display:none}@media (prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}html:focus-within{scroll-behavior:auto}a,button,input,select,textarea,summary{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-duration:var(--sf-duration-fast);transition-timing-function:var(--sf-ease-out)}@supports (view-transition-name:none){::view-transition-old(root),::view-transition-new(root){animation-duration:var(--sf-duration-normal);animation-timing-function:var(--sf-ease-out)}}.sf-fade-in{animation:var(--sf-animation-fade-in)}.sf-fade-out{animation:var(--sf-animation-fade-out)}.sf-slide-in-up{animation:var(--sf-animation-slide-in-up)}.sf-slide-in-down{animation:var(--sf-animation-slide-in-down)}.sf-slide-in-left{animation:var(--sf-animation-slide-in-left)}.sf-slide-in-right{animation:var(--sf-animation-slide-in-right)}.sf-scale-up{animation:var(--sf-animation-scale-up)}.sf-scale-down{animation:var(--sf-animation-scale-down)}.sf-entrance--fade,.sf-entrance--fade-up,.sf-entrance--fade-down,.sf-entrance--fade-left,.sf-entrance--fade-right,.sf-entrance--scale-up{animation-duration:var(--sf-duration-slow);animation-timing-function:var(--sf-ease-out);animation-fill-mode:both}@supports (animation-timeline:view()){.sf-entrance--fade,.sf-entrance--fade-up,.sf-entrance--fade-down,.sf-entrance--fade-left,.sf-entrance--fade-right,.sf-entrance--scale-up{animation-timeline:view();animation-range:var(--sf-scroll-timeline-range-start,entry 0%) var(--sf-scroll-timeline-range-end,cover 30%)}}.sf-entrance--fade{animation-name:sf-fade-in}.sf-entrance--fade-up{animation-name:sf-slide-in-up}.sf-entrance--fade-down{animation-name:sf-slide-in-down}.sf-entrance--fade-left{animation-name:sf-slide-in-left}.sf-entrance--fade-right{animation-name:sf-slide-in-right}.sf-entrance--scale-up{animation-name:sf-scale-up}}@keyframes sf-fade-in{0%{opacity:0}to{opacity:1}}@keyframes sf-fade-out{0%{opacity:1}to{opacity:0}}@keyframes sf-slide-in-up{0%{opacity:0;translate:0 1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-down{0%{opacity:0;translate:0 -1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-left{0%{opacity:0;translate:1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-right{0%{opacity:0;translate:-1rem}to{opacity:1;translate:0}}@keyframes sf-scale-up{0%{opacity:0;scale:.92}to{opacity:1;scale:1}}@keyframes sf-scale-down{0%{opacity:1;scale:1}to{opacity:0;scale:.92}}@keyframes sf-spin{to{rotate:360deg}}@keyframes sf-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@keyframes sf-ping{75%,to{opacity:0;scale:2}}@keyframes sf-blink{0%,50%{opacity:1}50.01%,to{opacity:0}}@keyframes sf-float{0%,to{translate:0}50%{translate:0 -.5rem}}@keyframes sf-color-pulse{0%{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) l c h)}50%{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) calc(l + .25) c h)}to{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) l c h)}}@media (prefers-reduced-motion:no-preference){.sf-color-pulse{animation:var(--sf-animation-color-pulse)}}:focus:not(:focus-visible){outline:none}:focus-visible{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-focus-ring-color)!important;outline-offset:var(--sf-focus-ring-offset)!important}@media (prefers-reduced-motion:reduce){:root{--sf-duration-instant:.01ms;--sf-duration-fast:.01ms;--sf-duration-normal:.01ms;--sf-duration-slow:.01ms;--sf-duration-slower:.01ms}*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.no-motion,.no-motion *,.no-motion :before,.no-motion :after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}@media (prefers-contrast:more){:root{--sf-focus-ring-width:3px;--sf-contrast-bias:.06}hr{border-block-start-width:var(--sf-border-width-2)}}@media (prefers-reduced-transparency:reduce){::backdrop{background-color:var(--sf-color-neutral);opacity:1}}@media (pointer:coarse){button,input[type=button],input[type=submit],input[type=reset],select,summary,a{min-height:var(--sf-touch-target);min-width:var(--sf-touch-target)}}[disabled],[aria-disabled=true]{cursor:not-allowed}.sr-only,.visually-hidden,.sr-only-focusable:not(:focus,:focus-within){clip-path:inset(50%)!important;white-space:nowrap!important;border:0!important;width:1px!important;height:1px!important;margin:-1px!important;padding:0!important;position:absolute!important;overflow:clip!important}.skip-link{top:-100%;left:var(--sf-space-m);z-index:var(--sf-z-max);padding:var(--sf-space-s) var(--sf-space-m);background:var(--sf-color-bg);color:var(--sf-color-text);border:var(--sf-border-width-1) solid var(--sf-color-border);border-radius:var(--sf-radius-m);transition:top var(--sf-duration-fast) var(--sf-ease-out);position:absolute}.skip-link:focus{top:var(--sf-space-m)}.focus-parent:focus-within{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}.sf-clickable-parent{--sf-clickable-overlay-z:1;cursor:pointer;position:relative}.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):after,.sf-clickable-parent [data-overlay-link]:after{content:"";z-index:var(--sf-clickable-overlay-z);pointer-events:auto;position:absolute;inset:0}.sf-clickable-parent :is(button,input,textarea,select,summary,video,audio,[role=button],[data-no-overlay]),.sf-clickable-parent:has([data-overlay-link]) a:where(:any-link):not([data-overlay-link]),.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):not(:first-of-type){z-index:calc(var(--sf-clickable-overlay-z) + 1);position:relative}.sf-clickable-parent:focus-within{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}@media (forced-colors:active){:root{--sf-focus-ring-color:Highlight}:focus-visible{outline-color:highlight!important}:where(*){box-shadow:none;text-shadow:none}input,select,textarea{border:var(--sf-border-width-1) solid}}.print-only{display:none}@media print{@page{size:var(--sf-print-page-size,a4);margin:var(--sf-print-page-margin,2cm)}body{font-size:var(--sf-print-base-size)}*,:before,:after{box-shadow:none;text-shadow:none}a[href]:after{content:" (" attr(href) ")";word-break:break-all;font-size:90%}a[href^=\#]:after,a[href^=javascript\: i]:after{content:""}abbr[title]:after{content:" (" attr(title) ")"}img,svg,video,canvas,figure,table,pre,blockquote{break-inside:avoid}thead{display:table-header-group}tr{break-inside:avoid}h1,h2,h3,h4,h5,h6{break-after:avoid;orphans:3;widows:3}p{orphans:3;widows:3}pre,blockquote{border:var(--sf-border-width-1) solid var(--sf-color-border--strong);padding:var(--sf-space-s)}nav,aside,button,input,select,textarea,audio,video,dialog,[popover],.no-print{display:none!important}.print-only{display:revert!important}details{display:block}details>summary{display:none!important}details:not([open])>:not(summary){display:block}.print-color-exact,.print-color-exact *{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}.print-no-color,.print-no-color *,.print-no-color :before,.print-no-color :after{color:canvastext!important;background:0 0!important}}:root{--sf-color-primary-50:color-mix(in oklab, var(--sf-color-primary) 4%, var(--sf-color-base));--sf-color-primary-100:color-mix(in oklab, var(--sf-color-primary) 8%, var(--sf-color-base));--sf-color-primary-200:color-mix(in oklab, var(--sf-color-primary) 20%, var(--sf-color-base));--sf-color-primary-300:color-mix(in oklab, var(--sf-color-primary) 40%, var(--sf-color-base));--sf-color-primary-400:color-mix(in oklab, var(--sf-color-primary) 65%, var(--sf-color-base));--sf-color-primary-500:var(--sf-color-primary);--sf-color-primary-600:color-mix(in oklab, var(--sf-color-primary) 82%, var(--sf-color-text));--sf-color-primary-700:color-mix(in oklab, var(--sf-color-primary) 62%, var(--sf-color-text));--sf-color-primary-800:color-mix(in oklab, var(--sf-color-primary) 38%, var(--sf-color-text));--sf-color-primary-900:color-mix(in oklab, var(--sf-color-primary) 18%, var(--sf-color-text));--sf-color-primary-950:color-mix(in oklab, var(--sf-color-primary) 8%, var(--sf-color-text));--sf-color-primary-a5:color-mix(in oklab, var(--sf-color-primary) 5%, transparent);--sf-color-primary-a10:color-mix(in oklab, var(--sf-color-primary) 10%, transparent);--sf-color-primary-a20:color-mix(in oklab, var(--sf-color-primary) 20%, transparent);--sf-color-primary-a30:color-mix(in oklab, var(--sf-color-primary) 30%, transparent);--sf-color-primary-a40:color-mix(in oklab, var(--sf-color-primary) 40%, transparent);--sf-color-primary-a50:color-mix(in oklab, var(--sf-color-primary) 50%, transparent);--sf-color-primary-a60:color-mix(in oklab, var(--sf-color-primary) 60%, transparent);--sf-color-primary-a70:color-mix(in oklab, var(--sf-color-primary) 70%, transparent);--sf-color-primary-a80:color-mix(in oklab, var(--sf-color-primary) 80%, transparent);--sf-color-primary-a90:color-mix(in oklab, var(--sf-color-primary) 90%, transparent);--sf-color-primary-a95:color-mix(in oklab, var(--sf-color-primary) 95%, transparent);--sf-color-primary-superlight:var(--sf-color-primary-50);--sf-color-primary-xlight:var(--sf-color-primary-200);--sf-color-primary-lighter:var(--sf-color-primary-400);--sf-color-primary-darker:var(--sf-color-primary-600);--sf-color-primary-xdark:var(--sf-color-primary-800);--sf-color-primary-superdark:var(--sf-color-primary-950);--sf-color-primary-hover:var(--sf-color-primary-darker);--sf-color-primary-active:var(--sf-color-primary-xdark);--sf-color-primary-subtle:var(--sf-color-primary-a10);--sf-color-primary-muted:var(--sf-color-primary-a30);--sf-color-primary-ghost:var(--sf-color-primary-a5);--sf-color-secondary-50:color-mix(in oklab, var(--sf-color-secondary) 4%, var(--sf-color-base));--sf-color-secondary-100:color-mix(in oklab, var(--sf-color-secondary) 8%, var(--sf-color-base));--sf-color-secondary-200:color-mix(in oklab, var(--sf-color-secondary) 20%, var(--sf-color-base));--sf-color-secondary-300:color-mix(in oklab, var(--sf-color-secondary) 40%, var(--sf-color-base));--sf-color-secondary-400:color-mix(in oklab, var(--sf-color-secondary) 65%, var(--sf-color-base));--sf-color-secondary-500:var(--sf-color-secondary);--sf-color-secondary-600:color-mix(in oklab, var(--sf-color-secondary) 82%, var(--sf-color-text));--sf-color-secondary-700:color-mix(in oklab, var(--sf-color-secondary) 62%, var(--sf-color-text));--sf-color-secondary-800:color-mix(in oklab, var(--sf-color-secondary) 38%, var(--sf-color-text));--sf-color-secondary-900:color-mix(in oklab, var(--sf-color-secondary) 18%, var(--sf-color-text));--sf-color-secondary-950:color-mix(in oklab, var(--sf-color-secondary) 8%, var(--sf-color-text));--sf-color-secondary-a5:color-mix(in oklab, var(--sf-color-secondary) 5%, transparent);--sf-color-secondary-a10:color-mix(in oklab, var(--sf-color-secondary) 10%, transparent);--sf-color-secondary-a20:color-mix(in oklab, var(--sf-color-secondary) 20%, transparent);--sf-color-secondary-a30:color-mix(in oklab, var(--sf-color-secondary) 30%, transparent);--sf-color-secondary-a40:color-mix(in oklab, var(--sf-color-secondary) 40%, transparent);--sf-color-secondary-a50:color-mix(in oklab, var(--sf-color-secondary) 50%, transparent);--sf-color-secondary-a60:color-mix(in oklab, var(--sf-color-secondary) 60%, transparent);--sf-color-secondary-a70:color-mix(in oklab, var(--sf-color-secondary) 70%, transparent);--sf-color-secondary-a80:color-mix(in oklab, var(--sf-color-secondary) 80%, transparent);--sf-color-secondary-a90:color-mix(in oklab, var(--sf-color-secondary) 90%, transparent);--sf-color-secondary-a95:color-mix(in oklab, var(--sf-color-secondary) 95%, transparent);--sf-color-secondary-superlight:var(--sf-color-secondary-50);--sf-color-secondary-xlight:var(--sf-color-secondary-200);--sf-color-secondary-lighter:var(--sf-color-secondary-400);--sf-color-secondary-darker:var(--sf-color-secondary-600);--sf-color-secondary-xdark:var(--sf-color-secondary-800);--sf-color-secondary-superdark:var(--sf-color-secondary-950);--sf-color-secondary-hover:var(--sf-color-secondary-darker);--sf-color-secondary-active:var(--sf-color-secondary-xdark);--sf-color-secondary-subtle:var(--sf-color-secondary-a10);--sf-color-secondary-muted:var(--sf-color-secondary-a30);--sf-color-secondary-ghost:var(--sf-color-secondary-a5);--sf-color-tertiary-50:color-mix(in oklab, var(--sf-color-tertiary) 4%, var(--sf-color-base));--sf-color-tertiary-100:color-mix(in oklab, var(--sf-color-tertiary) 8%, var(--sf-color-base));--sf-color-tertiary-200:color-mix(in oklab, var(--sf-color-tertiary) 20%, var(--sf-color-base));--sf-color-tertiary-300:color-mix(in oklab, var(--sf-color-tertiary) 40%, var(--sf-color-base));--sf-color-tertiary-400:color-mix(in oklab, var(--sf-color-tertiary) 65%, var(--sf-color-base));--sf-color-tertiary-500:var(--sf-color-tertiary);--sf-color-tertiary-600:color-mix(in oklab, var(--sf-color-tertiary) 82%, var(--sf-color-text));--sf-color-tertiary-700:color-mix(in oklab, var(--sf-color-tertiary) 62%, var(--sf-color-text));--sf-color-tertiary-800:color-mix(in oklab, var(--sf-color-tertiary) 38%, var(--sf-color-text));--sf-color-tertiary-900:color-mix(in oklab, var(--sf-color-tertiary) 18%, var(--sf-color-text));--sf-color-tertiary-950:color-mix(in oklab, var(--sf-color-tertiary) 8%, var(--sf-color-text));--sf-color-tertiary-a5:color-mix(in oklab, var(--sf-color-tertiary) 5%, transparent);--sf-color-tertiary-a10:color-mix(in oklab, var(--sf-color-tertiary) 10%, transparent);--sf-color-tertiary-a20:color-mix(in oklab, var(--sf-color-tertiary) 20%, transparent);--sf-color-tertiary-a30:color-mix(in oklab, var(--sf-color-tertiary) 30%, transparent);--sf-color-tertiary-a40:color-mix(in oklab, var(--sf-color-tertiary) 40%, transparent);--sf-color-tertiary-a50:color-mix(in oklab, var(--sf-color-tertiary) 50%, transparent);--sf-color-tertiary-a60:color-mix(in oklab, var(--sf-color-tertiary) 60%, transparent);--sf-color-tertiary-a70:color-mix(in oklab, var(--sf-color-tertiary) 70%, transparent);--sf-color-tertiary-a80:color-mix(in oklab, var(--sf-color-tertiary) 80%, transparent);--sf-color-tertiary-a90:color-mix(in oklab, var(--sf-color-tertiary) 90%, transparent);--sf-color-tertiary-a95:color-mix(in oklab, var(--sf-color-tertiary) 95%, transparent);--sf-color-tertiary-superlight:var(--sf-color-tertiary-50);--sf-color-tertiary-xlight:var(--sf-color-tertiary-200);--sf-color-tertiary-lighter:var(--sf-color-tertiary-400);--sf-color-tertiary-darker:var(--sf-color-tertiary-600);--sf-color-tertiary-xdark:var(--sf-color-tertiary-800);--sf-color-tertiary-superdark:var(--sf-color-tertiary-950);--sf-color-tertiary-hover:var(--sf-color-tertiary-darker);--sf-color-tertiary-active:var(--sf-color-tertiary-xdark);--sf-color-tertiary-subtle:var(--sf-color-tertiary-a10);--sf-color-tertiary-muted:var(--sf-color-tertiary-a30);--sf-color-tertiary-ghost:var(--sf-color-tertiary-a5);--sf-color-action-50:color-mix(in oklab, var(--sf-color-action) 4%, var(--sf-color-base));--sf-color-action-100:color-mix(in oklab, var(--sf-color-action) 8%, var(--sf-color-base));--sf-color-action-200:color-mix(in oklab, var(--sf-color-action) 20%, var(--sf-color-base));--sf-color-action-300:color-mix(in oklab, var(--sf-color-action) 40%, var(--sf-color-base));--sf-color-action-400:color-mix(in oklab, var(--sf-color-action) 65%, var(--sf-color-base));--sf-color-action-500:var(--sf-color-action);--sf-color-action-600:color-mix(in oklab, var(--sf-color-action) 82%, var(--sf-color-text));--sf-color-action-700:color-mix(in oklab, var(--sf-color-action) 62%, var(--sf-color-text));--sf-color-action-800:color-mix(in oklab, var(--sf-color-action) 38%, var(--sf-color-text));--sf-color-action-900:color-mix(in oklab, var(--sf-color-action) 18%, var(--sf-color-text));--sf-color-action-950:color-mix(in oklab, var(--sf-color-action) 8%, var(--sf-color-text));--sf-color-action-a5:color-mix(in oklab, var(--sf-color-action) 5%, transparent);--sf-color-action-a10:color-mix(in oklab, var(--sf-color-action) 10%, transparent);--sf-color-action-a20:color-mix(in oklab, var(--sf-color-action) 20%, transparent);--sf-color-action-a30:color-mix(in oklab, var(--sf-color-action) 30%, transparent);--sf-color-action-a40:color-mix(in oklab, var(--sf-color-action) 40%, transparent);--sf-color-action-a50:color-mix(in oklab, var(--sf-color-action) 50%, transparent);--sf-color-action-a60:color-mix(in oklab, var(--sf-color-action) 60%, transparent);--sf-color-action-a70:color-mix(in oklab, var(--sf-color-action) 70%, transparent);--sf-color-action-a80:color-mix(in oklab, var(--sf-color-action) 80%, transparent);--sf-color-action-a90:color-mix(in oklab, var(--sf-color-action) 90%, transparent);--sf-color-action-a95:color-mix(in oklab, var(--sf-color-action) 95%, transparent);--sf-color-action-superlight:var(--sf-color-action-50);--sf-color-action-xlight:var(--sf-color-action-200);--sf-color-action-lighter:var(--sf-color-action-400);--sf-color-action-darker:var(--sf-color-action-600);--sf-color-action-xdark:var(--sf-color-action-800);--sf-color-action-superdark:var(--sf-color-action-950);--sf-color-action-hover:var(--sf-color-action-darker);--sf-color-action-active:var(--sf-color-action-xdark);--sf-color-action-subtle:var(--sf-color-action-a10);--sf-color-action-muted:var(--sf-color-action-a30);--sf-color-action-ghost:var(--sf-color-action-a5);--sf-color-neutral-50:color-mix(in oklab, var(--sf-color-neutral) 4%, var(--sf-color-base));--sf-color-neutral-100:color-mix(in oklab, var(--sf-color-neutral) 8%, var(--sf-color-base));--sf-color-neutral-200:color-mix(in oklab, var(--sf-color-neutral) 20%, var(--sf-color-base));--sf-color-neutral-300:color-mix(in oklab, var(--sf-color-neutral) 40%, var(--sf-color-base));--sf-color-neutral-400:color-mix(in oklab, var(--sf-color-neutral) 65%, var(--sf-color-base));--sf-color-neutral-500:var(--sf-color-neutral);--sf-color-neutral-600:color-mix(in oklab, var(--sf-color-neutral) 82%, var(--sf-color-text));--sf-color-neutral-700:color-mix(in oklab, var(--sf-color-neutral) 62%, var(--sf-color-text));--sf-color-neutral-800:color-mix(in oklab, var(--sf-color-neutral) 38%, var(--sf-color-text));--sf-color-neutral-900:color-mix(in oklab, var(--sf-color-neutral) 18%, var(--sf-color-text));--sf-color-neutral-950:color-mix(in oklab, var(--sf-color-neutral) 8%, var(--sf-color-text));--sf-color-neutral-a5:color-mix(in oklab, var(--sf-color-neutral) 5%, transparent);--sf-color-neutral-a10:color-mix(in oklab, var(--sf-color-neutral) 10%, transparent);--sf-color-neutral-a20:color-mix(in oklab, var(--sf-color-neutral) 20%, transparent);--sf-color-neutral-a30:color-mix(in oklab, var(--sf-color-neutral) 30%, transparent);--sf-color-neutral-a40:color-mix(in oklab, var(--sf-color-neutral) 40%, transparent);--sf-color-neutral-a50:color-mix(in oklab, var(--sf-color-neutral) 50%, transparent);--sf-color-neutral-a60:color-mix(in oklab, var(--sf-color-neutral) 60%, transparent);--sf-color-neutral-a70:color-mix(in oklab, var(--sf-color-neutral) 70%, transparent);--sf-color-neutral-a80:color-mix(in oklab, var(--sf-color-neutral) 80%, transparent);--sf-color-neutral-a90:color-mix(in oklab, var(--sf-color-neutral) 90%, transparent);--sf-color-neutral-a95:color-mix(in oklab, var(--sf-color-neutral) 95%, transparent);--sf-color-neutral-superlight:var(--sf-color-neutral-50);--sf-color-neutral-xlight:var(--sf-color-neutral-200);--sf-color-neutral-lighter:var(--sf-color-neutral-400);--sf-color-neutral-darker:var(--sf-color-neutral-600);--sf-color-neutral-xdark:var(--sf-color-neutral-800);--sf-color-neutral-superdark:var(--sf-color-neutral-950);--sf-color-neutral-hover:var(--sf-color-neutral-darker);--sf-color-neutral-active:var(--sf-color-neutral-xdark);--sf-color-neutral-subtle:var(--sf-color-neutral-a10);--sf-color-neutral-muted:var(--sf-color-neutral-a30);--sf-color-neutral-ghost:var(--sf-color-neutral-a5);--sf-color-base-50:color-mix(in oklab, var(--sf-color-text) 4%, var(--sf-color-base));--sf-color-base-100:color-mix(in oklab, var(--sf-color-text) 8%, var(--sf-color-base));--sf-color-base-200:color-mix(in oklab, var(--sf-color-text) 20%, var(--sf-color-base));--sf-color-base-300:color-mix(in oklab, var(--sf-color-text) 40%, var(--sf-color-base));--sf-color-base-400:color-mix(in oklab, var(--sf-color-text) 65%, var(--sf-color-base));--sf-color-base-500:var(--sf-color-base);--sf-color-base-600:color-mix(in oklab, var(--sf-color-base) 82%, var(--sf-color-text));--sf-color-base-700:color-mix(in oklab, var(--sf-color-base) 62%, var(--sf-color-text));--sf-color-base-800:color-mix(in oklab, var(--sf-color-base) 38%, var(--sf-color-text));--sf-color-base-900:color-mix(in oklab, var(--sf-color-base) 18%, var(--sf-color-text));--sf-color-base-950:color-mix(in oklab, var(--sf-color-base) 8%, var(--sf-color-text));--sf-color-base-a5:color-mix(in oklab, var(--sf-color-base) 5%, transparent);--sf-color-base-a10:color-mix(in oklab, var(--sf-color-base) 10%, transparent);--sf-color-base-a20:color-mix(in oklab, var(--sf-color-base) 20%, transparent);--sf-color-base-a30:color-mix(in oklab, var(--sf-color-base) 30%, transparent);--sf-color-base-a40:color-mix(in oklab, var(--sf-color-base) 40%, transparent);--sf-color-base-a50:color-mix(in oklab, var(--sf-color-base) 50%, transparent);--sf-color-base-a60:color-mix(in oklab, var(--sf-color-base) 60%, transparent);--sf-color-base-a70:color-mix(in oklab, var(--sf-color-base) 70%, transparent);--sf-color-base-a80:color-mix(in oklab, var(--sf-color-base) 80%, transparent);--sf-color-base-a90:color-mix(in oklab, var(--sf-color-base) 90%, transparent);--sf-color-base-a95:color-mix(in oklab, var(--sf-color-base) 95%, transparent);--sf-color-base-superlight:var(--sf-color-base-50);--sf-color-base-xlight:var(--sf-color-base-200);--sf-color-base-lighter:var(--sf-color-base-400);--sf-color-base-darker:var(--sf-color-base-600);--sf-color-base-xdark:var(--sf-color-base-800);--sf-color-base-superdark:var(--sf-color-base-950);--sf-color-base-hover:var(--sf-color-base-darker);--sf-color-base-active:var(--sf-color-base-xdark);--sf-color-base-subtle:var(--sf-color-base-a10);--sf-color-base-muted:var(--sf-color-base-a30);--sf-color-base-ghost:var(--sf-color-base-a5)}input:where([type=text],[type=email],[type=password],[type=url],[type=tel],[type=number],[type=search],[type=date],[type=datetime-local],[type=month],[type=week],[type=time],[type=color],:not([type])),select,textarea{appearance:none;inline-size:100%;padding-block:var(--sf-space-xs);padding-inline:var(--sf-space-s);font-size:var(--sf-text-m);line-height:var(--sf-leading-normal);color:var(--sf-color-text);background-color:var(--sf-color-surface);border:var(--sf-border-width-1) solid var(--sf-field-border-color,var(--sf-color-border));border-radius:var(--sf-radius-m);transition:var(--sf-transition-colors), border-color var(--sf-duration-fast) var(--sf-ease-out);display:block}input:where([type=text],[type=email],[type=password],[type=url],[type=tel],[type=number],[type=search],[type=date],[type=datetime-local],[type=month],[type=week],[type=time],[type=color],:not([type])):focus-visible,select:focus-visible,textarea:focus-visible{border-color:var(--sf-color-border--focus);outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}input:disabled,select:disabled,textarea:disabled{background-color:var(--sf-color-bg--disabled);color:var(--sf-color-text--disabled);opacity:var(--sf-opacity-disabled);cursor:not-allowed}input::placeholder,textarea::placeholder{color:var(--sf-color-text--placeholder);opacity:1}textarea{min-block-size:6rem}select{padding-inline-end:calc(var(--sf-space-s) + 1.5em)}select:not([multiple],[size]){background-repeat:no-repeat;background-position:right var(--sf-space-s) center;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='FieldText' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M2.5 4.5L6 8l3.5-3.5'/%3E%3C/svg%3E");background-size:1em}:dir(rtl) select:not([multiple],[size]){background-position:left var(--sf-space-s) center}button,input[type=submit],input[type=reset],input[type=button]{justify-content:center;align-items:center;gap:var(--sf-space-xs);padding-block:var(--sf-space-xs);padding-inline:var(--sf-space-m);font-size:var(--sf-text-m);font-weight:var(--sf-font-weight-heading);line-height:var(--sf-leading-normal);color:var(--sf-color-text--on-action);background-color:var(--sf-color-action);border:var(--sf-border-width-1) solid transparent;border-radius:var(--sf-radius-m);cursor:pointer;transition:var(--sf-transition-colors);text-decoration:none;display:inline-flex}button:hover,input[type=submit]:hover,input[type=reset]:hover,input[type=button]:hover{background-color:var(--sf-color-action-hover)}button:focus-visible,input[type=submit]:focus-visible,input[type=reset]:focus-visible,input[type=button]:focus-visible{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}button:disabled,input[type=submit]:disabled,input[type=reset]:disabled,input[type=button]:disabled{opacity:var(--sf-opacity-disabled);cursor:not-allowed;pointer-events:none}input[type=checkbox],input[type=radio]{block-size:1.125em;inline-size:1.125em;accent-color:var(--sf-color-action);cursor:pointer}input[type=file]{cursor:pointer}input[type=file]::file-selector-button{appearance:none;padding-block:var(--sf-space-xs);padding-inline:var(--sf-space-m);font-weight:var(--sf-font-weight-heading);color:var(--sf-color-text--on-action);background-color:var(--sf-color-action);border:var(--sf-border-width-1) solid transparent;border-radius:var(--sf-radius-m);cursor:pointer;transition:var(--sf-transition-colors);margin-inline-end:var(--sf-space-s)}input[type=file]::file-selector-button:hover{background-color:var(--sf-color-action-hover)}input[type=range]{appearance:none;cursor:pointer;background:0 0;inline-size:100%}input[type=range]::-webkit-slider-runnable-track{block-size:var(--sf-border-width-3,.25rem);background-color:var(--sf-color-border);border-radius:var(--sf-radius-full)}input[type=range]::-moz-range-track{block-size:var(--sf-border-width-3,.25rem);background-color:var(--sf-color-border);border-radius:var(--sf-radius-full)}input[type=range]::-webkit-slider-thumb{appearance:none;block-size:1em;inline-size:1em;background-color:var(--sf-color-action);border-radius:var(--sf-radius-full);margin-block-start:calc((var(--sf-border-width-3,.25rem) - 1em) / 2)}input[type=range]::-moz-range-thumb{background-color:var(--sf-color-action);border-radius:var(--sf-radius-full);border:none;block-size:1em;inline-size:1em}input[type=range]:focus-visible{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}fieldset{border:var(--sf-border-width-1) solid var(--sf-color-border--subtle);border-radius:var(--sf-radius-m);padding:var(--sf-space-m)}legend{font-weight:var(--sf-font-weight-heading);padding-inline:var(--sf-space-xs)}label{font-weight:var(--sf-font-weight-heading);color:var(--sf-field-text-color,inherit);margin-block-end:var(--sf-space-xs);display:block}label:has(:required):after,label:has(+:required):after{content:var(--sf-field-required-marker," *");color:var(--sf-color-error)}@supports not (height:100dvh){body{min-height:100vh}}@supports not selector(:focus-visible){:focus{outline:var(--sf-focus-ring-width,2px) var(--sf-focus-ring-style,solid) var(--sf-focus-ring-color,var(--sf-color-action));outline-offset:var(--sf-focus-ring-offset,2px)}}@supports not (scrollbar-gutter:stable){html{overflow-y:scroll}} +@property --sf-color-primary-light{syntax:"";inherits:true;initial-value:oklch(45% .2 264)}@property --sf-color-secondary-light{syntax:"";inherits:true;initial-value:oklch(25% .03 260)}@property --sf-color-tertiary-light{syntax:"";inherits:true;initial-value:oklch(48% .14 310)}@property --sf-color-action-light{syntax:"";inherits:true;initial-value:oklch(60% .16 210)}@property --sf-color-neutral-light{syntax:"";inherits:true;initial-value:oklch(45% .02 260)}@property --sf-color-base-light{syntax:"";inherits:true;initial-value:oklch(98% .005 260)}@property --sf-color-success-light{syntax:"";inherits:true;initial-value:oklch(48% .17 150)}@property --sf-color-warning-light{syntax:"";inherits:true;initial-value:oklch(75% .17 80)}@property --sf-color-error-light{syntax:"";inherits:true;initial-value:oklch(62% .2 35)}@property --sf-color-info-light{syntax:"";inherits:true;initial-value:oklch(48% .15 240)}@property --sf-color-danger-light{syntax:"";inherits:true;initial-value:oklch(48% .24 12)}@property --sf-is-dark{syntax:"";inherits:true;initial-value:0}@property --sf-is-active{syntax:"";inherits:true;initial-value:0}@property --sf-is-current{syntax:"";inherits:true;initial-value:0}@property --sf-is-pressed{syntax:"";inherits:true;initial-value:0}@property --sf-is-open{syntax:"";inherits:true;initial-value:0}:root{--sf-color-scheme:light dark;--sf-lumlocker:.65;--sf-color-primary:light-dark(var(--sf-color-primary-light),var(--sf-color-primary-dark,oklch(from var(--sf-color-primary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-secondary:light-dark(var(--sf-color-secondary-light),var(--sf-color-secondary-dark,oklch(from var(--sf-color-secondary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-tertiary:light-dark(var(--sf-color-tertiary-light),var(--sf-color-tertiary-dark,oklch(from var(--sf-color-tertiary-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-action:light-dark(var(--sf-color-action-light),var(--sf-color-action-dark,oklch(from var(--sf-color-action-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-neutral:light-dark(var(--sf-color-neutral-light),var(--sf-color-neutral-dark,oklch(from var(--sf-color-neutral-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-base:light-dark(var(--sf-color-base-light),var(--sf-color-base-dark,oklch(from var(--sf-color-base-light) clamp(.16, calc(1.18 - l), .24) calc(c * .5) h)));--sf-color-success:light-dark(var(--sf-color-success-light),var(--sf-color-success-dark,oklch(from var(--sf-color-success-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-warning:light-dark(var(--sf-color-warning-light),var(--sf-color-warning-dark,oklch(from var(--sf-color-warning-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-error:light-dark(var(--sf-color-error-light),var(--sf-color-error-dark,oklch(from var(--sf-color-error-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-info:light-dark(var(--sf-color-info-light),var(--sf-color-info-dark,oklch(from var(--sf-color-info-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-danger:light-dark(var(--sf-color-danger-light),var(--sf-color-danger-dark,oklch(from var(--sf-color-danger-light) clamp(.65, calc(.95 - l * .5), .88) calc(c * .9) h)));--sf-color-bg:oklch(from var(--sf-color-base) calc(l + .02) c h);--sf-color-surface:var(--sf-color-base);--sf-color-well:oklch(from var(--sf-color-base) calc(l - .02) c h);--sf-color-raised:oklch(from var(--sf-color-base) calc(l + .04) c h);--sf-color-overlay:oklch(from var(--sf-color-base) l c h / .9);--sf-color-inverse:oklch(from var(--sf-color-base) calc(1 - l) c h);--sf-color-text:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.05, calc(l - .4 - var(--sf-contrast-bias)), .35) c h),oklch(from var(--sf-color-neutral) clamp(.7, calc(l + .25 + var(--sf-contrast-bias)), 1) c h));--sf-color-text--secondary:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.15, calc(l - .25 - var(--sf-contrast-bias)), .45) c h),oklch(from var(--sf-color-neutral) clamp(.55, calc(l + .1 + var(--sf-contrast-bias)), .9) c h));--sf-color-text--muted:var(--sf-color-neutral);--sf-color-text--placeholder:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.45, calc(l + .15), .75) c h),oklch(from var(--sf-color-neutral) clamp(.35, calc(l - .1), .65) c h));--sf-color-text--disabled:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.55, calc(l + .25), .82) c h),oklch(from var(--sf-color-neutral) clamp(.25, calc(l - .2), .55) c h));--sf-color-text--inverse:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.85, calc(l + .4), .98) c h),oklch(from var(--sf-color-neutral) clamp(.05, calc(l - .4), .35) c h));--sf-color-heading:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.05, calc(l - .4 - var(--sf-contrast-bias)), .35) c h),oklch(from var(--sf-color-neutral) clamp(.7, calc(l + .25 + var(--sf-contrast-bias)), 1) c h));--sf-color-code-text:oklch(from var(--sf-color-code-bg) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-primary:oklch(from var(--sf-color-primary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-secondary:oklch(from var(--sf-color-secondary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-tertiary:oklch(from var(--sf-color-tertiary) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-action:oklch(from var(--sf-color-action) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-neutral:oklch(from var(--sf-color-neutral) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-base:var(--sf-color-text);--sf-color-text--on-inverse:var(--sf-color-text--inverse);--sf-color-text--on-success:oklch(from var(--sf-color-success) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-warning:oklch(from var(--sf-color-warning) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-error:oklch(from var(--sf-color-error) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-info:oklch(from var(--sf-color-info) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-text--on-danger:oklch(from var(--sf-color-danger) clamp(.1, sign(var(--sf-contrast-threshold) - l) * 999, .95) 0 0);--sf-color-border:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.7, calc(l + .35), .95) .005 h),oklch(from var(--sf-color-neutral) clamp(.25, calc(l - .3), .55) .005 h));--sf-color-border--subtle:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.75, calc(l + .4), .97) .005 h),oklch(from var(--sf-color-neutral) clamp(.2, calc(l - .38), .45) .005 h));--sf-color-border--strong:light-dark(oklch(from var(--sf-color-neutral-light) clamp(.55, calc(l + .1), .85) .02 h),oklch(from var(--sf-color-neutral) clamp(.38, calc(l - .1), .65) .02 h));--sf-color-border--focus:var(--sf-color-action);--sf-color-border--disabled:oklch(from var(--sf-color-border--subtle) l 0 h / .5);--sf-color-border--translucent:oklch(from var(--sf-color-neutral) l c h / .15);--sf-color-link:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .07, .48), 1) c h),oklch(from var(--sf-color-action) clamp(.68, l, 1) c h));--sf-color-link--hover:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .15, .4), 1) c h),oklch(from var(--sf-color-action) clamp(0, max(l + .1, .68), 1) c h));--sf-color-link--active:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .21, .34), 1) c h),oklch(from var(--sf-color-action) clamp(0, max(l + .15, .74), 1) c h));--sf-color-link--visited:light-dark(oklch(from var(--sf-color-action) clamp(0, min(l - .07, .48), 1) c calc(h + 60)),oklch(from var(--sf-color-action) clamp(.68, l, 1) c calc(h + 60)));--sf-color-link--underline:oklch(from var(--sf-color-action) l c h / .3);--sf-color-link--disabled:var(--sf-color-text--disabled);--sf-color-bg--hover:oklch(from var(--sf-color-neutral) l c h / .08);--sf-color-bg--active:oklch(from var(--sf-color-neutral) l c h / .12);--sf-color-bg--selected:oklch(from var(--sf-color-action) l c h / .1);--sf-color-bg--focus:oklch(from var(--sf-color-action) l c h / .06);--sf-color-bg--disabled:var(--sf-color-well);--sf-color-code-bg:var(--sf-color-well);--sf-color-selection-bg:oklch(from var(--sf-color-action) l c h / .22);--sf-color-selection-text:inherit;--sf-color-mark-bg:oklch(from var(--sf-color-warning) l c h / .25);--sf-color-mark-text:inherit;--sf-color-dim:oklch(0% 0 0/.5);--sf-scrollbar-thumb:var(--sf-color-neutral);--sf-scrollbar-track:transparent;--sf-color-success-subtle:oklch(from var(--sf-color-success) l c h / .12);--sf-color-success-strong:light-dark(oklch(from var(--sf-color-success-light) calc(l - .15) c h),oklch(from var(--sf-color-success) clamp(.7, calc(l + .15), 1) c h));--sf-color-success-muted:oklch(from var(--sf-color-success) l c h / .3);--sf-color-warning-subtle:oklch(from var(--sf-color-warning) l c h / .12);--sf-color-warning-strong:light-dark(oklch(from var(--sf-color-warning-light) calc(l - .25) c h),oklch(from var(--sf-color-warning) clamp(.7, calc(l + .05), 1) c h));--sf-color-warning-muted:oklch(from var(--sf-color-warning) l c h / .3);--sf-color-error-subtle:oklch(from var(--sf-color-error) l c h / .1);--sf-color-error-strong:light-dark(oklch(from var(--sf-color-error-light) calc(l - .1) c h),oklch(from var(--sf-color-error) clamp(.7, calc(l + .15), 1) c h));--sf-color-error-muted:oklch(from var(--sf-color-error) l c h / .3);--sf-color-info-subtle:oklch(from var(--sf-color-info) l c h / .1);--sf-color-info-strong:light-dark(oklch(from var(--sf-color-info-light) calc(l - .1) c h),oklch(from var(--sf-color-info) clamp(.7, calc(l + .15), 1) c h));--sf-color-info-muted:oklch(from var(--sf-color-info) l c h / .3);--sf-color-danger-subtle:oklch(from var(--sf-color-danger) l c h / .1);--sf-color-danger-strong:light-dark(oklch(from var(--sf-color-danger-light) calc(l - .1) c h),oklch(from var(--sf-color-danger) clamp(.7, calc(l + .15), 1) c h));--sf-color-danger-muted:oklch(from var(--sf-color-danger) l c h / .3);--sf-gradient-primary:linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) calc(l - .08) c h));--sf-gradient-secondary:linear-gradient(135deg, var(--sf-color-secondary), oklch(from var(--sf-color-secondary) calc(l - .08) c h));--sf-gradient-tertiary:linear-gradient(135deg, var(--sf-color-tertiary), oklch(from var(--sf-color-tertiary) calc(l - .08) c h));--sf-gradient-brand:linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) l c calc(h + 30)));--sf-gradient-surface:linear-gradient(180deg, var(--sf-color-surface), var(--sf-color-bg));--sf-gradient-fade--r:linear-gradient(to right, transparent, var(--sf-color-bg));--sf-gradient-fade--l:linear-gradient(to left, transparent, var(--sf-color-bg));--sf-gradient-fade--t:linear-gradient(to top, transparent, var(--sf-color-bg));--sf-gradient-fade--b:linear-gradient(to bottom, transparent, var(--sf-color-bg));--sf-shadow-strength:calc(.08 + var(--sf-is-dark) * .17);--sf-shadow-glow-color:var(--sf-color-primary);--sf-shadow-glow:0 0 15px 2px oklch(from var(--sf-shadow-glow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-opacity-disabled:.45;--sf-state-pending-opacity:.7;--sf-focus-ring-color:var(--sf-color-action);--sf-focus-ring-shadow:0 0 0 var(--sf-focus-ring-offset) var(--sf-color-bg), 0 0 0 calc(var(--sf-focus-ring-offset) + var(--sf-focus-ring-width)) var(--sf-focus-ring-color);--sf-caret-color:var(--sf-color-action);--sf-space-scale:1;--sf-text-scale:1;--sf-text-display-scale:1;--sf-radius-scale:1;--sf-motion-scale:1;--sf-font-body:system-ui, -apple-system, sans-serif;--sf-font-heading:var(--sf-font-body);--sf-font-display:var(--sf-font-heading);--sf-font-mono:ui-monospace, monospace;--sf-font-humanist:"Seravek", "Gill Sans Nova", "Ubuntu", "Calibri", "DejaVu Sans", source-sans-pro, sans-serif;--sf-font-geometric:"Avenir", "Montserrat", "Corbel", "URW Gothic", source-sans-pro, sans-serif;--sf-font-slab:"Rockwell", "Rockwell Nova", "Roboto Slab", "DejaVu Serif", "Sitka Small", serif;--sf-font-features:normal;--sf-font-variation:normal;--sf-optical-sizing:auto;--sf-font-weight-thin:100;--sf-font-weight-extralight:200;--sf-font-weight-light:300;--sf-font-weight-normal:400;--sf-font-weight-medium:500;--sf-font-weight-semibold:600;--sf-font-weight-bold:700;--sf-font-weight-extrabold:800;--sf-font-weight-black:900;--sf-font-weight-body:var(--sf-font-weight-normal);--sf-font-weight-heading:var(--sf-font-weight-semibold);--sf-font-weight-display:var(--sf-font-weight-bold);--sf-current-font-weight:var(--sf-font-weight-bold);--sf-text-2xs:clamp(.51rem, calc(.000296296 * (100vw - 22.5rem) + .51rem), .53rem);--sf-text-xs:clamp(.64rem, calc(.000888889 * (100vw - 22.5rem) + .64rem), .7rem);--sf-text-s:clamp(.8rem, calc(.00207407 * (100vw - 22.5rem) + .8rem), .94rem);--sf-text-m:clamp(1rem, calc(.0037037 * (100vw - 22.5rem) + 1rem), 1.25rem);--sf-text-l:clamp(1.25rem, calc(.00622222 * (100vw - 22.5rem) + 1.25rem), 1.67rem);--sf-text-xl:clamp(1.56rem, calc(.00977778 * (100vw - 22.5rem) + 1.56rem), 2.22rem);--sf-text-2xl:clamp(1.95rem, calc(.014963 * (100vw - 22.5rem) + 1.95rem), 2.96rem);--sf-text-3xl:clamp(2.44rem, calc(.0223704 * (100vw - 22.5rem) + 2.44rem), 3.95rem);--sf-text-4xl:clamp(3.05rem, calc(.0327407 * (100vw - 22.5rem) + 3.05rem), 5.26rem);--sf-text-display-s:clamp(2.4rem, calc(.00888889 * (100vw - 22.5rem) + 2.4rem), 3rem);--sf-text-display-m:clamp(3rem, calc(.0148148 * (100vw - 22.5rem) + 3rem), 4rem);--sf-text-display-l:clamp(3.75rem, calc(.0234074 * (100vw - 22.5rem) + 3.75rem), 5.33rem);--sf-leading-tight:1.1;--sf-leading-snug:1.3;--sf-leading-normal:1.5;--sf-leading-relaxed:1.625;--sf-tracking-tight:-.025em;--sf-tracking-normal:0;--sf-tracking-wide:.025em;--sf-tracking-wider:.05em;--sf-tracking-widest:.1em;--sf-icon-xs:.875em;--sf-icon-s:1em;--sf-icon-m:1.5em;--sf-icon-l:2em;--sf-icon-xl:3em;--sf-space-none:0;--sf-space-px:1px;--sf-space-gutter:var(--sf-space-l);--sf-space-2xs:calc(clamp(.51rem, calc(.00488889 * (100vw - 22.5rem) + .51rem), .84rem) * var(--sf-space-scale));--sf-space-xs:calc(clamp(.64rem, calc(.00725926 * (100vw - 22.5rem) + .64rem), 1.13rem) * var(--sf-space-scale));--sf-space-s:calc(clamp(.8rem, calc(.0103704 * (100vw - 22.5rem) + .8rem), 1.5rem) * var(--sf-space-scale));--sf-space-m:calc(clamp(1rem, calc(.0148148 * (100vw - 22.5rem) + 1rem), 2rem) * var(--sf-space-scale));--sf-space-l:calc(clamp(1.25rem, calc(.021037 * (100vw - 22.5rem) + 1.25rem), 2.67rem) * var(--sf-space-scale));--sf-space-xl:calc(clamp(1.56rem, calc(.0294815 * (100vw - 22.5rem) + 1.56rem), 3.55rem) * var(--sf-space-scale));--sf-space-2xl:calc(clamp(1.95rem, calc(.0413333 * (100vw - 22.5rem) + 1.95rem), 4.74rem) * var(--sf-space-scale));--sf-space-3xl:calc(clamp(2.44rem, calc(.0573333 * (100vw - 22.5rem) + 2.44rem), 6.31rem) * var(--sf-space-scale));--sf-space-4xl:calc(clamp(3.05rem, calc(.0795556 * (100vw - 22.5rem) + 3.05rem), 8.42rem) * var(--sf-space-scale));--sf-size-xs:1.5rem;--sf-size-s:2rem;--sf-size-m:2.5rem;--sf-size-l:2.75rem;--sf-size-xl:3.5rem;--sf-container-narrow:38rem;--sf-container-prose:65ch;--sf-container-default:75rem;--sf-container-wide:90rem;--sf-container-full:100%;--sf-ratio-square:1;--sf-ratio-video:16 / 9;--sf-ratio-cinema:21 / 9;--sf-ratio-4-3:4 / 3;--sf-ratio-3-2:3 / 2;--sf-ratio-portrait:3 / 4;--sf-ratio-golden:1.618 / 1;--sf-border-width-hairline:.5px;--sf-border-width-1:1px;--sf-border-width-2:2px;--sf-border-width-3:3px;--sf-border-width-4:4px;--sf-border-style:solid;--sf-border-style-strong:solid;--sf-border-style-soft:dashed;--sf-border-style-dotted:dotted;--sf-divider-width:var(--sf-border-width-1);--sf-divider-style:solid;--sf-divider-color:var(--sf-color-border);--sf-radius-none:0;--sf-radius-xs:calc(2px * var(--sf-radius-scale));--sf-radius-s:calc(4px * var(--sf-radius-scale));--sf-radius-m:calc(8px * var(--sf-radius-scale));--sf-radius-l:calc(12px * var(--sf-radius-scale));--sf-radius-xl:calc(16px * var(--sf-radius-scale));--sf-radius-2xl:calc(24px * var(--sf-radius-scale));--sf-radius-3xl:calc(32px * var(--sf-radius-scale));--sf-radius-4xl:calc(48px * var(--sf-radius-scale));--sf-radius-full:9999px;--sf-radius-pill:var(--sf-radius-full);--sf-radius-outer:calc(var(--sf-radius-m) + var(--sf-component-pad));--sf-shadow-color:oklch(from var(--sf-color-neutral) .15 c h);--sf-shadow-none:none;--sf-shadow-xs:0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7));--sf-shadow-s:0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 2px 6px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, var(--sf-shadow-strength), .7));--sf-shadow-m:0 1px 3px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-shadow-l:0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 8px 24px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), .7)), 0 16px 48px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-shadow-xl:0 2px 8px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .5), .7)), 0 12px 36px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3.5), .7)), 0 24px 72px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7));--sf-shadow-2xl:0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * .6), .7)), 0 20px 60px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 4), .7)), 0 40px 100px -8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 5), .7));--sf-shadow-inner:inset 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-text-shadow-none:none;--sf-text-shadow-s:0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), .7));--sf-text-shadow-m:0 2px 4px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7));--sf-text-shadow-l:0 4px 8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7));--sf-drop-shadow-s:drop-shadow(0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), .7)));--sf-drop-shadow-m:drop-shadow(0 4px 6px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), .7)));--sf-drop-shadow-l:drop-shadow(0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), .7)));--sf-blur-xs:4px;--sf-blur-s:8px;--sf-blur-m:16px;--sf-blur-l:32px;--sf-blur-xl:48px;--sf-perspective-near:500px;--sf-perspective-normal:1000px;--sf-perspective-far:2000px;--sf-opacity-0:0;--sf-opacity-10:.1;--sf-opacity-25:.25;--sf-opacity-50:.5;--sf-opacity-75:.75;--sf-opacity-100:1;--sf-duration-none:0s;--sf-duration-instant:calc(.1s * var(--sf-motion-scale));--sf-duration-fast:calc(.15s * var(--sf-motion-scale));--sf-duration-normal:calc(.25s * var(--sf-motion-scale));--sf-duration-slow:calc(.4s * var(--sf-motion-scale));--sf-duration-slower:calc(.6s * var(--sf-motion-scale));--sf-ease-linear:linear;--sf-ease-out:cubic-bezier(.25, 0, .15, 1);--sf-ease-in:cubic-bezier(.5, 0, .75, .25);--sf-ease-in-out:cubic-bezier(.4, 0, .2, 1);--sf-ease-spring:linear(0, .5, 1.1, .95, 1.02, 1);--sf-ease-elastic:linear(0, .3, 1.2, .9, 1.05, 1);--sf-ease-bounce:linear(0, .35 18%, 1 32%, .86 42%, 1.02 56%, .98 72%, 1);--sf-ease-overshoot:linear(0, .6 30%, 1.08 55%, .98 75%, 1);--sf-animation-fade-in:sf-fade-in var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-fade-out:sf-fade-out var(--sf-duration-normal) var(--sf-ease-in) both;--sf-animation-slide-in-up:sf-slide-in-up var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-down:sf-slide-in-down var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-left:sf-slide-in-left var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-slide-in-right:sf-slide-in-right var(--sf-duration-normal) var(--sf-ease-out) both;--sf-animation-scale-up:sf-scale-up var(--sf-duration-normal) var(--sf-ease-overshoot) both;--sf-animation-scale-down:sf-scale-down var(--sf-duration-normal) var(--sf-ease-in) both;--sf-animation-color-pulse:sf-color-pulse var(--sf-duration-slow) var(--sf-ease-in-out) infinite;--sf-animation-ping:sf-ping var(--sf-duration-slow) var(--sf-ease-out) infinite;--sf-animation-blink:sf-blink calc(1s * var(--sf-motion-scale)) steps(1, end) infinite;--sf-animation-float:sf-float calc(3s * var(--sf-motion-scale)) var(--sf-ease-in-out) infinite;--sf-animation-spin:sf-spin var(--sf-duration-slower) linear infinite;--sf-animation-shimmer:sf-shimmer calc(1.5s * var(--sf-motion-scale)) ease-in-out infinite;--sf-animation-delay-1:calc(75ms * var(--sf-motion-scale));--sf-animation-delay-2:calc(.15s * var(--sf-motion-scale));--sf-animation-delay-3:calc(.225s * var(--sf-motion-scale));--sf-animation-delay-4:calc(.3s * var(--sf-motion-scale));--sf-animation-delay-5:calc(.375s * var(--sf-motion-scale));--sf-scroll-timeline-range-start:0%;--sf-scroll-timeline-range-end:100%;--sf-mask-scrim-start:var(--sf-space-l);--sf-mask-scrim-end:var(--sf-space-l);--sf-z-below:-1;--sf-z-base:0;--sf-z-raised:1;--sf-z-low:10;--sf-z-mid:100;--sf-z-high:500;--sf-z-top:900;--sf-z-max:9999;--sf-header-height:8rem;--sf-sticky-offset:var(--sf-header-height);--sf-focus-ring-width:2px;--sf-focus-ring-offset:2px;--sf-focus-ring-style:solid;--sf-touch-target:var(--sf-size-l);--sf-contrast-bias:0;--sf-contrast-threshold:.6;--sf-safe-top:env(safe-area-inset-top,0px);--sf-safe-bottom:env(safe-area-inset-bottom,0px);--sf-safe-left:env(safe-area-inset-left,0px);--sf-safe-right:env(safe-area-inset-right,0px);--sf-print-page-margin:2cm;--sf-print-page-size:a4;--sf-print-base-size:11pt;--sf-stroke-thin:1px;--sf-stroke-regular:1.5px;--sf-stroke-bold:2px;--sf-stroke-heavy:3px;--sf-body-font-size:var(--sf-text-m);--sf-body-line-height:var(--sf-leading-normal);--sf-body-font-weight:var(--sf-font-weight-body);--sf-body-font-family:var(--sf-font-body);--sf-body-color:var(--sf-color-text);--sf-body-text-wrap:pretty;--sf-body-strong-weight:var(--sf-font-weight-bold);--sf-body-em-style:italic;--sf-code-font-size:.875em;--sf-heading-font-family:var(--sf-font-heading);--sf-heading-color:var(--sf-color-heading);--sf-heading-text-wrap:balance;--sf-h1-size:var(--sf-text-4xl);--sf-h1-line-height:var(--sf-leading-tight);--sf-h1-font-weight:var(--sf-font-weight-heading);--sf-h1-letter-spacing:var(--sf-tracking-tight);--sf-h2-size:var(--sf-text-3xl);--sf-h2-line-height:var(--sf-leading-tight);--sf-h2-font-weight:var(--sf-font-weight-heading);--sf-h2-letter-spacing:var(--sf-tracking-tight);--sf-h3-size:var(--sf-text-2xl);--sf-h3-line-height:var(--sf-leading-snug);--sf-h3-font-weight:var(--sf-font-weight-heading);--sf-h3-letter-spacing:var(--sf-tracking-normal);--sf-h4-size:var(--sf-text-xl);--sf-h4-line-height:var(--sf-leading-snug);--sf-h4-font-weight:var(--sf-font-weight-heading);--sf-h4-letter-spacing:var(--sf-tracking-normal);--sf-h5-size:var(--sf-text-l);--sf-h5-line-height:var(--sf-leading-normal);--sf-h5-font-weight:var(--sf-font-weight-heading);--sf-h5-letter-spacing:var(--sf-tracking-normal);--sf-h6-size:var(--sf-text-m);--sf-h6-line-height:var(--sf-leading-normal);--sf-h6-font-weight:var(--sf-font-weight-heading);--sf-h6-letter-spacing:var(--sf-tracking-wide);--sf-gap:var(--sf-space-m);--sf-content-gap:var(--sf-space-s);--sf-component-pad:var(--sf-space-m);--sf-field-block:var(--sf-space-l);--sf-field-required-marker:" *";--sf-link-external-marker:" ↗";--sf-section-pad:var(--sf-section-pad--m);--sf-section-pad--xs:var(--sf-space-xl);--sf-section-pad--s:var(--sf-space-2xl);--sf-section-pad--m:var(--sf-space-3xl);--sf-section-pad--l:var(--sf-space-4xl);--sf-section-pad--xl:calc(var(--sf-space-4xl) * 1.5);--sf-section-pad--2xl:calc(var(--sf-space-4xl) * 2);--sf-transition-all:all var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-colors:color var(--sf-duration-normal) var(--sf-ease-out), background-color var(--sf-duration-normal) var(--sf-ease-out), border-color var(--sf-duration-normal) var(--sf-ease-out), text-decoration-color var(--sf-duration-normal) var(--sf-ease-out), fill var(--sf-duration-normal) var(--sf-ease-out), stroke var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-transform:transform var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-opacity:opacity var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-shadow:box-shadow var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-fast:all var(--sf-duration-fast) var(--sf-ease-out);--sf-transition-slow:all var(--sf-duration-slow) var(--sf-ease-in-out);--sf-transition-enter:all var(--sf-duration-normal) var(--sf-ease-out);--sf-transition-exit:all var(--sf-duration-fast) var(--sf-ease-in);--sf-space-gap:var(--sf-gap);--sf-space-content:var(--sf-content-gap);--sf-stack-gap:var(--sf-space-content);--sf-gap-size:var(--sf-space-gap);--sf-box-padding:var(--sf-space-m);--sf-box-border-width:0;--sf-box-border-color:var(--sf-color-border);--sf-center-max:var(--sf-container-default);--sf-center-gutter:var(--sf-space-gutter);--sf-cluster-gap:var(--sf-space-gap);--sf-cluster-align:center;--sf-cluster-justify:flex-start;--sf-sidebar-gap:var(--sf-space-gap);--sf-sidebar-min-width:50%;--sf-sidebar-width:18rem;--sf-switcher-threshold:30rem;--sf-switcher-gap:var(--sf-space-gap);--sf-grid-min:16rem;--sf-grid-gap:var(--sf-space-gap);--sf-grid-min-xs:10rem;--sf-grid-min-s:13rem;--sf-grid-min-m:16rem;--sf-grid-min-l:20rem;--sf-grid-min-xl:24rem;--sf-grid-min-2xl:28rem;--sf-equal-cols:2;--sf-equal-gap:var(--sf-space-gap);--sf-cover-min-height:100dvh;--sf-cover-padding:var(--sf-section-pad);--sf-frame-ratio:16 / 9;--sf-icon-box-pad:.5em;--sf-icon-box-radius:var(--sf-radius-s);--sf-icon-box-bg:var(--sf-color-well);--sf-icon-box-border:var(--sf-border-width-1) solid var(--sf-color-border);--sf-reel-item-width:max-content;--sf-reel-gap:var(--sf-space-gap);--sf-reel-height:auto;--sf-imposter-margin:var(--sf-space-m);--sf-bento-cols-default:3;--sf-bento-row-default:10rem;--sf-bento-row-compact:6rem;--sf-bento-row-tall:16rem;--sf-bento-gap:var(--sf-space-gap);--sf-breakout-width:var(--sf-container-wide);--sf-content-width:var(--sf-container-default);--sf-prose-paragraph:var(--sf-space-content);--sf-alternate-gap:var(--sf-space-content);--sf-alternate-inner-gap:var(--sf-space-gap);--sf-flow-space:var(--sf-space-content);--sf-line-clamp:3;--sf-truncate-suffix:"…";--sf-aspect:16 / 9;--sf-scroll-shadow-size:2rem}*,:before,:after{box-sizing:border-box;margin:0;padding:0}html{text-size-adjust:100%;scroll-padding-top:var(--sf-header-height,3.5rem);scrollbar-gutter:stable;color-scheme:var(--sf-color-scheme,light dark);hanging-punctuation:first last}@supports (interpolate-size:allow-keywords){html{interpolate-size:allow-keywords}}body{min-height:100dvh;min-inline-size:320px}menu,ul,ol{list-style:none}img,picture,video,canvas,svg{max-width:100%;height:auto;display:block}iframe,embed,object{max-width:100%;height:unset;border:none;display:block}button,input,select,textarea{font:inherit;color:inherit;letter-spacing:inherit;color-scheme:inherit;line-height:inherit}button{text-transform:none}search{display:block}fieldset{border:none;min-width:0;padding:0}legend{padding:0}a,button,input,select,textarea,summary{touch-action:manipulation}summary{cursor:pointer;list-style:none}textarea{resize:vertical}button{cursor:pointer}table{border-collapse:collapse;border-spacing:0}[hidden]{display:none!important}dialog,[popover]{color:inherit;background:0 0;border:none;padding:0}[inert]{cursor:default}body{font-family:var(--sf-body-font-family,var(--sf-font-body));font-size:var(--sf-body-font-size,var(--sf-text-m));line-height:var(--sf-body-line-height,var(--sf-leading-normal));font-weight:var(--sf-body-font-weight,var(--sf-font-weight-body));color:var(--sf-body-color,var(--sf-color-text));background-color:var(--sf-color-bg,#fff);font-synthesis:none}h1,h2,h3,h4,h5,h6{font-family:var(--sf-heading-font-family,var(--sf-font-heading));line-height:var(--sf-leading-tight);color:var(--sf-heading-color,var(--sf-color-heading));text-wrap:var(--sf-heading-text-wrap);overflow-wrap:break-word;text-rendering:optimizelegibility;scroll-margin-top:calc(var(--sf-header-height,3.5rem) + var(--sf-space-m))}h1{font-size:var(--sf-h1-size);line-height:var(--sf-h1-line-height);font-weight:var(--sf-h1-font-weight);letter-spacing:var(--sf-h1-letter-spacing)}h2{font-size:var(--sf-h2-size);line-height:var(--sf-h2-line-height);font-weight:var(--sf-h2-font-weight);letter-spacing:var(--sf-h2-letter-spacing)}h3{font-size:var(--sf-h3-size);line-height:var(--sf-h3-line-height);font-weight:var(--sf-h3-font-weight);letter-spacing:var(--sf-h3-letter-spacing)}h4{font-size:var(--sf-h4-size);line-height:var(--sf-h4-line-height);font-weight:var(--sf-h4-font-weight);letter-spacing:var(--sf-h4-letter-spacing)}h5{font-size:var(--sf-h5-size);line-height:var(--sf-h5-line-height);font-weight:var(--sf-h5-font-weight);letter-spacing:var(--sf-h5-letter-spacing)}h6{font-size:var(--sf-h6-size);line-height:var(--sf-h6-line-height);font-weight:var(--sf-h6-font-weight);letter-spacing:var(--sf-h6-letter-spacing)}p{text-wrap:var(--sf-body-text-wrap);overflow-wrap:break-word;orphans:2;widows:2}b,strong{font-weight:var(--sf-body-strong-weight)}em{font-style:var(--sf-body-em-style)}small{font-size:var(--sf-text-s,80%)}mark{background-color:var(--sf-color-mark-bg,yellow);color:var(--sf-color-mark-text,black);border-radius:var(--sf-radius-xs);padding-inline:.2em}abbr[title]{cursor:help;text-decoration:underline dotted}kbd,samp{font-family:var(--sf-font-mono);font-size:inherit}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sup{top:-.5em}sub{bottom:-.25em}a:link{color:var(--sf-color-link);text-decoration-color:var(--sf-color-link--underline);text-underline-offset:.15em;text-underline-position:from-font;transition:color var(--sf-duration-fast) var(--sf-ease-out), text-decoration-color var(--sf-duration-fast) var(--sf-ease-out)}a:hover{color:var(--sf-color-link--hover);text-decoration-color:currentColor}a:visited{color:var(--sf-color-link--visited)}a:active{color:var(--sf-color-link--active)}a:not([class]){text-decoration-skip-ink:auto}a,code{overflow-wrap:break-word}code{font-family:var(--sf-font-mono);font-size:var(--sf-code-font-size);border-radius:var(--sf-radius-xs);background-color:var(--sf-color-code-bg);color:var(--sf-color-code-text,inherit);padding:.125em .3em}pre{font-family:var(--sf-font-mono);font-size:var(--sf-text-s);padding:var(--sf-space-m);border-radius:var(--sf-radius-m);background:var(--sf-color-code-block-bg,var(--sf-color-code-bg));color:var(--sf-color-code-block-text,inherit);tab-size:2;hyphens:none;overflow-x:auto}pre code{color:inherit;font-size:inherit;background:0 0;border:0;border-radius:0;padding:0}svg{fill:currentColor}hr{border:none;border-block-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color)}:target{scroll-margin-top:calc(var(--sf-header-height,3.5rem) + var(--sf-space-m))}::selection{background:var(--sf-color-selection-bg);color:var(--sf-color-selection-text);text-shadow:none}th,caption{text-align:start}address,cite,dfn,var{font-style:normal}input,textarea{caret-color:var(--sf-caret-color)}input,progress{accent-color:var(--sf-color-action)}::backdrop{background-color:var(--sf-color-dim)}optgroup{font:inherit}::file-selector-button{font:inherit}::placeholder{opacity:1;color:var(--sf-color-text--placeholder,currentcolor)}blockquote{border-inline-start:var(--sf-border-width-3) solid var(--sf-color-border);padding-inline-start:var(--sf-space-m);font-style:italic}dt{font-weight:var(--sf-font-weight-heading)}dd{margin-inline-start:var(--sf-space-m)}table{text-align:start;width:100%}th{font-weight:var(--sf-font-weight-heading)}td,th{padding:var(--sf-space-2xs) var(--sf-space-xs);border-block-end:var(--sf-border-width-hairline) solid var(--sf-color-border--subtle)}@media (prefers-color-scheme:dark){:root:not([data-theme]){color-scheme:dark;--sf-is-dark:1}}[data-theme=light]{color-scheme:light;--sf-is-dark:0}[data-theme=dark]{color-scheme:dark;--sf-is-dark:1}:root[data-lumlocker]{--sf-color-primary:light-dark(oklch(from var(--sf-color-primary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-primary-dark,var(--sf-color-primary-light)) var(--sf-lumlocker) c h));--sf-color-secondary:light-dark(oklch(from var(--sf-color-secondary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-secondary-dark,var(--sf-color-secondary-light)) var(--sf-lumlocker) c h));--sf-color-tertiary:light-dark(oklch(from var(--sf-color-tertiary-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-tertiary-dark,var(--sf-color-tertiary-light)) var(--sf-lumlocker) c h));--sf-color-action:light-dark(oklch(from var(--sf-color-action-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-action-dark,var(--sf-color-action-light)) var(--sf-lumlocker) c h));--sf-color-neutral:light-dark(oklch(from var(--sf-color-neutral-light) var(--sf-lumlocker) c h),oklch(from var(--sf-color-neutral-dark,var(--sf-color-neutral-light)) var(--sf-lumlocker) c h))}.sf-section{padding-block:var(--sf-section-pad)}.sf-section--xs{padding-block:var(--sf-section-pad--xs)}.sf-section--s{padding-block:var(--sf-section-pad--s)}.sf-section--m{padding-block:var(--sf-section-pad--m)}.sf-section--l{padding-block:var(--sf-section-pad--l)}.sf-section--xl{padding-block:var(--sf-section-pad--xl)}.sf-section--2xl{padding-block:var(--sf-section-pad--2xl)}.sf-section-group>.sf-section+.sf-section{padding-block-start:0}.sf-section--collapse:has(+.sf-section--collapse){padding-block-end:calc(var(--sf-section-pad) / 2)}.sf-section--collapse+.sf-section--collapse{padding-block-start:calc(var(--sf-section-pad) / 2)}.sf-divider{border:none;border-block-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);margin-block:var(--sf-space-m)}.sf-divider--vertical{border-block-start:none;border-inline-start:var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);margin-block:0;margin-inline:var(--sf-space-m);align-self:stretch}.sf-container{width:100%;max-width:var(--sf-container-default);padding-inline:var(--sf-space-gutter);margin-inline:auto;container:sf-layout/inline-size}.sf-container--narrow{max-width:var(--sf-container-narrow)}.sf-container--prose{max-width:var(--sf-container-prose)}.sf-container--wide{max-width:var(--sf-container-wide)}.sf-container--full{max-width:var(--sf-container-full)}.sf-stack{gap:var(--sf-stack-gap);flex-direction:column;display:flex}.sf-stack--xs{gap:var(--sf-space-xs)}.sf-stack--s{gap:var(--sf-space-s)}.sf-stack--m{gap:var(--sf-space-m)}.sf-stack--l{gap:var(--sf-space-l)}.sf-stack--xl{gap:var(--sf-space-xl)}.sf-stack--2xl{gap:var(--sf-space-2xl)}.sf-stack--center{align-items:center}.sf-stack--end{align-items:flex-end}.sf-stack--stretch{align-items:stretch}.sf-gap{gap:var(--sf-gap-size)}.sf-gap--xs{gap:var(--sf-space-xs)}.sf-gap--s{gap:var(--sf-space-s)}.sf-gap--m{gap:var(--sf-space-m)}.sf-gap--l{gap:var(--sf-space-l)}.sf-gap--xl{gap:var(--sf-space-xl)}.sf-gap--2xl{gap:var(--sf-space-2xl)}.sf-box{padding:var(--sf-box-padding);outline:var(--sf-box-border-width) solid var(--sf-box-border-color)}.sf-center{box-sizing:content-box;max-inline-size:var(--sf-center-max);padding-inline:var(--sf-center-gutter);margin-inline:auto}.sf-center--intrinsic{flex-direction:column;align-items:center;display:flex}.sf-cluster{gap:var(--sf-cluster-gap);align-items:var(--sf-cluster-align);justify-content:var(--sf-cluster-justify);flex-wrap:wrap;display:flex}.sf-cluster--xs{gap:var(--sf-space-xs)}.sf-cluster--s{gap:var(--sf-space-s)}.sf-cluster--m{gap:var(--sf-space-m)}.sf-cluster--l{gap:var(--sf-space-l)}.sf-cluster--xl{gap:var(--sf-space-xl)}.sf-cluster--2xl{gap:var(--sf-space-2xl)}.sf-cluster--no-wrap{flex-wrap:nowrap}.sf-cluster--center{justify-content:center}.sf-cluster--end{justify-content:flex-end}.sf-cluster--between{justify-content:space-between}.sf-sidebar{gap:var(--sf-sidebar-gap);flex-wrap:wrap;display:flex}.sf-sidebar>:first-child{flex-basis:var(--sf-sidebar-width);flex-grow:1}.sf-sidebar>:last-child,.sf-sidebar--right>:first-child{min-inline-size:var(--sf-sidebar-min-width);flex-grow:999;flex-basis:0}.sf-sidebar--right>:last-child{flex-basis:var(--sf-sidebar-width);flex-grow:1}.sf-sidebar--narrow{--sf-sidebar-width:12rem}.sf-sidebar--wide{--sf-sidebar-width:26rem}.sf-switcher{gap:var(--sf-switcher-gap);flex-wrap:wrap;display:flex}.sf-switcher>*{flex-grow:1;flex-basis:calc((var(--sf-switcher-threshold) - 100%) * 999)}.sf-switcher--no-wrap{flex-wrap:nowrap;overflow-x:auto}.sf-switcher--vertical{flex-direction:column}.sf-grid{grid-template-columns:repeat(auto-fill, minmax(min(var(--sf-grid-min), 100%), 1fr));gap:var(--sf-grid-gap);display:grid}.sf-grid--fit{grid-template-columns:repeat(auto-fit, minmax(min(var(--sf-grid-min), 100%), 1fr))}.sf-grid--xs{--sf-grid-min:var(--sf-grid-min-xs)}.sf-grid--s{--sf-grid-min:var(--sf-grid-min-s)}.sf-grid--m{--sf-grid-min:var(--sf-grid-min-m)}.sf-grid--l{--sf-grid-min:var(--sf-grid-min-l)}.sf-grid--xl{--sf-grid-min:var(--sf-grid-min-xl)}.sf-grid--2xl{--sf-grid-min:var(--sf-grid-min-2xl)}.sf-grid--dense{grid-auto-flow:dense}.sf-icon{inline-size:var(--sf-icon-size,var(--sf-icon-m));block-size:var(--sf-icon-size,var(--sf-icon-m));vertical-align:text-bottom;fill:currentColor;flex-shrink:0;display:inline-block}.sf-icon--xs{--sf-icon-size:var(--sf-icon-xs)}.sf-icon--s{--sf-icon-size:var(--sf-icon-s)}.sf-icon--m{--sf-icon-size:var(--sf-icon-m)}.sf-icon--l{--sf-icon-size:var(--sf-icon-l)}.sf-icon--xl{--sf-icon-size:var(--sf-icon-xl)}.sf-icon--boxed{inline-size:calc(var(--sf-icon-size,var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);block-size:calc(var(--sf-icon-size,var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);padding:var(--sf-icon-box-pad);border:var(--sf-icon-box-border);border-radius:var(--sf-icon-box-radius);background:var(--sf-icon-box-bg);box-sizing:content-box}.sf-cover{min-height:var(--sf-cover-min-height);padding-block:var(--sf-cover-padding);flex-direction:column;display:flex}.sf-cover>.sf-cover__center{margin-block:auto}.sf-cover--min{min-height:50dvh}.sf-cover--max{min-height:0;max-height:100dvh}.sf-cover--padding-s{padding-block:var(--sf-space-xl)}.sf-cover--padding-l{padding-block:var(--sf-space-4xl)}.sf-frame{aspect-ratio:var(--sf-frame-ratio);justify-content:center;align-items:center;display:flex;overflow:hidden}.sf-frame>img,.sf-frame>video{object-fit:cover;width:100%;height:100%}.sf-frame--square{aspect-ratio:var(--sf-ratio-square)}.sf-frame--video{aspect-ratio:var(--sf-ratio-video)}.sf-frame--cinema{aspect-ratio:var(--sf-ratio-cinema)}.sf-frame--portrait{aspect-ratio:var(--sf-ratio-portrait)}.sf-frame--4-3{aspect-ratio:var(--sf-ratio-4-3)}.sf-frame--3-2{aspect-ratio:var(--sf-ratio-3-2)}.sf-frame--golden{aspect-ratio:var(--sf-ratio-golden)}.sf-reel{block-size:var(--sf-reel-height);gap:var(--sf-reel-gap);scroll-snap-type:x mandatory;scroll-padding-inline:var(--sf-reel-gap);overscroll-behavior-x:contain;scrollbar-width:thin;display:flex;overflow-x:auto}.sf-reel>*{flex:0 0 var(--sf-reel-item-width);scroll-snap-align:start}.sf-imposter{position:absolute;inset-block-start:50%;inset-inline-start:50%;translate:-50% -50%}.sf-imposter--fixed{position:fixed}.sf-imposter--contain{max-inline-size:calc(100% - var(--sf-imposter-margin) * 2);max-block-size:calc(100% - var(--sf-imposter-margin) * 2);overflow:auto}.sf-alternate{gap:var(--sf-alternate-gap);display:grid;container:sf-alternate/inline-size}.sf-alternate>*{gap:var(--sf-alternate-inner-gap);align-items:center;display:grid}@container sf-alternate (width>=48em){.sf-alternate>*{grid-template-columns:1fr 1fr}.sf-alternate>:nth-child(2n)>:first-child{order:2}.sf-alternate>:nth-child(2n)>:last-child{order:1}}.sf-pancake{grid-template-rows:auto 1fr auto;min-height:100dvh;display:grid}.sf-grid-1,.sf-grid-2,.sf-grid-3,.sf-grid-4,.sf-grid-6{gap:var(--sf-grid-gap);display:grid}@container (width>=30em){.sf-grid-4{grid-template-columns:repeat(2,1fr)}.sf-grid-6{grid-template-columns:repeat(3,1fr)}}@container (width>=48em){.sf-grid-2{grid-template-columns:repeat(2,1fr)}.sf-grid-3{grid-template-columns:repeat(3,1fr)}.sf-grid-4{grid-template-columns:repeat(4,1fr)}.sf-grid-6{grid-template-columns:repeat(6,1fr)}}.sf-grid-1-2,.sf-grid-2-1,.sf-grid-1-3,.sf-grid-3-1{gap:var(--sf-grid-gap);display:grid}@container (width>=48em){.sf-grid-1-2{grid-template-columns:1fr 2fr}.sf-grid-2-1{grid-template-columns:2fr 1fr}.sf-grid-1-3{grid-template-columns:1fr 3fr}.sf-grid-3-1{grid-template-columns:3fr 1fr}}.sf-equal{grid-template-columns:repeat(var(--sf-equal-cols), 1fr);gap:var(--sf-equal-gap);display:grid}.sf-equal--2{--sf-equal-cols:2}.sf-equal--3{--sf-equal-cols:3}.sf-equal--4{--sf-equal-cols:4}.sf-equal--6{--sf-equal-cols:6}.sf-content-grid{grid-template-columns:[full-start] minmax(var(--sf-space-gutter), 1fr) [breakout-start] minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2)) [content-start] min(var(--sf-content-width), 100% - var(--sf-space-gutter) * 2) [content-end] minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2)) [breakout-end] minmax(var(--sf-space-gutter), 1fr) [full-end];display:grid}.sf-content-grid>*{grid-column:content}.sf-content-grid>.sf-breakout{grid-column:breakout}.sf-content-grid>.sf-full-bleed{grid-column:full}.sf-bento{grid-template-columns:repeat(var(--sf-bento-cols,var(--sf-bento-cols-default)), 1fr);grid-auto-rows:minmax(var(--sf-bento-row,var(--sf-bento-row-default)), auto);grid-auto-flow:dense;gap:var(--sf-bento-gap);display:grid}@container (width>=30em) and (width<=47.99em){.sf-bento{grid-template-columns:repeat(2,1fr)}}.sf-bento--2{--sf-bento-cols:2}.sf-bento--4{--sf-bento-cols:4}.sf-bento--compact{--sf-bento-row:var(--sf-bento-row-compact)}.sf-bento--tall{--sf-bento-row:var(--sf-bento-row-tall)}.sf-subgrid{grid-template-columns:subgrid;display:grid}.sf-subgrid-rows{grid-template-rows:subgrid;display:grid}.sf-prose{max-width:var(--sf-container-prose);overflow-wrap:break-word}.sf-prose>*+*{margin-block-start:var(--sf-prose-paragraph)}.sf-prose :is(h2,h3,h4)+*{margin-block-start:var(--sf-space-s)}.sf-prose li+li{margin-block-start:var(--sf-space-xs)}.sf-prose :is(blockquote,pre){margin-block:var(--sf-space-m)}.sf-prose img{margin-block:var(--sf-space-m);border-radius:var(--sf-radius-m)}.sf-prose :is(ul,ol){padding-inline-start:1.25em}.sf-prose ul{list-style:outside}.sf-prose ol{list-style:decimal}.sf-prose ::marker{color:var(--sf-color-primary)}.sf-prose figure{margin-block:var(--sf-space-l)}.sf-prose figcaption{font-size:var(--sf-text-s);color:var(--sf-color-text--muted);margin-block-start:var(--sf-space-xs)}.sf-prose table{margin-block:var(--sf-space-m);max-width:100%;display:block;overflow-x:auto}.sf-prose video{margin-block:var(--sf-space-m);border-radius:var(--sf-radius-m)}.sf-prose .sf-not-prose>*+*,.sf-prose .sf-not-prose li+li{margin-block-start:0}.sf-prose .sf-not-prose :is(ul,ol){list-style:revert;padding-inline-start:revert}.sf-prose .sf-not-prose ::marker{color:inherit}.sf-prose .sf-not-prose img{border-radius:0;margin-block:0}.sf-prose .sf-not-prose figure{margin-block:0}.sf-prose .sf-not-prose figcaption{font-size:inherit;color:inherit;margin-block-start:0}.sf-prose .sf-not-prose table{display:revert;overflow-x:revert;max-width:revert;margin-block:0}.sf-prose .sf-not-prose video{border-radius:0;margin-block:0}.sf-flow>*+*{margin-block-start:var(--sf-flow-space)}.sf-truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.sf-line-clamp-2,.sf-line-clamp-3,.sf-line-clamp-N{-webkit-box-orient:vertical;display:-webkit-box;overflow:hidden}.sf-line-clamp-2{-webkit-line-clamp:2;line-clamp:2}.sf-line-clamp-3{-webkit-line-clamp:3;line-clamp:3}.sf-line-clamp-N{-webkit-line-clamp:var(--sf-line-clamp);line-clamp:var(--sf-line-clamp)}.sf-equal-height{align-items:stretch;display:flex}.sf-equal-height>*{flex:auto;block-size:auto}.sf-aspect{aspect-ratio:var(--sf-aspect)}.sf-scroll-shadow{mask-image:linear-gradient(to bottom, transparent 0, black var(--sf-scroll-shadow-size), black calc(100% - var(--sf-scroll-shadow-size)), transparent 100%);overflow-y:auto}.sf-scroll-snap{scroll-snap-type:y mandatory;overscroll-behavior-y:contain;overflow-y:auto}.sf-scroll-snap>*{scroll-snap-align:start}.sf-overflow-fade{mask-image:linear-gradient(to right, black 0, black calc(100% - var(--sf-scroll-shadow-size)), transparent 100%);overflow:hidden}.sf-no-tap-highlight{-webkit-tap-highlight-color:transparent}.sf-surface--primary{background:var(--sf-color-primary);color:var(--sf-color-text--on-primary)}.sf-surface--secondary{background:var(--sf-color-secondary);color:var(--sf-color-text--on-secondary)}.sf-surface--tertiary{background:var(--sf-color-tertiary);color:var(--sf-color-text--on-tertiary)}.sf-surface--action{background:var(--sf-color-action);color:var(--sf-color-text--on-action)}.sf-surface--neutral{background:var(--sf-color-neutral);color:var(--sf-color-text--on-neutral)}.sf-surface--inverse{background:var(--sf-color-inverse);color:var(--sf-color-text--on-inverse)}.sf-surface--success{background:var(--sf-color-success);color:var(--sf-color-text--on-success)}.sf-surface--warning{background:var(--sf-color-warning);color:var(--sf-color-text--on-warning)}.sf-surface--error{background:var(--sf-color-error);color:var(--sf-color-text--on-error)}.sf-surface--info{background:var(--sf-color-info);color:var(--sf-color-text--on-info)}.sf-surface--danger{background:var(--sf-color-danger);color:var(--sf-color-text--on-danger)}.sf-text-gradient{background-image:var(--sf-gradient-primary);-webkit-background-clip:text;background-clip:text}@supports (background-clip:text){.sf-text-gradient{color:#0000}}.sf-link-external:after{content:var(--sf-link-external-marker);font-size:.85em;text-decoration:none;display:inline-block}.is-hidden{display:none!important}.is-invisible{visibility:hidden}.is-visible{visibility:visible}.is-disabled{opacity:var(--sf-opacity-disabled);pointer-events:none;cursor:not-allowed;user-select:none}.is-readonly{pointer-events:none;user-select:none}.is-loading{pointer-events:none;position:relative;color:#0000!important}.is-loading:after{content:"";border:var(--sf-border-width-2) solid var(--sf-color-border--strong);border-block-start-color:var(--sf-color-action);border-radius:var(--sf-radius-full);width:1em;height:1em;animation:var(--sf-animation-spin);margin-block-start:-.5em;margin-inline-start:-.5em;position:absolute;inset-block-start:50%;inset-inline-start:50%}.is-busy{cursor:progress}.is-pending{opacity:var(--sf-state-pending-opacity);cursor:progress}.is-skeleton{background:linear-gradient(90deg, var(--sf-color-well) 25%, var(--sf-color-raised) 50%, var(--sf-color-well) 75%);animation:var(--sf-animation-shimmer);border-radius:var(--sf-radius-s);pointer-events:none;user-select:none;background-size:200% 100%;color:#0000!important}.is-active{--sf-is-active:1}.is-selected{background-color:var(--sf-color-bg--selected)}.is-current{--sf-is-current:1;font-weight:var(--sf-current-font-weight,var(--sf-font-weight-bold))}.is-highlighted{background-color:var(--sf-color-bg--focus)}.is-pressed{--sf-is-pressed:1;background-color:var(--sf-color-bg--selected)}.is-open{--sf-is-open:1}.is-collapsed{--sf-is-open:0}.is-expanded{--sf-is-open:1}.is-valid{--sf-field-border-color:var(--sf-color-success);--sf-field-text-color:var(--sf-color-success-strong)}.is-invalid{--sf-field-border-color:var(--sf-color-error);--sf-field-text-color:var(--sf-color-error-strong)}.is-warning{--sf-field-border-color:var(--sf-color-warning);--sf-field-text-color:var(--sf-color-warning-strong)}.is-success{--sf-field-border-color:var(--sf-color-success);--sf-field-text-color:var(--sf-color-success-strong)}.is-error{--sf-field-border-color:var(--sf-color-error);--sf-field-text-color:var(--sf-color-error-strong)}.is-info{--sf-field-border-color:var(--sf-color-info);--sf-field-text-color:var(--sf-color-info-strong)}.is-danger{--sf-field-border-color:var(--sf-color-danger);--sf-field-text-color:var(--sf-color-danger-strong)}.is-sticky{top:var(--sf-sticky-offset,0);z-index:var(--sf-z-low);position:sticky}.is-pinned{z-index:var(--sf-z-mid);position:sticky;top:0}.is-fixed{position:fixed}.is-fullscreen{z-index:var(--sf-z-max);position:fixed;inset:0}.is-resizable{resize:both;overflow:auto}.is-clipped{overflow:hidden!important}.is-scrollable{overscroll-behavior:contain;overflow:auto}.is-truncated{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.is-dragging{opacity:var(--sf-opacity-50);cursor:grabbing}.is-drop-target{outline:var(--sf-focus-ring-width) dashed var(--sf-color-action);outline-offset:var(--sf-focus-ring-offset)}.is-draggable{cursor:grab}.is-overlay{position:absolute;inset:0}.is-clickable{cursor:pointer}.is-unselectable{user-select:none}.is-focused{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-focus-ring-color);outline-offset:var(--sf-focus-ring-offset)}.is-empty:empty{display:none}@media (prefers-reduced-motion:no-preference){html{scroll-behavior:smooth}html:focus-within{scroll-behavior:auto}a,button,input,select,textarea,summary{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-duration:var(--sf-duration-fast);transition-timing-function:var(--sf-ease-out)}@supports (view-transition-name:none){::view-transition-old(root),::view-transition-new(root){animation-duration:var(--sf-duration-normal);animation-timing-function:var(--sf-ease-out)}}.sf-fade-in{animation:var(--sf-animation-fade-in)}.sf-fade-out{animation:var(--sf-animation-fade-out)}.sf-slide-in-up{animation:var(--sf-animation-slide-in-up)}.sf-slide-in-down{animation:var(--sf-animation-slide-in-down)}.sf-slide-in-left{animation:var(--sf-animation-slide-in-left)}.sf-slide-in-right{animation:var(--sf-animation-slide-in-right)}.sf-scale-up{animation:var(--sf-animation-scale-up)}.sf-scale-down{animation:var(--sf-animation-scale-down)}.sf-entrance--fade,.sf-entrance--fade-up,.sf-entrance--fade-down,.sf-entrance--fade-left,.sf-entrance--fade-right,.sf-entrance--scale-up{animation-duration:var(--sf-duration-slow);animation-timing-function:var(--sf-ease-out);animation-fill-mode:both}@supports (animation-timeline:view()){.sf-entrance--fade,.sf-entrance--fade-up,.sf-entrance--fade-down,.sf-entrance--fade-left,.sf-entrance--fade-right,.sf-entrance--scale-up{animation-timeline:view();animation-range:var(--sf-scroll-timeline-range-start,entry 0%) var(--sf-scroll-timeline-range-end,cover 30%)}}.sf-entrance--fade{animation-name:sf-fade-in}.sf-entrance--fade-up{animation-name:sf-slide-in-up}.sf-entrance--fade-down{animation-name:sf-slide-in-down}.sf-entrance--fade-left{animation-name:sf-slide-in-left}.sf-entrance--fade-right{animation-name:sf-slide-in-right}.sf-entrance--scale-up{animation-name:sf-scale-up}}@keyframes sf-fade-in{0%{opacity:0}to{opacity:1}}@keyframes sf-fade-out{0%{opacity:1}to{opacity:0}}@keyframes sf-slide-in-up{0%{opacity:0;translate:0 1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-down{0%{opacity:0;translate:0 -1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-left{0%{opacity:0;translate:1rem}to{opacity:1;translate:0}}@keyframes sf-slide-in-right{0%{opacity:0;translate:-1rem}to{opacity:1;translate:0}}@keyframes sf-scale-up{0%{opacity:0;scale:.92}to{opacity:1;scale:1}}@keyframes sf-scale-down{0%{opacity:1;scale:1}to{opacity:0;scale:.92}}@keyframes sf-spin{to{rotate:360deg}}@keyframes sf-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}@keyframes sf-ping{75%,to{opacity:0;scale:2}}@keyframes sf-blink{0%,50%{opacity:1}50.01%,to{opacity:0}}@keyframes sf-float{0%,to{translate:0}50%{translate:0 -.5rem}}@keyframes sf-color-pulse{0%{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) l c h)}50%{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) calc(l + .25) c h)}to{--sf-color-primary-light:oklch(from var(--sf-color-primary-light) l c h)}}@media (prefers-reduced-motion:no-preference){.sf-color-pulse{animation:var(--sf-animation-color-pulse)}}:focus:not(:focus-visible){outline:none}:focus-visible{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-focus-ring-color)!important;outline-offset:var(--sf-focus-ring-offset)!important}@media (prefers-reduced-motion:reduce){:root{--sf-duration-instant:.01ms;--sf-duration-fast:.01ms;--sf-duration-normal:.01ms;--sf-duration-slow:.01ms;--sf-duration-slower:.01ms}*,:before,:after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}}.no-motion,.no-motion *,.no-motion :before,.no-motion :after{scroll-behavior:auto!important;transition-duration:.01ms!important;animation-duration:.01ms!important;animation-iteration-count:1!important}@media (prefers-contrast:more){:root{--sf-focus-ring-width:3px;--sf-contrast-bias:.06}hr{border-block-start-width:var(--sf-border-width-2)}}@media (prefers-reduced-transparency:reduce){::backdrop{background-color:var(--sf-color-neutral);opacity:1}}@media (pointer:coarse){button,input[type=button],input[type=submit],input[type=reset],select,summary,a{min-height:var(--sf-touch-target);min-width:var(--sf-touch-target)}}[disabled],[aria-disabled=true]{cursor:not-allowed}.sr-only,.visually-hidden,.sr-only-focusable:not(:focus,:focus-within){clip-path:inset(50%)!important;white-space:nowrap!important;border:0!important;width:1px!important;height:1px!important;margin:-1px!important;padding:0!important;position:absolute!important;overflow:clip!important}.skip-link{top:-100%;left:var(--sf-space-m);z-index:var(--sf-z-max);padding:var(--sf-space-s) var(--sf-space-m);background:var(--sf-color-bg);color:var(--sf-color-text);border:var(--sf-border-width-1) solid var(--sf-color-border);border-radius:var(--sf-radius-m);transition:top var(--sf-duration-fast) var(--sf-ease-out);position:absolute}.skip-link:focus{top:var(--sf-space-m)}.focus-parent:focus-within{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}.sf-clickable-parent{--sf-clickable-overlay-z:1;cursor:pointer;position:relative}.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):after,.sf-clickable-parent [data-overlay-link]:after{content:"";z-index:var(--sf-clickable-overlay-z);pointer-events:auto;position:absolute;inset:0}.sf-clickable-parent :is(button,input,textarea,select,summary,video,audio,[role=button],[data-no-overlay]),.sf-clickable-parent:has([data-overlay-link]) a:where(:any-link):not([data-overlay-link]),.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):not(:first-of-type){z-index:calc(var(--sf-clickable-overlay-z) + 1);position:relative}.sf-clickable-parent:focus-within{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style,solid) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}@media (forced-colors:active){:root{--sf-focus-ring-color:Highlight}:focus-visible{outline-color:highlight!important}:where(*){box-shadow:none;text-shadow:none}input,select,textarea{border:var(--sf-border-width-1) solid}}.print-only{display:none}@media print{@page{size:var(--sf-print-page-size,a4);margin:var(--sf-print-page-margin,2cm)}body{font-size:var(--sf-print-base-size)}*,:before,:after{box-shadow:none;text-shadow:none}a[href]:after{content:" (" attr(href) ")";word-break:break-all;font-size:90%}a[href^=\#]:after,a[href^=javascript\: i]:after{content:""}abbr[title]:after{content:" (" attr(title) ")"}img,svg,video,canvas,figure,table,pre,blockquote{break-inside:avoid}thead{display:table-header-group}tr{break-inside:avoid}h1,h2,h3,h4,h5,h6{break-after:avoid;orphans:3;widows:3}p{orphans:3;widows:3}pre,blockquote{border:var(--sf-border-width-1) solid var(--sf-color-border--strong);padding:var(--sf-space-s)}nav,aside,button,input,select,textarea,audio,video,dialog,[popover],.no-print{display:none!important}.print-only{display:revert!important}details{display:block}details>summary{display:none!important}details:not([open])>:not(summary){display:block}.print-color-exact,.print-color-exact *{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}.print-no-color,.print-no-color *,.print-no-color :before,.print-no-color :after{color:canvastext!important;background:0 0!important}}:root{--sf-color-primary-50:color-mix(in oklab, var(--sf-color-primary) 4%, var(--sf-color-base));--sf-color-primary-100:color-mix(in oklab, var(--sf-color-primary) 8%, var(--sf-color-base));--sf-color-primary-200:color-mix(in oklab, var(--sf-color-primary) 20%, var(--sf-color-base));--sf-color-primary-300:color-mix(in oklab, var(--sf-color-primary) 40%, var(--sf-color-base));--sf-color-primary-400:color-mix(in oklab, var(--sf-color-primary) 65%, var(--sf-color-base));--sf-color-primary-500:var(--sf-color-primary);--sf-color-primary-600:color-mix(in oklab, var(--sf-color-primary) 82%, var(--sf-color-text));--sf-color-primary-700:color-mix(in oklab, var(--sf-color-primary) 62%, var(--sf-color-text));--sf-color-primary-800:color-mix(in oklab, var(--sf-color-primary) 38%, var(--sf-color-text));--sf-color-primary-900:color-mix(in oklab, var(--sf-color-primary) 18%, var(--sf-color-text));--sf-color-primary-950:color-mix(in oklab, var(--sf-color-primary) 8%, var(--sf-color-text));--sf-color-primary-a5:color-mix(in oklab, var(--sf-color-primary) 5%, transparent);--sf-color-primary-a10:color-mix(in oklab, var(--sf-color-primary) 10%, transparent);--sf-color-primary-a20:color-mix(in oklab, var(--sf-color-primary) 20%, transparent);--sf-color-primary-a30:color-mix(in oklab, var(--sf-color-primary) 30%, transparent);--sf-color-primary-a40:color-mix(in oklab, var(--sf-color-primary) 40%, transparent);--sf-color-primary-a50:color-mix(in oklab, var(--sf-color-primary) 50%, transparent);--sf-color-primary-a60:color-mix(in oklab, var(--sf-color-primary) 60%, transparent);--sf-color-primary-a70:color-mix(in oklab, var(--sf-color-primary) 70%, transparent);--sf-color-primary-a80:color-mix(in oklab, var(--sf-color-primary) 80%, transparent);--sf-color-primary-a90:color-mix(in oklab, var(--sf-color-primary) 90%, transparent);--sf-color-primary-a95:color-mix(in oklab, var(--sf-color-primary) 95%, transparent);--sf-color-primary-superlight:var(--sf-color-primary-50);--sf-color-primary-xlight:var(--sf-color-primary-200);--sf-color-primary-lighter:var(--sf-color-primary-400);--sf-color-primary-darker:var(--sf-color-primary-600);--sf-color-primary-xdark:var(--sf-color-primary-800);--sf-color-primary-superdark:var(--sf-color-primary-950);--sf-color-primary-hover:var(--sf-color-primary-darker);--sf-color-primary-active:var(--sf-color-primary-xdark);--sf-color-primary-subtle:var(--sf-color-primary-a10);--sf-color-primary-muted:var(--sf-color-primary-a30);--sf-color-primary-ghost:var(--sf-color-primary-a5);--sf-color-secondary-50:color-mix(in oklab, var(--sf-color-secondary) 4%, var(--sf-color-base));--sf-color-secondary-100:color-mix(in oklab, var(--sf-color-secondary) 8%, var(--sf-color-base));--sf-color-secondary-200:color-mix(in oklab, var(--sf-color-secondary) 20%, var(--sf-color-base));--sf-color-secondary-300:color-mix(in oklab, var(--sf-color-secondary) 40%, var(--sf-color-base));--sf-color-secondary-400:color-mix(in oklab, var(--sf-color-secondary) 65%, var(--sf-color-base));--sf-color-secondary-500:var(--sf-color-secondary);--sf-color-secondary-600:color-mix(in oklab, var(--sf-color-secondary) 82%, var(--sf-color-text));--sf-color-secondary-700:color-mix(in oklab, var(--sf-color-secondary) 62%, var(--sf-color-text));--sf-color-secondary-800:color-mix(in oklab, var(--sf-color-secondary) 38%, var(--sf-color-text));--sf-color-secondary-900:color-mix(in oklab, var(--sf-color-secondary) 18%, var(--sf-color-text));--sf-color-secondary-950:color-mix(in oklab, var(--sf-color-secondary) 8%, var(--sf-color-text));--sf-color-secondary-a5:color-mix(in oklab, var(--sf-color-secondary) 5%, transparent);--sf-color-secondary-a10:color-mix(in oklab, var(--sf-color-secondary) 10%, transparent);--sf-color-secondary-a20:color-mix(in oklab, var(--sf-color-secondary) 20%, transparent);--sf-color-secondary-a30:color-mix(in oklab, var(--sf-color-secondary) 30%, transparent);--sf-color-secondary-a40:color-mix(in oklab, var(--sf-color-secondary) 40%, transparent);--sf-color-secondary-a50:color-mix(in oklab, var(--sf-color-secondary) 50%, transparent);--sf-color-secondary-a60:color-mix(in oklab, var(--sf-color-secondary) 60%, transparent);--sf-color-secondary-a70:color-mix(in oklab, var(--sf-color-secondary) 70%, transparent);--sf-color-secondary-a80:color-mix(in oklab, var(--sf-color-secondary) 80%, transparent);--sf-color-secondary-a90:color-mix(in oklab, var(--sf-color-secondary) 90%, transparent);--sf-color-secondary-a95:color-mix(in oklab, var(--sf-color-secondary) 95%, transparent);--sf-color-secondary-superlight:var(--sf-color-secondary-50);--sf-color-secondary-xlight:var(--sf-color-secondary-200);--sf-color-secondary-lighter:var(--sf-color-secondary-400);--sf-color-secondary-darker:var(--sf-color-secondary-600);--sf-color-secondary-xdark:var(--sf-color-secondary-800);--sf-color-secondary-superdark:var(--sf-color-secondary-950);--sf-color-secondary-hover:var(--sf-color-secondary-darker);--sf-color-secondary-active:var(--sf-color-secondary-xdark);--sf-color-secondary-subtle:var(--sf-color-secondary-a10);--sf-color-secondary-muted:var(--sf-color-secondary-a30);--sf-color-secondary-ghost:var(--sf-color-secondary-a5);--sf-color-tertiary-50:color-mix(in oklab, var(--sf-color-tertiary) 4%, var(--sf-color-base));--sf-color-tertiary-100:color-mix(in oklab, var(--sf-color-tertiary) 8%, var(--sf-color-base));--sf-color-tertiary-200:color-mix(in oklab, var(--sf-color-tertiary) 20%, var(--sf-color-base));--sf-color-tertiary-300:color-mix(in oklab, var(--sf-color-tertiary) 40%, var(--sf-color-base));--sf-color-tertiary-400:color-mix(in oklab, var(--sf-color-tertiary) 65%, var(--sf-color-base));--sf-color-tertiary-500:var(--sf-color-tertiary);--sf-color-tertiary-600:color-mix(in oklab, var(--sf-color-tertiary) 82%, var(--sf-color-text));--sf-color-tertiary-700:color-mix(in oklab, var(--sf-color-tertiary) 62%, var(--sf-color-text));--sf-color-tertiary-800:color-mix(in oklab, var(--sf-color-tertiary) 38%, var(--sf-color-text));--sf-color-tertiary-900:color-mix(in oklab, var(--sf-color-tertiary) 18%, var(--sf-color-text));--sf-color-tertiary-950:color-mix(in oklab, var(--sf-color-tertiary) 8%, var(--sf-color-text));--sf-color-tertiary-a5:color-mix(in oklab, var(--sf-color-tertiary) 5%, transparent);--sf-color-tertiary-a10:color-mix(in oklab, var(--sf-color-tertiary) 10%, transparent);--sf-color-tertiary-a20:color-mix(in oklab, var(--sf-color-tertiary) 20%, transparent);--sf-color-tertiary-a30:color-mix(in oklab, var(--sf-color-tertiary) 30%, transparent);--sf-color-tertiary-a40:color-mix(in oklab, var(--sf-color-tertiary) 40%, transparent);--sf-color-tertiary-a50:color-mix(in oklab, var(--sf-color-tertiary) 50%, transparent);--sf-color-tertiary-a60:color-mix(in oklab, var(--sf-color-tertiary) 60%, transparent);--sf-color-tertiary-a70:color-mix(in oklab, var(--sf-color-tertiary) 70%, transparent);--sf-color-tertiary-a80:color-mix(in oklab, var(--sf-color-tertiary) 80%, transparent);--sf-color-tertiary-a90:color-mix(in oklab, var(--sf-color-tertiary) 90%, transparent);--sf-color-tertiary-a95:color-mix(in oklab, var(--sf-color-tertiary) 95%, transparent);--sf-color-tertiary-superlight:var(--sf-color-tertiary-50);--sf-color-tertiary-xlight:var(--sf-color-tertiary-200);--sf-color-tertiary-lighter:var(--sf-color-tertiary-400);--sf-color-tertiary-darker:var(--sf-color-tertiary-600);--sf-color-tertiary-xdark:var(--sf-color-tertiary-800);--sf-color-tertiary-superdark:var(--sf-color-tertiary-950);--sf-color-tertiary-hover:var(--sf-color-tertiary-darker);--sf-color-tertiary-active:var(--sf-color-tertiary-xdark);--sf-color-tertiary-subtle:var(--sf-color-tertiary-a10);--sf-color-tertiary-muted:var(--sf-color-tertiary-a30);--sf-color-tertiary-ghost:var(--sf-color-tertiary-a5);--sf-color-action-50:color-mix(in oklab, var(--sf-color-action) 4%, var(--sf-color-base));--sf-color-action-100:color-mix(in oklab, var(--sf-color-action) 8%, var(--sf-color-base));--sf-color-action-200:color-mix(in oklab, var(--sf-color-action) 20%, var(--sf-color-base));--sf-color-action-300:color-mix(in oklab, var(--sf-color-action) 40%, var(--sf-color-base));--sf-color-action-400:color-mix(in oklab, var(--sf-color-action) 65%, var(--sf-color-base));--sf-color-action-500:var(--sf-color-action);--sf-color-action-600:color-mix(in oklab, var(--sf-color-action) 82%, var(--sf-color-text));--sf-color-action-700:color-mix(in oklab, var(--sf-color-action) 62%, var(--sf-color-text));--sf-color-action-800:color-mix(in oklab, var(--sf-color-action) 38%, var(--sf-color-text));--sf-color-action-900:color-mix(in oklab, var(--sf-color-action) 18%, var(--sf-color-text));--sf-color-action-950:color-mix(in oklab, var(--sf-color-action) 8%, var(--sf-color-text));--sf-color-action-a5:color-mix(in oklab, var(--sf-color-action) 5%, transparent);--sf-color-action-a10:color-mix(in oklab, var(--sf-color-action) 10%, transparent);--sf-color-action-a20:color-mix(in oklab, var(--sf-color-action) 20%, transparent);--sf-color-action-a30:color-mix(in oklab, var(--sf-color-action) 30%, transparent);--sf-color-action-a40:color-mix(in oklab, var(--sf-color-action) 40%, transparent);--sf-color-action-a50:color-mix(in oklab, var(--sf-color-action) 50%, transparent);--sf-color-action-a60:color-mix(in oklab, var(--sf-color-action) 60%, transparent);--sf-color-action-a70:color-mix(in oklab, var(--sf-color-action) 70%, transparent);--sf-color-action-a80:color-mix(in oklab, var(--sf-color-action) 80%, transparent);--sf-color-action-a90:color-mix(in oklab, var(--sf-color-action) 90%, transparent);--sf-color-action-a95:color-mix(in oklab, var(--sf-color-action) 95%, transparent);--sf-color-action-superlight:var(--sf-color-action-50);--sf-color-action-xlight:var(--sf-color-action-200);--sf-color-action-lighter:var(--sf-color-action-400);--sf-color-action-darker:var(--sf-color-action-600);--sf-color-action-xdark:var(--sf-color-action-800);--sf-color-action-superdark:var(--sf-color-action-950);--sf-color-action-hover:var(--sf-color-action-darker);--sf-color-action-active:var(--sf-color-action-xdark);--sf-color-action-subtle:var(--sf-color-action-a10);--sf-color-action-muted:var(--sf-color-action-a30);--sf-color-action-ghost:var(--sf-color-action-a5);--sf-color-neutral-50:color-mix(in oklab, var(--sf-color-neutral) 4%, var(--sf-color-base));--sf-color-neutral-100:color-mix(in oklab, var(--sf-color-neutral) 8%, var(--sf-color-base));--sf-color-neutral-200:color-mix(in oklab, var(--sf-color-neutral) 20%, var(--sf-color-base));--sf-color-neutral-300:color-mix(in oklab, var(--sf-color-neutral) 40%, var(--sf-color-base));--sf-color-neutral-400:color-mix(in oklab, var(--sf-color-neutral) 65%, var(--sf-color-base));--sf-color-neutral-500:var(--sf-color-neutral);--sf-color-neutral-600:color-mix(in oklab, var(--sf-color-neutral) 82%, var(--sf-color-text));--sf-color-neutral-700:color-mix(in oklab, var(--sf-color-neutral) 62%, var(--sf-color-text));--sf-color-neutral-800:color-mix(in oklab, var(--sf-color-neutral) 38%, var(--sf-color-text));--sf-color-neutral-900:color-mix(in oklab, var(--sf-color-neutral) 18%, var(--sf-color-text));--sf-color-neutral-950:color-mix(in oklab, var(--sf-color-neutral) 8%, var(--sf-color-text));--sf-color-neutral-a5:color-mix(in oklab, var(--sf-color-neutral) 5%, transparent);--sf-color-neutral-a10:color-mix(in oklab, var(--sf-color-neutral) 10%, transparent);--sf-color-neutral-a20:color-mix(in oklab, var(--sf-color-neutral) 20%, transparent);--sf-color-neutral-a30:color-mix(in oklab, var(--sf-color-neutral) 30%, transparent);--sf-color-neutral-a40:color-mix(in oklab, var(--sf-color-neutral) 40%, transparent);--sf-color-neutral-a50:color-mix(in oklab, var(--sf-color-neutral) 50%, transparent);--sf-color-neutral-a60:color-mix(in oklab, var(--sf-color-neutral) 60%, transparent);--sf-color-neutral-a70:color-mix(in oklab, var(--sf-color-neutral) 70%, transparent);--sf-color-neutral-a80:color-mix(in oklab, var(--sf-color-neutral) 80%, transparent);--sf-color-neutral-a90:color-mix(in oklab, var(--sf-color-neutral) 90%, transparent);--sf-color-neutral-a95:color-mix(in oklab, var(--sf-color-neutral) 95%, transparent);--sf-color-neutral-superlight:var(--sf-color-neutral-50);--sf-color-neutral-xlight:var(--sf-color-neutral-200);--sf-color-neutral-lighter:var(--sf-color-neutral-400);--sf-color-neutral-darker:var(--sf-color-neutral-600);--sf-color-neutral-xdark:var(--sf-color-neutral-800);--sf-color-neutral-superdark:var(--sf-color-neutral-950);--sf-color-neutral-hover:var(--sf-color-neutral-darker);--sf-color-neutral-active:var(--sf-color-neutral-xdark);--sf-color-neutral-subtle:var(--sf-color-neutral-a10);--sf-color-neutral-muted:var(--sf-color-neutral-a30);--sf-color-neutral-ghost:var(--sf-color-neutral-a5);--sf-color-base-50:color-mix(in oklab, var(--sf-color-text) 4%, var(--sf-color-base));--sf-color-base-100:color-mix(in oklab, var(--sf-color-text) 8%, var(--sf-color-base));--sf-color-base-200:color-mix(in oklab, var(--sf-color-text) 20%, var(--sf-color-base));--sf-color-base-300:color-mix(in oklab, var(--sf-color-text) 40%, var(--sf-color-base));--sf-color-base-400:color-mix(in oklab, var(--sf-color-text) 65%, var(--sf-color-base));--sf-color-base-500:var(--sf-color-base);--sf-color-base-600:color-mix(in oklab, var(--sf-color-base) 82%, var(--sf-color-text));--sf-color-base-700:color-mix(in oklab, var(--sf-color-base) 62%, var(--sf-color-text));--sf-color-base-800:color-mix(in oklab, var(--sf-color-base) 38%, var(--sf-color-text));--sf-color-base-900:color-mix(in oklab, var(--sf-color-base) 18%, var(--sf-color-text));--sf-color-base-950:color-mix(in oklab, var(--sf-color-base) 8%, var(--sf-color-text));--sf-color-base-a5:color-mix(in oklab, var(--sf-color-base) 5%, transparent);--sf-color-base-a10:color-mix(in oklab, var(--sf-color-base) 10%, transparent);--sf-color-base-a20:color-mix(in oklab, var(--sf-color-base) 20%, transparent);--sf-color-base-a30:color-mix(in oklab, var(--sf-color-base) 30%, transparent);--sf-color-base-a40:color-mix(in oklab, var(--sf-color-base) 40%, transparent);--sf-color-base-a50:color-mix(in oklab, var(--sf-color-base) 50%, transparent);--sf-color-base-a60:color-mix(in oklab, var(--sf-color-base) 60%, transparent);--sf-color-base-a70:color-mix(in oklab, var(--sf-color-base) 70%, transparent);--sf-color-base-a80:color-mix(in oklab, var(--sf-color-base) 80%, transparent);--sf-color-base-a90:color-mix(in oklab, var(--sf-color-base) 90%, transparent);--sf-color-base-a95:color-mix(in oklab, var(--sf-color-base) 95%, transparent);--sf-color-base-superlight:var(--sf-color-base-50);--sf-color-base-xlight:var(--sf-color-base-200);--sf-color-base-lighter:var(--sf-color-base-400);--sf-color-base-darker:var(--sf-color-base-600);--sf-color-base-xdark:var(--sf-color-base-800);--sf-color-base-superdark:var(--sf-color-base-950);--sf-color-base-hover:var(--sf-color-base-darker);--sf-color-base-active:var(--sf-color-base-xdark);--sf-color-base-subtle:var(--sf-color-base-a10);--sf-color-base-muted:var(--sf-color-base-a30);--sf-color-base-ghost:var(--sf-color-base-a5)}input:where([type=text],[type=email],[type=password],[type=url],[type=tel],[type=number],[type=search],[type=date],[type=datetime-local],[type=month],[type=week],[type=time],[type=color],:not([type])),select,textarea{appearance:none;inline-size:100%;padding-block:var(--sf-space-xs);padding-inline:var(--sf-space-s);font-size:var(--sf-text-m);line-height:var(--sf-leading-normal);color:var(--sf-color-text);background-color:var(--sf-color-surface);border:var(--sf-border-width-1) solid var(--sf-field-border-color,var(--sf-color-border));border-radius:var(--sf-radius-m);transition:var(--sf-transition-colors), border-color var(--sf-duration-fast) var(--sf-ease-out);display:block}input:where([type=text],[type=email],[type=password],[type=url],[type=tel],[type=number],[type=search],[type=date],[type=datetime-local],[type=month],[type=week],[type=time],[type=color],:not([type])):focus-visible,select:focus-visible,textarea:focus-visible{border-color:var(--sf-color-border--focus);outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}input:disabled,select:disabled,textarea:disabled{background-color:var(--sf-color-bg--disabled);color:var(--sf-color-text--disabled);opacity:var(--sf-opacity-disabled);cursor:not-allowed}input::placeholder,textarea::placeholder{color:var(--sf-color-text--placeholder);opacity:1}textarea{min-block-size:6rem}select{padding-inline-end:calc(var(--sf-space-s) + 1.5em)}select:not([multiple],[size]){background-repeat:no-repeat;background-position:right var(--sf-space-s) center;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='FieldText' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M2.5 4.5L6 8l3.5-3.5'/%3E%3C/svg%3E");background-size:1em}:dir(rtl) select:not([multiple],[size]){background-position:left var(--sf-space-s) center}button,input[type=submit],input[type=reset],input[type=button]{justify-content:center;align-items:center;gap:var(--sf-space-xs);padding-block:var(--sf-space-xs);padding-inline:var(--sf-space-m);font-size:var(--sf-text-m);font-weight:var(--sf-font-weight-heading);line-height:var(--sf-leading-normal);color:var(--sf-color-text--on-action);background-color:var(--sf-color-action);border:var(--sf-border-width-1) solid transparent;border-radius:var(--sf-radius-m);cursor:pointer;transition:var(--sf-transition-colors);text-decoration:none;display:inline-flex}button:hover,input[type=submit]:hover,input[type=reset]:hover,input[type=button]:hover{background-color:var(--sf-color-action-hover)}button:focus-visible,input[type=submit]:focus-visible,input[type=reset]:focus-visible,input[type=button]:focus-visible{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}button:disabled,input[type=submit]:disabled,input[type=reset]:disabled,input[type=button]:disabled{opacity:var(--sf-opacity-disabled);cursor:not-allowed;pointer-events:none}input[type=checkbox],input[type=radio]{block-size:1.125em;inline-size:1.125em;accent-color:var(--sf-color-action);cursor:pointer}input[type=file]{cursor:pointer}input[type=file]::file-selector-button{appearance:none;padding-block:var(--sf-space-xs);padding-inline:var(--sf-space-m);font-weight:var(--sf-font-weight-heading);color:var(--sf-color-text--on-action);background-color:var(--sf-color-action);border:var(--sf-border-width-1) solid transparent;border-radius:var(--sf-radius-m);cursor:pointer;transition:var(--sf-transition-colors);margin-inline-end:var(--sf-space-s)}input[type=file]::file-selector-button:hover{background-color:var(--sf-color-action-hover)}input[type=range]{appearance:none;cursor:pointer;background:0 0;inline-size:100%}input[type=range]::-webkit-slider-runnable-track{block-size:var(--sf-border-width-3,.25rem);background-color:var(--sf-color-border);border-radius:var(--sf-radius-full)}input[type=range]::-moz-range-track{block-size:var(--sf-border-width-3,.25rem);background-color:var(--sf-color-border);border-radius:var(--sf-radius-full)}input[type=range]::-webkit-slider-thumb{appearance:none;block-size:1em;inline-size:1em;background-color:var(--sf-color-action);border-radius:var(--sf-radius-full);margin-block-start:calc((var(--sf-border-width-3,.25rem) - 1em) / 2)}input[type=range]::-moz-range-thumb{background-color:var(--sf-color-action);border-radius:var(--sf-radius-full);border:none;block-size:1em;inline-size:1em}input[type=range]:focus-visible{outline:var(--sf-focus-ring-width) var(--sf-focus-ring-style) var(--sf-color-border--focus);outline-offset:var(--sf-focus-ring-offset)}fieldset{border:var(--sf-border-width-1) solid var(--sf-color-border--subtle);border-radius:var(--sf-radius-m);padding:var(--sf-space-m)}legend{font-weight:var(--sf-font-weight-heading);padding-inline:var(--sf-space-xs)}label{font-weight:var(--sf-font-weight-heading);color:var(--sf-field-text-color,inherit);margin-block-end:var(--sf-space-xs);display:block}label:has(:required):after,label:has(+:required):after{content:var(--sf-field-required-marker," *");color:var(--sf-color-error)}@supports not (height:100dvh){body{min-height:100vh}}@supports not selector(:focus-visible){:focus{outline:var(--sf-focus-ring-width,2px) var(--sf-focus-ring-style,solid) var(--sf-focus-ring-color,var(--sf-color-action));outline-offset:var(--sf-focus-ring-offset,2px)}}@supports not (scrollbar-gutter:stable){html{overflow-y:scroll}} /*# sourceMappingURL=slashed.full.flat.min.css.map */ diff --git a/dist/slashed.full.flat.min.css.map b/dist/slashed.full.flat.min.css.map index e8a442b1..9f431a71 100644 --- a/dist/slashed.full.flat.min.css.map +++ b/dist/slashed.full.flat.min.css.map @@ -1 +1 @@ -{"version":3,"sourceRoot":null,"mappings":"AAkJA,kGACA,qGACA,oGACA,kGACA,mGACA,iGAIA,mGACA,kGACA,+FACA,gGACA,iGAIA,wEAEA,0EACA,2EACA,2EACA,wEAOA,qvyBA4gCA,0DAMA,mLAaA,4CACE,sCAGF,6CAKA,2BAIA,sEAMA,0EAOA,wHAQA,2BAQA,qBAIA,2CAMA,iBAIA,iEAIA,uCAKA,yBAIA,sBAIA,gDAKA,gCAIA,oEAOA,uBAcA,mWAUA,yVAUA,oJACA,oJACA,oJACA,oJACA,oJACA,oJAEA,iFAOA,kDAIA,sCAIA,qCAIA,iJAOA,yDAKA,2DAKA,8EAMA,cACA,kBAcA,oRASA,6EACA,8CACA,4CACA,6CAEA,gCAIA,+MASA,iRAWA,2FASA,sBAIA,0GAKA,mFAIA,0GAMA,4BAIA,uCAIA,iDAIA,mDAIA,gDAIA,sBACA,oCACA,6EAIA,8IAMA,6CAIA,yCAIA,kCAKA,6CAIA,2IA6CA,mCACE,0DAQF,qDACA,mDAuBA,89BAqCA,gDACA,wDACA,sDACA,sDACA,sDACA,wDACA,0DAEA,gEAgBA,oGAGA,gGAQA,kJAKA,4MAkBA,wJAOA,2DACA,yDACA,uDACA,uDAOA,qEAKA,qCACA,mCACA,mCACA,mCACA,qCACA,uCAEA,qCACA,oCACA,uCAQA,0GAWA,iIAOA,4EAWA,gJAOA,uCACA,qCACA,qCACA,qCACA,uCACA,yCAEA,sCACA,2CACA,0CACA,mDAQA,kEAMA,wEAKA,+HAYA,8EAKA,6CACA,2CAQA,oEAMA,wFAKA,uDACA,6CASA,iIAMA,iGAIA,iDACA,+CACA,+CACA,+CACA,iDACA,mDAIA,qCAMA,0LAQA,8CACA,4CACA,4CACA,4CACA,8CAOA,uVAgBA,yHAOA,8CAIA,gCACA,8CACA,sDACA,uDAOA,oHAQA,sEAOA,sDACA,oDACA,sDACA,0DACA,gDACA,gDACA,sDAOA,uNAWA,sEAUA,gGAOA,mCAIA,yJAgBA,0FAMA,kFAMA,sCACE,8CAGA,kDACA,kDAOF,4EAUA,2FAKA,yBACE,+CACA,gDAGF,yBACE,+CACA,+CACA,+CACA,gDAOF,wFAMA,yBACE,2CACA,2CACA,2CACA,4CAQF,saAgBA,uCACA,mDACA,iDAOA,sOAQA,8CACE,+CAGF,+BACA,+BACA,8DACA,wDAMA,uDACA,yDAgCA,uEAKA,2DACA,+DACA,sDACA,6DACA,8EACA,iDACA,gCACA,gCACA,iDACA,gDACA,wHACA,4FACA,gFAKA,+EAMA,iFACA,+CACA,2DACA,8CACA,wFACA,+FACA,6DAQA,qDAcA,uEAMA,mHAOA,mDACA,mDACA,yFAUA,kDAIA,6CAWA,yCAUA,8LAeA,2FAKA,0CASA,mJAeA,6DAiBA,+FACA,qGACA,kGACA,4FACA,+FACA,+FACA,+FACA,+FACA,yFACA,sFACA,4FAmBA,gHAMA,iCACE,+BAmBF,yHAiCA,kCAIA,gCAIA,+BAQA,wGAOA,kDASA,wEAMA,gWAkBA,yBAIA,oEAOA,2RAmBA,4BAMA,2DAIA,mGASA,2DAIA,4EAeA,wBAIA,6BAIA,4BAgBA,+GAKA,6GAKA,iHAKA,iHAKA,2GAKA,wGAKA,8GASA,iFAMA,yDAMA,yBAIA,8DASA,wCASA,sCAIA,yDAKA,wEAUA,0DAKA,4HAKA,0BAQA,sCASA,6BAIA,kCAIA,sJAWA,6BAgCA,8CAEE,4BAKA,uCAMA,+QASA,sCACE,mJAaF,kDACA,oDACA,0DACA,8DACA,8DACA,gEACA,oDACA,wDA4BA,0PAUA,sCACE,iRAWF,6CACA,oDACA,wDACA,wDACA,0DACA,mDASF,iDACA,kDACA,kFACA,qFACA,kFACA,oFACA,oEACA,sEAEA,qCACA,oFAMA,6CACA,0DACA,8DAMA,4QAMA,8CACE,2DAkCF,wCAIA,6KAWA,uCACE,0IAQA,8JAaF,yMAcA,+BACE,uDAOA,sDAQF,6CACE,+DAYF,wBACE,oJAeF,mDAUA,6RAmBA,mVAYA,uCAYA,wKA4CA,iFAYA,2NAgBA,kWAeA,+KAYA,8BACE,sCAIA,iDAIA,2CAQA,6DAwCF,yBAIA,aAEE,+EAKA,yCAMA,kDASA,6EAMA,2DAKA,+CAIA,oEAIA,iCAIA,sBAIA,uDAMA,qBAKA,8GASA,qGAMA,qCASA,sBAGA,uCAGA,gDASA,sHAUA,sIA6CF,ksdA2SA,yqBAmCA,qbAcA,0LAUA,2FAUA,6BAQA,0DAUA,mZAOA,0FAQA,4iBAsBA,qIAOA,8PAQA,6KAaA,iIAYA,gCAGA,8ZAaA,2FAQA,iFAMA,wKAKA,2JAKA,wOAQA,2JAOA,uKASA,yIAMA,mFASA,2IAUA,gIA4bA,8BACE,uBAmBF,uCACE,iLAkBF,wCACE","sources":["slashed.full.flat.css"],"sourcesContent":["/* SLASHED v0.3.0 — slashed.full.flat.css (flat) */\n\n/* ─── core/layers.css ─── */\n/* SLASHED — core/layers.css\n CSS cascade layer order. Load this file first, always.\n ─────────────────────────────────────────────────── */\n\n/* ─── core/tokens.css ─── */\n/* ============================================================\n SLASHED — core/tokens.css\n @layer slashed.tokens\n\n ARCHITECTURE — 3 layers:\n 1. @property source tokens — animatable, 6 brand + 5 status (-light only); -dark is optional\n 2. Resolved semantic tokens — light-dark() with auto-derivation of dark from light (relative color syntax)\n 3. Derived tokens — relative color syntax (auto-contrast, tints/shades)\n\n THEMING — override just the 6 -light tokens. Dark mode auto-adapts via\n relative color syntax — zero extra work. For full control over dark mode,\n optionally override the -dark tokens (unregistered custom properties).\n\n Quick branding (6 tokens):\n :root {\n --sf-color-primary-light: oklch(0.45 0.20 264);\n --sf-color-secondary-light: oklch(0.25 0.03 260);\n --sf-color-tertiary-light: oklch(0.48 0.14 310);\n --sf-color-action-light: oklch(0.60 0.16 210);\n --sf-color-neutral-light: oklch(0.45 0.02 260);\n --sf-color-base-light: oklch(0.98 0.005 260);\n }\n Full per-mode control (add -dark overrides):\n :root {\n --sf-color-primary-dark: oklch(0.75 0.18 264);\n }\n\n BEM CONSUMER API — most tokens are intentionally unused by the\n framework itself. Tokens such as --sf-shadow-*, --sf-blur-*, --sf-gap,\n --sf-gradient-* etc. are meant for use in your own BEM classes:\n .card { box-shadow: var(--sf-shadow-m); gap: var(--sf-gap); }\n \"Not used internally\" does NOT mean dead code: every token is exercised\n in docs/demo.html and validated by the regression suite (tests/), so an\n unreferenced token is never untested or orphaned.\n\n RELATION TO tokens.layout.css — the spacing aliases in the \"Spacing\n aliases\" section are the source of truth for --sf-space-gap and\n --sf-space-content in tokens.layout.css. Overriding --sf-gap changes the\n gaps across all layout primitives (cluster, grid, sidebar, etc.).\n\n PUBLIC API vs INTERNAL (token-API freeze contract)\n ─────────────────────────────────────────────────\n Tokens are classified into three stability tiers (PUBLIC,\n PUBLIC-ADVANCED, INTERNAL).\n PUBLIC and PUBLIC-ADVANCED share the same SemVer guarantee\n (removal/rename = major bump after freeze). The distinction is\n documentation-level: ADVANCED tokens are powerful but niche — they\n live in a separate \"Advanced\" section in docs.\n\n PUBLIC — everyday knobs, prominently documented:\n · brand source tokens (--sf-color-{primary,secondary,tertiary,action,neutral,base}-light)\n · status source tokens (--sf-color-{success,warning,error,info,danger}-light)\n · resolved semantic colors (--sf-color-{primary,...,danger}, surfaces, text, borders, links, interactive bg, selection)\n · text-on-color (--sf-color-text--on-*)\n · code colors (--sf-color-code-bg, --sf-color-code-text)\n · status triplets (--sf-color-{success,...,danger}-{subtle,strong,muted})\n · gradients (--sf-gradient-*)\n · shadows (--sf-shadow-{none,xs,s,m,l,xl,2xl,inner})\n · text/drop shadows (--sf-text-shadow-*, --sf-drop-shadow-*)\n · blur (--sf-blur-*)\n · opacity (--sf-opacity-{0,10,25,50,75,100}, --sf-opacity-disabled, --sf-state-pending-opacity)\n · spacing (--sf-space-{none,px,gutter,2xs..4xl})\n · font families (--sf-font-{body,heading,display,mono,humanist,geometric,slab})\n · font weights (--sf-font-weight-{thin..black,body,heading,display})\n · font sizes (--sf-text-{2xs..4xl}, --sf-text-display-{s,m,l})\n · leading (--sf-leading-{tight,snug,normal,relaxed})\n · tracking (--sf-tracking-{tight,normal,wide,wider,widest})\n · icon sizes (--sf-icon-{xs,s,m,l,xl})\n · UI sizes (--sf-size-{xs,s,m,l,xl})\n · containers (--sf-container-{narrow,prose,default,wide,full})\n · ratios (--sf-ratio-{square,video,cinema,portrait,golden,4-3,3-2})\n · border widths (--sf-border-width-{hairline,1,2,3,4})\n · border styles (--sf-border-style, --strong, --soft, --dotted)\n · divider (--sf-divider-{width,style,color})\n · radius (--sf-radius-{none,xs,s,m,l,xl,2xl,3xl,4xl,full,pill})\n · duration (--sf-duration-{none,instant,fast,normal,slow,slower})\n · easing (--sf-ease-{linear,out,in,in-out,spring,elastic,bounce,overshoot})\n · transitions (--sf-transition-{all,colors,transform,opacity,shadow,fast,slow,enter,exit})\n · animations (--sf-animation-{fade-in,fade-out,slide-in-*,scale-*,color-pulse,ping,blink,float,spin,shimmer})\n · animation delays (--sf-animation-delay-{1..5})\n · z-index (--sf-z-{below,base,raised,low,mid,high,top,max})\n · header/sticky (--sf-header-height, --sf-sticky-offset)\n · touch target (--sf-touch-target)\n · focus ring (--sf-focus-ring-{width,offset,style,color})\n · caret (--sf-caret-color)\n · scrollbar (--sf-scrollbar-{thumb,track})\n · BEM aliases (--sf-gap, --sf-content-gap, --sf-component-pad, --sf-field-block,\n --sf-field-required-marker, --sf-link-external-marker)\n · section padding (--sf-section-pad, --sf-section-pad--{xs,s,m,l,xl,2xl})\n · typography aliases (--sf-body-*, --sf-code-font-size, --sf-heading-*, --sf-h{1..6}-*)\n · current-font-weight (--sf-current-font-weight)\n · color scheme (--sf-color-scheme)\n · stroke (--sf-stroke-{thin,regular,bold,heavy})\n · layout tokens — all of tokens.layout.css (per-primitive gaps, sidebar, grid, bento, etc.)\n · macro tokens — all of tokens.macros.css (flow-space, line-clamp, aspect, scroll-shadow-size)\n · palette tokens — all of optional/tokens.palette.css\n · scoped override tokens (--sf-icon-size, --sf-bento-cols, --sf-bento-row,\n --sf-color-code-block-bg, --sf-color-code-block-text,\n --sf-field-border-color, --sf-field-text-color)\n\n PUBLIC-ADVANCED — stable (same SemVer guarantee) but niche/powerful:\n · --sf-lumlocker (LumLocker lightness override)\n · --sf-contrast-bias, --sf-contrast-threshold (text-contrast tuning)\n · --sf-shadow-strength, --sf-shadow-color, --sf-shadow-glow-color (shadow internals)\n · --sf-font-features, --sf-font-variation, --sf-optical-sizing (OpenType)\n · --sf-space-scale, --sf-text-scale, --sf-text-display-scale,\n --sf-radius-scale, --sf-motion-scale (global multipliers)\n · --sf-scroll-timeline-range-{start,end} (scroll-driven animation)\n · --sf-mask-scrim-{start,end} (edge-fade masks)\n · --sf-safe-{top,bottom,left,right} (safe-area insets)\n · --sf-perspective-{near,normal,far} (3D transforms)\n · --sf-truncate-suffix (ellipsis character override)\n · --sf-radius-outer (concentric radius helper)\n · --sf-is-active, --sf-is-current, --sf-is-pressed, --sf-is-open (state flags)\n · --sf-focus-ring-shadow (composite focus shadow)\n · --sf-print-{page-margin,page-size,base-size} (print tokens)\n\n INTERNAL — implementation detail, may change without a major bump:\n · --sf-is-dark (mode flag — set via data-theme, not directly)\n\n CANONICAL-SOURCE ALIASES (intentional synonym hops, kept by design):\n · --sf-space-gap → --sf-gap (layout primitives read this name)\n · --sf-space-content → --sf-content-gap (stack / prose rhythm)\n · --sf-section-pad → --sf-section-pad--m (unsuffixed = the -m default)\n These are deliberate two-name contracts: override the canonical token\n (right side) to move everything downstream. The alias graph is otherwise\n ≤3 hops (per-primitive → layout-system → canonical source), giving three\n override levels: local (style=\"--sf-cluster-gap\"), all-primitives\n (--sf-space-gap), or global (--sf-gap).\n ============================================================ */\n\n\n/* ============================================================\n @property — BRAND COLORS (-light source tokens, animatable)\n Override these 6 tokens to rebrand — dark mode auto-derives.\n Optionally set --sf-color-X-dark (no @property) for full control.\n ============================================================ */\n\n@property --sf-color-primary-light { syntax: \"\"; inherits: true; initial-value: oklch(0.45 0.20 264); }\n@property --sf-color-secondary-light { syntax: \"\"; inherits: true; initial-value: oklch(0.25 0.03 260); }\n@property --sf-color-tertiary-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.14 310); }\n@property --sf-color-action-light { syntax: \"\"; inherits: true; initial-value: oklch(0.60 0.16 210); }\n@property --sf-color-neutral-light { syntax: \"\"; inherits: true; initial-value: oklch(0.45 0.02 260); }\n@property --sf-color-base-light { syntax: \"\"; inherits: true; initial-value: oklch(0.98 0.005 260); }\n\n/* @property — STATUS COLORS (-light source tokens) */\n\n@property --sf-color-success-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.17 150); }\n@property --sf-color-warning-light { syntax: \"\"; inherits: true; initial-value: oklch(0.75 0.17 80); }\n@property --sf-color-error-light { syntax: \"\"; inherits: true; initial-value: oklch(0.62 0.20 35); }\n@property --sf-color-info-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.15 240); }\n@property --sf-color-danger-light { syntax: \"\"; inherits: true; initial-value: oklch(0.48 0.24 12); }\n\n/* Mode flag — drives formula direction for non-color dark overrides.\n Set by base.css via color-scheme switches. */\n@property --sf-is-dark { syntax: \"\"; inherits: true; initial-value: 0; }\n\n@property --sf-is-active { syntax: \"\"; inherits: true; initial-value: 0; }\n@property --sf-is-current { syntax: \"\"; inherits: true; initial-value: 0; }\n@property --sf-is-pressed { syntax: \"\"; inherits: true; initial-value: 0; }\n@property --sf-is-open { syntax: \"\"; inherits: true; initial-value: 0; }\n\n\n/* ============================================================\n DERIVED SEMANTIC TOKENS\n ============================================================ */\n\n:root {\n\n /* ----------------------------------------------------------\n Color scheme\n ---------------------------------------------------------- */\n --sf-color-scheme: light dark;\n\n /* ----------------------------------------------------------\n LumLocker — OKLCH L value used by the\n :root[data-lumlocker] override in core/themes.css.\n Locks all 5 brand colors (primary, secondary, tertiary,\n action, neutral) to one shared lightness while keeping their\n individual hue and chroma. Base is excluded — it must remain\n a light surface for page background.\n Override to change the locked lightness:\n :root { --sf-lumlocker: 0.55 }\n ---------------------------------------------------------- */\n --sf-lumlocker: 0.65;\n\n /* ----------------------------------------------------------\n Resolved color tokens — auto-switch via light-dark().\n Components reference these. Override the -light / -dark\n source tokens above to customise; or override these directly\n with your own light-dark() expression.\n ---------------------------------------------------------- */\n /* Dark auto-derivation formula (brand + status):\n clamp(0.65, 0.95 - l*0.5, 0.88) lightens dark-mode value relative to the light source.\n Base inverts: clamp(0.16, 1.18 - l, 0.24) — near-white flips to near-dark.\n Override any --sf-color-X-dark to take full per-mode control. */\n --sf-color-primary: light-dark(var(--sf-color-primary-light), var(--sf-color-primary-dark, oklch(from var(--sf-color-primary-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-secondary: light-dark(var(--sf-color-secondary-light), var(--sf-color-secondary-dark, oklch(from var(--sf-color-secondary-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-tertiary: light-dark(var(--sf-color-tertiary-light), var(--sf-color-tertiary-dark, oklch(from var(--sf-color-tertiary-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-action: light-dark(var(--sf-color-action-light), var(--sf-color-action-dark, oklch(from var(--sf-color-action-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-neutral: light-dark(var(--sf-color-neutral-light), var(--sf-color-neutral-dark, oklch(from var(--sf-color-neutral-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-base: light-dark(var(--sf-color-base-light), var(--sf-color-base-dark, oklch(from var(--sf-color-base-light) clamp(0.16, calc(1.18 - l), 0.24) calc(c * 0.5) h)));\n\n --sf-color-success: light-dark(var(--sf-color-success-light), var(--sf-color-success-dark, oklch(from var(--sf-color-success-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-warning: light-dark(var(--sf-color-warning-light), var(--sf-color-warning-dark, oklch(from var(--sf-color-warning-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-error: light-dark(var(--sf-color-error-light), var(--sf-color-error-dark, oklch(from var(--sf-color-error-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-info: light-dark(var(--sf-color-info-light), var(--sf-color-info-dark, oklch(from var(--sf-color-info-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n --sf-color-danger: light-dark(var(--sf-color-danger-light), var(--sf-color-danger-dark, oklch(from var(--sf-color-danger-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));\n\n\n /* ----------------------------------------------------------\n Surfaces — derived from --sf-color-base (auto-adapts)\n ---------------------------------------------------------- */\n --sf-color-bg: oklch(from var(--sf-color-base) calc(l + 0.02) c h);\n --sf-color-surface: var(--sf-color-base);\n --sf-color-well: oklch(from var(--sf-color-base) calc(l - 0.02) c h);\n --sf-color-raised: oklch(from var(--sf-color-base) calc(l + 0.04) c h);\n --sf-color-overlay: oklch(from var(--sf-color-base) l c h / 0.9);\n --sf-color-inverse: oklch(from var(--sf-color-base) calc(1 - l) c h);\n\n\n /* ----------------------------------------------------------\n Text — light and dark require opposite formula directions,\n so each side of light-dark() references its source token.\n ---------------------------------------------------------- */\n --sf-color-text: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h),\n oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1) c h)\n );\n --sf-color-text--secondary: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.15, calc(l - 0.25 - var(--sf-contrast-bias)), 0.45) c h),\n oklch(from var(--sf-color-neutral) clamp(0.55, calc(l + 0.1 + var(--sf-contrast-bias)), 0.90) c h)\n );\n --sf-color-text--muted: var(--sf-color-neutral);\n --sf-color-text--placeholder: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.45, calc(l + 0.15), 0.75) c h),\n oklch(from var(--sf-color-neutral) clamp(0.35, calc(l - 0.1), 0.65) c h)\n );\n --sf-color-text--disabled: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.55, calc(l + 0.25), 0.82) c h),\n oklch(from var(--sf-color-neutral) clamp(0.25, calc(l - 0.2), 0.55) c h)\n );\n --sf-color-text--inverse: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.85, calc(l + 0.4), 0.98) c h),\n oklch(from var(--sf-color-neutral) clamp(0.05, calc(l - 0.4), 0.35) c h)\n );\n --sf-color-heading: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h),\n oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1) c h)\n );\n /* Inline-code text colour. Auto-contrasts with --sf-color-code-bg using\n the same luminance formula as --sf-color-text--on-*. This prevents\n invisible text when code appears inside inverted-colour containers\n (e.g. sf-imposter with a dark background where inherited colour is\n near-white). Override with a tint value if desired. */\n --sf-color-code-text: oklch(from var(--sf-color-code-bg) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n\n\n /* ----------------------------------------------------------\n Text-on-color — sign(var(--sf-contrast-threshold) - l) * 999 is mode-agnostic:\n auto-selects light or dark text based on background luminance.\n Uses resolved tokens — works for any user-supplied color.\n\n CONTRAST NOTE: Guarantees ≥ 3:1 (WCAG AA Large Text / UI).\n Colors in the mid-luminance range (L ≈ 0.52–0.67) may not\n reach 4.5:1 (AA Normal Text) regardless of threshold chosen —\n this is a mathematical limitation of any binary black/white\n decision. The SLASHED configurator will warn users when their\n brand colors fall in this range. For body text on colored\n backgrounds, override --sf-color-text--on-* manually.\n ---------------------------------------------------------- */\n --sf-color-text--on-primary: oklch(from var(--sf-color-primary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-secondary: oklch(from var(--sf-color-secondary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-tertiary: oklch(from var(--sf-color-tertiary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-action: oklch(from var(--sf-color-action) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-neutral: oklch(from var(--sf-color-neutral) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-base: var(--sf-color-text);\n --sf-color-text--on-inverse: var(--sf-color-text--inverse);\n --sf-color-text--on-success: oklch(from var(--sf-color-success) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-warning: oklch(from var(--sf-color-warning) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-error: oklch(from var(--sf-color-error) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-info: oklch(from var(--sf-color-info) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n --sf-color-text--on-danger: oklch(from var(--sf-color-danger) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);\n\n\n /* ----------------------------------------------------------\n Borders — direction-dependent, reference source tokens.\n Light: borders are lighter than neutral (l + offset).\n Dark: borders are darker than neutral (l - offset).\n ---------------------------------------------------------- */\n --sf-color-border: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.70, calc(l + 0.35), 0.95) 0.005 h),\n oklch(from var(--sf-color-neutral) clamp(0.25, calc(l - 0.3), 0.55) 0.005 h)\n );\n --sf-color-border--subtle: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.75, calc(l + 0.4), 0.97) 0.005 h),\n oklch(from var(--sf-color-neutral) clamp(0.20, calc(l - 0.38), 0.45) 0.005 h)\n );\n --sf-color-border--strong: light-dark(\n oklch(from var(--sf-color-neutral-light) clamp(0.55, calc(l + 0.1), 0.85) 0.02 h),\n oklch(from var(--sf-color-neutral) clamp(0.38, calc(l - 0.1), 0.65) 0.02 h)\n );\n --sf-color-border--focus: var(--sf-color-action);\n --sf-color-border--disabled: oklch(from var(--sf-color-border--subtle) l 0 h / 0.5);\n --sf-color-border--translucent: oklch(from var(--sf-color-neutral) l c h / 0.15);\n\n\n /* ----------------------------------------------------------\n Links — derived from --sf-color-action (auto-adapts)\n ---------------------------------------------------------- */\n /* Links keep the action hue but CLAMP lightness toward a contrast-safe\n band — a ceiling in light mode, a floor in dark mode — so link text\n clears WCAG AA (4.5:1) on the page background for the default palette\n AND the large majority of brand overrides (a fixed offset alone only\n fixed the default). Lightness dominates OKLCH contrast but doesn't\n fully determine it: very high-chroma hues (saturated yellow/green) can\n still fall short even at a safe lightness — those remain the consumer's\n responsibility (see docs/theming.md → \"Link contrast\").\n\n NOTE: the consumer-facing :link rule in core/base.css uses\n `a:link` (not bare `a`) so its specificity matches WebKit's UA\n stylesheet `a:link { color: -webkit-link }`. Without that\n pseudo-class the framework's `--sf-color-link` value is\n silently ignored on unvisited anchors in WebKit and\n `-webkit-link` (≈ #00728f in dark mode) paints instead —\n regardless of how this token is computed. PR #73 and several\n follow-up attempts mistakenly chased the formula. */\n --sf-color-link: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.07, 0.48), 1) c h),\n oklch(from var(--sf-color-action) clamp(0.68, l, 1) c h)\n );\n --sf-color-link--hover: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.15, 0.40), 1) c h),\n oklch(from var(--sf-color-action) clamp(0, max(l + 0.10, 0.68), 1) c h)\n );\n --sf-color-link--active: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.21, 0.34), 1) c h),\n oklch(from var(--sf-color-action) clamp(0, max(l + 0.15, 0.74), 1) c h)\n );\n /* Visited link — 60° hue shift from action gives clearly distinct\n chroma without colliding with default tertiary (h ≈ 310). Same\n lightness clamp as the base link. If your brand puts action and\n tertiary close in hue, override directly:\n :root { --sf-color-link--visited: #6b46c1; } */\n --sf-color-link--visited: light-dark(\n oklch(from var(--sf-color-action) clamp(0, min(l - 0.07, 0.48), 1) c calc(h + 60)),\n oklch(from var(--sf-color-action) clamp(0.68, l, 1) c calc(h + 60))\n );\n --sf-color-link--underline: oklch(from var(--sf-color-action) l c h / 0.3);\n --sf-color-link--disabled: var(--sf-color-text--disabled);\n\n\n /* ----------------------------------------------------------\n Interactive states — derived from --sf-color-neutral (auto-adapts)\n ---------------------------------------------------------- */\n --sf-color-bg--hover: oklch(from var(--sf-color-neutral) l c h / 0.08);\n --sf-color-bg--active: oklch(from var(--sf-color-neutral) l c h / 0.12);\n --sf-color-bg--selected: oklch(from var(--sf-color-action) l c h / 0.1);\n --sf-color-bg--focus: oklch(from var(--sf-color-action) l c h / 0.06);\n --sf-color-bg--disabled: var(--sf-color-well);\n --sf-color-code-bg: var(--sf-color-well);\n\n /* ----------------------------------------------------------\n Selection & backdrop\n ---------------------------------------------------------- */\n --sf-color-selection-bg: oklch(from var(--sf-color-action) l c h / 0.22);\n --sf-color-selection-text: inherit;\n --sf-color-mark-bg: oklch(from var(--sf-color-warning) l c h / 0.25);\n --sf-color-mark-text: inherit;\n --sf-color-dim: oklch(0 0 0 / 0.5);\n\n\n /* ----------------------------------------------------------\n Scrollbar\n ---------------------------------------------------------- */\n --sf-scrollbar-thumb: var(--sf-color-neutral);\n --sf-scrollbar-track: transparent;\n\n\n /* ----------------------------------------------------------\n Status triplets — bg/border use same formula both modes;\n text is direction-dependent so uses source tokens.\n error = form validation, input errors\n danger = destructive actions (delete, remove)\n ---------------------------------------------------------- */\n --sf-color-success-subtle: oklch(from var(--sf-color-success) l c h / 0.12);\n --sf-color-success-strong: light-dark(\n oklch(from var(--sf-color-success-light) calc(l - 0.15) c h),\n oklch(from var(--sf-color-success) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-success-muted: oklch(from var(--sf-color-success) l c h / 0.3);\n\n --sf-color-warning-subtle: oklch(from var(--sf-color-warning) l c h / 0.12);\n --sf-color-warning-strong: light-dark(\n oklch(from var(--sf-color-warning-light) calc(l - 0.25) c h),\n oklch(from var(--sf-color-warning) clamp(0.70, calc(l + 0.05), 1) c h)\n );\n --sf-color-warning-muted: oklch(from var(--sf-color-warning) l c h / 0.3);\n\n --sf-color-error-subtle: oklch(from var(--sf-color-error) l c h / 0.1);\n --sf-color-error-strong: light-dark(\n oklch(from var(--sf-color-error-light) calc(l - 0.1) c h),\n oklch(from var(--sf-color-error) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-error-muted: oklch(from var(--sf-color-error) l c h / 0.3);\n\n --sf-color-info-subtle: oklch(from var(--sf-color-info) l c h / 0.1);\n --sf-color-info-strong: light-dark(\n oklch(from var(--sf-color-info-light) calc(l - 0.1) c h),\n oklch(from var(--sf-color-info) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-info-muted: oklch(from var(--sf-color-info) l c h / 0.3);\n\n --sf-color-danger-subtle: oklch(from var(--sf-color-danger) l c h / 0.1);\n --sf-color-danger-strong: light-dark(\n oklch(from var(--sf-color-danger-light) calc(l - 0.1) c h),\n oklch(from var(--sf-color-danger) clamp(0.70, calc(l + 0.15), 1) c h)\n );\n --sf-color-danger-muted: oklch(from var(--sf-color-danger) l c h / 0.3);\n\n\n /* ----------------------------------------------------------\n Gradients — derived from brand colors (auto-adapt)\n ---------------------------------------------------------- */\n --sf-gradient-primary: linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) calc(l - 0.08) c h));\n --sf-gradient-secondary: linear-gradient(135deg, var(--sf-color-secondary), oklch(from var(--sf-color-secondary) calc(l - 0.08) c h));\n --sf-gradient-tertiary: linear-gradient(135deg, var(--sf-color-tertiary), oklch(from var(--sf-color-tertiary) calc(l - 0.08) c h));\n --sf-gradient-brand: linear-gradient(135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) l c calc(h + 30)));\n --sf-gradient-surface: linear-gradient(180deg, var(--sf-color-surface), var(--sf-color-bg));\n\n --sf-gradient-fade--r: linear-gradient(to right, transparent, var(--sf-color-bg));\n --sf-gradient-fade--l: linear-gradient(to left, transparent, var(--sf-color-bg));\n --sf-gradient-fade--t: linear-gradient(to top, transparent, var(--sf-color-bg));\n --sf-gradient-fade--b: linear-gradient(to bottom, transparent, var(--sf-color-bg));\n\n\n /* ----------------------------------------------------------\n Shadow — strength scales up in dark mode via --sf-is-dark\n ---------------------------------------------------------- */\n --sf-shadow-strength: calc(0.08 + var(--sf-is-dark) * 0.17);\n --sf-shadow-glow-color: var(--sf-color-primary);\n --sf-shadow-glow: 0 0 15px 2px oklch(from var(--sf-shadow-glow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-opacity-disabled: 0.45;\n --sf-state-pending-opacity: 0.7;\n\n\n /* ----------------------------------------------------------\n Focus / form colors\n ---------------------------------------------------------- */\n --sf-focus-ring-color: var(--sf-color-action);\n --sf-focus-ring-shadow: 0 0 0 var(--sf-focus-ring-offset) var(--sf-color-bg),\n 0 0 0 calc(var(--sf-focus-ring-offset) + var(--sf-focus-ring-width)) var(--sf-focus-ring-color);\n --sf-caret-color: var(--sf-color-action);\n\n}\n\n\n\n\n/* ============================================================\n NON-COLOR DESIGN TOKENS\n ============================================================ */\n\n:root {\n\n /* ----------------------------------------------------------\n Scale multipliers\n ---------------------------------------------------------- */\n --sf-space-scale: 1;\n --sf-text-scale: 1;\n --sf-text-display-scale: 1;\n --sf-radius-scale: 1;\n --sf-motion-scale: 1;\n\n\n /* ----------------------------------------------------------\n Font families\n ---------------------------------------------------------- */\n --sf-font-body: system-ui, -apple-system, sans-serif;\n --sf-font-heading: var(--sf-font-body);\n --sf-font-display: var(--sf-font-heading);\n --sf-font-mono: ui-monospace, monospace;\n\n /* Optional system-font stacks (zero-cost, no @import) */\n --sf-font-humanist: \"Seravek\", \"Gill Sans Nova\", \"Ubuntu\", \"Calibri\", \"DejaVu Sans\", source-sans-pro, sans-serif;\n --sf-font-geometric: \"Avenir\", \"Montserrat\", \"Corbel\", \"URW Gothic\", source-sans-pro, sans-serif;\n --sf-font-slab: \"Rockwell\", \"Rockwell Nova\", \"Roboto Slab\", \"DejaVu Serif\", \"Sitka Small\", serif;\n\n /* Opt-in OpenType / variable-font controls (token-only, not applied by the framework) */\n --sf-font-features: normal; /* e.g. \"cv11\", \"ss01\" */\n --sf-font-variation: normal; /* e.g. \"wght\" 450, \"opsz\" 32 */\n --sf-optical-sizing: auto;\n\n\n /* ----------------------------------------------------------\n Font weights\n ---------------------------------------------------------- */\n /* Numeric scale */\n --sf-font-weight-thin: 100;\n --sf-font-weight-extralight: 200;\n --sf-font-weight-light: 300;\n --sf-font-weight-normal: 400;\n --sf-font-weight-medium: 500;\n --sf-font-weight-semibold: 600;\n --sf-font-weight-bold: 700;\n --sf-font-weight-extrabold: 800;\n --sf-font-weight-black: 900;\n\n /* Semantic aliases — reference the numeric scale */\n --sf-font-weight-body: var(--sf-font-weight-normal);\n --sf-font-weight-heading: var(--sf-font-weight-semibold);\n --sf-font-weight-display: var(--sf-font-weight-bold);\n\n /* State token — consumed by .is-current in states.css.\n Override to match your nav design (e.g. semibold for thin fonts). */\n --sf-current-font-weight: var(--sf-font-weight-bold);\n\n\n /* ----------------------------------------------------------\n Font sizes — fluid\n ---------------------------------------------------------- */\n --sf-text-2xs: clamp(0.51rem, calc(0.00029629629629629656 * (100vw - 22.5rem) + 0.51rem), 0.53rem);\n --sf-text-xs: clamp(0.64rem, calc(0.0008888888888888881 * (100vw - 22.5rem) + 0.64rem), 0.7rem);\n --sf-text-s: clamp(0.8rem, calc(0.002074074074074073 * (100vw - 22.5rem) + 0.8rem), 0.94rem);\n --sf-text-m: clamp(1rem, calc(0.003703703703703704 * (100vw - 22.5rem) + 1rem), 1.25rem);\n --sf-text-l: clamp(1.25rem, calc(0.006222222222222221 * (100vw - 22.5rem) + 1.25rem), 1.67rem);\n --sf-text-xl: clamp(1.56rem, calc(0.00977777777777778 * (100vw - 22.5rem) + 1.56rem), 2.22rem);\n --sf-text-2xl: clamp(1.95rem, calc(0.014962962962962963 * (100vw - 22.5rem) + 1.95rem), 2.96rem);\n --sf-text-3xl: clamp(2.44rem, calc(0.022370370370370374 * (100vw - 22.5rem) + 2.44rem), 3.95rem);\n --sf-text-4xl: clamp(3.05rem, calc(0.03274074074074074 * (100vw - 22.5rem) + 3.05rem), 5.26rem);\n\n --sf-text-display-s: clamp(2.4rem, calc(0.00888888888888889 * (100vw - 22.5rem) + 2.4rem), 3rem);\n --sf-text-display-m: clamp(3rem, calc(0.014814814814814815 * (100vw - 22.5rem) + 3rem), 4rem);\n --sf-text-display-l: clamp(3.75rem, calc(0.023407407407407408 * (100vw - 22.5rem) + 3.75rem), 5.33rem);\n\n\n /* ----------------------------------------------------------\n Line heights & letter spacing\n ---------------------------------------------------------- */\n --sf-leading-tight: 1.1;\n --sf-leading-snug: 1.3;\n --sf-leading-normal: 1.5;\n --sf-leading-relaxed: 1.625;\n\n --sf-tracking-tight: -0.025em;\n --sf-tracking-normal: 0;\n --sf-tracking-wide: 0.025em;\n --sf-tracking-wider: 0.05em;\n --sf-tracking-widest: 0.1em;\n\n\n /* ----------------------------------------------------------\n Icon sizes — em-based by design.\n Icons are sized in em so they scale with the surrounding\n text: an icon in a button at --sf-text-m renders at 1rem;\n moved into a heading at --sf-text-2xl, the same icon scales\n up automatically.\n Global scaling flows transitively through --sf-text-scale\n (parent text grows → em-icons grow) — there is no separate\n --sf-icon-scale knob by design. clamp() is intentionally\n absent here: making icons fluid INDEPENDENTLY of context\n would break the relationship to surrounding text.\n Capped at --xl (3em ≈ 48px on default text); above that the\n element ceases to be an icon and becomes an illustration.\n ---------------------------------------------------------- */\n --sf-icon-xs: 0.875em;\n --sf-icon-s: 1em;\n --sf-icon-m: 1.5em;\n --sf-icon-l: 2em;\n --sf-icon-xl: 3em;\n\n\n /* ----------------------------------------------------------\n Spacing — fixed\n ---------------------------------------------------------- */\n --sf-space-none: 0;\n --sf-space-px: 1px;\n --sf-space-gutter: var(--sf-space-l);\n\n /* Spacing — fluid */\n --sf-space-2xs: calc(clamp(0.51rem, calc(0.004888888888888888 * (100vw - 22.5rem) + 0.51rem), 0.84rem) * var(--sf-space-scale));\n --sf-space-xs: calc(clamp(0.64rem, calc(0.007259259259259258 * (100vw - 22.5rem) + 0.64rem), 1.13rem) * var(--sf-space-scale));\n --sf-space-s: calc(clamp(0.8rem, calc(0.01037037037037037 * (100vw - 22.5rem) + 0.8rem), 1.5rem) * var(--sf-space-scale));\n --sf-space-m: calc(clamp(1rem, calc(0.014814814814814815 * (100vw - 22.5rem) + 1rem), 2rem) * var(--sf-space-scale));\n --sf-space-l: calc(clamp(1.25rem, calc(0.021037037037037035 * (100vw - 22.5rem) + 1.25rem), 2.67rem) * var(--sf-space-scale));\n --sf-space-xl: calc(clamp(1.56rem, calc(0.029481481481481477 * (100vw - 22.5rem) + 1.56rem), 3.55rem) * var(--sf-space-scale));\n --sf-space-2xl: calc(clamp(1.95rem, calc(0.04133333333333333 * (100vw - 22.5rem) + 1.95rem), 4.74rem) * var(--sf-space-scale));\n --sf-space-3xl: calc(clamp(2.44rem, calc(0.057333333333333326 * (100vw - 22.5rem) + 2.44rem), 6.31rem) * var(--sf-space-scale));\n --sf-space-4xl: calc(clamp(3.05rem, calc(0.07955555555555556 * (100vw - 22.5rem) + 3.05rem), 8.42rem) * var(--sf-space-scale));\n\n\n /* ----------------------------------------------------------\n UI sizes\n ---------------------------------------------------------- */\n --sf-size-xs: 1.5rem;\n --sf-size-s: 2rem;\n --sf-size-m: 2.5rem;\n --sf-size-l: 2.75rem;\n --sf-size-xl: 3.5rem;\n\n\n /* ----------------------------------------------------------\n Containers\n ---------------------------------------------------------- */\n --sf-container-narrow: 38rem;\n --sf-container-prose: 65ch;\n --sf-container-default: 75rem;\n --sf-container-wide: 90rem;\n --sf-container-full: 100%;\n\n\n /* ----------------------------------------------------------\n Aspect ratios\n ---------------------------------------------------------- */\n --sf-ratio-square: 1;\n --sf-ratio-video: 16 / 9;\n --sf-ratio-cinema: 21 / 9;\n --sf-ratio-4-3: 4 / 3;\n --sf-ratio-3-2: 3 / 2;\n --sf-ratio-portrait: 3 / 4;\n --sf-ratio-golden: 1.618 / 1;\n\n\n /* ----------------------------------------------------------\n Border widths\n --sf-border-width-hairline: device-pixel-dependent. On 1x DPR\n displays (Windows non-Retina) the browser will round 0.5px up\n to 1px, so it visually matches --sf-border-width-1. On 2x+\n DPR (macOS/iOS Retina, most modern phones) it renders as a\n true half-pixel hairline. Use it for subtle separators where\n 1px on low-DPR is acceptable.\n ---------------------------------------------------------- */\n --sf-border-width-hairline: 0.5px;\n --sf-border-width-1: 1px;\n --sf-border-width-2: 2px;\n --sf-border-width-3: 3px;\n --sf-border-width-4: 4px;\n\n /* ----------------------------------------------------------\n Border styles — paired with --sf-border-width-* and\n --sf-color-border*. Consumer BEM:\n .my-card { border: var(--sf-border-width-1) var(--sf-border-style) var(--sf-color-border); }\n Override per element to switch decorative styles without\n rewriting the rule.\n ---------------------------------------------------------- */\n --sf-border-style: solid; /* default */\n --sf-border-style-strong: solid; /* alias for emphasis */\n --sf-border-style-soft: dashed;\n --sf-border-style-dotted: dotted;\n\n /* ----------------------------------------------------------\n Divider — global separator system. Drives `hr` (base) and\n the `.sf-divider` layout primitive.\n ---------------------------------------------------------- */\n --sf-divider-width: var(--sf-border-width-1);\n --sf-divider-style: solid;\n --sf-divider-color: var(--sf-color-border);\n\n\n /* ----------------------------------------------------------\n Border radius\n --sf-radius-full: intentionally not scaled by --sf-radius-scale.\n A pill/circle is a topological constant, not a relative size:\n any --sf-radius-scale: 0 (\"sharp\" design system) should still\n render pill buttons as pills. 9999px is a magic-number convention\n large enough to clip to half the shorter edge for any realistic UI.\n ---------------------------------------------------------- */\n --sf-radius-none: 0;\n --sf-radius-xs: calc(2px * var(--sf-radius-scale));\n --sf-radius-s: calc(4px * var(--sf-radius-scale));\n --sf-radius-m: calc(8px * var(--sf-radius-scale));\n --sf-radius-l: calc(12px * var(--sf-radius-scale));\n --sf-radius-xl: calc(16px * var(--sf-radius-scale));\n --sf-radius-2xl: calc(24px * var(--sf-radius-scale));\n --sf-radius-3xl: calc(32px * var(--sf-radius-scale));\n --sf-radius-4xl: calc(48px * var(--sf-radius-scale));\n --sf-radius-full: 9999px;\n /* Pill — semantic alias of --sf-radius-full. Reach for this name\n on rectangles you want capsule-shaped (badges, tags, chips); use\n --sf-radius-full when you mean \"as round as topologically possible\". */\n --sf-radius-pill: var(--sf-radius-full);\n /* Concentric helper — outer radius for a container that wraps\n content padded by --sf-component-pad with inner radius m.\n Keeps inner elements visually proportional inside a padded box.\n Override per use:\n .my-card { border-radius: var(--sf-radius-outer); }\n Maths: outer = inner + padding ⇒ aligned curves at corners. */\n --sf-radius-outer: calc(var(--sf-radius-m) + var(--sf-component-pad));\n\n\n /* ----------------------------------------------------------\n Shadows — BEM consumer API\n Use in your own classes: .card { box-shadow: var(--sf-shadow-m); }\n ---------------------------------------------------------- */\n /* Shadow tint — near-black derived from the neutral; the tint simply\n inherits the neutral's own chroma/hue, so a colourless neutral yields\n colourless shadows. Forced dark in both modes; --sf-shadow-strength\n handles dark-mode intensity. Override to a brand colour for explicitly\n tinted shadows — keep it dark or you get bright slabs:\n oklch(from var(--sf-color-primary) 0.15 c h). */\n --sf-shadow-color: oklch(from var(--sf-color-neutral) 0.15 c h);\n\n --sf-shadow-none: none;\n --sf-shadow-xs: 0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7));\n --sf-shadow-s: 0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 2px 6px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, var(--sf-shadow-strength), 0.7));\n --sf-shadow-m: 0 1px 3px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-shadow-l: 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 8px 24px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), 0.7)),\n 0 16px 48px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-shadow-xl: 0 2px 8px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7)),\n 0 12px 36px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3.5), 0.7)),\n 0 24px 72px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7));\n --sf-shadow-2xl: 0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.6), 0.7)),\n 0 20px 60px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 4), 0.7)),\n 0 40px 100px -8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 5), 0.7));\n --sf-shadow-inner: inset 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n\n\n /* ----------------------------------------------------------\n Text shadows — share --sf-shadow-color / --sf-shadow-strength\n ---------------------------------------------------------- */\n --sf-text-shadow-none: none;\n --sf-text-shadow-s: 0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7));\n --sf-text-shadow-m: 0 2px 4px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));\n --sf-text-shadow-l: 0 4px 8px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7));\n\n /* Drop shadows — for filter: drop-shadow() (respects alpha edges) */\n --sf-drop-shadow-s: drop-shadow(0 1px 2px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7)));\n --sf-drop-shadow-m: drop-shadow(0 4px 6px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7)));\n --sf-drop-shadow-l: drop-shadow(0 8px 16px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7)));\n\n\n /* ----------------------------------------------------------\n Blur & opacity\n ---------------------------------------------------------- */\n --sf-blur-xs: 4px;\n --sf-blur-s: 8px;\n --sf-blur-m: 16px;\n --sf-blur-l: 32px;\n --sf-blur-xl: 48px;\n\n /* 3D perspective */\n --sf-perspective-near: 500px;\n --sf-perspective-normal: 1000px;\n --sf-perspective-far: 2000px;\n\n --sf-opacity-0: 0;\n --sf-opacity-10: 0.1;\n --sf-opacity-25: 0.25;\n --sf-opacity-50: 0.5;\n --sf-opacity-75: 0.75;\n --sf-opacity-100: 1;\n\n\n /* ----------------------------------------------------------\n Motion & easing\n ---------------------------------------------------------- */\n --sf-duration-none: 0ms;\n --sf-duration-instant: calc(100ms * var(--sf-motion-scale));\n --sf-duration-fast: calc(150ms * var(--sf-motion-scale));\n --sf-duration-normal: calc(250ms * var(--sf-motion-scale));\n --sf-duration-slow: calc(400ms * var(--sf-motion-scale));\n --sf-duration-slower: calc(600ms * var(--sf-motion-scale));\n\n --sf-ease-linear: linear;\n --sf-ease-out: cubic-bezier(0.25, 0, 0.15, 1);\n --sf-ease-in: cubic-bezier(0.5, 0, 0.75, 0.25);\n --sf-ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);\n --sf-ease-spring: linear(0, 0.5, 1.1, 0.95, 1.02, 1);\n --sf-ease-elastic: linear(0, 0.3, 1.2, 0.9, 1.05, 1);\n --sf-ease-bounce: linear(0, 0.35 18%, 1 32%, 0.86 42%, 1.02 56%, 0.98 72%, 1);\n --sf-ease-overshoot: linear(0, 0.6 30%, 1.08 55%, 0.98 75%, 1);\n\n\n /* ----------------------------------------------------------\n Animation presets — pair keyframes (core/motion.css) with\n duration + easing. Use directly: animation: var(--sf-animation-fade-in);\n ---------------------------------------------------------- */\n --sf-animation-fade-in: sf-fade-in var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-fade-out: sf-fade-out var(--sf-duration-normal) var(--sf-ease-in) both;\n --sf-animation-slide-in-up: sf-slide-in-up var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-slide-in-down: sf-slide-in-down var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-slide-in-left: sf-slide-in-left var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-slide-in-right: sf-slide-in-right var(--sf-duration-normal) var(--sf-ease-out) both;\n --sf-animation-scale-up: sf-scale-up var(--sf-duration-normal) var(--sf-ease-overshoot) both;\n --sf-animation-scale-down: sf-scale-down var(--sf-duration-normal) var(--sf-ease-in) both;\n --sf-animation-color-pulse: sf-color-pulse var(--sf-duration-slow) var(--sf-ease-in-out) infinite;\n --sf-animation-ping: sf-ping var(--sf-duration-slow) var(--sf-ease-out) infinite;\n --sf-animation-blink: sf-blink calc(1s * var(--sf-motion-scale)) steps(1, end) infinite;\n --sf-animation-float: sf-float calc(3s * var(--sf-motion-scale)) var(--sf-ease-in-out) infinite;\n --sf-animation-spin: sf-spin var(--sf-duration-slower) linear infinite;\n --sf-animation-shimmer: sf-shimmer calc(1.5s * var(--sf-motion-scale)) ease-in-out infinite;\n\n /* Stagger delays — apply to successive items for entrance choreography\n (e.g. style=\"animation-delay: var(--sf-animation-delay-2)\"). Tokens\n only; SLASHED ships no .sf-stagger utility class (by design — apply via token presets or your own BEM rules). */\n --sf-animation-delay-1: calc(75ms * var(--sf-motion-scale));\n --sf-animation-delay-2: calc(150ms * var(--sf-motion-scale));\n --sf-animation-delay-3: calc(225ms * var(--sf-motion-scale));\n --sf-animation-delay-4: calc(300ms * var(--sf-motion-scale));\n --sf-animation-delay-5: calc(375ms * var(--sf-motion-scale));\n\n\n /* ----------------------------------------------------------\n Scroll-driven animation range — consumer API. Attach these\n to your own scroll-timeline via animation-range. Not consumed\n by the framework itself; intended for BEM component authors.\n ---------------------------------------------------------- */\n --sf-scroll-timeline-range-start: 0%;\n --sf-scroll-timeline-range-end: 100%;\n\n\n /* ----------------------------------------------------------\n Mask scrim — edge-fade stops for scroll reels / overflow.\n Use in a mask-image gradient to fade content near an edge:\n mask-image: linear-gradient(to right,\n transparent 0, #000 var(--sf-mask-scrim-start),\n #000 calc(100% - var(--sf-mask-scrim-end)), transparent 100%);\n ---------------------------------------------------------- */\n --sf-mask-scrim-start: var(--sf-space-l);\n --sf-mask-scrim-end: var(--sf-space-l);\n\n\n /* ----------------------------------------------------------\n Z-index\n ---------------------------------------------------------- */\n --sf-z-below: -1;\n --sf-z-base: 0;\n --sf-z-raised: 1;\n --sf-z-low: 10;\n --sf-z-mid: 100;\n --sf-z-high: 500;\n --sf-z-top: 900;\n --sf-z-max: 9999;\n\n\n /* ----------------------------------------------------------\n Layout & a11y\n ---------------------------------------------------------- */\n --sf-header-height: 8rem;\n --sf-sticky-offset: var(--sf-header-height);\n --sf-focus-ring-width: 2px;\n --sf-focus-ring-offset: 2px;\n --sf-focus-ring-style: solid;\n --sf-touch-target: var(--sf-size-l);\n /* Global text-contrast knob. Nudges derived reading-text colours\n toward the extremes (darker in light mode, lighter in dark mode).\n Positive = more contrast; 0 = no change. Consumed by\n --sf-color-text, --sf-color-text--secondary, --sf-color-heading. */\n --sf-contrast-bias: 0;\n /* Lightness crossover for text-on-color auto-contrast. Colours with\n L above this value get dark text; below get light text. Default 0.6\n guarantees ≥ 3:1 for most colours. Shift down (e.g. 0.55) if your\n brand sits in the 0.52-0.60 range and you prefer white text on it. */\n --sf-contrast-threshold: 0.6;\n\n --sf-safe-top: env(safe-area-inset-top, 0px);\n --sf-safe-bottom: env(safe-area-inset-bottom, 0px);\n --sf-safe-left: env(safe-area-inset-left, 0px);\n --sf-safe-right: env(safe-area-inset-right, 0px);\n\n\n /* ----------------------------------------------------------\n Print\n ---------------------------------------------------------- */\n --sf-print-page-margin: 2cm;\n --sf-print-page-size: a4;\n --sf-print-base-size: 11pt;\n\n\n /* ----------------------------------------------------------\n Stroke widths\n ---------------------------------------------------------- */\n --sf-stroke-thin: 1px;\n --sf-stroke-regular: 1.5px;\n --sf-stroke-bold: 2px;\n --sf-stroke-heavy: 3px;\n\n}\n\n\n/* ============================================================\n TYPOGRAPHY ALIASES\n ============================================================ */\n\n:root {\n --sf-body-font-size: var(--sf-text-m);\n --sf-body-line-height: var(--sf-leading-normal);\n --sf-body-font-weight: var(--sf-font-weight-body);\n --sf-body-font-family: var(--sf-font-body);\n --sf-body-color: var(--sf-color-text);\n --sf-body-text-wrap: pretty;\n --sf-body-strong-weight: var(--sf-font-weight-bold);\n --sf-body-em-style: italic;\n\n --sf-code-font-size: 0.875em;\n\n --sf-heading-font-family: var(--sf-font-heading);\n --sf-heading-color: var(--sf-color-heading);\n --sf-heading-text-wrap: balance;\n\n --sf-h1-size: var(--sf-text-4xl);\n --sf-h1-line-height: var(--sf-leading-tight);\n --sf-h1-font-weight: var(--sf-font-weight-heading);\n --sf-h1-letter-spacing: var(--sf-tracking-tight);\n\n --sf-h2-size: var(--sf-text-3xl);\n --sf-h2-line-height: var(--sf-leading-tight);\n --sf-h2-font-weight: var(--sf-font-weight-heading);\n --sf-h2-letter-spacing: var(--sf-tracking-tight);\n\n --sf-h3-size: var(--sf-text-2xl);\n --sf-h3-line-height: var(--sf-leading-snug);\n --sf-h3-font-weight: var(--sf-font-weight-heading);\n --sf-h3-letter-spacing: var(--sf-tracking-normal);\n\n --sf-h4-size: var(--sf-text-xl);\n --sf-h4-line-height: var(--sf-leading-snug);\n --sf-h4-font-weight: var(--sf-font-weight-heading);\n --sf-h4-letter-spacing: var(--sf-tracking-normal);\n\n --sf-h5-size: var(--sf-text-l);\n --sf-h5-line-height: var(--sf-leading-normal);\n --sf-h5-font-weight: var(--sf-font-weight-heading);\n --sf-h5-letter-spacing: var(--sf-tracking-normal);\n\n --sf-h6-size: var(--sf-text-m);\n --sf-h6-line-height: var(--sf-leading-normal);\n --sf-h6-font-weight: var(--sf-font-weight-heading);\n --sf-h6-letter-spacing: var(--sf-tracking-wide);\n\n\n /* ----------------------------------------------------------\n Spacing aliases — BEM consumer API\n Use in your own BEM classes: .nav { gap: var(--sf-gap); }\n Source of truth for tokens.layout.css:\n --sf-space-gap → var(--sf-gap) (all layout primitives)\n --sf-space-content → var(--sf-content-gap) (stack, prose)\n ---------------------------------------------------------- */\n --sf-gap: var(--sf-space-m); /* gap between BEM elements */\n --sf-content-gap: var(--sf-space-s); /* rhythm within a component */\n --sf-component-pad: var(--sf-space-m); /* standard component padding */\n --sf-field-block: var(--sf-space-l); /* form-field block spacing */\n --sf-field-required-marker: \" *\";\n /* Leading space is baked into the default so consumers can fully\n disable the indicator with `--sf-link-external-marker: \"\"` —\n without the space a bare \"\" still left a stray space after the\n link text. Override with your own glyph (include leading space\n if you want one): `:root { --sf-link-external-marker: \" →\" }`. */\n --sf-link-external-marker: \" \\2197\"; /* \" ↗\" — consumed by .sf-link-external */\n\n\n /* ----------------------------------------------------------\n Section padding\n ---------------------------------------------------------- */\n --sf-section-pad: var(--sf-section-pad--m);\n --sf-section-pad--xs: var(--sf-space-xl);\n --sf-section-pad--s: var(--sf-space-2xl);\n --sf-section-pad--m: var(--sf-space-3xl);\n --sf-section-pad--l: var(--sf-space-4xl);\n --sf-section-pad--xl: calc(var(--sf-space-4xl) * 1.5);\n --sf-section-pad--2xl: calc(var(--sf-space-4xl) * 2);\n\n\n /* ----------------------------------------------------------\n Transition shorthands\n\n --sf-transition-all transitions ALL properties — convenient but\n a performance footgun (forces the browser to watch every computed\n value). Prefer the granular tokens below for production UI.\n ---------------------------------------------------------- */\n --sf-transition-all: all var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-colors:\n color var(--sf-duration-normal) var(--sf-ease-out),\n background-color var(--sf-duration-normal) var(--sf-ease-out),\n border-color var(--sf-duration-normal) var(--sf-ease-out),\n text-decoration-color var(--sf-duration-normal) var(--sf-ease-out),\n fill var(--sf-duration-normal) var(--sf-ease-out),\n stroke var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-transform: transform var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-opacity: opacity var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-shadow: box-shadow var(--sf-duration-normal) var(--sf-ease-out);\n\n --sf-transition-fast: all var(--sf-duration-fast) var(--sf-ease-out);\n --sf-transition-slow: all var(--sf-duration-slow) var(--sf-ease-in-out);\n --sf-transition-enter: all var(--sf-duration-normal) var(--sf-ease-out);\n --sf-transition-exit: all var(--sf-duration-fast) var(--sf-ease-in);\n}\n\n/* ─── core/tokens.layout.css ─── */\n/* ============================================================\n SLASHED — core/tokens.layout.css\n Semantic tokens for layout primitives.\n Load before: core/layout.css\n Layer: slashed.tokens\n Prefix: --sf-\n\n DEPENDENCIES — requires tokens.css (loaded earlier).\n Spacing aliases inherit from the BEM consumer API in tokens.css:\n --sf-space-gap → var(--sf-gap) from tokens.css\n --sf-space-content → var(--sf-content-gap) from tokens.css\n Override --sf-gap or --sf-content-gap (the canonical source) to change\n the gaps across all layout primitives at once. --sf-space-gap and\n --sf-space-content are CANONICAL-SOURCE ALIASES — a second public name\n kept for layout-primitive readability, not a redundant indirection.\n\n PUBLIC API — every --sf-*-gap / --sf-*-padding / --sf-*-min / etc. here\n is a per-primitive override knob (e.g. style=\"--sf-cluster-gap: …\").\n No INTERNAL tokens live in this file.\n\n Each layout primitive (cluster, grid, sidebar…) has its own\n tokens (--sf-cluster-gap etc.) for local overrides.\n ============================================================ */\n\n:root {\n\n /* ----------------------------------------------------------\n Shared spacing aliases\n ---------------------------------------------------------- */\n --sf-space-gap: var(--sf-gap); /* component-level gap */\n --sf-space-content: var(--sf-content-gap); /* within-component rhythm */\n\n /* ----------------------------------------------------------\n Stack\n ---------------------------------------------------------- */\n --sf-stack-gap: var(--sf-space-content);\n\n /* ----------------------------------------------------------\n Box\n ---------------------------------------------------------- */\n --sf-box-padding: var(--sf-space-m);\n --sf-box-border-width: 0;\n --sf-box-border-color: var(--sf-color-border);\n\n /* ----------------------------------------------------------\n Center\n ---------------------------------------------------------- */\n --sf-center-max: var(--sf-container-default);\n --sf-center-gutter: var(--sf-space-gutter);\n\n /* ----------------------------------------------------------\n Cluster\n ---------------------------------------------------------- */\n --sf-cluster-gap: var(--sf-space-gap);\n --sf-cluster-align: center;\n --sf-cluster-justify: flex-start;\n\n /* ----------------------------------------------------------\n Sidebar\n ---------------------------------------------------------- */\n --sf-sidebar-gap: var(--sf-space-gap);\n --sf-sidebar-min-width: 50%; /* content column minimum */\n --sf-sidebar-width: 18rem; /* sidebar panel default size */\n\n /* ----------------------------------------------------------\n Switcher\n ---------------------------------------------------------- */\n --sf-switcher-threshold: 30rem;\n --sf-switcher-gap: var(--sf-space-gap);\n\n /* ----------------------------------------------------------\n Grid (auto-fill, breakpoint-free)\n ---------------------------------------------------------- */\n --sf-grid-min: 16rem;\n --sf-grid-gap: var(--sf-space-gap);\n\n --sf-grid-min-xs: 10rem;\n --sf-grid-min-s: 13rem;\n --sf-grid-min-m: 16rem;\n --sf-grid-min-l: 20rem;\n --sf-grid-min-xl: 24rem;\n --sf-grid-min-2xl: 28rem;\n\n /* ----------------------------------------------------------\n Cover\n ---------------------------------------------------------- */\n --sf-cover-min-height: 100dvh;\n --sf-cover-padding: var(--sf-section-pad);\n\n /* ----------------------------------------------------------\n Frame\n ---------------------------------------------------------- */\n --sf-frame-ratio: 16 / 9; /* literal , valid in aspect-ratio */\n\n /* ----------------------------------------------------------\n Icon (boxed variant) — extras for .sf-icon--boxed.\n The naked .sf-icon sizing tokens (--sf-icon-xs..xl) live in\n core/tokens.css alongside the typography scale.\n ---------------------------------------------------------- */\n --sf-icon-box-pad: 0.5em;\n --sf-icon-box-radius: var(--sf-radius-s);\n --sf-icon-box-bg: var(--sf-color-well);\n --sf-icon-box-border: var(--sf-border-width-1) solid var(--sf-color-border);\n\n /* ----------------------------------------------------------\n Reel\n ---------------------------------------------------------- */\n --sf-reel-item-width: max-content;\n --sf-reel-gap: var(--sf-space-gap);\n --sf-reel-height: auto;\n\n /* ----------------------------------------------------------\n Imposter (centered absolute/fixed overlay)\n ---------------------------------------------------------- */\n --sf-imposter-margin: var(--sf-space-m);\n\n /* ----------------------------------------------------------\n Bento grid\n ---------------------------------------------------------- */\n --sf-bento-cols-default: 3;\n --sf-bento-row-default: 10rem;\n --sf-bento-row-compact: 6rem;\n --sf-bento-row-tall: 16rem;\n --sf-bento-gap: var(--sf-space-gap);\n\n /* ----------------------------------------------------------\n Content grid (breakout pattern)\n ---------------------------------------------------------- */\n --sf-breakout-width: var(--sf-container-wide);\n --sf-content-width: var(--sf-container-default);\n\n /* ----------------------------------------------------------\n Prose\n ---------------------------------------------------------- */\n --sf-prose-paragraph: var(--sf-space-content);\n\n /* ----------------------------------------------------------\n Alternate (zigzag)\n ---------------------------------------------------------- */\n --sf-alternate-gap: var(--sf-space-content);\n --sf-alternate-inner-gap: var(--sf-space-gap);\n\n}\n\n/* ─── core/tokens.macros.css ─── */\n/* ============================================================\n SLASHED — core/tokens.macros.css\n Semantic tokens for macros (recipes / patterns).\n Load before: core/macros.css\n Layer: slashed.tokens\n Prefix: --sf-\n\n DEPENDENCIES — requires tokens.css (loaded earlier).\n Macro tokens reference --sf-space-* / --sf-radius-* / etc.\n\n PUBLIC API — every token here is a per-recipe override knob\n (e.g. style=\"--sf-line-clamp: 5\"). No INTERNAL tokens live in\n this file.\n\n Architectural parallel — these tokens stand alongside\n tokens.layout.css: same prefix, same shape, same status\n (essential, public, override-friendly).\n ============================================================ */\n\n:root {\n\n /* ----------------------------------------------------------\n Flow — distance between flow children (lobotomized owl).\n Consumed by .sf-flow > * + *.\n ---------------------------------------------------------- */\n --sf-flow-space: var(--sf-space-content);\n\n /* ----------------------------------------------------------\n Line clamp — default line count for .sf-line-clamp-N.\n Override per element: style=\"--sf-line-clamp: 5\".\n The fixed-N variants .sf-line-clamp-2 / -3 don't read this\n token (they hardcode the value), so the default only\n applies to .sf-line-clamp-N callers.\n ---------------------------------------------------------- */\n --sf-line-clamp: 3;\n\n /* ----------------------------------------------------------\n Truncate — ellipsis character for .sf-truncate.\n Stays as a token so consumers can swap to ASCII \"...\" or a\n localised character without rewriting the rule. The CSS\n text-overflow: ellipsis property doesn't read it directly;\n it's exposed for consumer-built variants.\n ---------------------------------------------------------- */\n --sf-truncate-suffix: \"\\2026\";\n\n /* ----------------------------------------------------------\n Aspect ratio — default for .sf-aspect (generic ratio container,\n distinct from layout's .sf-frame which has its own --sf-frame-ratio).\n ---------------------------------------------------------- */\n --sf-aspect: 16 / 9;\n\n /* ----------------------------------------------------------\n Scroll shadow / overflow fade — mask gradient size.\n Used by .sf-scroll-shadow (top+bottom) and .sf-overflow-fade\n (end-edge horizontal fade).\n ---------------------------------------------------------- */\n --sf-scroll-shadow-size: 2rem;\n\n}\n\n/* ─── core/reset.css ─── */\n/* SLASHED — core/reset.css\n @layer slashed.reset\n ─────────────────────────────────────────────────── */\n\n\n*, *::before, *::after {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n\nhtml {\n text-size-adjust: 100%;\n scroll-padding-top: var(--sf-header-height, 3.5rem);\n scrollbar-gutter: stable;\n color-scheme: var(--sf-color-scheme, light dark);\n hanging-punctuation: first last;\n}\n\n/* interpolate-size enables animations to/from intrinsic sizing\n keywords (auto, min-content, max-content, fit-content). Shipped:\n Chrome 129 (Aug 2024), Safari 18 (Sep 2024). Firefox: behind a\n flag as of 2026-Q2. Wrapped in @supports so unsupporting engines\n get clean progressive enhancement, not an unknown-declaration. */\n@supports (interpolate-size: allow-keywords) {\n html { interpolate-size: allow-keywords; }\n}\n\nbody {\n min-height: 100dvh;\n min-inline-size: 320px;\n}\n\nmenu, ul, ol {\n list-style: none;\n}\n\nimg, picture, video, canvas, svg {\n display: block;\n max-width: 100%;\n height: auto;\n}\n\niframe, embed, object {\n display: block;\n max-width: 100%;\n height: unset;\n border: none;\n}\n\nbutton, input, select, textarea {\n font: inherit;\n color: inherit;\n letter-spacing: inherit;\n color-scheme: inherit;\n line-height: inherit;\n}\n\nbutton {\n text-transform: none;\n}\n\n::file-selector-button {\n font: inherit;\n}\n\nsearch {\n display: block;\n}\n\nfieldset {\n border: none;\n padding: 0;\n min-width: 0;\n}\n\nlegend {\n padding: 0;\n}\n\na, button, input, select, textarea, summary {\n touch-action: manipulation;\n}\n\nsummary {\n list-style: none;\n cursor: pointer;\n}\n\ntextarea {\n resize: vertical;\n}\n\nbutton {\n cursor: pointer;\n}\n\ntable {\n border-collapse: collapse;\n border-spacing: 0;\n}\n\n[hidden] {\n display: none !important;\n}\n\ndialog, [popover] {\n padding: 0;\n border: none;\n background: none;\n color: inherit;\n}\n\n[inert] {\n cursor: default;\n}\n\n/* ─── core/base.css ─── */\n/* SLASHED — core/base.css\n @layer slashed.base\n ─────────────────────────────────────────────────── */\n\n\n/* Dark/light mode lives in core/themes.css (slashed.themes layer).\n The baseline color-scheme: var(--sf-color-scheme, light dark) is\n set on html in core/reset.css. */\n\nbody {\n font-family: var(--sf-body-font-family, var(--sf-font-body));\n font-size: var(--sf-body-font-size, var(--sf-text-m));\n line-height: var(--sf-body-line-height, var(--sf-leading-normal));\n font-weight: var(--sf-body-font-weight, var(--sf-font-weight-body));\n color: var(--sf-body-color, var(--sf-color-text));\n background-color: var(--sf-color-bg, #fff);\n font-synthesis: none;\n}\n\nh1, h2, h3, h4, h5, h6 {\n font-family: var(--sf-heading-font-family, var(--sf-font-heading));\n line-height: var(--sf-leading-tight);\n color: var(--sf-heading-color, var(--sf-color-heading));\n text-wrap: var(--sf-heading-text-wrap);\n overflow-wrap: break-word;\n text-rendering: optimizelegibility;\n scroll-margin-top: calc(var(--sf-header-height, 3.5rem) + var(--sf-space-m));\n}\n\nh1 { font-size: var(--sf-h1-size); line-height: var(--sf-h1-line-height); font-weight: var(--sf-h1-font-weight); letter-spacing: var(--sf-h1-letter-spacing); }\nh2 { font-size: var(--sf-h2-size); line-height: var(--sf-h2-line-height); font-weight: var(--sf-h2-font-weight); letter-spacing: var(--sf-h2-letter-spacing); }\nh3 { font-size: var(--sf-h3-size); line-height: var(--sf-h3-line-height); font-weight: var(--sf-h3-font-weight); letter-spacing: var(--sf-h3-letter-spacing); }\nh4 { font-size: var(--sf-h4-size); line-height: var(--sf-h4-line-height); font-weight: var(--sf-h4-font-weight); letter-spacing: var(--sf-h4-letter-spacing); }\nh5 { font-size: var(--sf-h5-size); line-height: var(--sf-h5-line-height); font-weight: var(--sf-h5-font-weight); letter-spacing: var(--sf-h5-letter-spacing); }\nh6 { font-size: var(--sf-h6-size); line-height: var(--sf-h6-line-height); font-weight: var(--sf-h6-font-weight); letter-spacing: var(--sf-h6-letter-spacing); }\n\np {\n text-wrap: var(--sf-body-text-wrap);\n overflow-wrap: break-word;\n orphans: 2;\n widows: 2;\n}\n\nb, strong {\n font-weight: var(--sf-body-strong-weight);\n}\n\nem {\n font-style: var(--sf-body-em-style);\n}\n\nsmall {\n font-size: var(--sf-text-s, 80%);\n}\n\nmark {\n background-color: var(--sf-color-mark-bg, yellow);\n color: var(--sf-color-mark-text, black);\n border-radius: var(--sf-radius-xs);\n padding-inline: 0.2em;\n}\n\nabbr[title] {\n text-decoration: underline dotted;\n cursor: help;\n}\n\nkbd, samp {\n font-family: var(--sf-font-mono);\n font-size: inherit;\n}\n\nsub, sup {\n font-size: 75%;\n line-height: 0;\n position: relative;\n vertical-align: baseline;\n}\nsup { top: -0.5em; }\nsub { bottom: -0.25em; }\n\n/* `a:link` (specificity 0,1,1) rather than bare `a` (0,0,1) so the\n author rule wins over WebKit's UA stylesheet rule\n `a:link { color: -webkit-link; }`. The bare-`a` form silently\n loses to the UA `:link` rule on unvisited anchors in WebKit,\n which paints `-webkit-link` (≈ #00728f in dark mode) regardless\n of whatever `--sf-color-link` resolves to — producing axe's\n 3.12 : 1 dark-mode contrast failure that survived four attempts\n to \"fix\" the link colour formula (PR #73, then three iterations\n of PR #76). The colour formula was never the problem; the\n selector specificity was. Hover / visited / active rules already\n used pseudo-classes so they had matching specificity and were\n never affected. */\na:link {\n color: var(--sf-color-link);\n text-decoration-color: var(--sf-color-link--underline);\n text-underline-offset: 0.15em;\n text-underline-position: from-font;\n transition:\n color var(--sf-duration-fast) var(--sf-ease-out),\n text-decoration-color var(--sf-duration-fast) var(--sf-ease-out);\n}\na:hover { color: var(--sf-color-link--hover); text-decoration-color: currentcolor; }\na:visited { color: var(--sf-color-link--visited); }\na:active { color: var(--sf-color-link--active); }\na:not([class]) { text-decoration-skip-ink: auto; }\n\na, code {\n overflow-wrap: break-word;\n}\n\ncode {\n font-family: var(--sf-font-mono);\n font-size: var(--sf-code-font-size);\n padding: 0.125em 0.3em;\n border-radius: var(--sf-radius-xs);\n background-color: var(--sf-color-code-bg);\n color: var(--sf-color-code-text, inherit);\n}\n\npre {\n font-family: var(--sf-font-mono);\n font-size: var(--sf-text-s);\n padding: var(--sf-space-m);\n border-radius: var(--sf-radius-m);\n background: var(--sf-color-code-block-bg, var(--sf-color-code-bg));\n color: var(--sf-color-code-block-text, inherit);\n overflow-x: auto;\n tab-size: 2;\n hyphens: none;\n}\npre code {\n background: none;\n color: inherit;\n border: 0;\n padding: 0;\n font-size: inherit;\n border-radius: 0;\n}\n\nsvg {\n fill: currentcolor;\n}\n\nhr {\n border: none;\n border-block-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);\n}\n\n:target {\n scroll-margin-top: calc(var(--sf-header-height, 3.5rem) + var(--sf-space-m));\n}\n\n::selection {\n background: var(--sf-color-selection-bg);\n color: var(--sf-color-selection-text);\n text-shadow: none;\n}\n\nth, caption {\n text-align: start;\n}\n\naddress, cite, dfn, var {\n font-style: normal;\n}\n\ninput, textarea {\n caret-color: var(--sf-caret-color);\n}\n\ninput, progress {\n accent-color: var(--sf-color-action);\n}\n\n::backdrop {\n background-color: var(--sf-color-dim);\n}\n\noptgroup { font: inherit; }\n::file-selector-button { font: inherit; }\n::placeholder { opacity: 1; color: var(--sf-color-text--placeholder, currentcolor); }\n\n/* Minimal rich-block defaults — just enough to distinguish from

.\n Full styling lives in .sf-prose (core/layout.css). */\nblockquote {\n border-inline-start: var(--sf-border-width-3) solid var(--sf-color-border);\n padding-inline-start: var(--sf-space-m);\n font-style: italic;\n}\n\ndt {\n font-weight: var(--sf-font-weight-heading);\n}\n\ndd {\n margin-inline-start: var(--sf-space-m);\n}\n\ntable {\n width: 100%;\n text-align: start;\n}\n\nth {\n font-weight: var(--sf-font-weight-heading);\n}\n\ntd, th {\n padding: var(--sf-space-2xs) var(--sf-space-xs);\n border-block-end: var(--sf-border-width-hairline) solid var(--sf-color-border--subtle);\n}\n\n/* ─── core/themes.css ─── */\n/* SLASHED — core/themes.css\n @layer slashed.themes\n\n Theme switching: token reassignments only. No new selectors,\n no new properties — strictly remaps existing tokens based on\n system preference and explicit data-theme attributes.\n\n Cascade position is significant. slashed.themes sits ABOVE\n slashed.{states, utilities, components, layout, base} (see\n core/layers.css), so a theme's token reassignment cannot be\n accidentally beaten by a component or utility rule of equal\n specificity. Only consumer code in slashed.overrides (or\n unlayered CSS) can override a theme.\n\n USAGE\n → follows OS preference\n → whole page dark\n

→ only this section dark\n
→ only this section light\n\n The base color-scheme: var(--sf-color-scheme, light dark)\n declaration lives in core/reset.css and provides the baseline.\n This layer flips it.\n\n MULTI-BRAND — scope a palette to any subtree by re-declaring the 6\n source tokens under a selector (e.g. [data-brand=\"x\"]). See\n optional/theme-example.css and docs/theming.md.\n\n THEME TRANSITION — toggling [data-theme] via document.startViewTransition()\n animates through the ::view-transition(root) rules in core/motion.css.\n For a plain cross-fade of colours, opt into a transition class on \n (example in optional/theme-example.css) — never transition every colour\n globally and permanently (perf footgun).\n ─────────────────────────────────────────────────── */\n\n\n/* OS preference — only when no data-theme is set on :root.\n A section-level [data-theme] still wins via the explicit\n selectors below (higher specificity within the same layer). */\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme]) {\n color-scheme: dark;\n --sf-is-dark: 1;\n }\n}\n\n/* Explicit theme — works on :root (whole page) or any element\n (section-level theming). */\n[data-theme=\"light\"] { color-scheme: light; --sf-is-dark: 0; }\n[data-theme=\"dark\"] { color-scheme: dark; --sf-is-dark: 1; }\n\n\n/* LumLocker — opt-in via [data-lumlocker] on :root.\n Locks the 5 brand colors (primary, secondary, tertiary, action,\n neutral) to one shared OKLCH L value (--sf-lumlocker,\n default 0.65 in core/tokens.css), keeping each color's own hue\n and chroma. Useful when a brand picker produces colors with\n wildly different L values that look mismatched in weight.\n\n Base is excluded by design — base IS the page surface (L ~0.98 in\n light mode, ~0.20 in dark) and locking it would destroy the page\n background.\n\n Default state: OFF. Without the attribute, every brand keeps its\n own L. Add :root[data-lumlocker] (or set via JS) to enable.\n\n Per-color escape: override --sf-color-X under\n :root[data-lumlocker] to opt that one color out:\n :root[data-lumlocker] { --sf-color-primary: #yourColor; }\n\n The dark-side input falls back to -light when -dark isn't\n explicitly set, so this works even without per-mode overrides. */\n:root[data-lumlocker] {\n --sf-color-primary: light-dark(\n oklch(from var(--sf-color-primary-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-primary-dark, var(--sf-color-primary-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-secondary: light-dark(\n oklch(from var(--sf-color-secondary-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-secondary-dark, var(--sf-color-secondary-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-tertiary: light-dark(\n oklch(from var(--sf-color-tertiary-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-tertiary-dark, var(--sf-color-tertiary-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-action: light-dark(\n oklch(from var(--sf-color-action-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-action-dark, var(--sf-color-action-light)) var(--sf-lumlocker) c h)\n );\n --sf-color-neutral: light-dark(\n oklch(from var(--sf-color-neutral-light) var(--sf-lumlocker) c h),\n oklch(from var(--sf-color-neutral-dark, var(--sf-color-neutral-light)) var(--sf-lumlocker) c h)\n );\n}\n\n/* ─── core/layout.css ─── */\n/* ============================================================\n SLASHED — core/layout.css\n ★ de facto required for most projects\n Layout primitives + structural patterns\n Requires: core/tokens.css, core/tokens.layout.css\n ============================================================ */\n\n\n/* -- Section -------------------------------------------------\n Page section with vertical padding. Size modifiers\n override the scoped token.\n ---------------------------------------------------------- */\n\n.sf-section { padding-block: var(--sf-section-pad); }\n.sf-section--xs { padding-block: var(--sf-section-pad--xs); }\n.sf-section--s { padding-block: var(--sf-section-pad--s); }\n.sf-section--m { padding-block: var(--sf-section-pad--m); }\n.sf-section--l { padding-block: var(--sf-section-pad--l); }\n.sf-section--xl { padding-block: var(--sf-section-pad--xl); }\n.sf-section--2xl { padding-block: var(--sf-section-pad--2xl); }\n\n.sf-section-group > .sf-section + .sf-section { padding-block-start: 0; }\n\n/* Collapse modifier — when two adjacent sections share this modifier,\n they redistribute padding 50/50 across the boundary so total spacing\n stays one full padding gap (instead of two). Works regardless of\n background color: each section always has half its padding on\n the boundary side, so content never touches a color edge.\n\n Use when sections are siblings outside a .sf-section-group wrapper\n and may share OR differ in background.\n\n Note: uses --sf-section-pad (the canonical alias = m by default).\n For other sizes, set --sf-section-pad locally on each section:\n
\n ---------------------------------------------------------- */\n.sf-section--collapse:has(+ .sf-section--collapse) {\n padding-block-end: calc(var(--sf-section-pad) / 2);\n}\n.sf-section--collapse + .sf-section--collapse {\n padding-block-start: calc(var(--sf-section-pad) / 2);\n}\n\n/* -- Divider -------------------------------------------------\n Standalone separator primitive. Token-driven; pairs with the\n `--sf-divider-*` tokens and mirrors the `hr` element default.\n ---------------------------------------------------------- */\n.sf-divider {\n border: none;\n border-block-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);\n margin-block: var(--sf-space-m);\n}\n.sf-divider--vertical {\n border-block-start: none;\n border-inline-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);\n margin-block: 0;\n margin-inline: var(--sf-space-m);\n align-self: stretch;\n}\n\n/* -- Container -----------------------------------------------\n Max-width container, centered with side gutters. Establishes\n a named inline-size container `sf-layout` so children can\n target it precisely with `@container sf-layout (...)`.\n The framework's own `.sf-bento` and `.sf-grid-*` rules use\n anonymous `@container` queries on purpose — they react to\n the nearest inline-size ancestor, which lets a consumer\n embed them inside any container without rewiring.\n ---------------------------------------------------------- */\n\n.sf-container {\n container: sf-layout / inline-size;\n width: 100%;\n max-width: var(--sf-container-default);\n margin-inline: auto;\n padding-inline: var(--sf-space-gutter);\n}\n.sf-container--narrow { max-width: var(--sf-container-narrow); }\n.sf-container--prose { max-width: var(--sf-container-prose); }\n.sf-container--wide { max-width: var(--sf-container-wide); }\n.sf-container--full { max-width: var(--sf-container-full); }\n\n/* -- Stack ---------------------------------------------------\n Flex column with even vertical spacing between children.\n Override: style=\"--sf-stack-gap: 2rem\"\n ---------------------------------------------------------- */\n\n.sf-stack {\n display: flex;\n flex-direction: column;\n gap: var(--sf-stack-gap);\n}\n.sf-stack--xs { gap: var(--sf-space-xs); }\n.sf-stack--s { gap: var(--sf-space-s); }\n.sf-stack--m { gap: var(--sf-space-m); }\n.sf-stack--l { gap: var(--sf-space-l); }\n.sf-stack--xl { gap: var(--sf-space-xl); }\n.sf-stack--2xl { gap: var(--sf-space-2xl); }\n\n.sf-stack--center { align-items: center; }\n.sf-stack--end { align-items: flex-end; }\n.sf-stack--stretch { align-items: stretch; }\n\n/* -- Box -----------------------------------------------------\n Isolated unit with padding and an optional border.\n Outline instead of border — doesn't disturb the box model when toggled.\n Override: style=\"--sf-box-padding: 1rem; --sf-box-border-width: 1px\"\n ---------------------------------------------------------- */\n\n.sf-box {\n padding: var(--sf-box-padding);\n outline: var(--sf-box-border-width) solid var(--sf-box-border-color);\n}\n\n/* -- Center --------------------------------------------------\n Centers content with a max-inline-size and side gutters.\n content-box: max-inline-size applies to content only; padding is additive.\n Override: style=\"--sf-center-max: 60rem\"\n ---------------------------------------------------------- */\n\n.sf-center {\n box-sizing: content-box;\n margin-inline: auto;\n max-inline-size: var(--sf-center-max);\n padding-inline: var(--sf-center-gutter);\n}\n\n.sf-center--intrinsic {\n display: flex;\n flex-direction: column;\n align-items: center;\n}\n\n/* -- Cluster -------------------------------------------------\n Flex-wrap group of variable-width items.\n Override: style=\"--sf-cluster-gap: 0.5rem; --sf-cluster-justify: center\"\n ---------------------------------------------------------- */\n\n.sf-cluster {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sf-cluster-gap);\n align-items: var(--sf-cluster-align);\n justify-content: var(--sf-cluster-justify);\n}\n.sf-cluster--xs { gap: var(--sf-space-xs); }\n.sf-cluster--s { gap: var(--sf-space-s); }\n.sf-cluster--m { gap: var(--sf-space-m); }\n.sf-cluster--l { gap: var(--sf-space-l); }\n.sf-cluster--xl { gap: var(--sf-space-xl); }\n.sf-cluster--2xl { gap: var(--sf-space-2xl); }\n\n.sf-cluster--no-wrap { flex-wrap: nowrap; }\n.sf-cluster--center { justify-content: center; }\n.sf-cluster--end { justify-content: flex-end; }\n.sf-cluster--between { justify-content: space-between; }\n\n/* -- Sidebar -------------------------------------------------\n Two-column layout: first-child = sidebar, last-child = main content.\n Collapses when the viewport is too narrow (flex-basis trick).\n Override: style=\"--sf-sidebar-width: 20rem\"\n ---------------------------------------------------------- */\n\n.sf-sidebar {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sf-sidebar-gap);\n}\n\n.sf-sidebar > :first-child {\n flex-basis: var(--sf-sidebar-width);\n flex-grow: 1;\n}\n\n.sf-sidebar > :last-child {\n flex-basis: 0;\n flex-grow: 999;\n min-inline-size: var(--sf-sidebar-min-width);\n}\n\n.sf-sidebar--right > :first-child {\n flex-basis: 0;\n flex-grow: 999;\n min-inline-size: var(--sf-sidebar-min-width);\n}\n\n.sf-sidebar--right > :last-child {\n flex-basis: var(--sf-sidebar-width);\n flex-grow: 1;\n}\n\n.sf-sidebar--narrow { --sf-sidebar-width: 12rem; }\n.sf-sidebar--wide { --sf-sidebar-width: 26rem; }\n\n/* -- Switcher ------------------------------------------------\n Switches between a column (narrow viewport) and a row (wide).\n Uses calc((threshold - 100%) * 999) — zero media queries.\n Override: style=\"--sf-switcher-threshold: 40rem\"\n ---------------------------------------------------------- */\n\n.sf-switcher {\n display: flex;\n flex-wrap: wrap;\n gap: var(--sf-switcher-gap);\n}\n\n.sf-switcher > * {\n flex-grow: 1;\n flex-basis: calc((var(--sf-switcher-threshold) - 100%) * 999);\n}\n\n.sf-switcher--no-wrap { flex-wrap: nowrap; overflow-x: auto; }\n.sf-switcher--vertical { flex-direction: column; }\n\n/* -- Grid ----------------------------------------------------\n Auto-fill CSS grid with no breakpoints. --fit switches to auto-fit.\n auto-fill: empty tracks preserved, last item doesn't stretch.\n auto-fit (--fit): empty tracks collapsed, items stretch to fill the gap.\n Override: style=\"--sf-grid-min: 12rem; --sf-grid-gap: 1rem\"\n ---------------------------------------------------------- */\n\n.sf-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(min(var(--sf-grid-min), 100%), 1fr));\n gap: var(--sf-grid-gap);\n}\n\n.sf-grid--fit {\n grid-template-columns: repeat(auto-fit, minmax(min(var(--sf-grid-min), 100%), 1fr));\n}\n\n.sf-grid--xs { --sf-grid-min: var(--sf-grid-min-xs); }\n.sf-grid--s { --sf-grid-min: var(--sf-grid-min-s); }\n.sf-grid--m { --sf-grid-min: var(--sf-grid-min-m); }\n.sf-grid--l { --sf-grid-min: var(--sf-grid-min-l); }\n.sf-grid--xl { --sf-grid-min: var(--sf-grid-min-xl); }\n.sf-grid--2xl { --sf-grid-min: var(--sf-grid-min-2xl); }\n\n/* Dense packing — let later items backfill earlier gaps (as .sf-bento\n does). Applies to any grid, including the fixed-column .sf-grid-N. */\n.sf-grid--dense { grid-auto-flow: dense; }\n\n/* -- Icon ----------------------------------------------------\n Inline icon sizing primitive. Uses em-based --sf-icon-* tokens\n so an icon scales with its surrounding text. Defaults to -m.\n ---------------------------------------------------------- */\n.sf-icon {\n display: inline-block;\n inline-size: var(--sf-icon-size, var(--sf-icon-m));\n block-size: var(--sf-icon-size, var(--sf-icon-m));\n flex-shrink: 0;\n vertical-align: text-bottom;\n fill: currentcolor;\n}\n.sf-icon--xs { --sf-icon-size: var(--sf-icon-xs); }\n.sf-icon--s { --sf-icon-size: var(--sf-icon-s); }\n.sf-icon--m { --sf-icon-size: var(--sf-icon-m); }\n.sf-icon--l { --sf-icon-size: var(--sf-icon-l); }\n.sf-icon--xl { --sf-icon-size: var(--sf-icon-xl); }\n\n/* Boxed variant — wraps the icon in a padded, bordered, optionally\n coloured frame. Adds inline-/block-size on top of the naked icon\n size so the visible glyph stays at --sf-icon-size and the box\n grows around it (content-box). Override per icon:\n */\n.sf-icon--boxed {\n inline-size: calc(var(--sf-icon-size, var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);\n block-size: calc(var(--sf-icon-size, var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);\n padding: var(--sf-icon-box-pad);\n border: var(--sf-icon-box-border);\n border-radius: var(--sf-icon-box-radius);\n background: var(--sf-icon-box-bg);\n box-sizing: content-box;\n}\n\n/* -- Cover ---------------------------------------------------\n Full-height flex column. A .sf-cover__center child is\n vertically centered via margin-block: auto.\n Override: style=\"--sf-cover-min-height: 50dvh\"\n ---------------------------------------------------------- */\n\n.sf-cover {\n display: flex;\n flex-direction: column;\n min-height: var(--sf-cover-min-height);\n padding-block: var(--sf-cover-padding);\n}\n\n.sf-cover > .sf-cover__center {\n margin-block: auto;\n}\n\n.sf-cover--min { min-height: 50dvh; }\n.sf-cover--max { min-height: 0; max-height: 100dvh; }\n.sf-cover--padding-s { padding-block: var(--sf-space-xl); }\n.sf-cover--padding-l { padding-block: var(--sf-space-4xl); }\n\n/* -- Frame ---------------------------------------------------\n Aspect-ratio container. Media children: fill + cover.\n Override: style=\"--sf-frame-ratio: 4 / 3\"\n ---------------------------------------------------------- */\n\n.sf-frame {\n aspect-ratio: var(--sf-frame-ratio);\n overflow: hidden;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n\n.sf-frame > img,\n.sf-frame > video {\n width: 100%;\n height: 100%;\n object-fit: cover;\n}\n\n.sf-frame--square { aspect-ratio: var(--sf-ratio-square); }\n.sf-frame--video { aspect-ratio: var(--sf-ratio-video); }\n.sf-frame--cinema { aspect-ratio: var(--sf-ratio-cinema); }\n.sf-frame--portrait { aspect-ratio: var(--sf-ratio-portrait); }\n.sf-frame--4-3 { aspect-ratio: var(--sf-ratio-4-3); }\n.sf-frame--3-2 { aspect-ratio: var(--sf-ratio-3-2); }\n.sf-frame--golden { aspect-ratio: var(--sf-ratio-golden); }\n\n/* -- Reel ----------------------------------------------------\n Horizontal scroll with scroll-snap. Each child snaps to start.\n Override: style=\"--sf-reel-gap: 1rem; --sf-reel-item-width: 280px\"\n ---------------------------------------------------------- */\n\n.sf-reel {\n display: flex;\n block-size: var(--sf-reel-height);\n gap: var(--sf-reel-gap);\n overflow-x: auto;\n scroll-snap-type: x mandatory;\n scroll-padding-inline: var(--sf-reel-gap);\n overscroll-behavior-x: contain;\n scrollbar-width: thin;\n}\n\n.sf-reel > * {\n flex: 0 0 var(--sf-reel-item-width);\n scroll-snap-align: start;\n}\n\n/* -- Imposter ------------------------------------------------\n Element centered absolutely within its parent.\n The parent must have position: relative.\n ---------------------------------------------------------- */\n\n.sf-imposter {\n position: absolute;\n inset-block-start: 50%;\n inset-inline-start: 50%;\n translate: -50% -50%;\n}\n\n.sf-imposter--fixed {\n position: fixed;\n}\n\n.sf-imposter--contain {\n max-inline-size: calc(100% - var(--sf-imposter-margin) * 2);\n max-block-size: calc(100% - var(--sf-imposter-margin) * 2);\n overflow: auto;\n}\n\n/* -- Alternate (zigzag / media-object) -----------------------\n Alternating two-column layouts (container-query responsive).\n Every other row reverses its children's order.\n\n Establishes a named container `sf-alternate` and binds the\n responsive query to that name — so a `.sf-alternate` nested\n inside another container (e.g. `.sf-bento`) still flips on\n its OWN width, not the outer container's.\n ---------------------------------------------------------- */\n\n.sf-alternate {\n display: grid;\n gap: var(--sf-alternate-gap);\n container: sf-alternate / inline-size;\n}\n\n.sf-alternate > * {\n display: grid;\n gap: var(--sf-alternate-inner-gap);\n align-items: center;\n}\n\n@container sf-alternate (min-width: 48em) {\n .sf-alternate > * {\n grid-template-columns: 1fr 1fr;\n }\n .sf-alternate > :nth-child(even) > :first-child { order: 2; }\n .sf-alternate > :nth-child(even) > :last-child { order: 1; }\n}\n\n/* -- Pancake (sticky footer) ---------------------------------\n Grid with three rows: header / main(1fr) / footer.\n ---------------------------------------------------------- */\n\n.sf-pancake {\n display: grid;\n grid-template-rows: auto 1fr auto;\n min-height: 100dvh;\n}\n\n/* -- Fixed column grids (container-query responsive) ---------\n Responsive fixed-column grids with no media queries.\n ---------------------------------------------------------- */\n\n.sf-grid-1, .sf-grid-2, .sf-grid-3, .sf-grid-4, .sf-grid-6 {\n display: grid;\n gap: var(--sf-grid-gap);\n}\n\n@container (min-width: 30em) {\n .sf-grid-4 { grid-template-columns: repeat(2, 1fr); }\n .sf-grid-6 { grid-template-columns: repeat(3, 1fr); }\n}\n\n@container (min-width: 48em) {\n .sf-grid-2 { grid-template-columns: repeat(2, 1fr); }\n .sf-grid-3 { grid-template-columns: repeat(3, 1fr); }\n .sf-grid-4 { grid-template-columns: repeat(4, 1fr); }\n .sf-grid-6 { grid-template-columns: repeat(6, 1fr); }\n}\n\n/* -- Ratio grids ---------------------------------------------\n Two-column ratio grids, container-query responsive.\n ---------------------------------------------------------- */\n\n.sf-grid-1-2, .sf-grid-2-1,\n.sf-grid-1-3, .sf-grid-3-1 {\n display: grid;\n gap: var(--sf-grid-gap);\n}\n\n@container (min-width: 48em) {\n .sf-grid-1-2 { grid-template-columns: 1fr 2fr; }\n .sf-grid-2-1 { grid-template-columns: 2fr 1fr; }\n .sf-grid-1-3 { grid-template-columns: 1fr 3fr; }\n .sf-grid-3-1 { grid-template-columns: 3fr 1fr; }\n}\n\n/* -- Content grid (breakout pattern) -------------------------\n Full-width grid that lets items break out to breakout or full\n width. Children default to the content column.\n ---------------------------------------------------------- */\n\n.sf-content-grid {\n display: grid;\n grid-template-columns:\n [full-start]\n minmax(var(--sf-space-gutter), 1fr)\n [breakout-start]\n minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2))\n [content-start]\n min(var(--sf-content-width), 100% - var(--sf-space-gutter) * 2)\n [content-end]\n minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2))\n [breakout-end]\n minmax(var(--sf-space-gutter), 1fr)\n [full-end];\n}\n\n.sf-content-grid > * { grid-column: content; }\n.sf-content-grid > .sf-breakout { grid-column: breakout; }\n.sf-content-grid > .sf-full-bleed { grid-column: full; }\n\n/* -- Bento grid ----------------------------------------------\n Free-form grid with auto-flow dense, responsive via CQ.\n Override: style=\"--sf-bento-cols: 4; --sf-bento-row: 12rem\"\n ---------------------------------------------------------- */\n\n.sf-bento {\n display: grid;\n grid-template-columns: repeat(var(--sf-bento-cols, var(--sf-bento-cols-default)), 1fr);\n grid-auto-rows: minmax(var(--sf-bento-row, var(--sf-bento-row-default)), auto);\n grid-auto-flow: dense;\n gap: var(--sf-bento-gap);\n}\n\n@container (min-width: 30em) and (max-width: 47.99em) {\n .sf-bento { grid-template-columns: repeat(2, 1fr); }\n}\n\n.sf-bento--2 { --sf-bento-cols: 2; }\n.sf-bento--4 { --sf-bento-cols: 4; }\n.sf-bento--compact { --sf-bento-row: var(--sf-bento-row-compact); }\n.sf-bento--tall { --sf-bento-row: var(--sf-bento-row-tall); }\n\n/* -- Subgrid -------------------------------------------------\n A grid child that inherits its parent's tracks.\n ---------------------------------------------------------- */\n\n.sf-subgrid { display: grid; grid-template-columns: subgrid; }\n.sf-subgrid-rows { display: grid; grid-template-rows: subgrid; }\n\n/* ─── core/macros.css ─── */\n/* ============================================================\n SLASHED — core/macros.css\n ★ shipped in essential bundle\n Macro-classes (recipes / patterns).\n Requires: core/tokens.css, core/tokens.macros.css\n Layer: slashed.macros\n\n Macro-classes vs layout primitives:\n · Layout primitive = answers \"where do my children go?\"\n (e.g. .sf-stack, .sf-grid, .sf-cluster)\n · Macro-class = answers \"what does this element DO or\n LOOK LIKE?\" (e.g. .sf-prose, .sf-flow, .sf-truncate)\n\n Cascade position:\n layout → components → macros → utilities → states → …\n Macros may compose with primitives and components, but a\n single-property utility still wins on the same selector.\n\n PUBLIC API — every class here is part of the public surface.\n Tokens consumed by these classes are listed in\n core/tokens.macros.css.\n ============================================================ */\n\n\n/* -- Prose ---------------------------------------------------\n Long-form text column with automatic vertical rhythm.\n .sf-not-prose resets styles inside a prose block.\n ---------------------------------------------------------- */\n\n.sf-prose {\n max-width: var(--sf-container-prose);\n overflow-wrap: break-word;\n}\n\n.sf-prose > * + * { margin-block-start: var(--sf-prose-paragraph); }\n.sf-prose :is(h2, h3, h4) + * { margin-block-start: var(--sf-space-s); }\n.sf-prose li + li { margin-block-start: var(--sf-space-xs); }\n.sf-prose :is(blockquote, pre) { margin-block: var(--sf-space-m); }\n.sf-prose img { margin-block: var(--sf-space-m); border-radius: var(--sf-radius-m); }\n.sf-prose :is(ul, ol) { padding-inline-start: 1.25em; }\n.sf-prose ul { list-style: disc; }\n.sf-prose ol { list-style: decimal; }\n.sf-prose ::marker { color: var(--sf-color-primary); }\n.sf-prose figure { margin-block: var(--sf-space-l); }\n.sf-prose figcaption { font-size: var(--sf-text-s); color: var(--sf-color-text--muted); margin-block-start: var(--sf-space-xs); }\n.sf-prose table { margin-block: var(--sf-space-m); display: block; overflow-x: auto; max-width: 100%; }\n.sf-prose video { margin-block: var(--sf-space-m); border-radius: var(--sf-radius-m); }\n\n/* .sf-not-prose resets — specificity 0-2-1 intentionally exceeds the\n general 0-2-0 budget because these rules must override the .sf-prose\n child selectors above (also 0-2-0). This is a documented exception. */\n.sf-prose .sf-not-prose > * + * { margin-block-start: 0; }\n.sf-prose .sf-not-prose li + li { margin-block-start: 0; }\n/* `revert` in a cascade layer rolls back past all layers to the UA\n stylesheet — restoring browser-default list styling. In the flat\n bundle (layers stripped), `revert` falls back to the UA stylesheet\n directly. Both paths produce the same result: native list bullets. */\n.sf-prose .sf-not-prose :is(ul,ol) { list-style: revert; padding-inline-start: revert; }\n.sf-prose .sf-not-prose ::marker { color: inherit; }\n.sf-prose .sf-not-prose img { margin-block: 0; border-radius: 0; }\n.sf-prose .sf-not-prose figure { margin-block: 0; }\n.sf-prose .sf-not-prose figcaption { font-size: inherit; color: inherit; margin-block-start: 0; }\n.sf-prose .sf-not-prose table { margin-block: 0; display: revert; overflow-x: revert; max-width: revert; }\n.sf-prose .sf-not-prose video { margin-block: 0; border-radius: 0; }\n\n/* -- Flow ----------------------------------------------------\n Heydon Pickering's \"lobotomized owl\" — every flow child gets\n equal margin-block-start. Useful for content blocks where you\n don't know in advance which elements will be present.\n Override: style=\"--sf-flow-space: 2rem\"\n ---------------------------------------------------------- */\n.sf-flow > * + * {\n margin-block-start: var(--sf-flow-space);\n}\n\n/* -- Truncate / line-clamp -----------------------------------\n Single-line ellipsis (.sf-truncate) and N-line clamp\n (.sf-line-clamp-2 / -3 / -N). The fixed-count variants\n hardcode -webkit-line-clamp; -N reads --sf-line-clamp.\n\n Note: -webkit-line-clamp is not vendor-locked despite the\n prefix — every modern browser implements it as the de-facto\n standard. The unprefixed CSS Overflow 4 `line-clamp` exists\n but is not yet widely shipped.\n ---------------------------------------------------------- */\n.sf-truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.sf-line-clamp-2,\n.sf-line-clamp-3,\n.sf-line-clamp-N {\n display: -webkit-box;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n.sf-line-clamp-2 { -webkit-line-clamp: 2; line-clamp: 2; }\n.sf-line-clamp-3 { -webkit-line-clamp: 3; line-clamp: 3; }\n.sf-line-clamp-N {\n -webkit-line-clamp: var(--sf-line-clamp);\n line-clamp: var(--sf-line-clamp);\n}\n\n/* -- Equal height --------------------------------------------\n Force grid/flex children to share the tallest one's height.\n Works in flex (default `align-items: stretch`) and is\n re-asserted here for cases where a wrapper resets it.\n ---------------------------------------------------------- */\n.sf-equal-height {\n display: flex;\n align-items: stretch;\n}\n.sf-equal-height > * {\n block-size: auto;\n flex: 1 1 auto;\n}\n\n/* -- Aspect --------------------------------------------------\n Generic aspect-ratio container. Distinct from layout's\n .sf-frame which targets media (object-fit: cover) — .sf-aspect\n is content-agnostic.\n Override: style=\"--sf-aspect: 4 / 3\"\n ---------------------------------------------------------- */\n.sf-aspect {\n aspect-ratio: var(--sf-aspect);\n}\n\n/* -- Scroll shadow -------------------------------------------\n Top + bottom mask gradient that reveals when content is\n scrolled inside a vertical-scroll container. Pure CSS,\n no JS. Combine with overflow-y: auto on the same element.\n Override: style=\"--sf-scroll-shadow-size: 3rem\"\n ---------------------------------------------------------- */\n.sf-scroll-shadow {\n overflow-y: auto;\n mask-image:\n linear-gradient(to bottom,\n transparent 0,\n black var(--sf-scroll-shadow-size),\n black calc(100% - var(--sf-scroll-shadow-size)),\n transparent 100%);\n}\n\n/* -- Scroll snap ---------------------------------------------\n Vertical scroll-snap container for vertically stacked sections.\n Pairs with consumer-supplied scroll-snap-align on children.\n For horizontal snap, use the .sf-reel layout primitive.\n ---------------------------------------------------------- */\n.sf-scroll-snap {\n overflow-y: auto;\n scroll-snap-type: y mandatory;\n overscroll-behavior-y: contain;\n}\n.sf-scroll-snap > * {\n scroll-snap-align: start;\n}\n\n/* -- Overflow fade -------------------------------------------\n End-edge horizontal fade for clipped inline content\n (e.g. a row of tags overflowing a card). Pure mask, so it\n respects the element's actual background.\n ---------------------------------------------------------- */\n.sf-overflow-fade {\n overflow: hidden;\n mask-image: linear-gradient(\n to right,\n black 0,\n black calc(100% - var(--sf-scroll-shadow-size)),\n transparent 100%\n );\n}\n\n/* -- No tap highlight ----------------------------------------\n Suppresses the WebKit/Android grey tap-highlight overlay on\n interactive elements where it conflicts with the framework's\n own .is-active / hover treatment.\n ---------------------------------------------------------- */\n.sf-no-tap-highlight {\n -webkit-tap-highlight-color: transparent;\n}\n\n/* -- Surface -------------------------------------------------\n Sets background + auto-flipped foreground in one class.\n Combines --sf-color-X (resolved semantic, mode-aware) with\n the matching --sf-color-text--on-X (auto-contrast) so text\n legibility is automatic without consumer math.\n\n 11 variants cover all brand + status + inverse colors. Base is\n excluded — base IS the page surface; use plain\n background: var(--sf-color-bg) if you need it explicitly.\n\n Composes naturally with .sf-section / .sf-card / .sf-stack:\n
\n ---------------------------------------------------------- */\n.sf-surface--primary { background: var(--sf-color-primary); color: var(--sf-color-text--on-primary); }\n.sf-surface--secondary { background: var(--sf-color-secondary); color: var(--sf-color-text--on-secondary); }\n.sf-surface--tertiary { background: var(--sf-color-tertiary); color: var(--sf-color-text--on-tertiary); }\n.sf-surface--action { background: var(--sf-color-action); color: var(--sf-color-text--on-action); }\n.sf-surface--neutral { background: var(--sf-color-neutral); color: var(--sf-color-text--on-neutral); }\n.sf-surface--inverse { background: var(--sf-color-inverse); color: var(--sf-color-text--on-inverse); }\n.sf-surface--success { background: var(--sf-color-success); color: var(--sf-color-text--on-success); }\n.sf-surface--warning { background: var(--sf-color-warning); color: var(--sf-color-text--on-warning); }\n.sf-surface--error { background: var(--sf-color-error); color: var(--sf-color-text--on-error); }\n.sf-surface--info { background: var(--sf-color-info); color: var(--sf-color-text--on-info); }\n.sf-surface--danger { background: var(--sf-color-danger); color: var(--sf-color-text--on-danger); }\n\n/* -- Text gradient -------------------------------------------\n Fills text with a gradient. Defaults to --sf-gradient-primary\n (auto-adapts to brand + dark mode). Override the background-image\n per-instance for any other gradient or radial:\n

\n\n Selection on gradient text inherits the clipping — that's a\n known browser limitation across all engines, accepted as-is.\n\n The `color: transparent` declaration is gated behind a feature\n query for background-clip:text. Without that gate, an engine\n that ignored background-clip:text would render the text fully\n invisible (transparent over no clipped fill). All current\n evergreen browsers support either the standard or the\n -webkit- prefixed form, so the supports check is a defensive\n guard rather than a real-world fallback.\n ---------------------------------------------------------- */\n.sf-text-gradient {\n background-image: var(--sf-gradient-primary);\n /* stylelint-disable-next-line property-no-vendor-prefix -- required for Safari */\n -webkit-background-clip: text;\n background-clip: text;\n}\n@supports ((background-clip: text) or (-webkit-background-clip: text)) {\n .sf-text-gradient {\n color: transparent;\n }\n}\n\n/* -- External link indication --------------------------------\n Adds an external-link glyph after the link text. Opt-in per\n element via class — for global auto-detection, write your\n own rule in unlayered CSS:\n a[rel~=\"external\"]:not(.no-external)::after {\n content: var(--sf-link-external-marker);\n }\n\n Marker is tokenised so consumers can swap to \" →\", \" ⤴\", or\n unset (empty string fully disables it — the default token\n value bakes the leading space in, so `\"\"` leaves nothing\n after the link):\n :root { --sf-link-external-marker: \"\" }\n ---------------------------------------------------------- */\n.sf-link-external::after {\n content: var(--sf-link-external-marker);\n display: inline-block;\n font-size: 0.85em;\n text-decoration: none;\n}\n\n/* ─── core/states.css ─── */\n/* SLASHED — core/states.css\n @layer slashed.states\n\n Stateful classes toggled by JS or ARIA attributes.\n Prefix: .is-* (exclusive to this layer — never used in utilities).\n\n These are NOT utility classes. They describe dynamic,\n runtime states that change in response to user interaction\n or application logic. Utilities describe static intent;\n states describe current condition.\n\n Design principles:\n ─ Generic states only — component-specific overrides belong\n in slashed.components (e.g. .sf-modal.is-open).\n ─ Tokens first — every value references a design token.\n ─ Low specificity — single class, no nesting.\n ─ Minimal property sets — states set the MINIMUM needed\n to communicate the state. Components layer on top.\n ─────────────────────────────────────────────────── */\n\n\n/* ============================================================\n VISIBILITY\n ============================================================ */\n\n.is-hidden {\n display: none !important;\n}\n\n.is-invisible {\n visibility: hidden;\n}\n\n.is-visible {\n visibility: visible;\n}\n\n/* ============================================================\n INTERACTIVITY — DISABLED / READONLY\n ============================================================ */\n\n.is-disabled {\n opacity: var(--sf-opacity-disabled);\n pointer-events: none;\n cursor: not-allowed;\n user-select: none;\n}\n\n.is-readonly {\n pointer-events: none;\n user-select: none;\n}\n\n/* ============================================================\n LOADING / ASYNC FEEDBACK\n ============================================================ */\n\n.is-loading {\n color: transparent !important;\n pointer-events: none;\n position: relative;\n}\n\n.is-loading::after {\n content: \"\";\n position: absolute;\n inset-block-start: 50%;\n inset-inline-start: 50%;\n width: 1em;\n height: 1em;\n margin-block-start: -0.5em;\n margin-inline-start: -0.5em;\n border: var(--sf-border-width-2) solid var(--sf-color-border--strong);\n border-block-start-color: var(--sf-color-action);\n border-radius: var(--sf-radius-full);\n /* Reduced-motion: accessibility.css kills animation-duration via\n !important on *, *::before, *::after — so this spinner stops\n even without a local reduced-motion wrapper. */\n animation: var(--sf-animation-spin);\n}\n\n.is-busy {\n cursor: progress;\n}\n\n.is-pending {\n /* Optimistic UI — request in flight but content still visible/usable\n (unlike .is-loading, which masks content with a spinner). */\n opacity: var(--sf-state-pending-opacity);\n cursor: progress;\n}\n\n.is-skeleton {\n color: transparent !important;\n background: linear-gradient(\n 90deg,\n var(--sf-color-well) 25%,\n var(--sf-color-raised) 50%,\n var(--sf-color-well) 75%\n );\n background-size: 200% 100%;\n animation: var(--sf-animation-shimmer);\n border-radius: var(--sf-radius-s);\n pointer-events: none;\n user-select: none;\n}\n\n/* ============================================================\n ACTIVE / SELECTED / CURRENT — navigation & toggling\n ============================================================ */\n\n.is-active {\n /* Generic active indicator — components add visual specifics.\n Sets a scoped token for components to consume. */\n --sf-is-active: 1;\n}\n\n.is-selected {\n background-color: var(--sf-color-bg--selected);\n}\n\n.is-current {\n /* Navigation: marks the current page/route.\n Components style via .sf-nav .is-current etc.\n Weight is tokenised so consumers can override without specificity battles:\n :root { --sf-current-font-weight: var(--sf-font-weight-semibold); } */\n --sf-is-current: 1;\n font-weight: var(--sf-current-font-weight, var(--sf-font-weight-bold));\n}\n\n.is-highlighted {\n background-color: var(--sf-color-bg--focus);\n}\n\n.is-pressed {\n /* Toggle buttons (aria-pressed=\"true\"). Components add specifics. */\n --sf-is-pressed: 1;\n background-color: var(--sf-color-bg--selected);\n}\n\n/* ============================================================\n OPEN / CLOSED — disclosure widgets, modals, dropdowns\n\n .is-open / .is-collapsed — a thing is shown vs hidden (modal,\n dropdown, drawer). .is-expanded — maps to aria-expanded on a\n disclosure/accordion trigger. They share the --sf-is-open flag\n but signal different intent to consumers.\n ============================================================ */\n\n.is-open {\n --sf-is-open: 1;\n}\n\n.is-collapsed {\n --sf-is-open: 0;\n}\n\n.is-expanded {\n --sf-is-open: 1;\n}\n\n/* ============================================================\n VALIDATION / FEEDBACK\n\n Overlap semantics (see docs/states.md):\n · .is-invalid / .is-valid — form-field validation (maps to\n aria-invalid). Use on inputs.\n · .is-error / .is-success — general component feedback, not tied\n to a single field (a failed save, a completed step).\n · .is-danger — destructive-action context (a delete button), NOT\n a validation result. Distinct from .is-error.\n ============================================================ */\n\n.is-valid {\n --sf-field-border-color: var(--sf-color-success);\n --sf-field-text-color: var(--sf-color-success-strong);\n}\n\n.is-invalid {\n --sf-field-border-color: var(--sf-color-error);\n --sf-field-text-color: var(--sf-color-error-strong);\n}\n\n.is-warning {\n --sf-field-border-color: var(--sf-color-warning);\n --sf-field-text-color: var(--sf-color-warning-strong);\n}\n\n.is-success {\n --sf-field-border-color: var(--sf-color-success);\n --sf-field-text-color: var(--sf-color-success-strong);\n}\n\n.is-error {\n --sf-field-border-color: var(--sf-color-error);\n --sf-field-text-color: var(--sf-color-error-strong);\n}\n\n.is-info {\n --sf-field-border-color: var(--sf-color-info);\n --sf-field-text-color: var(--sf-color-info-strong);\n}\n\n.is-danger {\n --sf-field-border-color: var(--sf-color-danger);\n --sf-field-text-color: var(--sf-color-danger-strong);\n}\n\n/* ============================================================\n POSITION / STICKINESS\n ============================================================ */\n\n.is-sticky {\n position: sticky;\n top: var(--sf-sticky-offset, 0);\n z-index: var(--sf-z-low);\n}\n\n.is-pinned {\n position: sticky;\n top: 0;\n z-index: var(--sf-z-mid);\n}\n\n.is-fixed {\n position: fixed;\n}\n\n.is-fullscreen {\n /* App-managed fullscreen overlay. For the native Fullscreen API,\n prefer the `:fullscreen` pseudo-class; this is the JS-toggled\n equivalent for elements not entered via that API. */\n position: fixed;\n inset: 0;\n z-index: var(--sf-z-max);\n}\n\n.is-resizable {\n resize: both;\n overflow: auto;\n}\n\n/* ============================================================\n OVERFLOW / CLIPPING\n ============================================================ */\n\n.is-clipped {\n overflow: hidden !important;\n}\n\n.is-scrollable {\n overflow: auto;\n overscroll-behavior: contain;\n}\n\n.is-truncated {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* ============================================================\n DRAG & DROP\n ============================================================ */\n\n.is-dragging {\n opacity: var(--sf-opacity-50);\n cursor: grabbing;\n}\n\n.is-drop-target {\n outline: var(--sf-focus-ring-width) dashed var(--sf-color-action);\n outline-offset: var(--sf-focus-ring-offset);\n}\n\n.is-draggable {\n cursor: grab;\n}\n\n/* ============================================================\n OVERLAY / BACKDROP\n ============================================================ */\n\n.is-overlay {\n position: absolute;\n inset: 0;\n}\n\n/* ============================================================\n FOCUS / INTERACTION MODIFIERS\n ============================================================ */\n\n.is-clickable {\n cursor: pointer;\n}\n\n.is-unselectable {\n user-select: none;\n}\n\n.is-focused {\n /* Programmatic / JS-driven focus indicator, independent of\n :focus-visible. Normal weight so consumers can restyle. */\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-focus-ring-color);\n outline-offset: var(--sf-focus-ring-offset);\n}\n\n/* ============================================================\n EMPTY STATE\n ============================================================ */\n\n.is-empty:empty {\n display: none;\n}\n\n/* ============================================================\n KEYFRAMES (used by states above)\n\n Spinner and shimmer keyframes live in core/motion.css alongside\n the rest of the framework's @keyframes. They are referenced from\n here by name (sf-spin, sf-shimmer) and are inert until applied.\n If you load core/states.css without core/motion.css, define them\n in your own CSS or copy them from motion.css.\n ============================================================ */\n\n/* ─── core/motion.css ─── */\n/* SLASHED — core/motion.css\n @layer slashed.motion\n\n Extends the base interactive transition (defined in base.css)\n with a broader property set and adds scroll/view-transition\n behaviour. Optional and independently loadable.\n\n Reduced-motion handling is intentionally NOT duplicated here:\n accessibility.css (a higher cascade layer, shipped in the\n 'essential' bundle) neutralises animation/transition via\n property-level !important. The no-preference wrapper below is\n a defensive opt-in stance — animations apply only when the\n user has not requested their absence — and remains correct\n even if motion.css is served without accessibility.css.\n ─────────────────────────────────────────────────── */\n\n\n@media (prefers-reduced-motion: no-preference) {\n\n html {\n scroll-behavior: smooth;\n }\n\n /* Keyboard navigation must not be hijacked by smooth scroll. */\n html:focus-within {\n scroll-behavior: auto;\n }\n\n /* Broadens the base.css link transition to all interactive\n elements and the full set of animatable UI properties. */\n a, button, input, select, textarea, summary {\n transition-property:\n color, background-color, border-color, text-decoration-color,\n fill, stroke, opacity, box-shadow, transform, filter,\n backdrop-filter;\n transition-duration: var(--sf-duration-fast);\n transition-timing-function: var(--sf-ease-out);\n }\n\n @supports (view-transition-name: none) {\n ::view-transition-old(root),\n ::view-transition-new(root) {\n animation-duration: var(--sf-duration-normal);\n animation-timing-function: var(--sf-ease-out);\n }\n }\n\n /* Animation presets — opt-in classes. Scoped to no-preference so\n reduced-motion users never trigger them (accessibility.css also\n neutralises animation-duration as a hard guarantee). These are\n DECORATIVE: under reduced motion they simply don't run, so never\n rely on one (e.g. .sf-fade-out) as the sole way to show/hide\n content — drive real state with hidden/display/JS instead. */\n .sf-fade-in { animation: var(--sf-animation-fade-in); }\n .sf-fade-out { animation: var(--sf-animation-fade-out); }\n .sf-slide-in-up { animation: var(--sf-animation-slide-in-up); }\n .sf-slide-in-down { animation: var(--sf-animation-slide-in-down); }\n .sf-slide-in-left { animation: var(--sf-animation-slide-in-left); }\n .sf-slide-in-right { animation: var(--sf-animation-slide-in-right); }\n .sf-scale-up { animation: var(--sf-animation-scale-up); }\n .sf-scale-down { animation: var(--sf-animation-scale-down); }\n\n /* Scroll-driven entrance effects — element animates into view\n as it enters the viewport. Uses CSS scroll-driven animations\n via animation-timeline: view(). Range tokens\n (--sf-scroll-timeline-range-{start,end}, declared in\n core/tokens.css) control where the animation starts and ends\n along the scroll progress.\n\n Example:\n
\n\n Reduced-motion: gated by the no-preference wrapper above, so\n these are inert when the user opts out. The core animation\n (translate/opacity) still composes correctly with reduced-motion\n neutralisation in accessibility.css if a consumer accidentally\n writes them outside the gate.\n\n Browser support — animation-timeline is shipping in Chrome 115+\n and Firefox 145+ (Nov 2026). Safari does not yet support it\n (tracked at WebKit bug 264057); on those engines the @supports\n block below is skipped, so the rule falls back to a one-shot\n time-driven animation playing once at --sf-duration-slow.\n animation-duration is set unconditionally because without it\n a non-supporting engine would default to 0s and snap to the\n end state instantly — there'd be no visible entrance at all.\n Where animation-timeline is supported the timeline drives\n progress and the duration is ignored. */\n .sf-entrance--fade,\n .sf-entrance--fade-up,\n .sf-entrance--fade-down,\n .sf-entrance--fade-left,\n .sf-entrance--fade-right,\n .sf-entrance--scale-up {\n animation-duration: var(--sf-duration-slow);\n animation-timing-function: var(--sf-ease-out);\n animation-fill-mode: both;\n }\n @supports (animation-timeline: view()) {\n .sf-entrance--fade,\n .sf-entrance--fade-up,\n .sf-entrance--fade-down,\n .sf-entrance--fade-left,\n .sf-entrance--fade-right,\n .sf-entrance--scale-up {\n animation-timeline: view();\n animation-range: var(--sf-scroll-timeline-range-start, entry 0%)\n var(--sf-scroll-timeline-range-end, cover 30%);\n }\n }\n .sf-entrance--fade { animation-name: sf-fade-in; }\n .sf-entrance--fade-up { animation-name: sf-slide-in-up; }\n .sf-entrance--fade-down { animation-name: sf-slide-in-down; }\n .sf-entrance--fade-left { animation-name: sf-slide-in-left; }\n .sf-entrance--fade-right { animation-name: sf-slide-in-right; }\n .sf-entrance--scale-up { animation-name: sf-scale-up; }\n\n}\n\n/* Keyframes — inert until referenced, so they live outside the\n no-preference wrapper (consumers may reference them at will).\n Spinner and shimmer are used by .is-loading / .is-skeleton in\n core/states.css; the rest power the .sf-* animation classes\n above. */\n@keyframes sf-fade-in { from { opacity: 0; } to { opacity: 1; } }\n@keyframes sf-fade-out { from { opacity: 1; } to { opacity: 0; } }\n@keyframes sf-slide-in-up { from { opacity: 0; translate: 0 1rem; } to { opacity: 1; translate: 0 0; } }\n@keyframes sf-slide-in-down { from { opacity: 0; translate: 0 -1rem; } to { opacity: 1; translate: 0 0; } }\n@keyframes sf-slide-in-left { from { opacity: 0; translate: 1rem 0; } to { opacity: 1; translate: 0 0; } }\n@keyframes sf-slide-in-right { from { opacity: 0; translate: -1rem 0; } to { opacity: 1; translate: 0 0; } }\n@keyframes sf-scale-up { from { opacity: 0; scale: 0.92; } to { opacity: 1; scale: 1; } }\n@keyframes sf-scale-down { from { opacity: 1; scale: 1; } to { opacity: 0; scale: 0.92; } }\n\n@keyframes sf-spin { to { rotate: 360deg; } }\n@keyframes sf-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }\n\n/* Notification ping (expanding fading ring), cursor blink, gentle float.\n Keyframes only — apply via your own BEM/component rules or the\n --sf-animation-ping / -blink / -float presets. No utility classes\n ship for these (by design). */\n@keyframes sf-ping { 75%, 100% { scale: 2; opacity: 0; } }\n@keyframes sf-blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }\n@keyframes sf-float { 0%, 100% { translate: 0 0; } 50% { translate: 0 -0.5rem; } }\n\n/* @property color interpolation — proves that registered custom\n properties animate smoothly in oklch. Because --sf-color-primary-light\n is registered with syntax:\"\", the browser interpolates the\n intermediate color values frame-by-frame. Opt-in via .sf-color-pulse. */\n@keyframes sf-color-pulse {\n 0% { --sf-color-primary-light: oklch(from var(--sf-color-primary-light) l c h); }\n 50% { --sf-color-primary-light: oklch(from var(--sf-color-primary-light) calc(l + 0.25) c h); }\n 100% { --sf-color-primary-light: oklch(from var(--sf-color-primary-light) l c h); }\n}\n\n@media (prefers-reduced-motion: no-preference) {\n .sf-color-pulse {\n animation: var(--sf-animation-color-pulse);\n }\n}\n\n/* @starting-style — entry animations for elements appearing in the\n top layer (dialog, [popover]) or via display:none → block. SLASHED\n ships no global @starting-style rules because the from-state is\n element/component specific. Consumers opt in per component, e.g.:\n dialog[open] { opacity: 1; @starting-style { opacity: 0; } }\n Tracked for the future components layer. */\n\n/* ─── core/accessibility.css ─── */\n/* SLASHED — core/accessibility.css\n @layer slashed.accessibility\n\n Critical a11y rules use !important to survive unlayered CSS and\n third-party resets (unlayered normal beats any layered normal).\n Protection is SELECTIVE — only rules whose override is a genuine\n user-facing barrier are hardened. Rules that are legitimately\n customised (touch targets, skip-link position, contrast boost,\n cursor) are left normal on purpose.\n\n Audit note: reset.css/base.css contain no !important on outline,\n animation-* or transition-*, so the reversed important-layer order\n cannot let a lower layer defeat these rules.\n ─────────────────────────────────────────────────── */\n\n\n/* ----------------------------------------------------------\n Focus — keyboard visibility is WCAG 2.4.7, hard barrier.\n Ring definition lives here (not base.css) so all focus\n logic is in one high-priority layer. Hardened.\n ---------------------------------------------------------- */\n:focus:not(:focus-visible) {\n outline: none;\n}\n\n:focus-visible {\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-focus-ring-color) !important;\n outline-offset: var(--sf-focus-ring-offset) !important;\n}\n\n/* ----------------------------------------------------------\n Reduced motion. Token override helps components that read\n duration tokens in other contexts; the property-level\n !important block is the real guarantee and works\n independently of tokens (and of unlayered token overrides).\n ---------------------------------------------------------- */\n@media (prefers-reduced-motion: reduce) {\n :root {\n --sf-duration-instant: 0.01ms;\n --sf-duration-fast: 0.01ms;\n --sf-duration-normal: 0.01ms;\n --sf-duration-slow: 0.01ms;\n --sf-duration-slower: 0.01ms;\n }\n\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n scroll-behavior: auto !important;\n }\n}\n\n/* Manual motion opt-out — same suppression as the OS preference above,\n but consumer-toggled (e.g. a site-level \"reduce motion\" switch). Works\n regardless of the OS setting. */\n.no-motion,\n.no-motion *,\n.no-motion *::before,\n.no-motion *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n scroll-behavior: auto !important;\n}\n\n/* ----------------------------------------------------------\n High contrast — enhancement, not a barrier. Left normal:\n if overridden the focus ring still exists, just thinner.\n ---------------------------------------------------------- */\n@media (prefers-contrast: more) {\n :root {\n --sf-focus-ring-width: 3px;\n /* Push derived reading-text colours toward the extremes (see the\n --sf-contrast-bias knob in tokens.css). */\n --sf-contrast-bias: 0.06;\n }\n\n hr {\n border-block-start-width: var(--sf-border-width-2);\n }\n}\n\n/* ----------------------------------------------------------\n Reduced transparency.\n ---------------------------------------------------------- */\n@media (prefers-reduced-transparency: reduce) {\n ::backdrop {\n background-color: var(--sf-color-neutral);\n opacity: 1;\n }\n}\n\n/* ----------------------------------------------------------\n Minimum touch targets — WCAG 2.5.5 has documented\n exceptions; dense desktop-first UIs legitimately go below\n 44px with other hit-area strategies. Left normal ON PURPOSE\n so it stays a recommendation, not an unbreakable rule.\n ---------------------------------------------------------- */\n@media (pointer: coarse) {\n button,\n input[type=\"button\"],\n input[type=\"submit\"],\n input[type=\"reset\"],\n select,\n summary,\n a {\n min-height: var(--sf-touch-target);\n min-width: var(--sf-touch-target);\n }\n}\n\n/* ----------------------------------------------------------\n Disabled states — cosmetic, left normal.\n ---------------------------------------------------------- */\n[disabled],\n[aria-disabled=\"true\"] {\n cursor: not-allowed;\n}\n\n/* ----------------------------------------------------------\n Screen-reader-only. Atomic contract: a partial override\n either exposes hidden content visually or hides intended\n content. All-or-nothing, so the whole set is hardened.\n ---------------------------------------------------------- */\n.sr-only,\n.visually-hidden,\n.sr-only-focusable:not(:focus, :focus-within) {\n position: absolute !important;\n width: 1px !important;\n height: 1px !important;\n padding: 0 !important;\n margin: -1px !important;\n overflow: hidden !important; /* fallback for Safari <16 */\n overflow: clip !important;\n clip-path: inset(50%) !important;\n white-space: nowrap !important;\n border: 0 !important;\n}\n\n/* ----------------------------------------------------------\n Skip link — appearance belongs to the integrator's design.\n Left normal so position/colour can be themed freely.\n ---------------------------------------------------------- */\n.skip-link {\n position: absolute;\n top: -100%;\n left: var(--sf-space-m);\n z-index: var(--sf-z-max);\n padding: var(--sf-space-s) var(--sf-space-m);\n background: var(--sf-color-bg);\n color: var(--sf-color-text);\n border: var(--sf-border-width-1) solid var(--sf-color-border);\n border-radius: var(--sf-radius-m);\n transition: top var(--sf-duration-fast) var(--sf-ease-out);\n}\n.skip-link:focus {\n top: var(--sf-space-m);\n}\n\n/* ----------------------------------------------------------\n Focus-parent — an a11y *pattern*, not a state. Lives in the\n accessibility layer (was previously in states.css; moved in\n v0.3.0) so that all focus-related a11y rules share one home\n and survive third-party resets at the same priority as the\n focus-visible ring above. Cannot be hardened with !important\n because the outline shorthand is consumer-customisable.\n ---------------------------------------------------------- */\n.focus-parent:focus-within {\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-color-border--focus);\n outline-offset: var(--sf-focus-ring-offset);\n}\n\n/* ----------------------------------------------------------\n Clickable-parent — the card-with-link a11y pattern.\n Applied to the card container. Two modes:\n\n Automatic (single primary link):\n
\n

Title

\n

Description

\n \n
\n\n Explicit (multiple links — add [data-overlay-link] to the primary):\n
\n

Title

\n

Description

\n Category ← lifted automatically\n ← lifted automatically\n
\n\n Mechanic: the link's ::after fills the card via position:absolute;\n inset:0. It is contained by .sf-clickable-parent (position:relative)\n only if the link itself has no positioned ancestor between it and the\n card — so no element containing the overlay link should be given\n position:relative. Flow children are therefore intentionally left at\n position:static; they sit below the overlay in stacking order.\n\n Trade-off: card text is NOT user-selectable by mouse. A JavaScript\n click-delegate could restore selectability while preserving\n full-card navigation.\n\n Key features:\n • [data-overlay-link] explicit mode — reliable multi-link cards\n • `:focus-within` card ring — always included, no extra recipe\n • --sf-clickable-overlay-z — escape parent stacking contexts\n • Wider interactive-element exceptions (summary, video, audio…)\n\n Tune --sf-clickable-overlay-z to escape a parent stacking context.\n [data-no-overlay] opts any descendant above the overlay.\n ---------------------------------------------------------- */\n.sf-clickable-parent {\n --sf-clickable-overlay-z: 1;\n position: relative;\n cursor: pointer;\n}\n\n/* Overlay: the link's ::after fills the containing card.\n Automatic mode — every :any-link gets the overlay; works reliably\n for single-link cards. Secondary links that also receive ::after are\n lifted to position:relative (interactive rule below), which scopes\n their ::after to themselves and keeps them independently clickable.\n Explicit mode — only [data-overlay-link] fills the card. */\n.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link)::after,\n.sf-clickable-parent [data-overlay-link]::after {\n content: \"\";\n position: absolute;\n inset: 0;\n z-index: var(--sf-clickable-overlay-z);\n pointer-events: auto;\n}\n\n/* Interactive descendants beat the overlay and stay functional.\n Explicit mode: every non-designated :any-link is lifted — robust at\n any nesting depth, no :first-of-type fragility.\n Automatic mode: links that are :not(:first-of-type) among siblings\n are lifted. Sibling-scoped — use explicit mode for cards where\n secondary links live at different nesting depths.\n [data-no-overlay] is the per-element consumer escape hatch. */\n.sf-clickable-parent :is(\n button, input, textarea, select,\n summary, video, audio,\n [role=\"button\"], [data-no-overlay]\n),\n.sf-clickable-parent:has([data-overlay-link]) a:where(:any-link):not([data-overlay-link]),\n.sf-clickable-parent:not(:has([data-overlay-link])) a:where(:any-link):not(:first-of-type) {\n position: relative;\n z-index: calc(var(--sf-clickable-overlay-z) + 1);\n}\n\n/* Card-level focus ring — keyboard users see which card is active.\n The focus ring is always included without requiring a separate\n recipe. Suppress with outline:none\n if the card design provides its own focus indicator. */\n.sf-clickable-parent:focus-within {\n outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-color-border--focus);\n outline-offset: var(--sf-focus-ring-offset);\n}\n\n/* ----------------------------------------------------------\n Forced colors (Windows High Contrast). Outline-based focus\n already survives; we only (a) adopt the system Highlight\n colour for the ring and (b) drop decorative shadows that\n forced-colors would otherwise flatten into solid slabs.\n Shadow removal is zero-specificity so any rule can win it back.\n ---------------------------------------------------------- */\n@media (forced-colors: active) {\n :root {\n --sf-focus-ring-color: Highlight;\n }\n\n :focus-visible {\n outline-color: Highlight !important;\n }\n\n :where(*) {\n box-shadow: none;\n text-shadow: none;\n }\n\n /* Form controls otherwise lose their visible boundary in forced-colors\n (their token-driven border colour is replaced by the system canvas).\n Re-assert a system-coloured border so fields stay distinguishable. */\n input,\n select,\n textarea {\n border: var(--sf-border-width-1) solid;\n }\n}\n\n/* ─── core/print.css ─── */\n/* SLASHED — core/print.css\n @layer slashed.print\n\n Print rules and @page definition. Print tokens (--sf-print-*)\n are declared in core/tokens.css.\n\n Color contract (Phase 3):\n authored colour is preserved by default. Browsers apply their own\n ink-saving heuristics via the default `print-color-adjust: economy`,\n so semantic colours (badges, status pills, , syntax highlighting)\n reach paper intact. Two opt-in classes give consumers control:\n\n .print-color-exact → force every descendant to print colours\n verbatim (for colour-coded data that loses\n meaning when the browser flattens it).\n .print-no-color → force everything to ink-on-paper:\n transparent backgrounds + system text colour.\n The legacy blanket reset, now opt-in.\n\n `!important` is reserved for selectors whose semantics require\n defeating consumer-authored CSS:\n - `.no-print` and the hide-list (nav, aside, button, …) — these\n are content negative-space and must vanish from print regardless\n of authored display rules.\n - `details > summary` — disclosure widget carries no print value.\n - The two opt-in colour classes above — when the consumer marks a\n region with them they are explicitly asking to override authored\n colour, so `!important` is the contract.\n ─────────────────────────────────────────────────── */\n\n\n/* Screen default: print-only content is hidden until the page is printed. */\n.print-only {\n display: none;\n}\n\n@media print {\n\n @page {\n size: var(--sf-print-page-size, a4);\n margin: var(--sf-print-page-margin, 2cm);\n }\n\n body {\n font-size: var(--sf-print-base-size);\n }\n\n /* Decorative shadows are paper-noise. No `!important` — an authored\n shadow that explicitly survives into print is the consumer's call. */\n *,\n *::before,\n *::after {\n box-shadow: none;\n text-shadow: none;\n }\n\n /* Expand link URLs after the link text so paper-readers can follow\n them. Anchors and javascript: pseudo-links are excluded. */\n a[href]::after {\n content: \" (\" attr(href) \")\";\n font-size: 90%;\n word-break: break-all;\n }\n\n a[href^=\"#\"]::after,\n a[href^=\"javascript:\" i]::after {\n content: \"\";\n }\n\n abbr[title]::after {\n content: \" (\" attr(title) \")\";\n }\n\n img, svg, video, canvas, figure, table, pre, blockquote {\n break-inside: avoid;\n }\n\n thead {\n display: table-header-group;\n }\n\n tr {\n break-inside: avoid;\n }\n\n h1, h2, h3, h4, h5, h6 {\n break-after: avoid;\n orphans: 3;\n widows: 3;\n }\n\n p {\n orphans: 3;\n widows: 3;\n }\n\n pre, blockquote {\n border: var(--sf-border-width-1) solid var(--sf-color-border--strong);\n padding: var(--sf-space-s);\n }\n\n /* Hide non-essential interactive and decorative elements.\n `!important` is justified: these elements are content negative-space\n in print, and consumer-authored display rules must not bring them\n back accidentally. */\n nav, aside, button, input, select, textarea,\n audio, video, dialog, [popover], .no-print {\n display: none !important;\n }\n\n /* Reveal print-only content (overrides the screen default above). */\n .print-only {\n display: revert !important;\n }\n\n /* Preserve
content regardless of open state. Only\n `summary` keeps `!important` — the disclosure chevron carries no\n print value and the consumer should not be able to bring it back\n inadvertently. The other two rules win against the UA stylesheet\n on layered-author > UA precedence alone. */\n details {\n display: block;\n }\n details > summary {\n display: none !important;\n }\n details:not([open]) > :not(summary) {\n display: block;\n }\n\n /* ── Opt-in colour treatment ─────────────────────────────────── */\n\n /* Force colour-faithful rendering. Use on regions where a tinted\n background or coloured text encodes meaning (status pills,\n severity callouts, syntax highlighting, charts). */\n .print-color-exact,\n .print-color-exact * {\n print-color-adjust: exact !important;\n -webkit-print-color-adjust: exact !important;\n }\n\n /* Opt back into the legacy blanket reset: transparent backgrounds\n and system text colour everywhere inside the marked subtree.\n Use on regions where ink-saving is the contract (corporate\n boilerplate forms, append-to-existing-document templates). */\n .print-no-color,\n .print-no-color *,\n .print-no-color *::before,\n .print-no-color *::after {\n background: transparent !important;\n color: CanvasText !important;\n }\n\n}\n\n/* ─── optional/tokens.palette.css ─── */\n/* ============================================================\n SLASHED — optional/tokens.palette.css\n Optional module. Auto-generates a full numeric scale, alpha\n variants, and semantic + functional aliases for the 6 brand\n color tokens using color-mix(in oklab). No build step required.\n oklab (Cartesian) is used instead of oklch (polar) to prevent\n hue drift when mixing colors with distant hues (e.g. yellow\n mixed toward a blue-ish base would sweep through greens in oklch).\n\n Status colors keep their subtle/muted/strong triplets in\n core/tokens.css — they are NOT duplicated here.\n\n This is a deliberate design choice: brand colours need a full 50-950\n ramp for decorative/compositional use (backgrounds, borders, hover\n states at arbitrary intensities). Status colours are functional — you\n need a subtle background, a strong readable text, and a muted accent.\n A full numeric scale for errors/warnings would add ~185 tokens that\n most consumers never use. If you need --sf-color-error-300, replicate\n the brand-palette formula:\n --sf-color-error-300: color-mix(in oklab, var(--sf-color-error) 40%, var(--sf-color-base));\n\n Load after core/tokens.css:\n\n Token families (per brand color):\n -50 … -950 numeric scale. 500 = the base color.\n Tints (50-400) mix toward --sf-color-base; shades\n (600-950) mix toward --sf-color-text. Both auto-adapt\n to light/dark. (base inverts the mix direction.)\n -a5 … -a95 alpha variants (mixed with transparent).\n shade aliases superlight/xlight/lighter/darker/xdark/superdark\n → numeric steps via var() (one override propagates).\n functional aliases hover/active/subtle/muted/ghost → other aliases.\n ============================================================ */\n\n:root {\n\n /* ----------------------------------------------------------\n primary\n ---------------------------------------------------------- */\n --sf-color-primary-50: color-mix(in oklab, var(--sf-color-primary) 4%, var(--sf-color-base));\n --sf-color-primary-100: color-mix(in oklab, var(--sf-color-primary) 8%, var(--sf-color-base));\n --sf-color-primary-200: color-mix(in oklab, var(--sf-color-primary) 20%, var(--sf-color-base));\n --sf-color-primary-300: color-mix(in oklab, var(--sf-color-primary) 40%, var(--sf-color-base));\n --sf-color-primary-400: color-mix(in oklab, var(--sf-color-primary) 65%, var(--sf-color-base));\n --sf-color-primary-500: var(--sf-color-primary);\n --sf-color-primary-600: color-mix(in oklab, var(--sf-color-primary) 82%, var(--sf-color-text));\n --sf-color-primary-700: color-mix(in oklab, var(--sf-color-primary) 62%, var(--sf-color-text));\n --sf-color-primary-800: color-mix(in oklab, var(--sf-color-primary) 38%, var(--sf-color-text));\n --sf-color-primary-900: color-mix(in oklab, var(--sf-color-primary) 18%, var(--sf-color-text));\n --sf-color-primary-950: color-mix(in oklab, var(--sf-color-primary) 8%, var(--sf-color-text));\n\n --sf-color-primary-a5: color-mix(in oklab, var(--sf-color-primary) 5%, transparent);\n --sf-color-primary-a10: color-mix(in oklab, var(--sf-color-primary) 10%, transparent);\n --sf-color-primary-a20: color-mix(in oklab, var(--sf-color-primary) 20%, transparent);\n --sf-color-primary-a30: color-mix(in oklab, var(--sf-color-primary) 30%, transparent);\n --sf-color-primary-a40: color-mix(in oklab, var(--sf-color-primary) 40%, transparent);\n --sf-color-primary-a50: color-mix(in oklab, var(--sf-color-primary) 50%, transparent);\n --sf-color-primary-a60: color-mix(in oklab, var(--sf-color-primary) 60%, transparent);\n --sf-color-primary-a70: color-mix(in oklab, var(--sf-color-primary) 70%, transparent);\n --sf-color-primary-a80: color-mix(in oklab, var(--sf-color-primary) 80%, transparent);\n --sf-color-primary-a90: color-mix(in oklab, var(--sf-color-primary) 90%, transparent);\n --sf-color-primary-a95: color-mix(in oklab, var(--sf-color-primary) 95%, transparent);\n\n --sf-color-primary-superlight: var(--sf-color-primary-50);\n --sf-color-primary-xlight: var(--sf-color-primary-200);\n --sf-color-primary-lighter: var(--sf-color-primary-400);\n --sf-color-primary-darker: var(--sf-color-primary-600);\n --sf-color-primary-xdark: var(--sf-color-primary-800);\n --sf-color-primary-superdark: var(--sf-color-primary-950);\n\n --sf-color-primary-hover: var(--sf-color-primary-darker);\n --sf-color-primary-active: var(--sf-color-primary-xdark);\n --sf-color-primary-subtle: var(--sf-color-primary-a10);\n --sf-color-primary-muted: var(--sf-color-primary-a30);\n --sf-color-primary-ghost: var(--sf-color-primary-a5);\n\n\n /* ----------------------------------------------------------\n secondary\n ---------------------------------------------------------- */\n --sf-color-secondary-50: color-mix(in oklab, var(--sf-color-secondary) 4%, var(--sf-color-base));\n --sf-color-secondary-100: color-mix(in oklab, var(--sf-color-secondary) 8%, var(--sf-color-base));\n --sf-color-secondary-200: color-mix(in oklab, var(--sf-color-secondary) 20%, var(--sf-color-base));\n --sf-color-secondary-300: color-mix(in oklab, var(--sf-color-secondary) 40%, var(--sf-color-base));\n --sf-color-secondary-400: color-mix(in oklab, var(--sf-color-secondary) 65%, var(--sf-color-base));\n --sf-color-secondary-500: var(--sf-color-secondary);\n --sf-color-secondary-600: color-mix(in oklab, var(--sf-color-secondary) 82%, var(--sf-color-text));\n --sf-color-secondary-700: color-mix(in oklab, var(--sf-color-secondary) 62%, var(--sf-color-text));\n --sf-color-secondary-800: color-mix(in oklab, var(--sf-color-secondary) 38%, var(--sf-color-text));\n --sf-color-secondary-900: color-mix(in oklab, var(--sf-color-secondary) 18%, var(--sf-color-text));\n --sf-color-secondary-950: color-mix(in oklab, var(--sf-color-secondary) 8%, var(--sf-color-text));\n\n --sf-color-secondary-a5: color-mix(in oklab, var(--sf-color-secondary) 5%, transparent);\n --sf-color-secondary-a10: color-mix(in oklab, var(--sf-color-secondary) 10%, transparent);\n --sf-color-secondary-a20: color-mix(in oklab, var(--sf-color-secondary) 20%, transparent);\n --sf-color-secondary-a30: color-mix(in oklab, var(--sf-color-secondary) 30%, transparent);\n --sf-color-secondary-a40: color-mix(in oklab, var(--sf-color-secondary) 40%, transparent);\n --sf-color-secondary-a50: color-mix(in oklab, var(--sf-color-secondary) 50%, transparent);\n --sf-color-secondary-a60: color-mix(in oklab, var(--sf-color-secondary) 60%, transparent);\n --sf-color-secondary-a70: color-mix(in oklab, var(--sf-color-secondary) 70%, transparent);\n --sf-color-secondary-a80: color-mix(in oklab, var(--sf-color-secondary) 80%, transparent);\n --sf-color-secondary-a90: color-mix(in oklab, var(--sf-color-secondary) 90%, transparent);\n --sf-color-secondary-a95: color-mix(in oklab, var(--sf-color-secondary) 95%, transparent);\n\n --sf-color-secondary-superlight: var(--sf-color-secondary-50);\n --sf-color-secondary-xlight: var(--sf-color-secondary-200);\n --sf-color-secondary-lighter: var(--sf-color-secondary-400);\n --sf-color-secondary-darker: var(--sf-color-secondary-600);\n --sf-color-secondary-xdark: var(--sf-color-secondary-800);\n --sf-color-secondary-superdark: var(--sf-color-secondary-950);\n\n --sf-color-secondary-hover: var(--sf-color-secondary-darker);\n --sf-color-secondary-active: var(--sf-color-secondary-xdark);\n --sf-color-secondary-subtle: var(--sf-color-secondary-a10);\n --sf-color-secondary-muted: var(--sf-color-secondary-a30);\n --sf-color-secondary-ghost: var(--sf-color-secondary-a5);\n\n\n /* ----------------------------------------------------------\n tertiary\n ---------------------------------------------------------- */\n --sf-color-tertiary-50: color-mix(in oklab, var(--sf-color-tertiary) 4%, var(--sf-color-base));\n --sf-color-tertiary-100: color-mix(in oklab, var(--sf-color-tertiary) 8%, var(--sf-color-base));\n --sf-color-tertiary-200: color-mix(in oklab, var(--sf-color-tertiary) 20%, var(--sf-color-base));\n --sf-color-tertiary-300: color-mix(in oklab, var(--sf-color-tertiary) 40%, var(--sf-color-base));\n --sf-color-tertiary-400: color-mix(in oklab, var(--sf-color-tertiary) 65%, var(--sf-color-base));\n --sf-color-tertiary-500: var(--sf-color-tertiary);\n --sf-color-tertiary-600: color-mix(in oklab, var(--sf-color-tertiary) 82%, var(--sf-color-text));\n --sf-color-tertiary-700: color-mix(in oklab, var(--sf-color-tertiary) 62%, var(--sf-color-text));\n --sf-color-tertiary-800: color-mix(in oklab, var(--sf-color-tertiary) 38%, var(--sf-color-text));\n --sf-color-tertiary-900: color-mix(in oklab, var(--sf-color-tertiary) 18%, var(--sf-color-text));\n --sf-color-tertiary-950: color-mix(in oklab, var(--sf-color-tertiary) 8%, var(--sf-color-text));\n\n --sf-color-tertiary-a5: color-mix(in oklab, var(--sf-color-tertiary) 5%, transparent);\n --sf-color-tertiary-a10: color-mix(in oklab, var(--sf-color-tertiary) 10%, transparent);\n --sf-color-tertiary-a20: color-mix(in oklab, var(--sf-color-tertiary) 20%, transparent);\n --sf-color-tertiary-a30: color-mix(in oklab, var(--sf-color-tertiary) 30%, transparent);\n --sf-color-tertiary-a40: color-mix(in oklab, var(--sf-color-tertiary) 40%, transparent);\n --sf-color-tertiary-a50: color-mix(in oklab, var(--sf-color-tertiary) 50%, transparent);\n --sf-color-tertiary-a60: color-mix(in oklab, var(--sf-color-tertiary) 60%, transparent);\n --sf-color-tertiary-a70: color-mix(in oklab, var(--sf-color-tertiary) 70%, transparent);\n --sf-color-tertiary-a80: color-mix(in oklab, var(--sf-color-tertiary) 80%, transparent);\n --sf-color-tertiary-a90: color-mix(in oklab, var(--sf-color-tertiary) 90%, transparent);\n --sf-color-tertiary-a95: color-mix(in oklab, var(--sf-color-tertiary) 95%, transparent);\n\n --sf-color-tertiary-superlight: var(--sf-color-tertiary-50);\n --sf-color-tertiary-xlight: var(--sf-color-tertiary-200);\n --sf-color-tertiary-lighter: var(--sf-color-tertiary-400);\n --sf-color-tertiary-darker: var(--sf-color-tertiary-600);\n --sf-color-tertiary-xdark: var(--sf-color-tertiary-800);\n --sf-color-tertiary-superdark: var(--sf-color-tertiary-950);\n\n --sf-color-tertiary-hover: var(--sf-color-tertiary-darker);\n --sf-color-tertiary-active: var(--sf-color-tertiary-xdark);\n --sf-color-tertiary-subtle: var(--sf-color-tertiary-a10);\n --sf-color-tertiary-muted: var(--sf-color-tertiary-a30);\n --sf-color-tertiary-ghost: var(--sf-color-tertiary-a5);\n\n\n /* ----------------------------------------------------------\n action\n ---------------------------------------------------------- */\n --sf-color-action-50: color-mix(in oklab, var(--sf-color-action) 4%, var(--sf-color-base));\n --sf-color-action-100: color-mix(in oklab, var(--sf-color-action) 8%, var(--sf-color-base));\n --sf-color-action-200: color-mix(in oklab, var(--sf-color-action) 20%, var(--sf-color-base));\n --sf-color-action-300: color-mix(in oklab, var(--sf-color-action) 40%, var(--sf-color-base));\n --sf-color-action-400: color-mix(in oklab, var(--sf-color-action) 65%, var(--sf-color-base));\n --sf-color-action-500: var(--sf-color-action);\n --sf-color-action-600: color-mix(in oklab, var(--sf-color-action) 82%, var(--sf-color-text));\n --sf-color-action-700: color-mix(in oklab, var(--sf-color-action) 62%, var(--sf-color-text));\n --sf-color-action-800: color-mix(in oklab, var(--sf-color-action) 38%, var(--sf-color-text));\n --sf-color-action-900: color-mix(in oklab, var(--sf-color-action) 18%, var(--sf-color-text));\n --sf-color-action-950: color-mix(in oklab, var(--sf-color-action) 8%, var(--sf-color-text));\n\n --sf-color-action-a5: color-mix(in oklab, var(--sf-color-action) 5%, transparent);\n --sf-color-action-a10: color-mix(in oklab, var(--sf-color-action) 10%, transparent);\n --sf-color-action-a20: color-mix(in oklab, var(--sf-color-action) 20%, transparent);\n --sf-color-action-a30: color-mix(in oklab, var(--sf-color-action) 30%, transparent);\n --sf-color-action-a40: color-mix(in oklab, var(--sf-color-action) 40%, transparent);\n --sf-color-action-a50: color-mix(in oklab, var(--sf-color-action) 50%, transparent);\n --sf-color-action-a60: color-mix(in oklab, var(--sf-color-action) 60%, transparent);\n --sf-color-action-a70: color-mix(in oklab, var(--sf-color-action) 70%, transparent);\n --sf-color-action-a80: color-mix(in oklab, var(--sf-color-action) 80%, transparent);\n --sf-color-action-a90: color-mix(in oklab, var(--sf-color-action) 90%, transparent);\n --sf-color-action-a95: color-mix(in oklab, var(--sf-color-action) 95%, transparent);\n\n --sf-color-action-superlight: var(--sf-color-action-50);\n --sf-color-action-xlight: var(--sf-color-action-200);\n --sf-color-action-lighter: var(--sf-color-action-400);\n --sf-color-action-darker: var(--sf-color-action-600);\n --sf-color-action-xdark: var(--sf-color-action-800);\n --sf-color-action-superdark: var(--sf-color-action-950);\n\n --sf-color-action-hover: var(--sf-color-action-darker);\n --sf-color-action-active: var(--sf-color-action-xdark);\n --sf-color-action-subtle: var(--sf-color-action-a10);\n --sf-color-action-muted: var(--sf-color-action-a30);\n --sf-color-action-ghost: var(--sf-color-action-a5);\n\n\n /* ----------------------------------------------------------\n neutral\n ---------------------------------------------------------- */\n --sf-color-neutral-50: color-mix(in oklab, var(--sf-color-neutral) 4%, var(--sf-color-base));\n --sf-color-neutral-100: color-mix(in oklab, var(--sf-color-neutral) 8%, var(--sf-color-base));\n --sf-color-neutral-200: color-mix(in oklab, var(--sf-color-neutral) 20%, var(--sf-color-base));\n --sf-color-neutral-300: color-mix(in oklab, var(--sf-color-neutral) 40%, var(--sf-color-base));\n --sf-color-neutral-400: color-mix(in oklab, var(--sf-color-neutral) 65%, var(--sf-color-base));\n --sf-color-neutral-500: var(--sf-color-neutral);\n --sf-color-neutral-600: color-mix(in oklab, var(--sf-color-neutral) 82%, var(--sf-color-text));\n --sf-color-neutral-700: color-mix(in oklab, var(--sf-color-neutral) 62%, var(--sf-color-text));\n --sf-color-neutral-800: color-mix(in oklab, var(--sf-color-neutral) 38%, var(--sf-color-text));\n --sf-color-neutral-900: color-mix(in oklab, var(--sf-color-neutral) 18%, var(--sf-color-text));\n --sf-color-neutral-950: color-mix(in oklab, var(--sf-color-neutral) 8%, var(--sf-color-text));\n\n --sf-color-neutral-a5: color-mix(in oklab, var(--sf-color-neutral) 5%, transparent);\n --sf-color-neutral-a10: color-mix(in oklab, var(--sf-color-neutral) 10%, transparent);\n --sf-color-neutral-a20: color-mix(in oklab, var(--sf-color-neutral) 20%, transparent);\n --sf-color-neutral-a30: color-mix(in oklab, var(--sf-color-neutral) 30%, transparent);\n --sf-color-neutral-a40: color-mix(in oklab, var(--sf-color-neutral) 40%, transparent);\n --sf-color-neutral-a50: color-mix(in oklab, var(--sf-color-neutral) 50%, transparent);\n --sf-color-neutral-a60: color-mix(in oklab, var(--sf-color-neutral) 60%, transparent);\n --sf-color-neutral-a70: color-mix(in oklab, var(--sf-color-neutral) 70%, transparent);\n --sf-color-neutral-a80: color-mix(in oklab, var(--sf-color-neutral) 80%, transparent);\n --sf-color-neutral-a90: color-mix(in oklab, var(--sf-color-neutral) 90%, transparent);\n --sf-color-neutral-a95: color-mix(in oklab, var(--sf-color-neutral) 95%, transparent);\n\n --sf-color-neutral-superlight: var(--sf-color-neutral-50);\n --sf-color-neutral-xlight: var(--sf-color-neutral-200);\n --sf-color-neutral-lighter: var(--sf-color-neutral-400);\n --sf-color-neutral-darker: var(--sf-color-neutral-600);\n --sf-color-neutral-xdark: var(--sf-color-neutral-800);\n --sf-color-neutral-superdark: var(--sf-color-neutral-950);\n\n --sf-color-neutral-hover: var(--sf-color-neutral-darker);\n --sf-color-neutral-active: var(--sf-color-neutral-xdark);\n --sf-color-neutral-subtle: var(--sf-color-neutral-a10);\n --sf-color-neutral-muted: var(--sf-color-neutral-a30);\n --sf-color-neutral-ghost: var(--sf-color-neutral-a5);\n\n\n /* ----------------------------------------------------------\n base — inverted mix direction (V-shaped ramp by design):\n tints mix --sf-color-text INTO base, shades mix base INTO text.\n 50 = lightest surface, 950 = darkest. See architecture.md.\n ---------------------------------------------------------- */\n --sf-color-base-50: color-mix(in oklab, var(--sf-color-text) 4%, var(--sf-color-base));\n --sf-color-base-100: color-mix(in oklab, var(--sf-color-text) 8%, var(--sf-color-base));\n --sf-color-base-200: color-mix(in oklab, var(--sf-color-text) 20%, var(--sf-color-base));\n --sf-color-base-300: color-mix(in oklab, var(--sf-color-text) 40%, var(--sf-color-base));\n --sf-color-base-400: color-mix(in oklab, var(--sf-color-text) 65%, var(--sf-color-base));\n --sf-color-base-500: var(--sf-color-base);\n --sf-color-base-600: color-mix(in oklab, var(--sf-color-base) 82%, var(--sf-color-text));\n --sf-color-base-700: color-mix(in oklab, var(--sf-color-base) 62%, var(--sf-color-text));\n --sf-color-base-800: color-mix(in oklab, var(--sf-color-base) 38%, var(--sf-color-text));\n --sf-color-base-900: color-mix(in oklab, var(--sf-color-base) 18%, var(--sf-color-text));\n --sf-color-base-950: color-mix(in oklab, var(--sf-color-base) 8%, var(--sf-color-text));\n\n --sf-color-base-a5: color-mix(in oklab, var(--sf-color-base) 5%, transparent);\n --sf-color-base-a10: color-mix(in oklab, var(--sf-color-base) 10%, transparent);\n --sf-color-base-a20: color-mix(in oklab, var(--sf-color-base) 20%, transparent);\n --sf-color-base-a30: color-mix(in oklab, var(--sf-color-base) 30%, transparent);\n --sf-color-base-a40: color-mix(in oklab, var(--sf-color-base) 40%, transparent);\n --sf-color-base-a50: color-mix(in oklab, var(--sf-color-base) 50%, transparent);\n --sf-color-base-a60: color-mix(in oklab, var(--sf-color-base) 60%, transparent);\n --sf-color-base-a70: color-mix(in oklab, var(--sf-color-base) 70%, transparent);\n --sf-color-base-a80: color-mix(in oklab, var(--sf-color-base) 80%, transparent);\n --sf-color-base-a90: color-mix(in oklab, var(--sf-color-base) 90%, transparent);\n --sf-color-base-a95: color-mix(in oklab, var(--sf-color-base) 95%, transparent);\n\n --sf-color-base-superlight: var(--sf-color-base-50);\n --sf-color-base-xlight: var(--sf-color-base-200);\n --sf-color-base-lighter: var(--sf-color-base-400);\n --sf-color-base-darker: var(--sf-color-base-600);\n --sf-color-base-xdark: var(--sf-color-base-800);\n --sf-color-base-superdark: var(--sf-color-base-950);\n\n --sf-color-base-hover: var(--sf-color-base-darker);\n --sf-color-base-active: var(--sf-color-base-xdark);\n --sf-color-base-subtle: var(--sf-color-base-a10);\n --sf-color-base-muted: var(--sf-color-base-a30);\n --sf-color-base-ghost: var(--sf-color-base-a5);\n\n}\n\n/* ─── optional/forms.css ─── */\n/* ============================================================\n SLASHED — optional/forms.css\n @layer slashed.forms\n\n Classless form styling — opt-in element-level defaults for\n ,