diff --git a/bundle.config.json b/bundle.config.json index f9e61312..d264b1d4 100644 --- a/bundle.config.json +++ b/bundle.config.json @@ -16,6 +16,23 @@ "core/print.css" ] }, + { + "output": "dist/slashed.essential.flat.css", + "flat": true, + "files": [ + "core/layers.css", + "core/tokens.css", + "core/tokens.layout.css", + "core/reset.css", + "core/base.css", + "core/themes.css", + "core/layout.css", + "core/states.css", + "core/motion.css", + "core/accessibility.css", + "core/print.css" + ] + }, { "output": "dist/slashed.optimal.css", "files": [ @@ -36,7 +53,7 @@ ] }, { - "output": "dist/slashed.bricks.css", + "output": "dist/slashed.optimal.flat.css", "flat": true, "files": [ "core/layers.css", @@ -76,6 +93,28 @@ "optional/legacy.css" ] }, + { + "output": "dist/slashed.optimal-components.flat.css", + "flat": true, + "files": [ + "core/layers.css", + "core/tokens.css", + "core/tokens.layout.css", + "core/reset.css", + "core/base.css", + "core/themes.css", + "core/layout.css", + "core/states.css", + "core/motion.css", + "core/accessibility.css", + "core/print.css", + "optional/tokens.palette.css", + "optional/forms.css", + "optional/tokens.components.css", + "optional/components.css", + "optional/legacy.css" + ] + }, { "output": "dist/slashed.optimal-utilities.css", "files": [ @@ -96,6 +135,27 @@ "optional/legacy.css" ] }, + { + "output": "dist/slashed.optimal-utilities.flat.css", + "flat": true, + "files": [ + "core/layers.css", + "core/tokens.css", + "core/tokens.layout.css", + "core/reset.css", + "core/base.css", + "core/themes.css", + "core/layout.css", + "core/states.css", + "core/motion.css", + "core/accessibility.css", + "core/print.css", + "optional/tokens.palette.css", + "optional/forms.css", + "optional/utilities.css", + "optional/legacy.css" + ] + }, { "output": "dist/slashed.full.css", "files": [ @@ -117,6 +177,29 @@ "optional/utilities.css", "optional/legacy.css" ] + }, + { + "output": "dist/slashed.full.flat.css", + "flat": true, + "files": [ + "core/layers.css", + "core/tokens.css", + "core/tokens.layout.css", + "core/reset.css", + "core/base.css", + "core/themes.css", + "core/layout.css", + "core/states.css", + "core/motion.css", + "core/accessibility.css", + "core/print.css", + "optional/tokens.palette.css", + "optional/forms.css", + "optional/tokens.components.css", + "optional/components.css", + "optional/utilities.css", + "optional/legacy.css" + ] } ] } diff --git a/dist/slashed.bricks.min.css.map b/dist/slashed.bricks.min.css.map deleted file mode 100644 index 91b09e98..00000000 --- a/dist/slashed.bricks.min.css.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"sourceRoot":null,"mappings":"AA+EA,kGACA,qGACA,oGACA,kGACA,mGACA,iGAIA,mGACA,kGACA,+FACA,gGACA,iGAIA,wEAOA,49vBAy3BA,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,kBAEA,+QASA,6EACA,8CACA,4CACA,6CAEA,gCAIA,6LASA,iRAWA,2FASA,sBAIA,0GAKA,mFAIA,0GAMA,4BAIA,uCAIA,kDAIA,mDAIA,gDAIA,sBACA,oCACA,6EA0CA,mCACE,0DAQF,qDACA,mDAgBA,gDACA,sDACA,sDACA,sDACA,wDAEA,gEAMA,kJAKA,4MAkBA,wJAOA,2DACA,yDACA,uDACA,uDAOA,qEAKA,uCACA,qCACA,mCACA,mCACA,mCACA,qCACA,uCACA,uCAEA,qCACA,oCACA,uCAQA,0GAWA,iIAOA,4EAWA,gJAOA,yCACA,uCACA,qCACA,qCACA,qCACA,uCAEA,sCACA,2CACA,0CACA,mDAQA,kEAMA,wGAKA,+HAYA,8GAKA,6CACA,2CAQA,oEAMA,wFAKA,uDACA,6CASA,iIAMA,iGAIA,iDACA,+CACA,+CACA,+CACA,iDAIA,qCAMA,0LAQA,8CACA,4CACA,4CACA,4CACA,8CAQA,yHAOA,8CAIA,gCACA,8CACA,sDACA,uDAOA,oHAQA,sEAOA,sDACA,oDACA,sDACA,0DACA,kDACA,gCACA,sDAOA,sLAUA,sEAUA,gGAOA,mCAIA,yJAgBA,0FAMA,wEAMA,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,yDAOA,uEAKA,2DACA,+DACA,sDACA,6DACA,8EACA,iDACA,gCACA,gCACA,iEACA,gDACA,wHACA,4FACA,gFAEA,+EAEA,iFACA,+CACA,2DACA,8CACA,wFACA,+FACA,6DA4BA,kCAIA,gCAIA,+BAQA,wGAOA,kDASA,wEAMA,8UAeA,yBAIA,uCAOA,oSAmBA,4BAMA,2DAIA,mGASA,2DAIA,4EAeA,wBAIA,6BAIA,4BAgBA,+GAKA,6GAKA,iHAKA,iHAKA,2GAKA,wGAKA,8GASA,iFAMA,yDAMA,yBAIA,8DASA,wCASA,sCAIA,yDAKA,wEAUA,wCAKA,6EAKA,0BAQA,sCASA,6BAIA,kCAIA,sJASA,wKASA,6BAgCA,8CAEE,4BAKA,uCAMA,+QASA,sCACE,mJAaF,kDACA,oDACA,0DACA,8DACA,8DACA,gEACA,oDACA,yDASF,iDACA,kDACA,kFACA,qFACA,kFACA,oFACA,oEACA,sEAEA,qCACA,oFAMA,6CACA,0DACA,8DAMA,4QAMA,8CACE,2DAkCF,wCAIA,6KAWA,uCACE,0IAQA,8JAaF,yMAcA,+BACE,uDAOA,iCAQF,6CACE,+DAYF,wBACE,gHAeF,mDAUA,6RAmBA,wUAYA,uCAWA,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,sIAoCF,ksdAmSA,ypBAuBA,qbAcA,0LAUA,2FAUA,6BAQA,0EAQA,0pBAUA,0FAQA,4iBAsBA,6JAOA,8PAQA,6KAaA,iIAYA,gCAGA,8ZAaA,mHAQA,iFAMA,wKAKA,2JAKA,wOAQA,2JAOA,uKASA,yIAMA,mFASA,kGASA,gIA+CA,8BACE,uBAmBF,uCACE,iLAkBF,wCACE","sources":["slashed.bricks.css"],"sourcesContent":["/* SLASHED v0.2.10 — slashed.bricks.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 PUBLIC — covered by SemVer; safe to consume and override:\n · brand/status source tokens (--sf-color-*-light / -dark)\n · resolved semantic tokens (--sf-color-*, --sf-color-text--*, surfaces)\n · scales (--sf-space-*, --sf-text-*, --sf-radius-*, --sf-font-weight-*,\n --sf-leading-*, --sf-tracking-*, --sf-border-width-*, --sf-shadow-*,\n --sf-duration-*, --sf-ease-*, --sf-transition-*, --sf-z-*)\n · BEM consumer aliases (--sf-gap, --sf-content-gap, --sf-component-pad,\n --sf-section-pad*, --sf-divider-*, --sf-field-*, behaviour tokens)\n INTERNAL — implementation detail, may change without a major bump:\n · --sf-is-dark (mode flag) and any token explicitly marked INTERNAL\n DEPRECATED — slated for removal on the documented timeline:\n · --sf-transition-base (→ use a scoped --sf-transition-* token)\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 ≤2 hops (primitive → semantic → scale), with no duplicates/dangling/cycles.\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\n/* ============================================================\n DERIVED SEMANTIC TOKENS\n ============================================================ */\n\n:root {\n\n /* ----------------------------------------------------------\n Color scheme\n ---------------------------------------------------------- */\n --sf-color-scheme: light dark;\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(0.6 - l) * 999, 0.95) 0 0);\n\n\n /* ----------------------------------------------------------\n Text-on-color — sign(0.6 - 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(0.6 - l) * 999, 0.95) 0 0);\n --sf-color-text--on-secondary: oklch(from var(--sf-color-secondary) clamp(0.1, sign(0.6 - l) * 999, 0.95) 0 0);\n --sf-color-text--on-tertiary: oklch(from var(--sf-color-tertiary) clamp(0.1, sign(0.6 - l) * 999, 0.95) 0 0);\n --sf-color-text--on-action: oklch(from var(--sf-color-action) clamp(0.1, sign(0.6 - l) * 999, 0.95) 0 0);\n --sf-color-text--on-neutral: oklch(from var(--sf-color-neutral) clamp(0.1, sign(0.6 - 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(0.6 - l) * 999, 0.95) 0 0);\n --sf-color-text--on-warning: oklch(from var(--sf-color-warning) clamp(0.1, sign(0.6 - l) * 999, 0.95) 0 0);\n --sf-color-text--on-error: oklch(from var(--sf-color-error) clamp(0.1, sign(0.6 - l) * 999, 0.95) 0 0);\n --sf-color-text--on-info: oklch(from var(--sf-color-info) clamp(0.1, sign(0.6 - l) * 999, 0.95) 0 0);\n --sf-color-text--on-danger: oklch(from var(--sf-color-danger) clamp(0.1, sign(0.6 - 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 --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, max(l, 0.62), 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.72), 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.78), 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, max(l, 0.62), 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 /* ----------------------------------------------------------\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\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 (Tailwind / Open Props parity) */\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\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-photo: 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 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\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 1s steps(1, end) infinite;\n --sf-animation-float: sf-float calc(3s * var(--sf-motion-scale)) var(--sf-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 (see audit §D9). */\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 — attach to your own timeline.\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\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-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\n\n /* ----------------------------------------------------------\n Section padding\n ---------------------------------------------------------- */\n --sf-section-pad: var(--sf-section-pad--m);\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\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. The\n alias --sf-transition-base is kept for one minor release to avoid\n breaking consumers; migrate to --sf-transition-all or, better,\n one of the scoped variants.\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 /* Legacy alias — deprecated, remove after v1.x */\n --sf-transition-base: var(--sf-transition-all);\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-default: 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-default: 16rem;\n --sf-grid-min-m: 16rem;\n --sf-grid-min-l: 20rem;\n --sf-grid-min-xl: 24rem;\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 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\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\na {\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: 0.875em;\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-color-action);\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/* ─── 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/* ─── 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--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\n.sf-section-group > .sf-section + .sf-section { padding-block-start: 0; }\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--2xs { gap: var(--sf-space-2xs); }\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.sf-stack--3xl { gap: var(--sf-space-3xl); }\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--2xs { gap: var(--sf-space-2xs); }\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\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, var(--sf-sidebar-width-default));\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, var(--sf-sidebar-width-default));\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\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/* -- 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-photo); }\n.sf-frame--3-2 { aspect-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 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-space-content);\n container: sf-alternate / inline-size;\n}\n\n.sf-alternate > * {\n display: grid;\n gap: var(--sf-space-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/* -- 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-brand, 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-prose .sf-not-prose > * + * { margin-block-start: 0; }\n.sf-prose .sf-not-prose li + li { margin-block-start: 0; }\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/* ─── 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 (informed by Bulma, ACSS, CUBE CSS):\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: 2px solid var(--sf-color-border--strong);\n border-block-start-color: var(--sf-color-action);\n border-radius: var(--sf-radius-full);\n animation: sf-spin 0.6s linear infinite;\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: 0.7;\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: sf-shimmer 1.5s ease-in-out infinite;\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: 0.5;\n cursor: grabbing;\n}\n\n.is-drop-target {\n outline: 2px dashed var(--sf-color-action);\n outline-offset: 2px;\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/* Parent reacts to a focused descendant (e.g. highlight a card while\n any field inside it has keyboard focus). Not is-/sf-prefixed by design. */\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 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}\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 (see audit §D9). */\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: 2px;\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: 44px;\n min-width: 44px;\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: 9999;\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 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, see audits/comparative-audit-2026.md F-04):\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 Load after core/tokens.css:\n\n Token families (per brand color):\n -50 … -950 numeric scale (Tailwind-style). 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 ,