/*! SLASHED v0.7.30 — slashed.optimal.css | MIT License | github.com/codeslash-dev/SLASHED */

/* ─── core/layers.css ─── */
/* SLASHED — core/layers.css
   Global cascade order for every SLASHED layer.
   Import before all framework modules. */

@layer
  slashed.tokens,
  slashed.reset,
  slashed.base,
  slashed.forms,
  slashed.components,
  slashed.layout,
  slashed.macros,
  slashed.utilities,
  slashed.states,
  slashed.themes,
  slashed.motion,
  slashed.accessibility,
  slashed.print,
  slashed.legacy,
  slashed.overrides;

/* ─── core/tokens.css ─── */
/* SLASHED — core/tokens.css
   @layer slashed.tokens
   Core design tokens: color sources, semantic colors, type, spacing, radii, shadows, motion, z-index.
   Prefix: --sf-*; brand theming starts with six -source-light tokens. */

@layer slashed.tokens {

  /* ============================================================
     @property — BRAND COLORS (-source-light source tokens, animatable)
     Override these 6 tokens to rebrand — dark mode auto-derives.
     Optionally set --sf-color-X-source-dark for full control.
     ============================================================ */

  @property --sf-color-primary-source-light   { syntax: "<color>"; inherits: true; initial-value: oklch(0.47 0.27 264); }
  @property --sf-color-secondary-source-light { syntax: "<color>"; inherits: true; initial-value: oklch(0.22 0.04 264); }
  @property --sf-color-tertiary-source-light  { syntax: "<color>"; inherits: true; initial-value: oklch(0.42 0.22 295); }
  @property --sf-color-action-source-light    { syntax: "<color>"; inherits: true; initial-value: oklch(0.50 0.22 235); }
  @property --sf-color-neutral-source-light   { syntax: "<color>"; inherits: true; initial-value: oklch(0.52 0.025 260); }
  /* initial-value is 0.96 (not 0.99) so the +0.02/+0.04 surface offsets produce
     visually distinct levels in light mode. At 0.99 all lighter surfaces clamp
     to pure white and become indistinguishable. 0.96 gives: bg=0.98, inset=0.94,
     raised=1.0→white — all distinct. Consumers using near-1.0 values should rely
     on shadows rather than lightness for elevation. */
  @property --sf-color-base-source-light  { syntax: "<color>"; inherits: true; initial-value: oklch(0.96 0.006 250); }

  /* @property — STATUS COLORS (-source-light source tokens) */

  @property --sf-color-success-source-light { syntax: "<color>"; inherits: true; initial-value: oklch(0.50 0.16 145); }
  @property --sf-color-warning-source-light { syntax: "<color>"; inherits: true; initial-value: oklch(0.75 0.17 80);  }
  @property --sf-color-info-source-light    { syntax: "<color>"; inherits: true; initial-value: oklch(0.48 0.18 235); }
  @property --sf-color-danger-source-light  { syntax: "<color>"; inherits: true; initial-value: oklch(0.48 0.22 12);  }

  /* Plain :root mirrors — character-for-character identical to each @property
     initial-value above. Engines lacking @property support read the token value
     from here. Do NOT edit these independently from the initial-value strings. */
  :root {
    --sf-color-primary-source-light:   oklch(0.47 0.27 264);
    --sf-color-secondary-source-light: oklch(0.22 0.04 264);
    --sf-color-tertiary-source-light:  oklch(0.42 0.22 295);
    --sf-color-action-source-light:    oklch(0.50 0.22 235);
    --sf-color-neutral-source-light:   oklch(0.52 0.025 260);
    --sf-color-base-source-light:      oklch(0.96 0.006 250);
    --sf-color-success-source-light:   oklch(0.50 0.16 145);
    --sf-color-warning-source-light:   oklch(0.75 0.17 80);
    --sf-color-info-source-light:      oklch(0.48 0.18 235);
    --sf-color-danger-source-light:    oklch(0.48 0.22 12);
  }

  /* @property — DARK SOURCE TOKENS (-dark, animatable) */
  /* Registers the auto-computed dark counterparts as typed <color> so their
     resolved absolute value is inherited by child elements (e.g. a section
     with [data-theme="dark"]). Without @property these tokens inherit as raw
     text, and the nested var()-in-oklch-from chain fails type-checking on
     registered child properties.
     initial-value = dark-mode value derived from the formula in themes.css
     (clamp(0.65, 0.95 - l*0.5, 0.88) for brand/status; inverted for base).
     Consumers who set --sf-color-X-dark explicitly do not need to register it
     — their override is a plain OKLCH literal and passes type-checking directly. */
  @property --sf-color-primary-source-dark   { syntax: "<color>"; inherits: true; initial-value: oklch(0.715 0.243 264);  }
  @property --sf-color-secondary-source-dark { syntax: "<color>"; inherits: true; initial-value: oklch(0.84 0.036 264);   }
  @property --sf-color-tertiary-source-dark  { syntax: "<color>"; inherits: true; initial-value: oklch(0.74 0.198 295);   }
  @property --sf-color-action-source-dark    { syntax: "<color>"; inherits: true; initial-value: oklch(0.70 0.198 235);   }
  @property --sf-color-neutral-source-dark   { syntax: "<color>"; inherits: true; initial-value: oklch(0.69 0.0225 260);  }
  @property --sf-color-base-source-dark      { syntax: "<color>"; inherits: true; initial-value: oklch(0.22 0.003 250);   }
  @property --sf-color-success-source-dark   { syntax: "<color>"; inherits: true; initial-value: oklch(0.70 0.144 145);   }
  @property --sf-color-warning-source-dark   { syntax: "<color>"; inherits: true; initial-value: oklch(0.65 0.153 80);    }
  @property --sf-color-info-source-dark      { syntax: "<color>"; inherits: true; initial-value: oklch(0.71 0.162 235);   }
  @property --sf-color-danger-source-dark    { syntax: "<color>"; inherits: true; initial-value: oklch(0.71 0.198 12);    }

  /* @property — COMPUTED COLOR TOKENS (brand + status)
     Registers the computed tokens as typed <color> so CSS transitions can
     interpolate between light and dark values when [data-theme] changes.
     initial-value = light-mode value (safe fallback if nothing sets the token).
     Child-element theming works because [data-theme="dark"] in themes.css
     assigns var(--sf-color-X-dark) — now a registered typed <color> whose
     absolute value is inherited, so no multi-level var() resolution is needed. */
  @property --sf-color-primary   { syntax: "<color>"; inherits: true; initial-value: oklch(0.47 0.27 264); }
  @property --sf-color-secondary { syntax: "<color>"; inherits: true; initial-value: oklch(0.22 0.04 264); }
  @property --sf-color-tertiary  { syntax: "<color>"; inherits: true; initial-value: oklch(0.42 0.22 295); }
  @property --sf-color-action    { syntax: "<color>"; inherits: true; initial-value: oklch(0.50 0.22 235); }
  @property --sf-color-neutral   { syntax: "<color>"; inherits: true; initial-value: oklch(0.52 0.025 260); }
  @property --sf-color-base      { syntax: "<color>"; inherits: true; initial-value: oklch(0.96 0.006 250); }
  @property --sf-color-success   { syntax: "<color>"; inherits: true; initial-value: oklch(0.50 0.16 145); }
  @property --sf-color-warning   { syntax: "<color>"; inherits: true; initial-value: oklch(0.75 0.17 80);  }
  @property --sf-color-info      { syntax: "<color>"; inherits: true; initial-value: oklch(0.48 0.18 235); }
  @property --sf-color-danger    { syntax: "<color>"; inherits: true; initial-value: oklch(0.48 0.22 12);  }

  /* Plain :root mirrors for computed tokens — resolve to the -source-light source token
     so user overrides to --sf-color-X-source-light are respected in all browsers.
     Overridden by the @supports block in themes.css whenever dark mode is active. */
  :root {
    --sf-color-primary:   var(--sf-color-primary-source-light);
    --sf-color-secondary: var(--sf-color-secondary-source-light);
    --sf-color-tertiary:  var(--sf-color-tertiary-source-light);
    --sf-color-action:    var(--sf-color-action-source-light);
    --sf-color-neutral:   var(--sf-color-neutral-source-light);
    --sf-color-base:      var(--sf-color-base-source-light);
    --sf-color-success:   var(--sf-color-success-source-light);
    --sf-color-warning:   var(--sf-color-warning-source-light);
    --sf-color-info:      var(--sf-color-info-source-light);
    --sf-color-danger:    var(--sf-color-danger-source-light);
  }

  /* Mode flag — INTERNAL, not a public hook (SL-003: same "--sf-is-*" naming
     pattern as the public flags below, opposite contract — read here, don't
     set). Drives formula direction for non-color dark overrides. Set only by
     themes.css via [data-theme="dark"] and the prefers-color-scheme media
     query; every other read site (core/tokens.css, the configurator's power
     knobs) only reads it inside calc() expressions. Setting it directly
     desyncs it from color-scheme/[data-theme] and produces an inconsistent
     theme. */
  @property --sf-is-dark { syntax: "<integer>"; inherits: true; initial-value: 0; }

  /* No --sf-is-active/-current/-pressed/-open here: they registered an
     INTERACTION STATE FLAGS mechanism (inheritable custom properties set by
     .sf-is-active/.sf-is-open/etc for a consumer's own calc() to branch on)
     with zero consumers anywhere in this codebase, its demo, or the
     configurator beyond its own preview toggle — speculative surface,
     removed. Style off [aria-current]/[aria-expanded]/[aria-pressed]
     instead; see docs/states.md § "Prefer native state". */

  /* ============================================================
     FLUID SCALE ENGINE — generative inputs
     ------------------------------------------------------------
     The fluid type / space / display scales are computed at runtime
     from these scalars. Override any of them on :root and every
     clamp() recalibrates — no build step, no token regeneration.

       --sf-fluid-min-vw / --sf-fluid-max-vw   viewport range (rem, unitless)
                                               over which values interpolate.
       --sf-text-ratio-min / -max              modular ratio of the TYPE scale
                                               at min / max viewport (dual-ratio:
                                               subtler on mobile, larger on desktop).
       --sf-text-base-min / -max               --sf-text-m endpoints (rem, unitless).
       --sf-text-display-base-min / -max       --sf-text-display-s endpoints.
       --sf-space-ratio-min / -max             modular ratio of the SPACE scale.
       --sf-space-base-min / -max              --sf-space-m endpoints (rem, unitless).
       --sf-fluid-width                        the length every fluid clamp()
                                               interpolates against. Defaults to
                                               100vw (viewport-relative). Override
                                               to 100cqi on a container scope to make
                                               the type + space + display scales
                                               container-relative — see .sf-fluid-cq
                                               in core/layout.css (issue #497).

     Registered as <number> so they can be used as pow() exponents/operands
     and multiplied by 1rem in calc(). Requires CSS pow() (Chrome 125+,
     Safari 15.4+, Firefox 118+); the effective SLASHED floor is higher
     (Chrome 125 / Safari 18.0 / Firefox 129), set by the colour system —
     see browser floor. Plain :root mirrors
     below keep values readable; do NOT edit them independently. */
  @property --sf-fluid-min-vw          { syntax: "<number>"; inherits: true; initial-value: 22.5; }
  @property --sf-fluid-max-vw          { syntax: "<number>"; inherits: true; initial-value: 90; }
  @property --sf-text-ratio-min        { syntax: "<number>"; inherits: true; initial-value: 1.25; }
  @property --sf-text-ratio-max        { syntax: "<number>"; inherits: true; initial-value: 1.333; }
  @property --sf-text-base-min         { syntax: "<number>"; inherits: true; initial-value: 1; }
  @property --sf-text-base-max         { syntax: "<number>"; inherits: true; initial-value: 1.25; }
  @property --sf-text-display-base-min { syntax: "<number>"; inherits: true; initial-value: 2.4; }
  @property --sf-text-display-base-max { syntax: "<number>"; inherits: true; initial-value: 3; }
  @property --sf-space-ratio-min       { syntax: "<number>"; inherits: true; initial-value: 1.25; }
  @property --sf-space-ratio-max       { syntax: "<number>"; inherits: true; initial-value: 1.333; }
  @property --sf-space-base-min        { syntax: "<number>"; inherits: true; initial-value: 1; }
  @property --sf-space-base-max        { syntax: "<number>"; inherits: true; initial-value: 2; }

  /* Fluid interpolation driver — the length term every fluid clamp() below
     measures against. Universal syntax ("*") not "<length>": a registered
     <length> requires a computationally-independent initial-value, and 100vw
     (viewport-dependent) is not one. The real default lives on :root; an
     opt-in scope (e.g. .sf-fluid-cq) overrides it to 100cqi so the whole
     generative scale becomes container-relative without duplicating a single
     formula. */
  @property --sf-fluid-width           { syntax: "*"; inherits: true; }

  /* ── Output token @property registrations ──────────────────────────────────
     Radius + space: <length> syntax enables CSS transitions on layout values —
     border-radius/padding/gap etc. now interpolate instead of snapping.
     Shadow: syntax:"*" (no <shadow> type in spec); inherits:true so :root values
     cascade normally; registers the properties formally for DevTools inspection.

     initial-value ≠ :root default (by spec): a registered <length> requires a
     computationally-independent initial-value, so these carry `initial-value: 0`
     while their real defaults are the calc()-derived values set on :root below
     (see --sf-fluid-width above for the same constraint stated at length). One
     consequence worth flagging: once a property is registered, the initial
     value IS a value — so a fallback like `var(--sf-radius-m, 12px)` will NEVER
     fire; an unset --sf-radius-m resolves to the registered `0`, not to "no
     value". Author fallbacks against the token's :root default, not this 0. */

  @property --sf-radius-none  { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-radius-2xs   { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-radius-xs    { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-radius-s     { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-radius-m     { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-radius-l     { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-radius-xl    { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-radius-2xl   { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-radius-3xl   { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-radius-4xl   { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-radius-full  { syntax: "<length>"; inherits: true; initial-value: 9999px; }
  @property --sf-radius-pill  { syntax: "<length>"; inherits: true; initial-value: 9999px; }
  @property --sf-radius-outer { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-media-radius { syntax: "<length>"; inherits: true; initial-value: 0; }

  @property --sf-space-none       { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-space-px         { syntax: "<length>"; inherits: true; initial-value: 1px; }
  @property --sf-space-2xs        { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-space-xs         { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-space-s          { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-space-m          { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-space-l          { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-space-xl         { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-space-2xl        { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-space-3xl        { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-space-4xl        { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-gap              { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-content-gap      { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-gutter           { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-component-pad    { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-field-block      { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-section-pad--xs  { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-section-pad--s   { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-section-pad--m   { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-section-pad--l   { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-section-pad--xl  { syntax: "<length>"; inherits: true; initial-value: 0; }
  @property --sf-section-pad--2xl { syntax: "<length>"; inherits: true; initial-value: 0; }

  @property --sf-shadow-none      { syntax: "*"; inherits: true; }
  @property --sf-shadow-xs        { syntax: "*"; inherits: true; }
  @property --sf-shadow-s         { syntax: "*"; inherits: true; }
  @property --sf-shadow-m         { syntax: "*"; inherits: true; }
  @property --sf-shadow-l         { syntax: "*"; inherits: true; }
  @property --sf-shadow-xl        { syntax: "*"; inherits: true; }
  @property --sf-shadow-2xl       { syntax: "*"; inherits: true; }
  @property --sf-shadow-inner     { syntax: "*"; inherits: true; }
  @property --sf-text-shadow-none { syntax: "*"; inherits: true; }
  @property --sf-text-shadow-xs   { syntax: "*"; inherits: true; }
  @property --sf-text-shadow-s    { syntax: "*"; inherits: true; }
  @property --sf-text-shadow-m    { syntax: "*"; inherits: true; }
  @property --sf-text-shadow-l    { syntax: "*"; inherits: true; }
  @property --sf-text-shadow-xl   { syntax: "*"; inherits: true; }
  @property --sf-drop-shadow-xs   { syntax: "*"; inherits: true; }
  @property --sf-drop-shadow-s    { syntax: "*"; inherits: true; }
  @property --sf-drop-shadow-m    { syntax: "*"; inherits: true; }
  @property --sf-drop-shadow-l    { syntax: "*"; inherits: true; }
  @property --sf-drop-shadow-xl   { syntax: "*"; inherits: true; }
  @property --sf-shadow-glow      { syntax: "*"; inherits: true; }

  :root {
    --sf-fluid-min-vw:          22.5;
    --sf-fluid-max-vw:          90;
    --sf-fluid-width:           100vw;  /* interpolation driver; 100cqi on a container scope → container-relative fluid (#497) */
    --sf-text-ratio-min:        1.25;
    --sf-text-ratio-max:        1.333;
    --sf-text-base-min:         1;
    --sf-text-base-max:         1.25;
    --sf-text-display-base-min: 2.4;
    --sf-text-display-base-max: 3;
    --sf-space-ratio-min:       1.25;
    --sf-space-ratio-max:       1.333;
    --sf-space-base-min:        1;
    --sf-space-base-max:        2;
  }

  /* ============================================================
     DERIVED SEMANTIC TOKENS
     ============================================================ */

  :root {

    /* ----------------------------------------------------------
       Color scheme
       ---------------------------------------------------------- */
    --sf-color-scheme: light dark;

    /* ----------------------------------------------------------
       LumLocker — OKLCH L value used by the
       :root[data-lumlocker] override in core/themes.css.
       Locks the 4 brand colors (primary, secondary, tertiary, action)
       to one shared lightness while keeping their individual hue and
       chroma. Neutral and base are excluded — neutral is a desaturated
       grey (locking it to a brand L would make it chromatic) and base
       must remain near-white in light mode for the page background.

       --sf-lumlocker is the LIGHT-mode target. Dark mode uses
       --sf-lumlocker-dark, which defaults to the mirror of the light L
       around 0.59 — the midpoint between the light (~0.96) and dark
       (~0.22) page backgrounds — i.e. calc(1.18 - L). Mirroring around
       the background midpoint (not 0.5) is what keeps the lock's
       contrast against the surface equal in both themes; a naive 1 - L
       would leave the dark lock too dark to read. Clamped to a visible
       band. A single knob that works in both themes. Override either:
         :root { --sf-lumlocker: 0.55 }          (light)
         :root { --sf-lumlocker-dark: 0.82 }     (dark, breaks the mirror)
       ---------------------------------------------------------- */
    --sf-lumlocker: 0.65;
    --sf-lumlocker-dark: clamp(0.5, calc(1.18 - var(--sf-lumlocker)), 0.92);

    /* ----------------------------------------------------------
       Resolved color tokens — auto-switch via light-dark().
       Components reference these. Override the -source-light / -source-dark
       source tokens above to customise; or override these directly
       with your own light-dark() expression.
       ---------------------------------------------------------- */
    /* Surface alias (plain var — no IACVT, stays ungated) */
    --sf-color-surface: var(--sf-color-base);

    /* ----------------------------------------------------------
       Text — plain var aliases (ungated)
       ---------------------------------------------------------- */

    /* ----------------------------------------------------------
       Text-on-color — plain var aliases (ungated)
       ---------------------------------------------------------- */
    --sf-color-text--on-base:    var(--sf-color-text);

    /* Static fallbacks for engines that support oklch(from …) but not sign()
       (Chrome 125–137, Firefox 128–130). The sign() gate below overrides these
       with the exact auto-contrast formula when the engine supports it.
       Derived from the default palette at threshold 0.6: surfaces with L < 0.6
       get near-white text (0.95); warning (L 0.75) and code (on light inset)
       get near-black (0.1). Custom palettes must override --sf-color-text--on-*
       manually if their surface colours differ from the defaults. */
    --sf-color-text--on-primary:   oklch(0.95 0 0);
    --sf-color-text--on-secondary: oklch(0.95 0 0);
    --sf-color-text--on-tertiary:  oklch(0.95 0 0);
    --sf-color-text--on-action:    oklch(0.95 0 0);
    --sf-color-text--on-neutral:   oklch(0.95 0 0);
    --sf-color-text--on-success:   oklch(0.95 0 0);
    --sf-color-text--on-warning:   oklch(0.1  0 0);
    --sf-color-text--on-info:      oklch(0.95 0 0);
    --sf-color-text--on-danger:    oklch(0.95 0 0);
    --sf-color-text--on-inverse:   oklch(0.95 0 0);
    --sf-color-code-text:          oklch(0.1  0 0);

    /* ----------------------------------------------------------
       Borders — plain var aliases (ungated)
       ---------------------------------------------------------- */
    --sf-color-border--focus: var(--sf-color-action);

    /* ----------------------------------------------------------
       Links — plain var aliases and geometry (ungated)
       ---------------------------------------------------------- */
    --sf-color-link--disabled:  var(--sf-color-text--disabled);
    /* Underline geometry — consumed by a:link in core/base.css and by
       the .sf-link--* macros. `auto` defers to the font's own metrics;
       set an explicit length (e.g. 0.08em) for a uniform rule. */
    --sf-link-underline-offset:    0.15em;
    --sf-link-underline-thickness: auto;

    /* ----------------------------------------------------------
       Interactive states — plain var aliases (ungated)
       ---------------------------------------------------------- */
    --sf-color-bg--disabled: var(--sf-color-inset);
    --sf-color-code-bg:      var(--sf-color-inset);

    /* ----------------------------------------------------------
       Selection & backdrop — static values (ungated)
       ---------------------------------------------------------- */
    --sf-color-selection-text: inherit;
    --sf-color-mark-text:      inherit;
    /* Dim: classic semi-transparent black for modal backdrops, distinct from --sf-color-overlay */
    --sf-color-dim:            oklch(0 0 0 / 0.5);
    --sf-color-white:          oklch(100% 0 0);
    --sf-color-black:          oklch(0% 0 0);

    /* ----------------------------------------------------------
       Scrollbar
       ---------------------------------------------------------- */
    --sf-scrollbar-thumb: var(--sf-color-neutral);
    --sf-scrollbar-track: transparent;

    /* ----------------------------------------------------------
       Shadow strength — base opacity for light mode; auto-boosted by
       +0.17 in dark mode via --sf-is-dark. Override with a calc() to
       preserve the adaptation: calc(0.1 + var(--sf-is-dark) * 0.17).
       ---------------------------------------------------------- */
    --sf-shadow-strength:   calc(0.08 + var(--sf-is-dark) * 0.17);
    /* Shadow lightness: 0.15 is the default density. Independent token allows
       tuning shadow depth globally across light/dark and surfaces. */
    --sf-shadow-lightness:  0.15;
    /* ----------------------------------------------------------
       Shadow glow color — tint used by --sf-shadow-glow. Override to
       retint glows; opacity/dark-mode adaptation is controlled by
       --sf-shadow-strength.
       ---------------------------------------------------------- */
    --sf-shadow-glow-color: var(--sf-color-primary);

    /* ----------------------------------------------------------
       Opacity — disabled state effects
       ---------------------------------------------------------- */
    --sf-opacity-disabled:  0.45;

    /* ----------------------------------------------------------
       Focus / form colors
       ---------------------------------------------------------- */
    --sf-focus-ring-color:  var(--sf-color-action);
    --sf-focus-ring-shadow: 0 0 0 var(--sf-focus-ring-offset) var(--sf-color-bg, var(--sf-color-base-source-light, #fff)),
                            0 0 0 calc(var(--sf-focus-ring-offset) + var(--sf-focus-ring-width)) var(--sf-focus-ring-color);
    --sf-caret-color:       var(--sf-color-action);

  }
  /* ── @supports gate: light-dark() + oklch(from …) ──
     Safari 15.4 supports light-dark() but only adds oklch(from) in 16.4.
     Gating on both prevents Safari 15.x/16.x from parsing the derived
     color tokens and silently failing all base colors. */
  @supports (color: light-dark(white, black)) and (color: oklch(from red l c h)) {
    :root {

      /* ----------------------------------------------------------
         Resolved color tokens — auto-switch via light-dark().
         Dark auto-derivation formula (brand + status):
         clamp(0.65, 0.95 - l*0.5, 0.88) lightens dark-mode value relative to the light source.
         Surface inverts: clamp(0.16, 1.18 - l, 0.24) — near-white flips to near-dark.
         Override any --sf-color-X-dark to take full per-mode control.
         SL-001: this same clamp() formula is re-declared flat in themes.css
         (SECTION-LEVEL THEMING) so [data-theme] overrides on non-:root elements
         still resolve correctly — light-dark() only bakes at :root. Keep both
         copies in sync if the formula ever changes. */
      --sf-color-primary:   light-dark(var(--sf-color-primary-source-light),   var(--sf-color-primary-source-dark,   oklch(from var(--sf-color-primary-source-light)   clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));
      --sf-color-secondary: light-dark(var(--sf-color-secondary-source-light), var(--sf-color-secondary-source-dark, oklch(from var(--sf-color-secondary-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));
      --sf-color-tertiary:  light-dark(var(--sf-color-tertiary-source-light),  var(--sf-color-tertiary-source-dark,  oklch(from var(--sf-color-tertiary-source-light)  clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));
      --sf-color-action:    light-dark(var(--sf-color-action-source-light),    var(--sf-color-action-source-dark,    oklch(from var(--sf-color-action-source-light)    clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));
      --sf-color-neutral:   light-dark(var(--sf-color-neutral-source-light),   var(--sf-color-neutral-source-dark,   oklch(from var(--sf-color-neutral-source-light)   clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));
      --sf-color-base:      light-dark(var(--sf-color-base-source-light),      var(--sf-color-base-source-dark,      oklch(from var(--sf-color-base-source-light)      clamp(0.16, calc(1.18 - l),        0.24) calc(c * 0.5) h)));

      --sf-color-success: light-dark(var(--sf-color-success-source-light), var(--sf-color-success-source-dark, oklch(from var(--sf-color-success-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));
      --sf-color-warning: light-dark(var(--sf-color-warning-source-light), var(--sf-color-warning-source-dark, oklch(from var(--sf-color-warning-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));

      --sf-color-info:    light-dark(var(--sf-color-info-source-light),    var(--sf-color-info-source-dark,    oklch(from var(--sf-color-info-source-light)    clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));
      --sf-color-danger:  light-dark(var(--sf-color-danger-source-light),  var(--sf-color-danger-source-dark,  oklch(from var(--sf-color-danger-source-light)  clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h)));

      /* ----------------------------------------------------------
         Text — light and dark require opposite formula directions,
         so each side of light-dark() references its source token.
         ---------------------------------------------------------- */
      --sf-color-text: light-dark(
        oklch(from var(--sf-color-neutral-source-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h),
        oklch(from var(--sf-color-neutral)       clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1)    c h)
      );
      --sf-color-text--muted: light-dark(
        oklch(from var(--sf-color-neutral-source-light) clamp(0.35, calc(l - var(--sf-contrast-bias)), 0.62) c h),
        oklch(from var(--sf-color-neutral)       clamp(0.48, calc(l + var(--sf-contrast-bias)), 0.74) c h)
      );
      --sf-color-text--subtle: light-dark(
        oklch(from var(--sf-color-neutral-source-light) clamp(0.15, calc(l - 0.25 - var(--sf-contrast-bias)), 0.45) c h),
        oklch(from var(--sf-color-neutral)       clamp(0.55, calc(l + 0.1 + var(--sf-contrast-bias)), 0.90) c h)
      );
      --sf-color-text--placeholder: light-dark(
        oklch(from var(--sf-color-neutral-source-light) clamp(0.45, calc(l + 0.15), 0.75) c h),
        oklch(from var(--sf-color-neutral)       clamp(0.35, calc(l - 0.1),  0.65) c h)
      );
      --sf-color-text--disabled: light-dark(
        oklch(from var(--sf-color-neutral-source-light) clamp(0.55, calc(l + 0.25), 0.82) c h),
        oklch(from var(--sf-color-neutral)       clamp(0.25, calc(l - 0.2),  0.55) c h)
      );
      --sf-color-text--inverse: light-dark(
        oklch(from var(--sf-color-neutral-source-light) clamp(0.85, calc(l + 0.4), 0.98) c h),
        oklch(from var(--sf-color-neutral)       clamp(0.05, calc(l - 0.4), 0.35) c h)
      );
      --sf-color-heading: light-dark(
        oklch(from var(--sf-color-neutral-source-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h),
        oklch(from var(--sf-color-neutral)       clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1)    c h)
      );

      /* ----------------------------------------------------------
         Borders — direction-dependent, reference source tokens.
         Light: borders are lighter than neutral (l + offset).
         Dark:  borders are darker  than neutral (l - offset).
         ---------------------------------------------------------- */
      --sf-color-border: light-dark(
        oklch(from var(--sf-color-neutral-source-light) clamp(0.70, calc(l + 0.35), 0.95) 0.005 h),
        oklch(from var(--sf-color-neutral)       clamp(0.25, calc(l - 0.3),  0.55) 0.005 h)
      );
      --sf-color-border--subtle: light-dark(
        oklch(from var(--sf-color-neutral-source-light) clamp(0.75, calc(l + 0.4),  0.97) 0.005 h),
        oklch(from var(--sf-color-neutral)       clamp(0.20, calc(l - 0.38), 0.45) 0.005 h)
      );
      --sf-color-border--strong: light-dark(
        oklch(from var(--sf-color-neutral-source-light) clamp(0.55, calc(l + 0.1),  0.85) 0.02  h),
        oklch(from var(--sf-color-neutral)       clamp(0.38, calc(l - 0.1),  0.65) 0.02  h)
      );

      /* ----------------------------------------------------------
         Links — derived from --sf-color-action (auto-adapts)
         ---------------------------------------------------------- */
      /* Links keep the action hue but CLAMP lightness toward a contrast-safe
         band — a ceiling in light mode, a floor in dark mode — so link text
         clears WCAG AA (4.5:1) on the page background for the default palette
         AND the large majority of brand overrides (a fixed offset alone only
         fixed the default). Lightness dominates OKLCH contrast but doesn't
         fully determine it: very high-chroma hues (saturated yellow/green) can
         still fall short even at a safe lightness — those remain the consumer's
         responsibility (see docs/theming.md → "Link contrast").

         NOTE: the consumer-facing :link rule in core/base.css uses
         `a:link` (not bare `a`) so its specificity matches WebKit's UA
         stylesheet `a:link { color: -webkit-link }`. Without that
         pseudo-class the framework's `--sf-color-link` value is
         silently ignored on unvisited anchors in WebKit and
         `-webkit-link` (≈ #00728f in dark mode) paints instead —
         regardless of how this token is computed. PR #73 and several
         follow-up attempts mistakenly chased the formula. */
      --sf-color-link:            light-dark(
        oklch(from var(--sf-color-action) clamp(0, min(l - 0.07, 0.48), 1) c h),
        oklch(from var(--sf-color-action) clamp(0.68, l, 1) c h)
      );
      --sf-color-link--hover:     light-dark(
        oklch(from var(--sf-color-action) clamp(0, min(l - 0.15, 0.40), 1) c h),
        oklch(from var(--sf-color-action) clamp(0, max(l + 0.10, 0.68), 1) c h)
      );
      --sf-color-link--active:    light-dark(
        oklch(from var(--sf-color-action) clamp(0, min(l - 0.21, 0.34), 1) c h),
        oklch(from var(--sf-color-action) clamp(0, max(l + 0.15, 0.74), 1) c h)
      );
      /* Visited link — 60° hue shift from action gives clearly distinct
         chroma without colliding with default tertiary (h ≈ 310). Same
         lightness clamp as the base link. If your brand puts action and
         tertiary close in hue, override directly:
           :root { --sf-color-link--visited: #6b46c1; } */
      --sf-color-link--visited:   light-dark(
        oklch(from var(--sf-color-action) clamp(0, min(l - 0.07, 0.48), 1) c calc(h + 60)),
        oklch(from var(--sf-color-action) clamp(0.68, l, 1) c calc(h + 60))
      );

      /* ----------------------------------------------------------
         Selection
         ---------------------------------------------------------- */
      --sf-color-selection-bg:   light-dark(
        oklch(from var(--sf-color-action-source-light) l c h / 0.28),
        oklch(from var(--sf-color-action-source-dark) clamp(0.62, calc(0.93 - l * 0.4), 0.78) c h / 0.55)
      );

      /* Alt selection — the opposite scheme's treatment, for a surface
         that deliberately inverts colours relative to the page (e.g. a
         dark card on a light page) where the default pairing above
         would land with poor contrast. Consumed by .sf-selection--alt
         (optional/utilities.css). */
      --sf-color-selection-bg--alt: light-dark(
        oklch(from var(--sf-color-action-source-dark) clamp(0.62, calc(0.93 - l * 0.4), 0.78) c h / 0.55),
        oklch(from var(--sf-color-action-source-light) l c h / 0.28)
      );
      --sf-color-selection-text--alt: inherit;

      /* ----------------------------------------------------------
         Status triplets — strong (direction-dependent, uses light-dark)
         danger = destructive actions, form validation errors, negative states
         ---------------------------------------------------------- */
      --sf-color-success-strong: light-dark(
        oklch(from var(--sf-color-success-source-light) calc(l - 0.15) c h),
        oklch(from var(--sf-color-success)       clamp(0.70, calc(l + 0.15), 1) c h)
      );
      --sf-color-warning-strong: light-dark(
        oklch(from var(--sf-color-warning-source-light) calc(l - 0.25) c h),
        oklch(from var(--sf-color-warning)       clamp(0.70, calc(l + 0.05), 1) c h)
      );
      --sf-color-info-strong: light-dark(
        oklch(from var(--sf-color-info-source-light) calc(l - 0.1) c h),
        oklch(from var(--sf-color-info)       clamp(0.70, calc(l + 0.15), 1) c h)
      );
      --sf-color-danger-strong: light-dark(
        oklch(from var(--sf-color-danger-source-light) calc(l - 0.1) c h),
        oklch(from var(--sf-color-danger)       clamp(0.70, calc(l + 0.15), 1) c h)
      );

    }
  }
  /* ── @supports gate: oklch(from …) relative color ── */
  @supports (color: oklch(from red l c h)) {
    :root {

      /* ----------------------------------------------------------
         Surfaces — derived from --sf-color-base (auto-adapts)
         ---------------------------------------------------------- */
      --sf-color-bg:      oklch(from var(--sf-color-base) calc(l + 0.02) c h);
      --sf-color-inset:   oklch(from var(--sf-color-base) calc(l - 0.02) c h);
      --sf-color-raised:  oklch(from var(--sf-color-base) calc(l + 0.04) c h);
      /* Overlay: high-opacity wash of the base color for light/dark mode safe layers */
      --sf-color-overlay: oklch(from var(--sf-color-base) l c h / 0.9);
      --sf-color-inverse: oklch(from var(--sf-color-base) calc(1 - l) c h);

      /* ----------------------------------------------------------
         Border alpha tokens
         ---------------------------------------------------------- */
      --sf-color-border--disabled: oklch(from var(--sf-color-border--subtle) l 0 h / 0.5);
      --sf-color-border--translucent: oklch(from var(--sf-color-neutral) l c h / 0.15);

      /* ----------------------------------------------------------
         Link alpha token
         ---------------------------------------------------------- */
      --sf-color-link--underline: oklch(from var(--sf-color-action) l c h / 0.3);

      /* ----------------------------------------------------------
         Interactive states — derived from --sf-color-neutral (auto-adapts)
         ---------------------------------------------------------- */
      --sf-color-bg--hover:    oklch(from var(--sf-color-neutral) l c h / 0.08);
      --sf-color-bg--active:   oklch(from var(--sf-color-neutral) l c h / 0.12);
      --sf-color-bg--selected: oklch(from var(--sf-color-action) l c h / 0.1);
      --sf-color-bg--focus:    oklch(from var(--sf-color-action) l c h / 0.06);

      /* ----------------------------------------------------------
         Selection & mark
         ---------------------------------------------------------- */
      --sf-color-mark-bg: oklch(from var(--sf-color-warning) l c h / 0.25);

      /* ----------------------------------------------------------
         Status triplets — subtle/muted/tint (alpha variants)
         bg/border use same formula both modes. -tint is the 5%
         near-transparent rung, mirroring the brand families'
         --sf-color-{family}-tint aliases (full 10-family coverage).
         ---------------------------------------------------------- */
      --sf-color-success-subtle: oklch(from var(--sf-color-success) l c h / 0.12);
      --sf-color-success-muted:  oklch(from var(--sf-color-success) l c h / 0.3);
      --sf-color-success-tint:   oklch(from var(--sf-color-success) l c h / 0.05);

      --sf-color-warning-subtle: oklch(from var(--sf-color-warning) l c h / 0.12);
      --sf-color-warning-muted:  oklch(from var(--sf-color-warning) l c h / 0.3);
      --sf-color-warning-tint:   oklch(from var(--sf-color-warning) l c h / 0.05);

      --sf-color-info-subtle:    oklch(from var(--sf-color-info) l c h / 0.12);
      --sf-color-info-muted:     oklch(from var(--sf-color-info) l c h / 0.3);
      --sf-color-info-tint:      oklch(from var(--sf-color-info) l c h / 0.05);

      --sf-color-danger-subtle:  oklch(from var(--sf-color-danger) l c h / 0.12);
      --sf-color-danger-muted:   oklch(from var(--sf-color-danger) l c h / 0.3);
      --sf-color-danger-tint:    oklch(from var(--sf-color-danger) l c h / 0.05);

      /* ----------------------------------------------------------
         Gradients — derived from brand colors (auto-adapt)
         All 4 core brand families (primary/secondary/tertiary/action)
         share the same darkening formula; --sf-gradient-brand is a
         separate decorative hue-shift option, independent of the
         colour axis.
         ---------------------------------------------------------- */
      --sf-gradient-primary:   linear-gradient(in oklch 135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) calc(l - 0.08) c h));
      --sf-gradient-secondary: linear-gradient(in oklch 135deg, var(--sf-color-secondary), oklch(from var(--sf-color-secondary) calc(l - 0.08) c h));
      --sf-gradient-tertiary:  linear-gradient(in oklch 135deg, var(--sf-color-tertiary), oklch(from var(--sf-color-tertiary) calc(l - 0.08) c h));
      --sf-gradient-action:    linear-gradient(in oklch 135deg, var(--sf-color-action), oklch(from var(--sf-color-action) calc(l - 0.08) c h));
      --sf-gradient-brand:     linear-gradient(in oklch 135deg, var(--sf-color-primary), oklch(from var(--sf-color-primary) l c calc(h + 30)));
      --sf-gradient-surface:   linear-gradient(in oklab 180deg, var(--sf-color-surface), var(--sf-color-bg));

      --sf-gradient-fade--r: linear-gradient(in oklch to right,  transparent, var(--sf-color-bg));
      --sf-gradient-fade--l: linear-gradient(in oklch to left,   transparent, var(--sf-color-bg));
      --sf-gradient-fade--t: linear-gradient(in oklch to top,    transparent, var(--sf-color-bg));
      --sf-gradient-fade--b: linear-gradient(in oklch to bottom, transparent, var(--sf-color-bg));

      /* ----------------------------------------------------------
         Shadow glow
         ---------------------------------------------------------- */
      --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));

    }
  }

  /* ── @supports gate: oklch(from …) + sign() ──────────────────
     Chrome added oklch(from) in v119 but sign() only in v138
     (Mar 2026). Without this gate, Chrome 119–137 evaluates the
     outer block to true but IACVT-crashes on sign() inside oklch(),
     making all text-on-color and auto-contrast code text invisible. */
  @supports (color: oklch(from red l c h)) and (width: calc(1px * sign(1))) {
    :root {

      /* ----------------------------------------------------------
         Code text — auto-contrasts with --sf-color-code-bg.
         ---------------------------------------------------------- */
      /* Inline-code text colour. Auto-contrasts with --sf-color-code-bg using
         the same luminance formula as --sf-color-text--on-*. This prevents
         invisible text when code appears inside inverted-colour containers
         (e.g. sf-imposter with a dark background where inherited colour is
         near-white). Override with a tint value if desired. */
      --sf-color-code-text: oklch(from var(--sf-color-code-bg) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);

      /* ----------------------------------------------------------
         Text-on-color — sign(var(--sf-contrast-threshold) - l) * 999 is mode-agnostic:
         auto-selects light or dark text based on background luminance.
         Uses resolved tokens — works for any user-supplied color.

         CONTRAST NOTE: Guarantees ≥ 3:1 (WCAG AA Large Text / UI).
         Colors in the mid-luminance range (L ≈ 0.52–0.67) may not
         reach 4.5:1 (AA Normal Text) regardless of threshold chosen —
         this is a mathematical limitation of any binary black/white
         decision. The SLASHED configurator will warn users when their
         brand colors fall in this range. For body text on colored
         backgrounds, override --sf-color-text--on-* manually.
         ---------------------------------------------------------- */
      --sf-color-text--on-primary:   oklch(from var(--sf-color-primary)   clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-secondary: oklch(from var(--sf-color-secondary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-tertiary:  oklch(from var(--sf-color-tertiary)   clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-action:    oklch(from var(--sf-color-action)     clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-neutral:   oklch(from var(--sf-color-neutral)    clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-success:   oklch(from var(--sf-color-success)    clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-warning:   oklch(from var(--sf-color-warning)    clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-info:      oklch(from var(--sf-color-info)       clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-danger:    oklch(from var(--sf-color-danger)     clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-inverse:   oklch(from var(--sf-color-inverse)    clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);

    }
  }

  /* ============================================================
     COLOR PALETTE — numeric ramps, alpha variants + aliases

     Numeric palette steps (-50…-950), alpha variants (-a5…-a80),
     and semantic convenience aliases live in core as the single source
     of truth for the 6 brand colour families. Shade steps mix toward
     --sf-color-surface (tints) or --sf-color-text (shades); the base
     family inverts tint direction so the ramp reads light→dark regardless
     of the base surface value.
     ============================================================ */

  /* ── @supports gate: palette ramps + alpha aliases ── */
  @supports (color: oklch(from red l c h)) {
  :root {

    /* ----------------------------------------------------------
       Palette ramp lightness anchors — PUBLIC-ADVANCED
       Each numeric step is generated in OKLCH by pulling the family
       colour's own lightness a fixed fraction toward an ABSOLUTE target:
       tints (50-400) toward --sf-palette-tint-l, shades (600-950) toward
       --sf-palette-shade-l. 500 = the family colour itself. Because the
       targets are absolute (not the theme's surface/text tokens) and the
       pull is clamped (max()/min()) so a tint can never end darker than
       the base nor a shade lighter, the ramp is MONOTONIC light→dark for
       ANY source colour and any anchor configuration — it cannot fold.
       Per-step pull fractions and chroma taper are baked into each family
       below; these two knobs set how light the lightest / how dark the
       darkest step may reach. The `base` family keeps its own absolute
       ladder (near-white→near-black) and does not read these.
       ---------------------------------------------------------- */
    --sf-palette-tint-l:  0.97;
    --sf-palette-shade-l: 0.1;

    /* ----------------------------------------------------------
       primary
       ---------------------------------------------------------- */
    --sf-color-primary-50: oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h);
    --sf-color-primary-100: oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h);
    --sf-color-primary-200: oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h);
    --sf-color-primary-300: oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h);
    --sf-color-primary-400: oklch(from var(--sf-color-primary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h);
    --sf-color-primary-500: var(--sf-color-primary);
    --sf-color-primary-600: oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h);
    --sf-color-primary-700: oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h);
    --sf-color-primary-800: oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h);
    --sf-color-primary-900: oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h);
    --sf-color-primary-950: oklch(from var(--sf-color-primary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h);

    --sf-color-primary-a5:  oklch(from var(--sf-color-primary) l c h / 0.05);
    --sf-color-primary-a10: oklch(from var(--sf-color-primary) l c h / 0.10);
    --sf-color-primary-a30: oklch(from var(--sf-color-primary) l c h / 0.30);
    --sf-color-primary-a50: oklch(from var(--sf-color-primary) l c h / 0.50);
    --sf-color-primary-a80: oklch(from var(--sf-color-primary) l c h / 0.80);

    --sf-color-primary-superlight: var(--sf-color-primary-50);
    --sf-color-primary-xlight:     var(--sf-color-primary-200);
    --sf-color-primary-lighter:    var(--sf-color-primary-400);
    --sf-color-primary-darker:     var(--sf-color-primary-600);
    --sf-color-primary-xdark:      var(--sf-color-primary-800);
    --sf-color-primary-superdark:  var(--sf-color-primary-950);
    --sf-color-primary--hover:     var(--sf-color-primary-darker);
    --sf-color-primary--active:    var(--sf-color-primary-xdark);
    --sf-color-primary-subtle:     var(--sf-color-primary-a10);
    --sf-color-primary-muted:      var(--sf-color-primary-a30);
    --sf-color-primary-tint:      var(--sf-color-primary-a5);



    /* ----------------------------------------------------------
       secondary
       ---------------------------------------------------------- */
    --sf-color-secondary-50: oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h);
    --sf-color-secondary-100: oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h);
    --sf-color-secondary-200: oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h);
    --sf-color-secondary-300: oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h);
    --sf-color-secondary-400: oklch(from var(--sf-color-secondary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h);
    --sf-color-secondary-500: var(--sf-color-secondary);
    --sf-color-secondary-600: oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h);
    --sf-color-secondary-700: oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h);
    --sf-color-secondary-800: oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h);
    --sf-color-secondary-900: oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h);
    --sf-color-secondary-950: oklch(from var(--sf-color-secondary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h);

    --sf-color-secondary-a5:  oklch(from var(--sf-color-secondary) l c h / 0.05);
    --sf-color-secondary-a10: oklch(from var(--sf-color-secondary) l c h / 0.10);
    --sf-color-secondary-a30: oklch(from var(--sf-color-secondary) l c h / 0.30);
    --sf-color-secondary-a50: oklch(from var(--sf-color-secondary) l c h / 0.50);
    --sf-color-secondary-a80: oklch(from var(--sf-color-secondary) l c h / 0.80);

    --sf-color-secondary-superlight: var(--sf-color-secondary-50);
    --sf-color-secondary-xlight:     var(--sf-color-secondary-200);
    --sf-color-secondary-lighter:    var(--sf-color-secondary-400);
    --sf-color-secondary-darker:     var(--sf-color-secondary-600);
    --sf-color-secondary-xdark:      var(--sf-color-secondary-800);
    --sf-color-secondary-superdark:  var(--sf-color-secondary-950);
    --sf-color-secondary--hover:     var(--sf-color-secondary-darker);
    --sf-color-secondary--active:    var(--sf-color-secondary-xdark);
    --sf-color-secondary-subtle:     var(--sf-color-secondary-a10);
    --sf-color-secondary-muted:      var(--sf-color-secondary-a30);
    --sf-color-secondary-tint:      var(--sf-color-secondary-a5);



    /* ----------------------------------------------------------
       tertiary
       ---------------------------------------------------------- */
    --sf-color-tertiary-50: oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h);
    --sf-color-tertiary-100: oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h);
    --sf-color-tertiary-200: oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h);
    --sf-color-tertiary-300: oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h);
    --sf-color-tertiary-400: oklch(from var(--sf-color-tertiary) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h);
    --sf-color-tertiary-500: var(--sf-color-tertiary);
    --sf-color-tertiary-600: oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h);
    --sf-color-tertiary-700: oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h);
    --sf-color-tertiary-800: oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h);
    --sf-color-tertiary-900: oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h);
    --sf-color-tertiary-950: oklch(from var(--sf-color-tertiary) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h);

    --sf-color-tertiary-a5:  oklch(from var(--sf-color-tertiary) l c h / 0.05);
    --sf-color-tertiary-a10: oklch(from var(--sf-color-tertiary) l c h / 0.10);
    --sf-color-tertiary-a30: oklch(from var(--sf-color-tertiary) l c h / 0.30);
    --sf-color-tertiary-a50: oklch(from var(--sf-color-tertiary) l c h / 0.50);
    --sf-color-tertiary-a80: oklch(from var(--sf-color-tertiary) l c h / 0.80);

    --sf-color-tertiary-superlight: var(--sf-color-tertiary-50);
    --sf-color-tertiary-xlight:     var(--sf-color-tertiary-200);
    --sf-color-tertiary-lighter:    var(--sf-color-tertiary-400);
    --sf-color-tertiary-darker:     var(--sf-color-tertiary-600);
    --sf-color-tertiary-xdark:      var(--sf-color-tertiary-800);
    --sf-color-tertiary-superdark:  var(--sf-color-tertiary-950);
    --sf-color-tertiary--hover:     var(--sf-color-tertiary-darker);
    --sf-color-tertiary--active:    var(--sf-color-tertiary-xdark);
    --sf-color-tertiary-subtle:     var(--sf-color-tertiary-a10);
    --sf-color-tertiary-muted:      var(--sf-color-tertiary-a30);
    --sf-color-tertiary-tint:      var(--sf-color-tertiary-a5);



    /* ----------------------------------------------------------
       action
       ---------------------------------------------------------- */
    --sf-color-action-50: oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h);
    --sf-color-action-100: oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h);
    --sf-color-action-200: oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h);
    --sf-color-action-300: oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h);
    --sf-color-action-400: oklch(from var(--sf-color-action) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h);
    --sf-color-action-500: var(--sf-color-action);
    --sf-color-action-600: oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h);
    --sf-color-action-700: oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h);
    --sf-color-action-800: oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h);
    --sf-color-action-900: oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h);
    --sf-color-action-950: oklch(from var(--sf-color-action) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h);

    --sf-color-action-a5:  oklch(from var(--sf-color-action) l c h / 0.05);
    --sf-color-action-a10: oklch(from var(--sf-color-action) l c h / 0.10);
    --sf-color-action-a30: oklch(from var(--sf-color-action) l c h / 0.30);
    --sf-color-action-a50: oklch(from var(--sf-color-action) l c h / 0.50);
    --sf-color-action-a80: oklch(from var(--sf-color-action) l c h / 0.80);

    --sf-color-action-superlight: var(--sf-color-action-50);
    --sf-color-action-xlight:     var(--sf-color-action-200);
    --sf-color-action-lighter:    var(--sf-color-action-400);
    --sf-color-action-darker:     var(--sf-color-action-600);
    --sf-color-action-xdark:      var(--sf-color-action-800);
    --sf-color-action-superdark:  var(--sf-color-action-950);
    --sf-color-action--hover:     var(--sf-color-action-darker);
    --sf-color-action--active:    var(--sf-color-action-xdark);
    --sf-color-action-subtle:     var(--sf-color-action-a10);
    --sf-color-action-muted:      var(--sf-color-action-a30);
    --sf-color-action-tint:      var(--sf-color-action-a5);



    /* ----------------------------------------------------------
       neutral
       ---------------------------------------------------------- */
    --sf-color-neutral-50: oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.95)) calc(c * 0.2) h);
    --sf-color-neutral-100: oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.88)) calc(c * 0.3) h);
    --sf-color-neutral-200: oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.72)) calc(c * 0.5) h);
    --sf-color-neutral-300: oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.52)) calc(c * 0.72) h);
    --sf-color-neutral-400: oklch(from var(--sf-color-neutral) max(l, calc(l + (var(--sf-palette-tint-l) - l) * 0.3)) calc(c * 0.88) h);
    --sf-color-neutral-500: var(--sf-color-neutral);
    --sf-color-neutral-600: oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.16)) calc(c * 0.96) h);
    --sf-color-neutral-700: oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.36)) calc(c * 0.88) h);
    --sf-color-neutral-800: oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.58)) calc(c * 0.72) h);
    --sf-color-neutral-900: oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.78)) calc(c * 0.52) h);
    --sf-color-neutral-950: oklch(from var(--sf-color-neutral) min(l, calc(l + (var(--sf-palette-shade-l) - l) * 0.9)) calc(c * 0.38) h);

    --sf-color-neutral-a5:  oklch(from var(--sf-color-neutral) l c h / 0.05);
    --sf-color-neutral-a10: oklch(from var(--sf-color-neutral) l c h / 0.10);
    --sf-color-neutral-a30: oklch(from var(--sf-color-neutral) l c h / 0.30);
    --sf-color-neutral-a50: oklch(from var(--sf-color-neutral) l c h / 0.50);
    --sf-color-neutral-a80: oklch(from var(--sf-color-neutral) l c h / 0.80);

    --sf-color-neutral-superlight: var(--sf-color-neutral-50);
    --sf-color-neutral-xlight:     var(--sf-color-neutral-200);
    --sf-color-neutral-lighter:    var(--sf-color-neutral-400);
    --sf-color-neutral-darker:     var(--sf-color-neutral-600);
    --sf-color-neutral-xdark:      var(--sf-color-neutral-800);
    --sf-color-neutral-superdark:  var(--sf-color-neutral-950);
    --sf-color-neutral--hover:     var(--sf-color-neutral-darker);
    --sf-color-neutral--active:    var(--sf-color-neutral-xdark);
    --sf-color-neutral-subtle:     var(--sf-color-neutral-a10);
    --sf-color-neutral-muted:      var(--sf-color-neutral-a30);
    --sf-color-neutral-tint:      var(--sf-color-neutral-a5);



    /* ----------------------------------------------------------
       base — fixed-lightness ramp (mode-independent, monotonic)
       ────────────────────────────────────────────────────────
       base is a surface-elevation family, not a mid-lightness brand
       colour. The ramp uses oklch() relative syntax with fixed L steps
       spanning 0.97 → 0.06, preserving the base source's hue and chroma.
       50 is always near-white; 950 is always near-black regardless of
       light/dark mode or the source colour's position.

       OKLCH L values (fixed, mode-independent):
         base-50  = 0.97   near-white
         base-100 = 0.94   very light gray
         base-200 = 0.88   light gray
         base-300 = 0.78   medium-light gray
         base-400 = 0.65   medium gray
         base-500 = 0.50   mid gray
         base-600 = 0.37   medium-dark gray
         base-700 = 0.27   dark gray
         base-800 = 0.17   very dark gray
         base-900 = 0.11   near-black
         base-950 = 0.06   near-black

       For mode-adaptive surface differentiation use the surface tokens
       (--sf-color-inset / --sf-color-raised / --sf-color-bg) which derive
       from --sf-color-base via relative oklch and adapt to light/dark mode.
       For overlays use alpha variants (-a10…-a80).
       ---------------------------------------------------------- */
    --sf-color-base-50:  oklch(from var(--sf-color-base) 0.97 c h);
    --sf-color-base-100: oklch(from var(--sf-color-base) 0.94 c h);
    --sf-color-base-200: oklch(from var(--sf-color-base) 0.88 c h);
    --sf-color-base-300: oklch(from var(--sf-color-base) 0.78 c h);
    --sf-color-base-400: oklch(from var(--sf-color-base) 0.65 c h);
    --sf-color-base-500: oklch(from var(--sf-color-base) 0.50 c h);
    --sf-color-base-600: oklch(from var(--sf-color-base) 0.37 c h);
    --sf-color-base-700: oklch(from var(--sf-color-base) 0.27 c h);
    --sf-color-base-800: oklch(from var(--sf-color-base) 0.17 c h);
    --sf-color-base-900: oklch(from var(--sf-color-base) 0.11 c h);
    --sf-color-base-950: oklch(from var(--sf-color-base) 0.06 c h);

    --sf-color-base-a5:  oklch(from var(--sf-color-base) l c h / 0.05);
    --sf-color-base-a10: oklch(from var(--sf-color-base) l c h / 0.10);
    --sf-color-base-a30: oklch(from var(--sf-color-base) l c h / 0.30);
    --sf-color-base-a50: oklch(from var(--sf-color-base) l c h / 0.50);
    --sf-color-base-a80: oklch(from var(--sf-color-base) l c h / 0.80);

    /* No relative aliases (lighter/darker/xlight/…) on base: it is an
       absolute scale, so a step's name would lie depending on where the
       source sits (e.g. "lighter" would be darker than a near-white base
       in light mode). For relative, mode-adaptive surface elevation use
       --sf-color-bg / --sf-color-inset / --sf-color-raised instead. */
    --sf-color-base--hover:     oklch(from var(--sf-color-base) clamp(0.01, calc(l - 0.04 + var(--sf-is-dark) * 0.08), 0.99) c h);
    --sf-color-base--active:    oklch(from var(--sf-color-base) clamp(0.01, calc(l - 0.08 + var(--sf-is-dark) * 0.16), 0.99) c h);
    --sf-color-base-subtle:     var(--sf-color-base-a10);
    --sf-color-base-muted:      var(--sf-color-base-a30);
    --sf-color-base-tint:      var(--sf-color-base-a5);


  }
  }

  /* ============================================================
     NON-COLOR DESIGN TOKENS
     ============================================================ */

  :root {

    /* ----------------------------------------------------------
       Scale multipliers
       ---------------------------------------------------------- */
    --sf-space-scale:        1;
    --sf-text-scale:         1;
    --sf-text-display-scale: 1;
    --sf-radius-scale:       1;
    --sf-motion-scale:       1;
    --sf-section-scale:      1;

    /* ----------------------------------------------------------
       Font families
       ---------------------------------------------------------- */
    --sf-font-body:    system-ui, -apple-system, sans-serif;
    --sf-font-heading: var(--sf-font-body);
    --sf-font-display: var(--sf-font-heading);
    --sf-font-mono:    ui-monospace, monospace;

    /* Optional system-font stacks (zero-cost, no @import) */
    --sf-font-humanist:  "Seravek", "Gill Sans Nova", "Ubuntu", "Calibri", "DejaVu Sans", source-sans-pro, sans-serif;
    --sf-font-geometric: "Avenir", "Montserrat", "Corbel", "URW Gothic", source-sans-pro, sans-serif;
    --sf-font-slab:      "Rockwell", "Rockwell Nova", "Roboto Slab", "DejaVu Serif", "Sitka Small", serif;

    /* Opt-in OpenType / variable-font controls (token-only, not applied by the framework) */
    --sf-font-features:  normal;  /* e.g. "cv11", "ss01" */
    --sf-font-variation: normal;  /* e.g. "wght" 450, "opsz" 32 */
    --sf-optical-sizing: auto;

    /* ----------------------------------------------------------
       Font weights
       ---------------------------------------------------------- */
    /* Base weights — trimmed, recognizable names. For an off-scale weight
       (e.g. 100/900 display work) write font-weight: 100 directly, or re-tune
       a role token below globally — every weight here is overridable. */
    --sf-font-weight-light:    300;
    --sf-font-weight-normal:   400;
    --sf-font-weight-medium:   500;
    --sf-font-weight-semibold: 600;
    --sf-font-weight-bold:     700;

    /* Semantic role aliases — set intent, not a number. Override these to
       reweight the whole site (e.g. --sf-font-weight-body: 300). */
    --sf-font-weight-body:        var(--sf-font-weight-normal);    /* body copy            */
    --sf-font-weight-heading:     var(--sf-font-weight-semibold);  /* h1–h6                */
    --sf-font-weight-display:     var(--sf-font-weight-bold);      /* hero / display       */
    --sf-font-weight-interactive: var(--sf-font-weight-semibold);  /* buttons, badges, nav.
       Note: "interactive" here names a TYPOGRAPHY WEIGHT TIER; .sf-card--interactive
       names a card BEHAVIOUR modifier. Deliberate coexistence (different property
       spaces) — do not introduce a third meaning of "interactive". */
    --sf-font-weight-strong:      var(--sf-font-weight-bold);      /* <strong> / emphasis  */

    /* ----------------------------------------------------------
       Font sizes — fluid
       ---------------------------------------------------------- */
    /* ----------------------------------------------------------
       Font sizes — fluid (generative)
       Computed from --sf-text-ratio-{min,max} + --sf-text-base-{min,max}
       over the --sf-fluid-{min,max}-vw range. Step exponent N is relative
       to --sf-text-m (N=0): 2xs=-3 … 4xl=5. Override the inputs above to
       reshape the whole scale; override an individual --sf-text-* to pin one.
       ---------------------------------------------------------- */
    --sf-text-2xs: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) * 1rem)) * var(--sf-text-scale));
    --sf-text-xs:  calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) * 1rem)) * var(--sf-text-scale));
    --sf-text-s:   calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) * 1rem)) * var(--sf-text-scale));
    --sf-text-m:   calc(clamp(calc(var(--sf-text-base-min) * 1rem), calc((var(--sf-text-base-max) - var(--sf-text-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * 1rem), calc(var(--sf-text-base-max) * 1rem)) * var(--sf-text-scale));
    --sf-text-l:   calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-scale));
    --sf-text-xl:  calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-scale));
    --sf-text-2xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) * 1rem)) * var(--sf-text-scale));
    --sf-text-3xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) * 1rem)) * var(--sf-text-scale));
    --sf-text-4xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) * 1rem)) * var(--sf-text-scale));

    /* Display — reuses the type ratios with its own larger base (s=0, m=1, l=2). */
    --sf-text-display-s: calc(clamp(calc(var(--sf-text-display-base-min) * 1rem), calc((var(--sf-text-display-base-max) - var(--sf-text-display-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * 1rem), calc(var(--sf-text-display-base-max) * 1rem)) * var(--sf-text-display-scale));
    --sf-text-display-m: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-display-scale));
    --sf-text-display-l: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-display-scale));

    /* ----------------------------------------------------------
       Line heights & letter spacing
       ---------------------------------------------------------- */
    --sf-leading-tight:   1.1;
    --sf-leading-snug:    1.3;
    --sf-leading-normal:  1.5;
    --sf-leading-relaxed: 1.625;

    /* Leading taper — progressive tightener for the per-size line-height
       tokens below. Each step up the type scale subtracts
       `step-index × taper` from that step's default leading.
       0 (default) keeps the curated stepped defaults; try 0.02 for a smooth
       metric taper across the scale. */
    --sf-leading-taper: 0;

    /* Display line-heights — display sizes need tighter leading than the
       --sf-leading-* scale provides. Not auto-applied (display sizes have no
       dedicated element); wire alongside font-size:
         h1.hero { font-size: var(--sf-text-display-l);
                   line-height: var(--sf-display-l-line-height); } */
    --sf-display-s-line-height: var(--sf-leading-tight);
    --sf-display-m-line-height: 1.05;
    --sf-display-l-line-height: 1;

    --sf-tracking-tight:   -0.025em;
    --sf-tracking-normal:   0;
    --sf-tracking-wide:     0.025em;
    --sf-tracking-wider:    0.05em;
    --sf-tracking-widest:   0.1em;

    /* Numeric figure style — tabular-nums gives every digit a fixed
       advance width so numbers line up in vertical columns (price
       lists, totals, invoices, dashboards). Consumed by number inputs
       in optional/forms.css and available to BEM components, e.g.
       `.price { font-variant-numeric: var(--sf-font-numeric); }`.
       Universal browser support. */
    --sf-font-numeric: tabular-nums;

    /* ----------------------------------------------------------
       Per-size typography sub-properties
       Not auto-applied — opt-in composable overrides. Wire alongside
       font-size in your own rules:
         .element { line-height: var(--sf-text-xl-line-height); }
       Small text: relaxed leading. Body: normal. Large: progressively
       tighter. --sf-leading-taper (default 0) tightens the whole
       scale at once.
       ---------------------------------------------------------- */

    --sf-text-2xs-line-height:            var(--sf-leading-relaxed);
    --sf-text-2xs-font-weight:            var(--sf-font-weight-body);
    --sf-text-2xs-letter-spacing:         var(--sf-tracking-normal);
    --sf-text-2xs-max-width:              55ch;

    --sf-text-xs-line-height:             calc(var(--sf-leading-relaxed) - 1 * var(--sf-leading-taper));
    --sf-text-xs-font-weight:             var(--sf-font-weight-body);
    --sf-text-xs-letter-spacing:          var(--sf-tracking-normal);
    --sf-text-xs-max-width:               60ch;

    --sf-text-s-line-height:              calc(var(--sf-leading-relaxed) - 2 * var(--sf-leading-taper));
    --sf-text-s-font-weight:              var(--sf-font-weight-body);
    --sf-text-s-letter-spacing:           var(--sf-tracking-normal);
    --sf-text-s-max-width:                65ch;

    --sf-text-m-line-height:              calc(var(--sf-leading-normal) - 3 * var(--sf-leading-taper));
    --sf-text-m-font-weight:              var(--sf-font-weight-body);
    --sf-text-m-letter-spacing:           var(--sf-tracking-normal);
    --sf-text-m-max-width:                65ch;

    --sf-text-l-line-height:              calc(var(--sf-leading-normal) - 4 * var(--sf-leading-taper));
    --sf-text-l-font-weight:              var(--sf-font-weight-body);
    --sf-text-l-letter-spacing:           var(--sf-tracking-normal);
    --sf-text-l-max-width:                none;

    --sf-text-xl-line-height:             calc(var(--sf-leading-snug) - 5 * var(--sf-leading-taper));
    --sf-text-xl-font-weight:             var(--sf-font-weight-body);
    --sf-text-xl-letter-spacing:          var(--sf-tracking-normal);
    --sf-text-xl-max-width:               none;

    --sf-text-2xl-line-height:            calc(var(--sf-leading-snug) - 6 * var(--sf-leading-taper));
    --sf-text-2xl-font-weight:            var(--sf-font-weight-heading);
    --sf-text-2xl-letter-spacing:         var(--sf-tracking-normal);
    --sf-text-2xl-max-width:              none;

    --sf-text-3xl-line-height:            calc(var(--sf-leading-tight) - 7 * var(--sf-leading-taper));
    --sf-text-3xl-font-weight:            var(--sf-font-weight-heading);
    --sf-text-3xl-letter-spacing:         var(--sf-tracking-tight);
    --sf-text-3xl-max-width:              none;

    --sf-text-4xl-line-height:            calc(var(--sf-leading-tight) - 8 * var(--sf-leading-taper));
    --sf-text-4xl-font-weight:            var(--sf-font-weight-heading);
    --sf-text-4xl-letter-spacing:         var(--sf-tracking-tight);
    --sf-text-4xl-max-width:              none;

    /* ----------------------------------------------------------
       Icon sizes — em-based by design.
       Icons are sized in em so they scale with the surrounding
       text: an icon in a button at --sf-text-m renders at 1rem;
       moved into a heading at --sf-text-2xl, the same icon scales
       up automatically.
       Global scaling flows transitively through --sf-text-scale
       (parent text grows → em-icons grow) — there is no separate
       --sf-icon-scale knob by design. clamp() is intentionally
       absent here: making icons fluid INDEPENDENTLY of context
       would break the relationship to surrounding text.
       Capped at --2xl (4em ≈ 64px on default text); above that the
       element ceases to be an icon and becomes an illustration.
       ---------------------------------------------------------- */
    --sf-icon-xs:  0.875em;
    --sf-icon-s:   1em;
    --sf-icon-m:   1.5em;
    --sf-icon-l:   2em;
    --sf-icon-xl:  3em;
    --sf-icon-2xl: 4em;

    /* ----------------------------------------------------------
       Spacing — fixed
       ---------------------------------------------------------- */
    --sf-space-none:   0;
    --sf-space-px:     1px;

    /* Spacing — fluid (generative). Same engine as type; space uses base_max=2
       (more dramatic growth than type's 1.25). Step exponent N relative to
       --sf-space-m (N=0): 2xs=-3 … 4xl=5. */
    --sf-space-2xs: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) * 1rem)) * var(--sf-space-scale));
    --sf-space-xs:  calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) * 1rem)) * var(--sf-space-scale));
    --sf-space-s:   calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) * 1rem)) * var(--sf-space-scale));
    --sf-space-m:   calc(clamp(calc(var(--sf-space-base-min) * 1rem), calc((var(--sf-space-base-max) - var(--sf-space-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * 1rem), calc(var(--sf-space-base-max) * 1rem)) * var(--sf-space-scale));
    --sf-space-l:   calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) * 1rem)) * var(--sf-space-scale));
    --sf-space-xl:  calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) * 1rem)) * var(--sf-space-scale));
    --sf-space-2xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) * 1rem)) * var(--sf-space-scale));
    --sf-space-3xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) * 1rem)) * var(--sf-space-scale));
    --sf-space-4xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) * 1rem)) * var(--sf-space-scale));

    /* ----------------------------------------------------------
       Density — the compact ↔ comfortable dial for interactive
       control GEOMETRY. 1 = default; <1 packs controls tighter
       (dashboards, data tables, power-user tools), >1 loosens them.
       It is a discrete design/product choice (or a user preference),
       NOT a viewport response — control height must never shrink on
       small screens where touch targets need to be larger, so unlike
       the fluid text and space scales the size ladder stays non-fluid
       and is scaled only by this deliberate knob. Orthogonal to
       --sf-space-scale (overall whitespace) and --sf-section-scale
       (macro section rhythm); combine them for a fully compact UI.
       Accessibility is unaffected: the coarse-pointer floor lives on
       --sf-touch-target (core/accessibility.css), never on a rung, so
       a small --sf-density can't shrink native controls below the
       WCAG target. Like the other global multipliers it is a :root
       dial — the size ladder is computed at :root and inherits, so a
       nested override does not retroactively rescale it (same
       computed-value rule as --sf-space-scale et al.). */
    --sf-density: 1;

    /* ----------------------------------------------------------
       UI sizes — a purely geometric rung ladder (+0.5rem / 8px per
       step: 24 · 32 · 40 · 48 · 56 px at density 1). These are free
       design knobs, scaled by --sf-density; overriding a rung just
       rescales that component height. Accessibility floors live on
       their own tokens (--sf-touch-target), never on a rung.
       ---------------------------------------------------------- */
    --sf-size-xs: calc(1.5rem * var(--sf-density));
    --sf-size-s:  calc(2rem   * var(--sf-density));
    --sf-size-m:  calc(2.5rem * var(--sf-density));
    --sf-size-l:  calc(3rem   * var(--sf-density));
    --sf-size-xl: calc(3.5rem * var(--sf-density));

    /* ----------------------------------------------------------
       Containers
       ---------------------------------------------------------- */
    --sf-container-narrow:  38rem;
    --sf-container-prose:   65ch;
    --sf-container-default: 75rem;
    --sf-container-wide:    90rem;
    --sf-container-full:    100%;

    /* ----------------------------------------------------------
       Aspect ratios
       ---------------------------------------------------------- */
    --sf-ratio-square:   1;
    --sf-ratio-video:    16 / 9;
    --sf-ratio-cinema:   21 / 9;
    --sf-ratio-4-3:      4 / 3;
    --sf-ratio-3-2:      3 / 2;
    --sf-ratio-portrait: 3 / 4;
    --sf-ratio-golden:   1.618 / 1;

    /* ----------------------------------------------------------
       Border widths
       --sf-border-width-hairline: device-pixel-dependent. On 1x DPR
       displays (Windows non-Retina) the browser will round 0.5px up
       to 1px, so it visually matches --sf-border-width-1. On 2x+
       DPR (macOS/iOS Retina, most modern phones) it renders as a
       true half-pixel hairline. Use it for subtle separators where
       1px on low-DPR is acceptable.
       ---------------------------------------------------------- */
    /* --sf-border-scale — global multiplier for border widths. Set to 0 for
       borderless, 2 for heavy borders. Hairline is intentionally excluded
       (it's a device-pixel constant, not a design variable). */
    --sf-border-scale:           1;
    --sf-border-width-hairline: 0.5px;
    --sf-border-width-1:        calc(1px * var(--sf-border-scale, 1));
    --sf-border-width-2:        calc(2px * var(--sf-border-scale, 1));
    --sf-border-width-3:        calc(3px * var(--sf-border-scale, 1));
    --sf-border-width-4:        calc(4px * var(--sf-border-scale, 1));

    /* ----------------------------------------------------------
       Border style — global knob paired with --sf-border-width-* and
       --sf-color-border*. Consumer BEM:
         .my-card { border: var(--sf-border-width-1) var(--sf-border-style) var(--sf-color-border); }
       Set once on :root to restyle every framework border, or override
       per element. For one-off decorative styles use the CSS keyword
       directly (e.g. `border-style: dashed`).
       ---------------------------------------------------------- */
    --sf-border-style: solid;     /* default */

    /* ----------------------------------------------------------
       Border shorthands — convenience tokens composing width, style,
       and color into a single value for the common cases.
       subtle / strong vary COLOR CONTRAST only — all three use
       --sf-border-width-1 (1px). This matches the IBM Carbon and
       GitHub Primer convention where subtle/strong name a color axis,
       not a weight axis. For a thicker border compose manually:
         border: var(--sf-border-width-2) var(--sf-border-style) var(--sf-color-border--strong)
       Usage: border: var(--sf-border);
              border-bottom: var(--sf-border-subtle);
       ---------------------------------------------------------- */
    --sf-border:        var(--sf-border-width-1) var(--sf-border-style) var(--sf-color-border);
    --sf-border-subtle: var(--sf-border-width-1) var(--sf-border-style) var(--sf-color-border--subtle);
    --sf-border-strong: var(--sf-border-width-1) var(--sf-border-style) var(--sf-color-border--strong);

    /* ----------------------------------------------------------
       Divider — global separator system. Drives `hr` (base) and
       the `.sf-divider` layout primitive.
       ---------------------------------------------------------- */
    --sf-divider-width: var(--sf-border-width-1);
    --sf-divider-style: solid;
    --sf-divider-color: var(--sf-color-border);
    --sf-divider-gap:   var(--sf-space-m);

    /* ----------------------------------------------------------
       Border radius — scale ramp, all multiplied by --sf-radius-scale.
       ---------------------------------------------------------- */
    --sf-radius-none: 0;
    --sf-radius-2xs:  calc(1px  * var(--sf-radius-scale));
    --sf-radius-xs:   calc(2px  * var(--sf-radius-scale));
    --sf-radius-s:    calc(4px  * var(--sf-radius-scale));
    --sf-radius-m:    calc(8px  * var(--sf-radius-scale));
    --sf-radius-l:    calc(12px * var(--sf-radius-scale));
    --sf-radius-xl:   calc(16px * var(--sf-radius-scale));
    --sf-radius-2xl:  calc(24px * var(--sf-radius-scale));
    --sf-radius-3xl:  calc(32px * var(--sf-radius-scale));
    --sf-radius-4xl:  calc(48px * var(--sf-radius-scale));
    /* ----------------------------------------------------------
       Full/pill — topological constants, not scaled by --sf-radius-scale.
       A pill/circle is a topological constant, not a relative size:
       any --sf-radius-scale: 0 ("sharp" design system) should still
       render pill buttons as pills. 9999px is a magic-number convention
       large enough to clip to half the shorter edge for any realistic UI.
       ---------------------------------------------------------- */
    --sf-radius-full: 9999px;
    /* Pill — semantic alias of --sf-radius-full. Reach for this name
       on rectangles you want capsule-shaped (badges, tags, chips); use
       --sf-radius-full when you mean "as round as topologically possible". */
    --sf-radius-pill: var(--sf-radius-full);
    /* Concentric helper — outer radius for a container that wraps
       content padded by --sf-component-pad with inner radius m.
       Keeps inner elements visually proportional inside a padded box.
       Override per use:
         .my-card { border-radius: var(--sf-radius-outer); }
       Maths: outer = inner + padding ⇒ aligned curves at corners. */
    --sf-radius-outer: calc(var(--sf-radius-m) + var(--sf-component-pad));

    /* Opt-in global media radius — 0 by default (off). Set to a
       --sf-radius-* value to round <img>/<figure> globally:
         :root { --sf-media-radius: var(--sf-radius-m); }
       Applied via :where() in core/base.css so it never fights
       .sf-bg-layer or a component's own radius. */
    --sf-media-radius: 0;

    /* ----------------------------------------------------------
       Shadows — BEM consumer API
       Use in your own classes: .card { box-shadow: var(--sf-shadow-m); }
       ---------------------------------------------------------- */


    /* ----------------------------------------------------------
       Blur & opacity
       One frosted-surface default (nav/overlay glass) and one general
       de-emphasis opacity. For other amounts write the value directly:
       backdrop-filter: blur(24px); opacity: .3;
       (--sf-opacity-disabled is defined with the interaction tokens.)
       ---------------------------------------------------------- */
    --sf-blur:          12px;
    --sf-opacity-muted: 0.5;

    /* ----------------------------------------------------------
       Motion & easing
       ---------------------------------------------------------- */
    --sf-duration-none:    0ms;
    --sf-duration-instant: calc(100ms * var(--sf-motion-scale));
    --sf-duration-fast:    calc(150ms * var(--sf-motion-scale));
    --sf-duration-normal:  calc(250ms * var(--sf-motion-scale));
    --sf-duration-slow:    calc(400ms * var(--sf-motion-scale));
    --sf-duration-slower:  calc(600ms * var(--sf-motion-scale));

    /* Opt-in light/dark cross-fade duration — the knob for .sf-theme-transition
       (core/themes.css). Registered <color> tokens make the fade possible;
       this controls how long it takes. */
    --sf-theme-transition-duration: calc(300ms * var(--sf-motion-scale));

    --sf-ease-linear:  linear;
    --sf-ease-out:     cubic-bezier(0.25, 0, 0.15, 1);
    --sf-ease-in:      cubic-bezier(0.5, 0, 0.75, 0.25);
    --sf-ease-in-out:  cubic-bezier(0.4, 0, 0.2, 1);
    --sf-ease-spring:    linear(0, 0.5, 1.1, 0.95, 1.02, 1);
    --sf-ease-elastic:   linear(0, 0.3, 1.2, 0.9, 1.05, 1);
    --sf-ease-bounce:    linear(0, 0.35 18%, 1 32%, 0.86 42%, 1.02 56%, 0.98 72%, 1);
    --sf-ease-overshoot: linear(0, 0.6 30%, 1.08 55%, 0.98 75%, 1);

    /* ----------------------------------------------------------
       Animation presets — pair keyframes (core/motion.css) with
       duration + easing. Use directly: animation: var(--sf-animation-fade-in);
       ---------------------------------------------------------- */
    --sf-animation-fade-in:        sf-fade-in        var(--sf-duration-normal) var(--sf-ease-out)       both;
    --sf-animation-fade-out:       sf-fade-out       var(--sf-duration-normal) var(--sf-ease-in)        both;
    --sf-animation-slide-in-up:    sf-slide-in-up    var(--sf-duration-normal) var(--sf-ease-out)       both;
    --sf-animation-slide-in-down:  sf-slide-in-down  var(--sf-duration-normal) var(--sf-ease-out)       both;
    --sf-animation-slide-in-left:  sf-slide-in-left  var(--sf-duration-normal) var(--sf-ease-out)       both;
    --sf-animation-slide-in-right: sf-slide-in-right var(--sf-duration-normal) var(--sf-ease-out)       both;
    --sf-animation-scale-up:       sf-scale-up       var(--sf-duration-normal) var(--sf-ease-overshoot) both;
    --sf-animation-scale-down:     sf-scale-down     var(--sf-duration-normal) var(--sf-ease-in)        both;
    --sf-animation-slide-out-up:    sf-slide-out-up    var(--sf-duration-normal) var(--sf-ease-in) both;
    --sf-animation-slide-out-down:  sf-slide-out-down  var(--sf-duration-normal) var(--sf-ease-in) both;
    --sf-animation-slide-out-left:  sf-slide-out-left  var(--sf-duration-normal) var(--sf-ease-in) both;
    --sf-animation-slide-out-right: sf-slide-out-right var(--sf-duration-normal) var(--sf-ease-in) both;
    --sf-animation-color-pulse:    sf-color-pulse    var(--sf-duration-slow)   var(--sf-ease-in-out)    infinite;
    --sf-animation-ping:           sf-ping           var(--sf-duration-slow)   var(--sf-ease-out)       infinite;
    --sf-animation-blink:          sf-blink          calc(1s * var(--sf-motion-scale)) steps(1, end) infinite;
    --sf-animation-float:          sf-float          calc(3s * var(--sf-motion-scale)) var(--sf-ease-in-out) infinite;
    --sf-animation-spin:    sf-spin var(--sf-duration-slower) linear infinite;
    --sf-animation-shimmer: sf-shimmer calc(1.5s * var(--sf-motion-scale)) var(--sf-ease-in-out) infinite;

    /* Stagger step — the per-item delay increment consumed by the .sf-stagger
       utility (core/motion.css). One knob retunes the whole choreography; the
       class multiplies it by the child's index and by --sf-motion-scale, so the
       rhythm follows the motion knob without baking the scale in here. */
    --sf-stagger-step: 75ms;

    /* Hover-transform magnitudes — the .sf-hover-* utilities (optional/
       utilities.css) read these so the effect strength is one global knob per
       axis rather than hard-coded per class. --grow/--shrink are scale factors;
       --lift is the float/sink translate distance; --slide is the slide
       distance. */
    --sf-hover-grow-scale:   1.05;
    --sf-hover-shrink-scale: 0.95;
    --sf-hover-lift:         0.25em;
    --sf-hover-slide:        0.5em;

    /* ----------------------------------------------------------
       Scroll-driven animation range — consumer API. Attach these
       to your own scroll-timeline via animation-range. Not consumed
       by the framework itself; intended for BEM component authors.
       ---------------------------------------------------------- */
    --sf-scroll-timeline-range-start: entry 0%;
    --sf-scroll-timeline-range-end:   cover 30%;

    /* Exit counterpart — element is well into view before starting to
       leave, finishing exactly as it scrolls fully out. */
    --sf-scroll-timeline-range-exit-start: cover 70%;
    --sf-scroll-timeline-range-exit-end:   exit 100%;

    /* ----------------------------------------------------------
       Mask scrim — edge-fade stops for scroll reels / overflow.
       Use in a mask-image gradient to fade content near an edge:
         mask-image: linear-gradient(to right,
           transparent 0, #000 var(--sf-mask-scrim-start),
           #000 calc(100% - var(--sf-mask-scrim-end)), transparent 100%);
       ---------------------------------------------------------- */
    --sf-mask-scrim-start: var(--sf-space-l);
    --sf-mask-scrim-end:   var(--sf-space-l);

    /* ----------------------------------------------------------
       Z-index
       ---------------------------------------------------------- */
    /* Semantic z-index ladder — UI roles, not magic integers. Follows the
       Bootstrap/Chakra convention (1000-base, ordered by stacking role) so
       the names are familiar and the gaps leave room to insert custom layers.
       Low-level rungs (below/base/raised) handle local stacking; the named
       roles handle page chrome and overlays.

       Modal / popover / tooltip components render in the browser TOP LAYER
       (native <dialog> / [popover]), ordered above the page independently of
       z-index. The --sf-z-modal / --sf-z-overlay / --sf-z-tooltip rungs are
       the fallback for non-top-layer implementations that must beat the page. */
    --sf-z-below:  -1;
    --sf-z-base:    0;
    --sf-z-raised:  1;
    --sf-z-sticky:   1000;  /* sticky headers / sidebars            */
    --sf-z-fixed:    1010;  /* fixed app chrome (navbar, FAB)       */
    --sf-z-dropdown: 1020;  /* menus — clear sticky + fixed         */
    --sf-z-overlay:  1030;  /* scrims / non-top-layer backdrops     */
    --sf-z-modal:    1040;  /* dialogs (non-top-layer fallback)     */
    --sf-z-toast:    1050;  /* transient notifications              */
    --sf-z-tooltip:  1060;  /* tooltips — above everything          */

    /* ----------------------------------------------------------
       Layout & a11y
       ---------------------------------------------------------- */
    /* Header height fluidly interpolates between the mobile and desktop
       tokens across the framework viewport range (--sf-fluid-{min,max}-vw),
       the same range used by the fluid type/space scales. Override either
       endpoint to retune; set both equal for a fixed height. clamp() keeps the
       result bounded to the endpoints even when they are overridden. */
    --sf-header-height-mobile:  3.5rem;
    --sf-header-height-desktop: 5rem;
    --sf-header-height: clamp(
      var(--sf-header-height-mobile),
      calc((var(--sf-header-height-desktop) - var(--sf-header-height-mobile)) / ((var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * 1rem) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-header-height-mobile)),
      var(--sf-header-height-desktop));
    --sf-sticky-offset-mobile:  var(--sf-header-height-mobile);
    --sf-sticky-offset-desktop: var(--sf-header-height-desktop);
    --sf-sticky-offset: clamp(
      var(--sf-sticky-offset-mobile),
      calc((var(--sf-sticky-offset-desktop) - var(--sf-sticky-offset-mobile)) / ((var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * 1rem) * (100vw - var(--sf-fluid-min-vw) * 1rem) + var(--sf-sticky-offset-mobile)),
      var(--sf-sticky-offset-desktop));
    /* Focus ring dimensions and minimum touch-target size. Governs keyboard
       navigation affordance and WCAG 2.5.5 pointer-target requirements.
       --sf-touch-target owns the 44px WCAG floor as a literal — it is NOT an
       alias of a --sf-size-* rung, so retuning the size scale can never drag
       the accessibility minimum below spec. Override it deliberately if you
       have your own target policy. */
    --sf-focus-ring-width:  2px;
    --sf-focus-ring-offset: 2px;
    --sf-focus-ring-style:  solid;
    --sf-touch-target:      2.75rem;  /* 44px — WCAG 2.5.5 minimum, independent of the size scale */
    /* Global text-contrast knob. Nudges derived reading-text colours
       toward the extremes (darker in light mode, lighter in dark mode).
       Positive = more contrast; 0 = no change. Consumed by
       --sf-color-text, --sf-color-text--subtle, --sf-color-heading. */
    --sf-contrast-bias:     0;
    /* Lightness crossover for text-on-color auto-contrast. Colours with
       L above this value get dark text; below get light text.
       Default 0.6 guarantees ≥ 3:1 for most colours.
       The binary choice (near-black or near-white) cannot guarantee 4.5:1 when
       the background sits in the L ≈ 0.52–0.67 band — both extremes land near
       the crossover. Two escape hatches:
         1. Shift the threshold per surface — lower (e.g. 0.55) if your brand
            lands near 0.6 and you prefer white text on it:
              @layer slashed.overrides {
                .sf-surface--primary { --sf-contrast-threshold: 0.55; }
              }
         2. Override the resolved token directly when an exact colour is needed:
              @layer slashed.overrides {
                .sf-surface--primary { --sf-color-text--on-primary: oklch(0.15 0 0); }
              }
       CSS `contrast-color()` (currently only in the spec, no browser support)
       will replace this formula once it ships. */
    --sf-contrast-threshold: 0.6;

    /* Device safe-area insets (notches, rounded corners, home indicators).
       0px on devices without intrusions. Read these in your own padding/
       margin; the framework consumes them in layout edge spacing. */
    --sf-safe-top:    env(safe-area-inset-top,    0px);
    --sf-safe-bottom: env(safe-area-inset-bottom, 0px);
    --sf-safe-left:   env(safe-area-inset-left,   0px);
    --sf-safe-right:  env(safe-area-inset-right,  0px);

    /* ----------------------------------------------------------
       Print
       ---------------------------------------------------------- */
    --sf-print-page-margin: 2cm;
    --sf-print-page-size:   a4;
    --sf-print-base-size:   11pt;

    /* ----------------------------------------------------------
       Object fit / position — global defaults for replaced elements.
       Applied in core/base.css on img and video. Override per element
       via inline style="--sf-object-position: top" or in BEM CSS.
       ---------------------------------------------------------- */
    --sf-object-fit:      cover;
    --sf-object-position: 50% 50%;

  }

  /* @supports — Shadow ramp (box-shadow, text-shadow, drop-shadow) */
  @supports (color: oklch(from red l c h)) {
    :root {

      /* Shadow tint — near-black derived from the neutral; the tint simply
         inherits the neutral's own chroma/hue, so a colourless neutral yields
         colourless shadows. Forced dark in both modes; --sf-shadow-strength
         handles dark-mode intensity. Override to a brand colour for explicitly
         tinted shadows — keep it dark or you get bright slabs:
         oklch(from var(--sf-color-primary) 0.15 c h). */
      --sf-shadow-color: oklch(from var(--sf-color-neutral) var(--sf-shadow-lightness) c h);

      /* Elevation shadow ramp — box-shadow presets from xs to 2xl.
         Opacity scales with --sf-shadow-strength so dark-mode boost is automatic. */
      --sf-shadow-none:  none;
      --sf-shadow-xs:    0 1px 2px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 0.5), 0.7));
      --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)),
                         0 2px 6px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, var(--sf-shadow-strength), 0.7));
      --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)),
                         0 4px 12px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));
      --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)),
                         0 8px 24px 0  oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3), 0.7)),
                         0 16px 48px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));
      --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)),
                         0 12px 36px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3.5), 0.7)),
                         0 24px 72px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7));
      --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)),
                         0 20px 60px 0       oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 4), 0.7)),
                         0 40px 100px -8px   oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 5), 0.7));
      --sf-shadow-inner: inset 0 2px 4px 0 oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2), 0.7));
  
      /* ----------------------------------------------------------
         Text shadows — share --sf-shadow-color / --sf-shadow-strength
         ---------------------------------------------------------- */
      --sf-text-shadow-none: none;
      --sf-text-shadow-xs: 0 0.5px 1px oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1),   0.7));
      --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));
      --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));
      --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));
      --sf-text-shadow-xl: 0 8px 16px  oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3),   0.7));

      /* Drop shadows — for filter: drop-shadow() (respects alpha edges) */
      --sf-drop-shadow-xs: drop-shadow(0 0.5px 1px  oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1),   0.7)));
      --sf-drop-shadow-s:  drop-shadow(0 1px 2px    oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 1.5), 0.7)));
      --sf-drop-shadow-m:  drop-shadow(0 4px 6px    oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2),   0.7)));
      --sf-drop-shadow-l:  drop-shadow(0 8px 16px   oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 2.5), 0.7)));
      --sf-drop-shadow-xl: drop-shadow(0 16px 32px  oklch(from var(--sf-shadow-color) l c h / clamp(0, calc(var(--sf-shadow-strength) * 3),   0.7)));

    }
  }

  /* ============================================================
     TYPOGRAPHY ALIASES — part of the per-style typography bundle
     for a named text role (body, code, heading, h1–h6, display).
     The full bundle spans font-family, size, weight, line-height,
     and letter-spacing across related tokens.
     ============================================================ */

  :root {
    --sf-body-font-size:     var(--sf-text-m);
    --sf-body-line-height:   var(--sf-leading-normal);
    --sf-body-font-weight:   var(--sf-font-weight-body);
    --sf-body-font-family:   var(--sf-font-body);
    --sf-body-color:         var(--sf-color-text);
    --sf-body-text-wrap:     pretty;
    --sf-body-strong-weight: var(--sf-font-weight-strong);
    --sf-body-em-style:      italic;

    --sf-code-font-size:     0.875em;

    --sf-heading-font-family: var(--sf-font-heading);
    --sf-heading-color:       var(--sf-color-heading);
    --sf-heading-text-wrap:   balance;

    --sf-h1-size:           var(--sf-text-4xl);
    --sf-h1-line-height:    var(--sf-leading-tight);
    --sf-h1-font-weight:    var(--sf-font-weight-heading);
    --sf-h1-letter-spacing: var(--sf-tracking-tight);

    --sf-h2-size:           var(--sf-text-3xl);
    --sf-h2-line-height:    var(--sf-leading-tight);
    --sf-h2-font-weight:    var(--sf-font-weight-heading);
    --sf-h2-letter-spacing: var(--sf-tracking-tight);

    --sf-h3-size:           var(--sf-text-2xl);
    --sf-h3-line-height:    var(--sf-leading-snug);
    --sf-h3-font-weight:    var(--sf-font-weight-heading);
    --sf-h3-letter-spacing: var(--sf-tracking-normal);

    --sf-h4-size:           var(--sf-text-xl);
    --sf-h4-line-height:    var(--sf-leading-snug);
    --sf-h4-font-weight:    var(--sf-font-weight-heading);
    --sf-h4-letter-spacing: var(--sf-tracking-normal);

    --sf-h5-size:           var(--sf-text-l);
    --sf-h5-line-height:    var(--sf-leading-normal);
    --sf-h5-font-weight:    var(--sf-font-weight-heading);
    --sf-h5-letter-spacing: var(--sf-tracking-normal);

    --sf-h6-size:           var(--sf-text-m);
    --sf-h6-line-height:    var(--sf-leading-normal);
    --sf-h6-font-weight:    var(--sf-font-weight-heading);
    --sf-h6-letter-spacing: var(--sf-tracking-wide);

    /* ----------------------------------------------------------
       Heading line-length constraints — public control hooks.
       Default `none` = no constraint. Set to a ch value to cap
       heading width independently of the prose container:
         :root { --sf-h1-max-width: 20ch; --sf-h2-max-width: 28ch; }
       Consumed by h1–h6 in core/base.css.
       ---------------------------------------------------------- */
    --sf-h1-max-width: none;
    --sf-h2-max-width: none;
    --sf-h3-max-width: none;
    --sf-h4-max-width: none;
    --sf-h5-max-width: none;
    --sf-h6-max-width: none;

    /* ----------------------------------------------------------
       Semantic gap tokens — the three rhythms every layout primitive
       defaults to. Also the BEM consumer API: .nav { gap: var(--sf-gap); }
       Override globally to retune all primitives of that rhythm at once.
       ---------------------------------------------------------- */
    --sf-gap:           var(--sf-space-m);  /* loose: between components   */
    --sf-content-gap:   var(--sf-space-s);  /* tight: within content       */
    --sf-gutter:        var(--sf-space-l);  /* wide: page/section gutters  */
    --sf-component-pad: var(--sf-space-m);  /* standard component padding  */
    --sf-field-block:   var(--sf-space-xs); /* form-field block padding — matches the shipped field default (see optional/forms.css) */
    --sf-field-required-marker: " *";
    /* Leading space is baked into the default so consumers can fully
       disable the indicator with `--sf-link-external-marker: ""` —
       without the space a bare "" still left a stray space after the
       link text. Override with your own glyph (include leading space
       if you want one): `:root { --sf-link-external-marker: " →" }`. */
    --sf-link-external-marker: " \2197";  /* " ↗" — consumed by .sf-link-external */
    /* Accessible name for the marker glyph, via the CSS alt-text syntax
       (`content: <value> / <string>`) — read by screen readers in place
       of the glyph, appended after the link's own text. Localise or
       disable (together with the marker) per-site. */
    --sf-link-external-label: "opens in a new window or external site";

    /* ----------------------------------------------------------
       Section padding
       ---------------------------------------------------------- */
    /* All sizes multiply by --sf-section-scale — one dial re-rhythms
       every section on the page (default 1 = no change). */
    --sf-section-pad:     var(--sf-section-pad--m);
    --sf-section-pad--xs: calc(var(--sf-space-xl)  * var(--sf-section-scale));
    --sf-section-pad--s:  calc(var(--sf-space-2xl) * var(--sf-section-scale));
    --sf-section-pad--m:  calc(var(--sf-space-3xl) * var(--sf-section-scale));
    --sf-section-pad--l:  calc(var(--sf-space-4xl) * var(--sf-section-scale));
    --sf-section-pad--xl: calc(var(--sf-space-4xl) * 1.5 * var(--sf-section-scale));
    --sf-section-pad--2xl: calc(var(--sf-space-4xl) * 2 * var(--sf-section-scale));

    /* ----------------------------------------------------------
       Transition shorthands

       All tokens name an explicit property set (no `transition: all`).
       The tempo presets (fast / slow / enter / exit) cover the curated
       "common interactive + entrance" set — color, background-color,
       border-color, box-shadow, opacity, transform, filter.
       ---------------------------------------------------------- */
    --sf-transition-colors:
      color var(--sf-duration-normal) var(--sf-ease-out),
      background-color var(--sf-duration-normal) var(--sf-ease-out),
      border-color var(--sf-duration-normal) var(--sf-ease-out),
      text-decoration-color var(--sf-duration-normal) var(--sf-ease-out),
      fill var(--sf-duration-normal) var(--sf-ease-out),
      stroke var(--sf-duration-normal) var(--sf-ease-out);
    /* Form fields: color/bg at normal pace, border/shadow/opacity at fast
       so focus rings and validation states react immediately. */
    --sf-transition-form-field:
      color              var(--sf-duration-normal) var(--sf-ease-out),
      background-color   var(--sf-duration-normal) var(--sf-ease-out),
      text-decoration-color var(--sf-duration-normal) var(--sf-ease-out),
      fill               var(--sf-duration-normal) var(--sf-ease-out),
      stroke             var(--sf-duration-normal) var(--sf-ease-out),
      border-color       var(--sf-duration-fast)   var(--sf-ease-out),
      box-shadow         var(--sf-duration-fast)   var(--sf-ease-out),
      opacity            var(--sf-duration-fast)   var(--sf-ease-out);
    /* Single-property transition: transform only. */
    --sf-transition-transform: transform var(--sf-duration-normal) var(--sf-ease-out);
    /* Single-property transition: opacity only. */
    --sf-transition-opacity:   opacity var(--sf-duration-normal) var(--sf-ease-out);
    /* Single-property transition: box-shadow only. */
    --sf-transition-shadow:    box-shadow var(--sf-duration-normal) var(--sf-ease-out);

    /* All interactive properties at fast duration — for hover/active states that need to feel snappy. */
    --sf-transition-fast:
      color var(--sf-duration-fast) var(--sf-ease-out),
      background-color var(--sf-duration-fast) var(--sf-ease-out),
      border-color var(--sf-duration-fast) var(--sf-ease-out),
      box-shadow var(--sf-duration-fast) var(--sf-ease-out),
      opacity var(--sf-duration-fast) var(--sf-ease-out),
      transform var(--sf-duration-fast) var(--sf-ease-out),
      filter var(--sf-duration-fast) var(--sf-ease-out);
    /* All interactive properties at slow duration — for deliberate, attention-drawing animations. */
    --sf-transition-slow:
      color var(--sf-duration-slow) var(--sf-ease-in-out),
      background-color var(--sf-duration-slow) var(--sf-ease-in-out),
      border-color var(--sf-duration-slow) var(--sf-ease-in-out),
      box-shadow var(--sf-duration-slow) var(--sf-ease-in-out),
      opacity var(--sf-duration-slow) var(--sf-ease-in-out),
      transform var(--sf-duration-slow) var(--sf-ease-in-out),
      filter var(--sf-duration-slow) var(--sf-ease-in-out);
    /* Entrance transition: normal duration + ease-out for elements entering the view. */
    --sf-transition-enter:
      color var(--sf-duration-normal) var(--sf-ease-out),
      background-color var(--sf-duration-normal) var(--sf-ease-out),
      border-color var(--sf-duration-normal) var(--sf-ease-out),
      box-shadow var(--sf-duration-normal) var(--sf-ease-out),
      opacity var(--sf-duration-normal) var(--sf-ease-out),
      transform var(--sf-duration-normal) var(--sf-ease-out),
      filter var(--sf-duration-normal) var(--sf-ease-out);
    /* Exit transition: fast duration + ease-in so exits feel crisp and don't linger. */
    --sf-transition-exit:
      color var(--sf-duration-fast) var(--sf-ease-in),
      background-color var(--sf-duration-fast) var(--sf-ease-in),
      border-color var(--sf-duration-fast) var(--sf-ease-in),
      box-shadow var(--sf-duration-fast) var(--sf-ease-in),
      opacity var(--sf-duration-fast) var(--sf-ease-in),
      transform var(--sf-duration-fast) var(--sf-ease-in),
      filter var(--sf-duration-fast) var(--sf-ease-in);

    /* Top-layer transition building block (dialog, [popover]).
       transition-behavior: allow-discrete keeps the discrete `display`
       and `overlay` properties in the transition so entry animations can
       fire and exit animations finish before the element leaves the top
       layer. Compose it into a component transition alongside the visual
       properties.
       NOTE: Requires @starting-style to be defined on the element for
       the entrance transition to trigger.
       Progressive enhancement — unsupporting engines simply show/hide instantly.
       Chrome/Edge/Safari and Firefox (2025+) support allow-discrete. */
    --sf-transition-overlay:
      overlay var(--sf-duration-normal) allow-discrete,
      display var(--sf-duration-normal) allow-discrete;
  }

}

/* ─── core/tokens.layout.css ─── */
/* SLASHED — core/tokens.layout.css
   @layer slashed.tokens
   Layout primitive override tokens for gaps, padding, min sizes, tracks, and containers.
   Prefix: --sf-*; loaded before core/layout.css. */

@layer slashed.tokens {
  :root {

    /* ----------------------------------------------------------
       Stack
       ---------------------------------------------------------- */
    --sf-stack-gap: var(--sf-content-gap);

    /* ----------------------------------------------------------
       Box
       ---------------------------------------------------------- */
    --sf-box-padding:      var(--sf-space-m);
    --sf-box-border-width: 0;
    --sf-box-border-color: var(--sf-color-border);

    /* ----------------------------------------------------------
       Center
       ---------------------------------------------------------- */
    --sf-center-max:    var(--sf-container-default);
    --sf-center-gutter: var(--sf-gutter);

    /* ----------------------------------------------------------
       Cluster
       ---------------------------------------------------------- */
    --sf-cluster-gap:     var(--sf-gap);
    --sf-cluster-align:   center;
    --sf-cluster-justify: flex-start;

    /* ----------------------------------------------------------
       Sidebar
       ---------------------------------------------------------- */
    --sf-sidebar-gap:           var(--sf-gap);
    --sf-sidebar-min-width:     50%;           /* content column minimum */
    --sf-sidebar-width:         18rem;         /* sidebar panel default size */

    /* ----------------------------------------------------------
       Switcher
       ---------------------------------------------------------- */
    --sf-switcher-threshold: 30rem;
    --sf-switcher-gap:       var(--sf-gap);

    /* ----------------------------------------------------------
       Grid (auto-fill, breakpoint-free)
       ---------------------------------------------------------- */
    --sf-grid-min:         16rem;
    --sf-grid-gap:         var(--sf-gap);

    --sf-grid-min-xs:      10rem;
    --sf-grid-min-s:       13rem;
    --sf-grid-min-m:       16rem;
    --sf-grid-min-l:       20rem;
    --sf-grid-min-xl:      24rem;
    --sf-grid-min-2xl:     28rem;

    /* ----------------------------------------------------------
       Equal columns — CSS multi-column flowing layout (not grid).
       Content flows between columns like a newspaper: fills one
       column top-to-bottom, continues at the top of the next.
       Use for FAQ lists, prose, anything that should distribute
       evenly without being pinned to discrete grid cells — reach
       for .sf-grid/.sf-grid--fit instead when items need their own
       fixed cell.
       ---------------------------------------------------------- */
    --sf-equal-min-col:    16rem;
    --sf-equal-gap:        var(--sf-gap);
    --sf-equal-rule-width: 0;
    --sf-equal-rule-style: solid;
    --sf-equal-rule-color: var(--sf-color-border);

    /* ----------------------------------------------------------
       Cover
       ---------------------------------------------------------- */
    --sf-cover-min-height: 100dvh;
    --sf-cover-padding:    var(--sf-section-pad);

    /* ----------------------------------------------------------
       Frame
       ---------------------------------------------------------- */
    --sf-frame-ratio: 16 / 9;    /* literal <ratio>, valid in aspect-ratio */

    /* ----------------------------------------------------------
       Background layer (.sf-bg-layer) — cover media behind sibling content.
       inset is a single length applied to every edge; the layer size is
       derived from it, so a non-zero inset still fills correctly.
       ---------------------------------------------------------- */
    /* 0px, not bare 0 — .sf-bg-layer's inline-size/block-size multiply this by 2
       inside a calc() alongside a percentage; a unitless 0 makes that
       calc() a <number>/<percentage> type mismatch (invalid at
       computed-value time), silently collapsing .sf-bg-layer to its intrinsic
       size. */
    --sf-bg-layer-inset:    0px;
    --sf-bg-layer-fit:      cover;
    --sf-bg-layer-position: 50% 50%;
    --sf-bg-layer-radius:   0;
    --sf-bg-layer-z:        -2;        /* behind content; above the parent background */

    /* ----------------------------------------------------------
       Icon (boxed variant) — extras for .sf-icon--boxed.
       The naked .sf-icon sizing tokens (--sf-icon-xs..xl) live in
       core/tokens.css alongside the typography scale.
       ---------------------------------------------------------- */
    --sf-icon-box-pad:    0.5em;
    --sf-icon-box-radius: var(--sf-radius-s);
    --sf-icon-box-bg:     var(--sf-color-inset);
    --sf-icon-box-border: var(--sf-border-width-1) solid var(--sf-color-border);

    /* ----------------------------------------------------------
       Reel
       ---------------------------------------------------------- */
    --sf-reel-item-width: max-content;
    --sf-reel-gap:        var(--sf-gap);
    --sf-reel-height:     auto;

    /* ----------------------------------------------------------
       Imposter (centered absolute/fixed overlay)
       ---------------------------------------------------------- */
    --sf-imposter-margin: var(--sf-space-m);

    /* ----------------------------------------------------------
       Bento grid
       ---------------------------------------------------------- */
    --sf-bento-cols-default: 4;
    --sf-bento-row-default:  10rem;
    --sf-bento-row-compact:  6rem;
    --sf-bento-row-tall:     16rem;
    --sf-bento-gap:          var(--sf-gap);

    /* ----------------------------------------------------------
       Content grid (breakout pattern)
       ---------------------------------------------------------- */
    --sf-breakout-width: var(--sf-container-wide);
    --sf-content-width:  var(--sf-container-default);

    /* ----------------------------------------------------------
       Prose
       ---------------------------------------------------------- */
    --sf-prose-paragraph: var(--sf-content-gap);

    /* ----------------------------------------------------------
       Alternate (zigzag)
       ---------------------------------------------------------- */
    --sf-alternate-gap:       var(--sf-content-gap);
    --sf-alternate-inner-gap: var(--sf-gap);

  }
}

/* ─── core/tokens.macros.css ─── */
/* SLASHED — core/tokens.macros.css
   @layer slashed.tokens
   Macro recipe override tokens for prose, surfaces, flow, media, hero, scrims, and clamps.
   Prefix: --sf-*; loaded before core/macros.css. */

@layer slashed.tokens {
  :root {

    /* ----------------------------------------------------------
       Flow — distance between flow children (lobotomized owl).
       Consumed by .sf-flow > * + *.
       ---------------------------------------------------------- */
    --sf-flow-space: var(--sf-content-gap);

    /* ----------------------------------------------------------
       Line clamp — default line count for .sf-line-clamp-N.
       Override per element: style="--sf-line-clamp: 5".
       The fixed-N variants .sf-line-clamp-2 / -3 don't read this
       token (they hardcode the value), so the default only
       applies to .sf-line-clamp-N callers.
       ---------------------------------------------------------- */
    --sf-line-clamp: 3;

    /* ----------------------------------------------------------
       Aspect ratio — default for .sf-aspect (generic ratio container,
       distinct from layout's .sf-frame which has its own --sf-frame-ratio).
       ---------------------------------------------------------- */
    --sf-aspect: 16 / 9;

    /* ----------------------------------------------------------
       Prose — per-instance override knobs for .sf-prose.
       All values default to system spacing/color tokens so prose
       stays in sync with the global scale unless overridden.

       Override per-element:
         style="--sf-prose-marker-color: var(--sf-color-secondary)"
       ---------------------------------------------------------- */
    --sf-prose-heading-gap:      var(--sf-space-s);
    --sf-prose-list-gap:         var(--sf-space-xs);
    --sf-prose-block-margin:     var(--sf-space-m);
    --sf-prose-media-margin:     var(--sf-space-m);
    --sf-prose-media-radius:     var(--sf-radius-m);
    --sf-prose-figure-margin:    var(--sf-space-l);
    --sf-prose-marker-color:     var(--sf-color-primary);
    --sf-prose-figcaption-size:  var(--sf-text-s);
    --sf-prose-blockquote-padding: var(--sf-space-m);
    --sf-prose-blockquote-border:  var(--sf-border-width-2) solid var(--sf-color-border--subtle);
    --sf-prose-hr-margin:          var(--sf-space-l);
    --sf-prose-nested-list-gap:    var(--sf-space-2xs);
    --sf-prose-table-pad:          var(--sf-space-xs);

    /* ----------------------------------------------------------
       Surface color — input for the generic .sf-surface macro.
       Set it on any element carrying the class and the macro derives
       background, auto-contrast foreground, and the full contextual
       token set from it:
         <section class="sf-surface"
                  style="--sf-surface-color: var(--sf-color-primary-100)">
       Inherits: a nested .sf-surface picks up the outer surface's
       color unless it sets its own. Any resolvable CSS color works.
       ---------------------------------------------------------- */
    --sf-surface-color: var(--sf-color-base);

    /* ----------------------------------------------------------
       Scroll shadow / overflow fade — mask gradient size.
       Used by .sf-scroll-shadow (top+bottom) and .sf-overflow-fade
       (end-edge horizontal fade).
       ---------------------------------------------------------- */
    --sf-scroll-shadow-size: 2rem;

    /* ----------------------------------------------------------
       Content visibility — placeholder size for .sf-content-auto.
       Feeds contain-intrinsic-size so offscreen sections reserve
       space before their first render, keeping the scrollbar and
       scroll position stable. Override per element:
       style="--sf-content-intrinsic-size: 800px".
       ---------------------------------------------------------- */
    --sf-content-intrinsic-size: 500px;

    /* ----------------------------------------------------------
       Scrim — darkening overlay for text-over-image legibility,
       consumed by the .sf-scrim macro. The gradient is composed
       from a direction + a color stop so consumers can retune
       either independently:
         style="--sf-scrim-color: oklch(0 0 0 / 0.75)"
         style="--sf-scrim-direction: to top right"
       --sf-scrim-gradient is the final composed value; override it
       directly for a multi-stop or radial scrim.
       ---------------------------------------------------------- */
    --sf-scrim-color:     oklch(0 0 0 / 0.55);
    --sf-scrim-direction: to top;
    --sf-scrim-gradient:  linear-gradient(var(--sf-scrim-direction), var(--sf-scrim-color), transparent);
    /* Text-on-image legibility WITHOUT darkening the picture — a soft
       shadow halo behind glyphs, consumed by .sf-text-protect. */
    --sf-scrim-text-shadow: 0 1px 3px oklch(0 0 0 / 0.6);

    /* ----------------------------------------------------------
       Named background surface preset — input set for the .sf-surface-bg
       macro (issue #523). One reusable, named background bundling a base
       colour fallback + image/gradient/pattern + sizing + an optional
       overlay + an optional animation. Define a preset by setting these
       on a scope, then apply .sf-surface-bg to compose them:
         .hero-surface {
           --sf-surface-bg-image:   url("/hero.avif");
           --sf-surface-bg-overlay: var(--sf-scrim-gradient);  // reuse the scrim
           --sf-surface-bg-animation: sf-pan 40s linear infinite;
         }
         <section class="hero-surface sf-surface-bg">…</section>
       -overlay layers ABOVE -image (first background-image layer). Builds on
       the existing scrim + gradient tokens rather than new infra; for a
       blurred backdrop compose .sf-scrim / a filter on top. Inert by default
       (image/overlay/animation = none) so the bare class is a no-op until a
       preset feeds it. ---------------------------------------------------- */
    --sf-surface-bg-color:      transparent;
    --sf-surface-bg-image:      none;
    --sf-surface-bg-overlay:    none;
    --sf-surface-bg-size:       cover;
    --sf-surface-bg-position:   center;
    --sf-surface-bg-repeat:     no-repeat;
    --sf-surface-bg-attachment: scroll;
    --sf-surface-bg-animation:  none;

    /* ----------------------------------------------------------
       Overlap recipe — how far .sf-overlap pulls into the adjacent
       element. .sf-overlap-host's padding compensation defaults to this
       same value via a var() fallback in the rule itself (not aliased
       here — see core/macros.css), so one override on the host tunes
       both unless --sf-overlap-host-pad is set independently.
       ---------------------------------------------------------- */
    --sf-overlap-pull: var(--sf-space-xl);

  }
}

/* ─── core/reset.css ─── */
/* SLASHED — core/reset.css
   @layer slashed.reset
   Browser normalization, sizing defaults, viewport behavior, media safety, and form inheritance.
   No component styling. */

@layer slashed.reset {

  *, *::before, *::after {
    box-sizing: border-box;
  }

  html {
    /* stylelint-disable-next-line property-no-vendor-prefix -- Safari <17: unprefixed text-size-adjust only landed in Safari 17 */
    -webkit-text-size-adjust: 100%;
    text-size-adjust:          100%;
    scroll-padding-block-start: calc(var(--sf-header-height, 5rem) + var(--sf-space-m, 1rem));
    scrollbar-gutter:    stable;
    color-scheme:        var(--sf-color-scheme, light dark);
    hanging-punctuation: first last;
  }

  @supports (interpolate-size: allow-keywords) {
    html { interpolate-size: allow-keywords; }
  }

  body {
    min-height:      100dvh;
    min-inline-size: 320px;
  }

  button {
    text-transform: none;
  }

  search {
    display: block;
  }

  summary {
    cursor: pointer;
  }

  table {
    border-collapse: collapse;
  }

  [hidden] {
    display: none !important;
  }

  dialog, [popover] {
    padding:      0;
    border:       none;
    background:   Canvas;
    color:        inherit;
    color-scheme: inherit;
  }

  fieldset {
    min-width: 0;
  }

  [inert] {
    cursor: default;
  }

}

/* ─── core/base.css ─── */
/* SLASHED — core/base.css
   @layer slashed.base
   Element defaults for typography, media, links, tables, and forms.
   Token-first, classless foundation. */

@layer slashed.base {

  html {
    scrollbar-color: var(--sf-scrollbar-thumb) var(--sf-scrollbar-track);
  }

  body {
    margin:           0;
    font-family:      var(--sf-body-font-family, var(--sf-font-body));
    font-size:        var(--sf-body-font-size,   var(--sf-text-m));
    line-height:      var(--sf-body-line-height, var(--sf-leading-normal));
    font-weight:      var(--sf-body-font-weight, var(--sf-font-weight-body));
    color:            var(--sf-body-color,       var(--sf-color-text));
    background-color: var(--sf-color-bg, #fff);
  }

  h1, h2, h3, h4, h5, h6 {
    margin:            0;
    font-family:       var(--sf-heading-font-family, var(--sf-font-heading));
    line-height:       var(--sf-leading-tight);
    color:             var(--sf-heading-color, var(--sf-color-heading));
    text-wrap:         var(--sf-heading-text-wrap);
    overflow-wrap:     break-word;
    text-rendering:    optimizelegibility;
  }

  h1 { font-size: var(--sf-h1-size); line-height: var(--sf-h1-line-height); font-weight: var(--sf-h1-font-weight); letter-spacing: var(--sf-h1-letter-spacing); max-inline-size: var(--sf-h1-max-width); }
  h2 { font-size: var(--sf-h2-size); line-height: var(--sf-h2-line-height); font-weight: var(--sf-h2-font-weight); letter-spacing: var(--sf-h2-letter-spacing); max-inline-size: var(--sf-h2-max-width); }
  h3 { font-size: var(--sf-h3-size); line-height: var(--sf-h3-line-height); font-weight: var(--sf-h3-font-weight); letter-spacing: var(--sf-h3-letter-spacing); max-inline-size: var(--sf-h3-max-width); }
  h4 { font-size: var(--sf-h4-size); line-height: var(--sf-h4-line-height); font-weight: var(--sf-h4-font-weight); letter-spacing: var(--sf-h4-letter-spacing); max-inline-size: var(--sf-h4-max-width); }
  h5 { font-size: var(--sf-h5-size); line-height: var(--sf-h5-line-height); font-weight: var(--sf-h5-font-weight); letter-spacing: var(--sf-h5-letter-spacing); max-inline-size: var(--sf-h5-max-width); }
  h6 { font-size: var(--sf-h6-size); line-height: var(--sf-h6-line-height); font-weight: var(--sf-h6-font-weight); letter-spacing: var(--sf-h6-letter-spacing); max-inline-size: var(--sf-h6-max-width); }

  p {
    margin:        0;
    text-wrap:     var(--sf-body-text-wrap);
    overflow-wrap: break-word;
  }

  b, strong {
    font-weight: var(--sf-body-strong-weight);
  }

  em {
    font-style: var(--sf-body-em-style);
  }

  small {
    font-size: var(--sf-text-s, 0.875em);
  }

  mark {
    background-color: var(--sf-color-mark-bg, Mark);
    color:            var(--sf-color-mark-text, MarkText);
    border-radius:    var(--sf-radius-xs);
    padding-inline:   0.2em;
  }

  abbr[title] {
    text-decoration: underline dotted;
    cursor:          help;
  }

  kbd, samp {
    font-family: var(--sf-font-mono);
    font-size:   inherit;
  }

  sub, sup {
    font-size:      75%;
    line-height:    0;
    position:       relative;
    vertical-align: baseline;
  }
  sup { top:    -0.5em; }
  sub { bottom: -0.25em; }

  /* :link (0,1,1) not bare a: beats WebKit UA rule a:link { color: -webkit-link }
     that silently overrides --sf-color-link in dark mode. */
  a:link {
    color:                   var(--sf-color-link);
    text-decoration-color:   var(--sf-color-link--underline);
    text-underline-offset:   var(--sf-link-underline-offset);
    text-decoration-thickness: var(--sf-link-underline-thickness);
    text-underline-position: from-font;
    transition:
      color                 var(--sf-duration-fast) var(--sf-ease-out),
      text-decoration-color var(--sf-duration-fast) var(--sf-ease-out);
  }
  a:hover   { color: var(--sf-color-link--hover);   text-decoration-color: currentcolor; }
  a:visited { color: var(--sf-color-link--visited); }
  a:active  { color: var(--sf-color-link--active);  }
  a:not([class]) { text-decoration-skip-ink: auto; }

  a, code {
    overflow-wrap: break-word;
  }

  code {
    font-family:      var(--sf-font-mono);
    font-size:        var(--sf-code-font-size);
    padding:          0.125em 0.3em;
    border-radius:    var(--sf-radius-xs);
    background-color: var(--sf-color-code-bg);
    color:            var(--sf-color-code-text, inherit);
  }

  pre {
    font-family:   var(--sf-font-mono);
    font-size:     var(--sf-text-s);
    padding:       var(--sf-space-m);
    border-radius: var(--sf-radius-m);
    background:    var(--sf-color-code-block-bg, var(--sf-color-code-bg));
    color:         var(--sf-color-code-block-text, inherit);
    overflow-x:      auto;
    overflow-inline: auto;
    tab-size:        2;
    hyphens:       none;
  }
  pre code {
    background:    none;
    color:         inherit;
    padding:       0;
    font-size:     inherit;
    border-radius: 0;
  }

  img, picture, video, canvas {
    display:         block;
    max-inline-size: 100%;
    block-size:      auto;
  }

  /* Zero-specificity so .sf-bg-layer, .sf-frame > img, and any component's own
     radius (later layer or higher specificity) always win. */
  :where(img, figure) {
    border-radius: var(--sf-media-radius);
  }

  img, video {
    object-fit:      var(--sf-object-fit);
    object-position: var(--sf-object-position);
  }

  iframe, embed, object {
    display:         block;
    max-inline-size: 100%;
    block-size:      auto;
    border:          none;
  }

  hr {
    border:             none;
    border-block-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);
  }

  ::selection {
    background:  var(--sf-color-selection-bg);
    color:       var(--sf-color-selection-text);
    text-shadow: none;
  }

  th, caption {
    text-align: start;
  }

  input, textarea {
    caret-color: var(--sf-caret-color);
  }

  input, progress {
    accent-color: var(--sf-color-action);
  }

  ::backdrop {
    background-color: var(--sf-color-dim);
  }

  button, input, select, textarea {
    font:           inherit;
    color:          inherit;
    letter-spacing: inherit;
    color-scheme:   inherit;
    line-height:    inherit;
  }
  button {
    cursor: pointer;
  }
  a, button, input, select, textarea, summary {
    touch-action: manipulation;
  }
  textarea {
    resize: vertical;
  }

  optgroup { font: inherit; }
  ::placeholder { opacity: 1; color: var(--sf-color-text--placeholder, currentcolor); }

  blockquote {
    border-inline-start: var(--sf-border-width-3) var(--sf-border-style) var(--sf-color-border);
    padding-inline-start: var(--sf-space-m);
  }

  dt {
    font-weight: var(--sf-font-weight-heading);
  }

  dd {
    margin-inline-start: var(--sf-space-m);
  }

  table {
    text-align: start;
  }

  th {
    font-weight: var(--sf-font-weight-heading);
  }

  td, th {
    padding: var(--sf-space-2xs) var(--sf-space-xs);
    border-block-end: var(--sf-border-width-hairline) var(--sf-border-style) var(--sf-color-border--subtle);
  }

}

/* ─── core/themes.css ─── */
/* SLASHED — core/themes.css
   @layer slashed.themes
   Light/dark activation, scoped theme color inheritance, LumLocker, and scheme-aware color adjustments.
   Uses data-theme, .sf-theme-light/.sf-theme-dark aliases, and token overrides. */

@layer slashed.themes {

  @media (prefers-color-scheme: dark) {
    :root:not([data-theme]) {
      color-scheme: dark;
      --sf-is-dark: 1;
    }
  }

  :is([data-theme="light"], .sf-theme-light) { color-scheme: light; --sf-is-dark: 0; }
  :is([data-theme="dark"], .sf-theme-dark)  { color-scheme: dark;  --sf-is-dark: 1; }

  :where([data-theme]:not(:root, html), .sf-theme-light:not(:root, html), .sf-theme-dark:not(:root, html)) {
    background-color: var(--sf-color-bg);
    color:            var(--sf-color-text);

    /* Component-layer and :root-only alias tokens that merely alias a themed
       core token (no light/dark branching of their own, e.g. --sf-card-bg:
       var(--sf-color-surface)) are declared once in :root (tokens.css /
       optional/tokens.components.css). Without a redeclaration here, that
       var() substitutes using :root's OWN --sf-color-surface/-heading/-text
       at computed-value time and the result inherits down frozen — a nested
       data-theme section further in never gets a chance to re-substitute it,
       breaking the "data-theme works on any element" guarantee
       (docs/theming.md) for .sf-card and for heading/body colour (#496).
       Re-declaring the aliases here, at the same non-root scope, forces a
       fresh substitution against this section's own (possibly overridden)
       --sf-color-surface / -border / -heading / -text. */
    --sf-card-bg:           var(--sf-color-surface);
    --sf-card-border-color: var(--sf-color-border);
    --sf-heading-color:     var(--sf-color-heading);
    --sf-body-color:        var(--sf-color-text);
  }

  /* LumLocker: locks 4 brand colors to a fixed OKLCH L — --sf-lumlocker in
     light, --sf-lumlocker-dark (default calc(1.18 - L), the mirror around the
     light/dark background midpoint) in dark, so contrast against the surface
     holds in both themes. Neutral/base excluded — forcing them to a brand L
     breaks their semantic role. */
  @supports (color: oklch(from red l c h)) {
  :root[data-lumlocker] {
    --sf-color-primary: light-dark(
      oklch(from var(--sf-color-primary-source-light) var(--sf-lumlocker) c h),
      oklch(from var(--sf-color-primary-source-dark, var(--sf-color-primary-source-light)) var(--sf-lumlocker-dark) c h)
    );
    --sf-color-secondary: light-dark(
      oklch(from var(--sf-color-secondary-source-light) var(--sf-lumlocker) c h),
      oklch(from var(--sf-color-secondary-source-dark, var(--sf-color-secondary-source-light)) var(--sf-lumlocker-dark) c h)
    );
    --sf-color-tertiary: light-dark(
      oklch(from var(--sf-color-tertiary-source-light) var(--sf-lumlocker) c h),
      oklch(from var(--sf-color-tertiary-source-dark, var(--sf-color-tertiary-source-light)) var(--sf-lumlocker-dark) c h)
    );
    --sf-color-action: light-dark(
      oklch(from var(--sf-color-action-source-light) var(--sf-lumlocker) c h),
      oklch(from var(--sf-color-action-source-dark, var(--sf-color-action-source-light)) var(--sf-lumlocker-dark) c h)
    );
  }

  :root[data-lumlocker] :is([data-theme="dark"], .sf-theme-dark) {
    --sf-color-primary:   oklch(from var(--sf-color-primary-source-dark,   var(--sf-color-primary-source-light))   var(--sf-lumlocker-dark) c h);
    --sf-color-secondary: oklch(from var(--sf-color-secondary-source-dark, var(--sf-color-secondary-source-light)) var(--sf-lumlocker-dark) c h);
    --sf-color-tertiary:  oklch(from var(--sf-color-tertiary-source-dark,  var(--sf-color-tertiary-source-light))  var(--sf-lumlocker-dark) c h);
    --sf-color-action:    oklch(from var(--sf-color-action-source-dark,    var(--sf-color-action-source-light))    var(--sf-lumlocker-dark) c h);
  }
  :root[data-lumlocker] :is([data-theme="light"], .sf-theme-light) {
    --sf-color-primary:   oklch(from var(--sf-color-primary-source-light)   var(--sf-lumlocker) c h);
    --sf-color-secondary: oklch(from var(--sf-color-secondary-source-light) var(--sf-lumlocker) c h);
    --sf-color-tertiary:  oklch(from var(--sf-color-tertiary-source-light)  var(--sf-lumlocker) c h);
    --sf-color-action:    oklch(from var(--sf-color-action-source-light)    var(--sf-lumlocker) c h);
  }
  }

  /* SECTION-LEVEL THEMING */
  /* light-dark() bakes at :root declaration time; re-declarations on [data-theme]
     elements are required for section-level theming to work.
     SL-001: the clamp() derivation formula below is the same one used inside
     tokens.css's "Resolved color tokens — auto-switch via light-dark()"
     block (search core/tokens.css for that comment) — duplicated here, not
     shared, because this file needs flat values outside light-dark().
     Keep both copies in sync if the formula ever changes. */
  @supports (color: oklch(from red l c h)) {

        :root {
      --sf-color-primary-source-dark:   oklch(from var(--sf-color-primary-source-light)   clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h);
      --sf-color-secondary-source-dark: oklch(from var(--sf-color-secondary-source-light) clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h);
      --sf-color-tertiary-source-dark:  oklch(from var(--sf-color-tertiary-source-light)  clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h);
      --sf-color-action-source-dark:    oklch(from var(--sf-color-action-source-light)    clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h);
      --sf-color-neutral-source-dark:   oklch(from var(--sf-color-neutral-source-light)   clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h);
      --sf-color-base-source-dark:      oklch(from var(--sf-color-base-source-light)      clamp(0.16, calc(1.18 - l),        0.24) calc(c * 0.5) h);
      --sf-color-success-source-dark:   oklch(from var(--sf-color-success-source-light)   clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h);
      --sf-color-warning-source-dark:   oklch(from var(--sf-color-warning-source-light)   clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h);
      --sf-color-info-source-dark:      oklch(from var(--sf-color-info-source-light)      clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h);
      --sf-color-danger-source-dark:    oklch(from var(--sf-color-danger-source-light)    clamp(0.65, calc(0.95 - l * 0.5), 0.88) calc(c * 0.9) h);
    }

        :is([data-theme="dark"], .sf-theme-dark) {

      --sf-color-primary:   var(--sf-color-primary-source-dark);
      --sf-color-secondary: var(--sf-color-secondary-source-dark);
      --sf-color-tertiary:  var(--sf-color-tertiary-source-dark);
      --sf-color-action:    var(--sf-color-action-source-dark);
      --sf-color-neutral:   var(--sf-color-neutral-source-dark);
      --sf-color-base:      var(--sf-color-base-source-dark);
      --sf-color-surface:   var(--sf-color-base);

      --sf-color-bg:      oklch(from var(--sf-color-base) calc(l + 0.02) c h);
      --sf-color-inset:   oklch(from var(--sf-color-base) calc(l - 0.02) c h);
      --sf-color-raised:  oklch(from var(--sf-color-base) calc(l + 0.04) c h);
      --sf-color-overlay: oklch(from var(--sf-color-base) l c h / 0.9);
      --sf-color-inverse: oklch(from var(--sf-color-base) calc(1 - l) c h);

      --sf-color-success: var(--sf-color-success-source-dark);
      --sf-color-warning: var(--sf-color-warning-source-dark);
      --sf-color-info:    var(--sf-color-info-source-dark);
      --sf-color-danger:  var(--sf-color-danger-source-dark);

      --sf-color-text:              oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1)    c h);
      --sf-color-text--subtle:   oklch(from var(--sf-color-neutral) clamp(0.55, calc(l + 0.1 + var(--sf-contrast-bias)), 0.90) c h);
      --sf-color-text--placeholder: oklch(from var(--sf-color-neutral) clamp(0.35, calc(l - 0.1),  0.65) c h);
      --sf-color-text--disabled:    oklch(from var(--sf-color-neutral) clamp(0.25, calc(l - 0.2),  0.55) c h);
      --sf-color-text--inverse:     oklch(from var(--sf-color-neutral) clamp(0.05, calc(l - 0.4),  0.35) c h);
      --sf-color-heading:           oklch(from var(--sf-color-neutral) clamp(0.70, calc(l + 0.25 + var(--sf-contrast-bias)), 1)    c h);

      --sf-color-border:         oklch(from var(--sf-color-neutral) clamp(0.25, calc(l - 0.3),  0.55) 0.005 h);
      --sf-color-border--subtle: oklch(from var(--sf-color-neutral) clamp(0.20, calc(l - 0.38), 0.45) 0.005 h);
      --sf-color-border--strong: oklch(from var(--sf-color-neutral) clamp(0.38, calc(l - 0.1),  0.65) 0.02  h);

      --sf-color-link:          oklch(from var(--sf-color-action) clamp(0.68, l, 1) c h);
      --sf-color-link--hover:   oklch(from var(--sf-color-action) clamp(0, max(l + 0.10, 0.68), 1) c h);
      --sf-color-link--active:  oklch(from var(--sf-color-action) clamp(0, max(l + 0.15, 0.74), 1) c h);
      --sf-color-link--visited: oklch(from var(--sf-color-action) clamp(0.68, l, 1) c calc(h + 60));

      --sf-color-selection-bg: oklch(from var(--sf-color-action-source-dark) clamp(0.62, calc(0.93 - l * 0.4), 0.78) c h / 0.55);

      --sf-color-success-strong: oklch(from var(--sf-color-success) clamp(0.70, calc(l + 0.15), 1) c h);
      --sf-color-warning-strong: oklch(from var(--sf-color-warning) clamp(0.70, calc(l + 0.05), 1) c h);
      --sf-color-info-strong:    oklch(from var(--sf-color-info)    clamp(0.70, calc(l + 0.15), 1) c h);
      --sf-color-danger-strong:  oklch(from var(--sf-color-danger)  clamp(0.70, calc(l + 0.15), 1) c h);

      /* :root-only aliases from tokens.css — var() resolves on the declaring
         element, so without these re-declarations a nested [data-theme]
         section inherits the :root-resolved (wrong-scheme) values (#496). */
      --sf-color-code-bg:        var(--sf-color-inset);
      --sf-color-bg--disabled:   var(--sf-color-inset);
      --sf-color-link--disabled: var(--sf-color-text--disabled);
      --sf-scrollbar-thumb:      var(--sf-color-neutral);
      /* Non-sign() fallback: keeps inline code readable in engines with
         oklch(from) but not sign() — the sign()-gated auto-contrast block
         below overrides this with a proper per-background contrast calc
         wherever sign() is supported. */
      --sf-color-code-text:      var(--sf-color-text);
    }

        :is([data-theme="light"], .sf-theme-light) {

      --sf-color-primary:   var(--sf-color-primary-source-light);
      --sf-color-secondary: var(--sf-color-secondary-source-light);
      --sf-color-tertiary:  var(--sf-color-tertiary-source-light);
      --sf-color-action:    var(--sf-color-action-source-light);
      --sf-color-neutral:   var(--sf-color-neutral-source-light);
      --sf-color-base:      var(--sf-color-base-source-light);
      --sf-color-surface:   var(--sf-color-base);

      --sf-color-bg:      oklch(from var(--sf-color-base) calc(l + 0.02) c h);
      --sf-color-inset:   oklch(from var(--sf-color-base) calc(l - 0.02) c h);
      --sf-color-raised:  oklch(from var(--sf-color-base) calc(l + 0.04) c h);
      --sf-color-overlay: oklch(from var(--sf-color-base) l c h / 0.9);
      --sf-color-inverse: oklch(from var(--sf-color-base) calc(1 - l) c h);

      --sf-color-success: var(--sf-color-success-source-light);
      --sf-color-warning: var(--sf-color-warning-source-light);
      --sf-color-info:    var(--sf-color-info-source-light);
      --sf-color-danger:  var(--sf-color-danger-source-light);

      --sf-color-text:              oklch(from var(--sf-color-neutral-source-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h);
      --sf-color-text--subtle:   oklch(from var(--sf-color-neutral-source-light) clamp(0.15, calc(l - 0.25 - var(--sf-contrast-bias)), 0.45) c h);
      --sf-color-text--placeholder: oklch(from var(--sf-color-neutral-source-light) clamp(0.45, calc(l + 0.15), 0.75) c h);
      --sf-color-text--disabled:    oklch(from var(--sf-color-neutral-source-light) clamp(0.55, calc(l + 0.25), 0.82) c h);
      --sf-color-text--inverse:     oklch(from var(--sf-color-neutral-source-light) clamp(0.85, calc(l + 0.4),  0.98) c h);
      --sf-color-heading:           oklch(from var(--sf-color-neutral-source-light) clamp(0.05, calc(l - 0.4 - var(--sf-contrast-bias)), 0.35) c h);

      --sf-color-border:         oklch(from var(--sf-color-neutral-source-light) clamp(0.70, calc(l + 0.35), 0.95) 0.005 h);
      --sf-color-border--subtle: oklch(from var(--sf-color-neutral-source-light) clamp(0.75, calc(l + 0.4),  0.97) 0.005 h);
      --sf-color-border--strong: oklch(from var(--sf-color-neutral-source-light) clamp(0.55, calc(l + 0.1),  0.85) 0.02  h);

      --sf-color-link:          oklch(from var(--sf-color-action) clamp(0, min(l - 0.07, 0.48), 1) c h);
      --sf-color-link--hover:   oklch(from var(--sf-color-action) clamp(0, min(l - 0.15, 0.40), 1) c h);
      --sf-color-link--active:  oklch(from var(--sf-color-action) clamp(0, min(l - 0.21, 0.34), 1) c h);
      --sf-color-link--visited: oklch(from var(--sf-color-action) clamp(0, min(l - 0.07, 0.48), 1) c calc(h + 60));

      --sf-color-selection-bg: oklch(from var(--sf-color-action-source-light) l c h / 0.28);

      --sf-color-success-strong: oklch(from var(--sf-color-success-source-light) calc(l - 0.15) c h);
      --sf-color-warning-strong: oklch(from var(--sf-color-warning-source-light) calc(l - 0.25) c h);
      --sf-color-info-strong:    oklch(from var(--sf-color-info-source-light)    calc(l - 0.1)  c h);
      --sf-color-danger-strong:  oklch(from var(--sf-color-danger-source-light)  calc(l - 0.1)  c h);

      /* :root-only aliases — see the matching note in the dark block (#496). */
      --sf-color-code-bg:        var(--sf-color-inset);
      --sf-color-bg--disabled:   var(--sf-color-inset);
      --sf-color-link--disabled: var(--sf-color-text--disabled);
      --sf-scrollbar-thumb:      var(--sf-color-neutral);
      /* Non-sign() fallback — see matching note in the dark block (#496). */
      --sf-color-code-text:      var(--sf-color-text);
    }

  }

  @supports (color: oklch(from red l c h)) and (width: calc(1px * sign(1))) {
    :is([data-theme="dark"], .sf-theme-dark) {
      --sf-color-text--on-primary:   oklch(from var(--sf-color-primary)   clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-secondary: oklch(from var(--sf-color-secondary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-tertiary:  oklch(from var(--sf-color-tertiary)  clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-action:    oklch(from var(--sf-color-action)    clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-neutral:   oklch(from var(--sf-color-neutral)   clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-success:   oklch(from var(--sf-color-success)   clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-warning:   oklch(from var(--sf-color-warning)   clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-info:      oklch(from var(--sf-color-info)      clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-danger:    oklch(from var(--sf-color-danger)    clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-inverse:   oklch(from var(--sf-color-inverse)   clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      /* Auto-contrast code text re-derived in-scope so it tracks the
         per-scope --sf-color-code-bg above (#496). Same formula as
         core/tokens.css — keep in sync. */
      --sf-color-code-text:          oklch(from var(--sf-color-code-bg)   clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
    }
    :is([data-theme="light"], .sf-theme-light) {
      --sf-color-text--on-primary:   oklch(from var(--sf-color-primary)   clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-secondary: oklch(from var(--sf-color-secondary) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-tertiary:  oklch(from var(--sf-color-tertiary)  clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-action:    oklch(from var(--sf-color-action)    clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-neutral:   oklch(from var(--sf-color-neutral)   clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-success:   oklch(from var(--sf-color-success)   clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-warning:   oklch(from var(--sf-color-warning)   clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-info:      oklch(from var(--sf-color-info)      clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-danger:    oklch(from var(--sf-color-danger)    clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      --sf-color-text--on-inverse:   oklch(from var(--sf-color-inverse)   clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
      /* See matching note in the dark block (#496). */
      --sf-color-code-text:          oklch(from var(--sf-color-code-bg)   clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0);
    }
  }

  /* OPT-IN THEME CROSS-FADE */
  .sf-theme-transition {
    transition:
      --sf-color-primary   var(--sf-theme-transition-duration) var(--sf-ease-out),
      --sf-color-secondary var(--sf-theme-transition-duration) var(--sf-ease-out),
      --sf-color-tertiary  var(--sf-theme-transition-duration) var(--sf-ease-out),
      --sf-color-action    var(--sf-theme-transition-duration) var(--sf-ease-out),
      --sf-color-neutral   var(--sf-theme-transition-duration) var(--sf-ease-out),
      --sf-color-base      var(--sf-theme-transition-duration) var(--sf-ease-out),
      --sf-color-success   var(--sf-theme-transition-duration) var(--sf-ease-out),
      --sf-color-warning   var(--sf-theme-transition-duration) var(--sf-ease-out),
      --sf-color-info      var(--sf-theme-transition-duration) var(--sf-ease-out),
      --sf-color-danger    var(--sf-theme-transition-duration) var(--sf-ease-out);
  }

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

}

/* ─── core/layout.css ─── */
/* SLASHED — core/layout.css
   @layer slashed.layout
   Layout primitives: section, container, stack, cluster, grid, sidebar, switcher, frame.
   Prefix: .sf-*; local rhythm via --sf-*-gap / sizing tokens.
   SL-005: every @container query in this file hardcodes its breakpoint
   instead of referencing a --sf-* token — var() is not allowed inside an
   @container condition per the CSS spec. This applies framework-wide to
   every @container site below; not re-explained at each one.
   SL-005a: those hardcoded breakpoints are expressed in REM, never em.
   Inside an @container condition a font-relative em resolves against the
   QUERY CONTAINER's own computed font-size — and under SLASHED's default
   fluid type that font-size is inherited from body (a clamp() over 100vw,
   ~16–20px), so an em breakpoint would silently slide with the VIEWPORT and
   break the "responds to its own container width, not the viewport" contract
   (docs/architecture.md). rem resolves against the ROOT (:root/html stays a
   fixed 16px; fluid type is applied to body, not html), so the breakpoint is
   viewport-independent while still honouring user zoom / root-font-size
   preferences. Note this is the OPPOSITE of the @media convention, where em
   and rem both resolve against the root — do not copy "use em for a11y
   breakpoints" from @media into @container. (Matches Tailwind, whose
   container-query breakpoints are rem; cf. issue #497 fluid-cq work.)
   SL-034: a query container cannot be the subject of its own @container
   query — conditions only ever match descendants of the nearest ancestor
   container, never the element that establishes it (self-querying is a
   silent no-op, not an error). Primitives that need to resize THEMSELVES
   (.sf-grid-cols-2/3/4/6, .sf-bento) must therefore rely on an ancestor
   container (.sf-container / .sf-cq / .sf-fluid-cq) rather than declaring
   `container` on themselves. .sf-grid-cols-1-2 etc. already follow this —
   they've never declared their own `container` — so they were unaffected.
   .sf-alternate is a different, unrelated-but-similar-looking pattern: it
   DOES declare `container` on itself, but only to restyle its CHILDREN
   (`.sf-alternate > *`), which is not self-querying and is fine. Not
   re-explained at each site below. */

@layer slashed.layout {

  /* Section */
  .sf-section     { padding-block: var(--sf-section-pad); }
  .sf-section--xs { --sf-section-pad: var(--sf-section-pad--xs); }
  .sf-section--s  { --sf-section-pad: var(--sf-section-pad--s); }
  .sf-section--m  { --sf-section-pad: var(--sf-section-pad--m); }
  .sf-section--l  { --sf-section-pad: var(--sf-section-pad--l); }
  .sf-section--xl { --sf-section-pad: var(--sf-section-pad--xl); }
  .sf-section--2xl { --sf-section-pad: var(--sf-section-pad--2xl); }

  .sf-section--guttered { padding-inline: var(--sf-gutter); }
  .sf-section--guttered .sf-container { padding-inline: 0; }
  /* .sf-content-grid carries the gutter in its own edge tracks
     (minmax(gutter,1fr)), not in padding — so unlike .sf-container it can't be
     de-duplicated by zeroing its padding (it has none). Left alone, the
     section's padding boxes the grid in: content ends up double-guttered and
     .sf-full-bleed stops at the padding edge instead of the section edge.
     Drop the section's own padding so the grid spans edge-to-edge and its
     tracks provide the single gutter; full-bleed then reaches the true edge.
     Only when the content grid is the SOLE direct child: dropping the section
     padding is right for the grid but would strip the gutter from any non-grid
     sibling (a heading, a plain block) that legitimately relies on it — and
     those two wants are physically irreconcilable (the gutter *is* the section
     padding that full-bleed must escape). So we restrict to the unambiguous
     case; a mixed section keeps its padding and behaves exactly as before this
     rule existed. Direct-child scope also stops a nested grid deeper in the
     section from stripping the gutter. */
  .sf-section--guttered:has(> .sf-content-grid):not(:has(> :not(.sf-content-grid))) {
    padding-inline: 0;
  }

  .sf-section-group > .sf-section + .sf-section { padding-block-start: 0; }

  /* Header — block padding on the standard spacing scale. Headers usually
     want lighter vertical rhythm than .sf-section, so these map to
     --sf-space-* directly. Only block padding is set; any inline gutter
     (from .sf-section--guttered, a container, or the element itself) is
     left intact. */
  .sf-header--xs { padding-block: var(--sf-space-xs); }
  .sf-header--s  { padding-block: var(--sf-space-s); }
  .sf-header--m  { padding-block: var(--sf-space-m); }
  .sf-header--l  { padding-block: var(--sf-space-l); }
  .sf-header--xl { padding-block: var(--sf-space-xl); }

  /* Collapse modifier */
  .sf-section--collapse:has(+ .sf-section--collapse) {
    padding-block-end: calc(var(--sf-section-pad) / 2);
  }
  .sf-section--collapse + .sf-section--collapse {
    padding-block-start: calc(var(--sf-section-pad) / 2);
  }

  .sf-divider {
    inline-size:        100%;
    border:             none;
    border-block-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);
    margin-block:       var(--sf-divider-gap);
  }
  .sf-divider--vertical {
    inline-size:          auto;
    border-block-start:   none;
    border-inline-start:  var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);
    margin-block:         0;
    margin-inline:        var(--sf-divider-gap);
    align-self:           stretch;
  }

  .sf-divider--soft   { --sf-divider-color: var(--sf-color-border--subtle); }
  .sf-divider--strong { --sf-divider-color: var(--sf-color-border--strong); }
  .sf-divider--dashed { --sf-divider-style: dashed; }
  .sf-divider--dotted { --sf-divider-style: dotted; }

  .sf-divider--gradient {
    border:        none;
    block-size:    var(--sf-divider-width);
    background:    linear-gradient(to right, transparent, var(--sf-divider-color), transparent);
  }

  /* Divide — rules between direct children, reusing .sf-divider's tokens.
     Unlike .sf-divider (a standalone separator element), .sf-divide is
     applied to the container and needs no extra markup per gap. */
  .sf-divide > * + * {
    border-block-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);
  }
  .sf-divide--vertical > * + * {
    border-block-start:  none;
    border-inline-start: var(--sf-divider-width) var(--sf-divider-style) var(--sf-divider-color);
  }


  /* Container */
  .sf-container {
    container:      sf-layout / inline-size;
    width:          100%;
    max-width:      var(--sf-container-default);
    margin-inline:  auto;
    padding-inline: var(--sf-gutter);
  }
  .sf-container--narrow { max-width: var(--sf-container-narrow); }
  .sf-container--prose  { max-width: var(--sf-container-prose); }
  .sf-container--wide   { max-width: var(--sf-container-wide); }
  .sf-container--full   { max-width: var(--sf-container-full); }

  /* .sf-cq: CQ scope without .sf-container's max-width/centering.
     Use when a CQ-dependent primitive needs a container ancestor but no layout chrome. */
  .sf-cq {
    container-type: inline-size;
  }

  /* Overlay — absolute-fill layout helper for overlays/backdrops. */
  .sf-overlay {
    position: absolute;
    inset:    0;
  }

  
  /* .sf-fluid-cq: opt a subtree into a CONTAINER-relative fluid scale (issue
     #497). By default --sf-text-* / --sf-space-* interpolate against 100vw (the
     viewport); inside .sf-fluid-cq they interpolate against 100cqi (this box's
     inline size), so type + spacing track the container the way the layout
     primitives (.sf-grid-cols-*, .sf-content-grid, .sf-bento) already do. Use
     on nested / narrow contexts (sidebars, cards, 1/3 columns) where a
     viewport-driven scale would size text as if it spanned the full page.

     Why the tokens are RE-DECLARED (not just --sf-fluid-width flipped): the
     --sf-text-* / --sf-space-* tokens are computed at :root, so their
     var(--sf-fluid-width) is substituted there (100vw) and the *resolved*
     length inherits down — overriding --sf-fluid-width lower in the tree can't
     retroactively rewrite an already-computed inherited value. The scale is
     therefore re-declared in-scope so var(--sf-fluid-width) re-resolves to
     100cqi. It is re-declared on the direct children, not the element itself:
     a query container's own cq units resolve against its ANCESTOR container, so
     the re-computation must happen one level in — children compute against
     .sf-fluid-cq and deeper descendants inherit that resolved value.

     The formulas below are character-identical to their :root twins in
     core/tokens.css (both read var(--sf-fluid-width)); keep the two copies in
     sync if the generative formula ever changes (cf. themes.css SL-001). */
  .sf-fluid-cq { container-type: inline-size; }
  .sf-fluid-cq > * {
      --sf-fluid-width: 100cqi;
      --sf-text-2xs: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -3) * 1rem)) * var(--sf-text-scale));
      --sf-text-xs: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -2) * 1rem)) * var(--sf-text-scale));
      --sf-text-s: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), -1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), -1) * 1rem)) * var(--sf-text-scale));
      --sf-text-m: calc(clamp(calc(var(--sf-text-base-min) * 1rem), calc((var(--sf-text-base-max) - var(--sf-text-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * 1rem), calc(var(--sf-text-base-max) * 1rem)) * var(--sf-text-scale));
      --sf-text-l: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-scale));
      --sf-text-xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-scale));
      --sf-text-2xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 3) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 3) * 1rem)) * var(--sf-text-scale));
      --sf-text-3xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 4) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 4) * 1rem)) * var(--sf-text-scale));
      --sf-text-4xl: calc(clamp(calc(var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc((var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) - var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-base-min) * pow(var(--sf-text-ratio-min), 5) * 1rem), calc(var(--sf-text-base-max) * pow(var(--sf-text-ratio-max), 5) * 1rem)) * var(--sf-text-scale));
      --sf-text-display-s: calc(clamp(calc(var(--sf-text-display-base-min) * 1rem), calc((var(--sf-text-display-base-max) - var(--sf-text-display-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * 1rem), calc(var(--sf-text-display-base-max) * 1rem)) * var(--sf-text-display-scale));
      --sf-text-display-m: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 1) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 1) * 1rem)) * var(--sf-text-display-scale));
      --sf-text-display-l: calc(clamp(calc(var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc((var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) - var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-text-display-base-min) * pow(var(--sf-text-ratio-min), 2) * 1rem), calc(var(--sf-text-display-base-max) * pow(var(--sf-text-ratio-max), 2) * 1rem)) * var(--sf-text-display-scale));
      --sf-space-2xs: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -3) * 1rem)) * var(--sf-space-scale));
      --sf-space-xs: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -2) * 1rem)) * var(--sf-space-scale));
      --sf-space-s: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), -1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), -1) * 1rem)) * var(--sf-space-scale));
      --sf-space-m: calc(clamp(calc(var(--sf-space-base-min) * 1rem), calc((var(--sf-space-base-max) - var(--sf-space-base-min)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * 1rem), calc(var(--sf-space-base-max) * 1rem)) * var(--sf-space-scale));
      --sf-space-l: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 1) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 1) * 1rem)) * var(--sf-space-scale));
      --sf-space-xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 2) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 2) * 1rem)) * var(--sf-space-scale));
      --sf-space-2xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 3) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 3) * 1rem)) * var(--sf-space-scale));
      --sf-space-3xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 4) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 4) * 1rem)) * var(--sf-space-scale));
      --sf-space-4xl: calc(clamp(calc(var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc((var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) - var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5)) / (var(--sf-fluid-max-vw) - var(--sf-fluid-min-vw)) * (var(--sf-fluid-width) - var(--sf-fluid-min-vw) * 1rem) + var(--sf-space-base-min) * pow(var(--sf-space-ratio-min), 5) * 1rem), calc(var(--sf-space-base-max) * pow(var(--sf-space-ratio-max), 5) * 1rem)) * var(--sf-space-scale));
  }


  /* Stack & gap */
  .sf-stack {
    display:        flex;
    flex-direction: column;
    gap:            var(--sf-stack-gap);
  }
  .sf-stack--xs  { gap: var(--sf-space-xs); }
  .sf-stack--s   { gap: var(--sf-space-s); }
  .sf-stack--m   { gap: var(--sf-space-m); }
  .sf-stack--l   { gap: var(--sf-space-l); }
  .sf-stack--xl  { gap: var(--sf-space-xl); }
  .sf-stack--2xl { gap: var(--sf-space-2xl); }

  .sf-stack--center  { align-items: center; }
  .sf-stack--end     { align-items: flex-end; }
  .sf-stack--stretch { align-items: stretch; }

  .sf-gap { gap: var(--sf-gap); }
  .sf-gap--xs  { gap: var(--sf-space-xs); }
  .sf-gap--s   { gap: var(--sf-space-s); }
  .sf-gap--m   { gap: var(--sf-space-m); }
  .sf-gap--l   { gap: var(--sf-space-l); }
  .sf-gap--xl  { gap: var(--sf-space-xl); }
  .sf-gap--2xl { gap: var(--sf-space-2xl); }

  
  /* Box */
  .sf-box {
    padding: var(--sf-box-padding);
    outline: var(--sf-box-border-width) solid var(--sf-box-border-color);
  }

  
  /* Center */
  .sf-center {
    box-sizing:      content-box;
    width:           auto;
    margin-inline:   auto;
    max-inline-size: var(--sf-center-max);
    padding-inline:  var(--sf-center-gutter);
  }

  .sf-center--intrinsic {
    display:        flex;
    flex-direction: column;
    align-items:    center;
  }

  /* Place-center — the box-neutral centring primitive. Unlike .sf-center (which
     centres ITSELF as a max-width wrapper) this centres an element's CONTENT on
     both axes, and unlike the flow primitives' --center modifiers it imposes no
     row/column/wrap semantics: a plain element becomes a centring box. It is
     the framework's named form of the community `display:grid; place-items:
     center` idiom (cf. Tailwind's place-items-center). Give the box a size
     (min-block-size / height) for the block-axis centring to have room. */
  .sf-place-center {
    display:     grid;
    place-items: center;
  }


  /* Cluster */
  .sf-cluster {
    display:         flex;
    flex-wrap:       wrap;
    gap:             var(--sf-cluster-gap);
    align-items:     var(--sf-cluster-align);
    justify-content: var(--sf-cluster-justify);
  }
  .sf-cluster--xs      { gap: var(--sf-space-xs); }
  .sf-cluster--s       { gap: var(--sf-space-s); }
  .sf-cluster--m       { gap: var(--sf-space-m); }
  .sf-cluster--l       { gap: var(--sf-space-l); }
  .sf-cluster--xl      { gap: var(--sf-space-xl); }
  .sf-cluster--2xl     { gap: var(--sf-space-2xl); }

  .sf-cluster--no-wrap  { flex-wrap: nowrap; }
  .sf-cluster--center   { justify-content: center; }
  .sf-cluster--end      { justify-content: flex-end; }
  .sf-cluster--between  { justify-content: space-between; }

  
  /* Sidebar */
  .sf-sidebar {
    display:   flex;
    flex-wrap: wrap;
    gap:       var(--sf-sidebar-gap);
  }

  .sf-sidebar > :first-child {
    flex-basis: var(--sf-sidebar-width);
    flex-grow:  1;
  }

  .sf-sidebar > :last-child {
    flex-basis:      0;
    flex-grow:       999;
    min-inline-size: var(--sf-sidebar-min-width);
  }

  .sf-sidebar--right > :first-child {
    flex-basis:      0;
    flex-grow:       999;
    min-inline-size: var(--sf-sidebar-min-width);
  }

  .sf-sidebar--right > :last-child {
    flex-basis: var(--sf-sidebar-width);
    flex-grow:  1;
  }

  .sf-sidebar--narrow { --sf-sidebar-width: 12rem; }
  .sf-sidebar--wide   { --sf-sidebar-width: 26rem; }

  
  /* Switcher */
  .sf-switcher {
    display:   flex;
    flex-wrap: wrap;
    gap:       var(--sf-switcher-gap);
  }

  .sf-switcher > * {
    flex-grow:  1;
    flex-basis: calc((var(--sf-switcher-threshold) - 100%) * 999);
  }

  .sf-switcher--no-wrap  { flex-wrap: nowrap; overflow-x: auto; overflow-inline: auto; }
  .sf-switcher--vertical { flex-direction: column; }

  
  /* Grid & icon */
  .sf-grid {
    display:               grid;
    grid-template-columns: repeat(auto-fill, minmax(min(var(--sf-grid-min), 100%), 1fr));
    gap:                   var(--sf-grid-gap);
  }

  .sf-grid--fit {
    grid-template-columns: repeat(auto-fit, minmax(min(var(--sf-grid-min), 100%), 1fr));
  }

  .sf-grid--xs { --sf-grid-min: var(--sf-grid-min-xs); }
  .sf-grid--s  { --sf-grid-min: var(--sf-grid-min-s); }
  .sf-grid--m  { --sf-grid-min: var(--sf-grid-min-m); }
  .sf-grid--l  { --sf-grid-min: var(--sf-grid-min-l); }
  .sf-grid--xl { --sf-grid-min: var(--sf-grid-min-xl); }
  .sf-grid--2xl { --sf-grid-min: var(--sf-grid-min-2xl); }

  .sf-grid--dense { grid-auto-flow: dense; }

  /* Grid-flex — flexbox alternative to .sf-grid for item counts that
     don't divide evenly: leftover last-row items stretch to fill the
     row by default, or stay fixed-width and centered with --center.
     CSS Grid keeps orphans pinned to their track; flex does not. */
  .sf-grid-flex {
    display:   flex;
    flex-wrap: wrap;
    gap:       var(--sf-grid-gap);
  }
  .sf-grid-flex > * {
    flex: 1 1 min(var(--sf-grid-min), 100%);
  }

  .sf-grid-flex--center { justify-content: center; }
  .sf-grid-flex--center > * {
    flex-grow:  0;
    flex-basis: min(var(--sf-grid-min), 100%);
  }

  .sf-grid-flex--xs  { --sf-grid-min: var(--sf-grid-min-xs); }
  .sf-grid-flex--s   { --sf-grid-min: var(--sf-grid-min-s); }
  .sf-grid-flex--m   { --sf-grid-min: var(--sf-grid-min-m); }
  .sf-grid-flex--l   { --sf-grid-min: var(--sf-grid-min-l); }
  .sf-grid-flex--xl  { --sf-grid-min: var(--sf-grid-min-xl); }
  .sf-grid-flex--2xl { --sf-grid-min: var(--sf-grid-min-2xl); }

  .sf-icon {
    display:        inline-block;
    inline-size:    var(--sf-icon-size, var(--sf-icon-m));
    block-size:     var(--sf-icon-size, var(--sf-icon-m));
    flex-shrink:    0;
    vertical-align: text-bottom;
    fill:           currentcolor;
  }
  .sf-icon--xs  { --sf-icon-size: var(--sf-icon-xs); }
  .sf-icon--s   { --sf-icon-size: var(--sf-icon-s); }
  .sf-icon--m   { --sf-icon-size: var(--sf-icon-m); }
  .sf-icon--l   { --sf-icon-size: var(--sf-icon-l); }
  .sf-icon--xl  { --sf-icon-size: var(--sf-icon-xl); }
  .sf-icon--2xl { --sf-icon-size: var(--sf-icon-2xl); }

  .sf-icon--boxed {
    inline-size:    calc(var(--sf-icon-size, var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);
    block-size:     calc(var(--sf-icon-size, var(--sf-icon-m)) + var(--sf-icon-box-pad) * 2);
    padding:        var(--sf-icon-box-pad);
    border:         var(--sf-icon-box-border);
    border-radius:  var(--sf-icon-box-radius);
    background:     var(--sf-icon-box-bg);
    box-sizing:     border-box;
  }

  
  /* Cover */
  .sf-cover {
    display:        flex;
    flex-direction: column;
    min-height:     var(--sf-cover-min-height);
    padding-block:  var(--sf-cover-padding);
  }

  .sf-cover > .sf-cover__center {
    margin-block: auto;
  }

  .sf-cover--min { min-height: 50dvh; }

  .sf-cover--max { min-height: 0; max-block-size: 100dvh; }
  .sf-cover--padding-s { padding-block: var(--sf-space-xl); }
  .sf-cover--padding-l { padding-block: var(--sf-space-4xl); }

  
  /* Frame */
  .sf-frame {
    aspect-ratio:    var(--sf-frame-ratio);
    overflow:        hidden;
    display:         flex;
    justify-content: center;
    align-items:     center;
  }

  .sf-frame > img,
  .sf-frame > video {
    inline-size: 100%;
    block-size:  100%;
    object-fit:  cover;
  }

  .sf-frame--square   { aspect-ratio: var(--sf-ratio-square); }
  .sf-frame--video    { aspect-ratio: var(--sf-ratio-video); }
  .sf-frame--cinema   { aspect-ratio: var(--sf-ratio-cinema); }
  .sf-frame--portrait { aspect-ratio: var(--sf-ratio-portrait); }
  .sf-frame--4-3      { aspect-ratio: var(--sf-ratio-4-3); }
  .sf-frame--3-2      { aspect-ratio: var(--sf-ratio-3-2); }
  .sf-frame--golden   { aspect-ratio: var(--sf-ratio-golden); }


  /* Background layer — absolutely-positioned cover media that sits behind a
     parent's content. The parent is auto-promoted to a positioned, isolated
     stacking context via :has(), so .sf-bg-layer composes natively under .sf-scrim
     (background → scrim gradient → content). Works as a bare <img class="sf-bg-layer">
     or as a wrapper with a nested > img / > video. */
  :where(:has(> .sf-bg-layer)) {
    position:  relative;
    isolation: isolate;
  }

  .sf-bg-layer {
    position:        absolute;
    inset:           var(--sf-bg-layer-inset);
    inline-size:     calc(100% - 2 * var(--sf-bg-layer-inset));
    block-size:      calc(100% - 2 * var(--sf-bg-layer-inset));
    object-fit:      var(--sf-bg-layer-fit);
    object-position: var(--sf-bg-layer-position);
    border-radius:   var(--sf-bg-layer-radius);
    z-index:         var(--sf-bg-layer-z);
    pointer-events:  none;
  }

  .sf-bg-layer > :is(img, video, picture) {
    inline-size:     100%;
    block-size:      100%;
    object-fit:      var(--sf-bg-layer-fit);
    object-position: var(--sf-bg-layer-position);
    border-radius:   var(--sf-bg-layer-radius);
    pointer-events:  none;
  }

  .sf-bg-layer > picture > img {
    inline-size:     100%;
    block-size:      100%;
    object-fit:      var(--sf-bg-layer-fit);
    object-position: var(--sf-bg-layer-position);
    border-radius:   var(--sf-bg-layer-radius);
    pointer-events:  none;
  }


  .sf-reel {
    display:               flex;
    block-size:            var(--sf-reel-height);
    gap:                   var(--sf-reel-gap);
    overflow-x:                 auto;
    overflow-inline:            auto;
    scroll-snap-type:           inline mandatory;
    scroll-padding-inline:      var(--sf-reel-gap);
    overscroll-behavior-inline: contain;
    scrollbar-width:       thin;
    scrollbar-color:       var(--sf-scrollbar-thumb) var(--sf-scrollbar-track);
  }

  .sf-reel::-webkit-scrollbar        { block-size: 6px; }
  .sf-reel::-webkit-scrollbar-track  { background: var(--sf-scrollbar-track); border-radius: var(--sf-radius-full); }
  .sf-reel::-webkit-scrollbar-thumb  { background: var(--sf-scrollbar-thumb); border-radius: var(--sf-radius-full); }

  .sf-reel > * {
    flex:              0 0 var(--sf-reel-item-width);
    scroll-snap-align: start;
  }

  
  /* Physical left (not inset-inline-start): translate() uses the physical axis,
     so logical + translate breaks centering in RTL layouts. */
  .sf-imposter {
    position:          absolute;
    inset-block-start: 50%;
    left:              50%;
    translate:         -50% -50%;
  }

  .sf-imposter--fixed {
    position: fixed;
  }

  .sf-imposter--contain {
    max-inline-size: calc(100% - var(--sf-imposter-margin) * 2);
    max-block-size:  calc(100% - var(--sf-imposter-margin) * 2);
    overflow:        auto;
  }

  
  .sf-alternate {
    display:   grid;
    gap:       var(--sf-alternate-gap);
    container: sf-alternate / inline-size;
  }

  .sf-alternate > * {
    display:     grid;
    gap:         var(--sf-alternate-inner-gap);
    align-items: center;
  }

  @container sf-alternate (min-width: 48rem) {
    .sf-alternate > * {
      grid-template-columns: 1fr 1fr;
    }
    .sf-alternate > :nth-child(even) > :first-child { order: 2; }
    .sf-alternate > :nth-child(even) > :last-child  { order: 1; }
  }

  
  .sf-pancake {
    display:             grid;
    grid-template-rows:  auto 1fr auto;
    min-height:          100dvh;
  }

  
  .sf-grid-cols-1 {
    display: grid;
    gap:     var(--sf-grid-gap);
  }

  
  .sf-grid-cols-2, .sf-grid-cols-3, .sf-grid-cols-4, .sf-grid-cols-6 {
    display: grid;
    gap:     var(--sf-grid-gap);
  }

  @container (min-width: 30rem) {
    .sf-grid-cols-4 { grid-template-columns: repeat(2, 1fr); }
    .sf-grid-cols-6 { grid-template-columns: repeat(3, 1fr); }
  }

  @container (min-width: 48rem) {
    .sf-grid-cols-2 { grid-template-columns: repeat(2, 1fr); }
    .sf-grid-cols-3 { grid-template-columns: repeat(3, 1fr); }
    .sf-grid-cols-4 { grid-template-columns: repeat(4, 1fr); }
    .sf-grid-cols-6 { grid-template-columns: repeat(6, 1fr); }
  }

  
  .sf-grid-cols-1-2, .sf-grid-cols-2-1,
  .sf-grid-cols-1-3, .sf-grid-cols-3-1 {
    display: grid;
    gap:     var(--sf-grid-gap);
  }

  @container (min-width: 48rem) {
    .sf-grid-cols-1-2 { grid-template-columns: 1fr 2fr; }
    .sf-grid-cols-2-1 { grid-template-columns: 2fr 1fr; }
    .sf-grid-cols-1-3 { grid-template-columns: 1fr 3fr; }
    .sf-grid-cols-3-1 { grid-template-columns: 3fr 1fr; }
  }

  .sf-equal {
    column-width: var(--sf-equal-min-col);
    column-gap:   var(--sf-equal-gap);
    column-rule:  var(--sf-equal-rule-width) var(--sf-equal-rule-style) var(--sf-equal-rule-color);
  }
  .sf-equal > * { break-inside: avoid; }

  .sf-equal--2 { column-count: 2; }
  .sf-equal--3 { column-count: 3; }
  .sf-equal--4 { column-count: 4; }
  .sf-equal--6 { column-count: 6; }

  
  .sf-content-grid {
    display: grid;
    /* Establish the same NAMED inline-size query container as .sf-container
       (`sf-layout`), not just an anonymous one. .sf-content-grid is a
       section-level wrapper that REPLACES .sf-container when you need
       breakout/full-bleed — but .sf-container carries the CQ scope that
       .sf-grid-cols-* / .sf-bento rely on, and dropping it silently killed
       those primitives inside a content grid. Matching the name too means a
       user's `@container sf-layout (…)` rule keeps matching across the swap, so
       the two wrappers are genuinely interchangeable (anonymous
       `@container (…)` queries already matched either — the framework's own
       primitives use those — but a name-targeted query would not have).
       Safe re SL-034: .sf-content-grid's own tracks are fixed (they never
       @container-query themselves), so becoming a query container can't create
       a self-query. Safe re full-bleed: inline-size containment doesn't change
       track resolution — the `full` track still reaches the wrapper's edges. */
    container: sf-layout / inline-size;
    grid-template-columns:
      [full-start]
      minmax(var(--sf-gutter), 1fr)
      [breakout-start]
      minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2))
      [content-start]
      min(var(--sf-content-width), 100% - var(--sf-gutter) * 2)
      [content-end]
      minmax(0, calc((var(--sf-breakout-width) - var(--sf-content-width)) / 2))
      [breakout-end]
      minmax(var(--sf-gutter), 1fr)
      [full-end];
  }

  .sf-content-grid > *              { grid-column: content; }
  .sf-content-grid > .sf-breakout   { grid-column: breakout; }
  .sf-content-grid > .sf-full-bleed { grid-column: full; }

  .sf-bento {
    display:               grid;
    grid-template-columns: repeat(var(--sf-bento-cols, var(--sf-bento-cols-default)), 1fr);
    grid-auto-rows:        minmax(var(--sf-bento-row, var(--sf-bento-row-default)), auto);
    grid-auto-flow:        dense;
    gap:                   var(--sf-bento-gap);
  }

  @container (max-width: 29.99rem) {
    .sf-bento { grid-template-columns: 1fr; }
  }

  @container (min-width: 30rem) and (max-width: 47.99rem) {
    .sf-bento { grid-template-columns: repeat(2, 1fr); }
  }

  /* Container column modifiers */
  .sf-bento--2       { --sf-bento-cols: 2; }
  .sf-bento--3       { --sf-bento-cols: 3; }
  .sf-bento--6       { --sf-bento-cols: 6; }

  /* Container row height modifiers — named after the --sf-bento-row-* token
     they set, not just "tall"/"compact", so they can't be mistaken for the
     child span modifier below. */
  .sf-bento--row-compact { --sf-bento-row: var(--sf-bento-row-compact); }
  .sf-bento--row-tall    { --sf-bento-row: var(--sf-bento-row-tall); }

  /* Child span modifiers — go on an item inside .sf-bento, never on
     .sf-bento itself. .sf-bento-tall (this item spans 2 rows) is not an
     alias of .sf-bento--row-tall above (every row in the grid is taller). */
  .sf-bento-wide     { grid-column: span 2; }
  .sf-bento-full     { grid-column: 1 / -1; }
  .sf-bento-tall     { grid-row:    span 2; }
  .sf-bento-featured { grid-column: span 2; grid-row: span 2; }

  /* A spanning child can't request more columns than the 1-column mobile
     grid has, or CSS Grid adds an implicit column to satisfy the span,
     breaking the single-column layout the query above exists to guarantee.
     Placed after the modifiers above (equal specificity) so it wins on
     source order regardless of query-match state. */
  @container (max-width: 29.99rem) {
    .sf-bento-wide, .sf-bento-featured { grid-column: span 1; }
  }

  
  .sf-subgrid      { display: grid; grid-template-columns: subgrid; }
  .sf-subgrid-rows { display: grid; grid-template-rows:    subgrid; }

}

/* ─── core/macros.css ─── */
/* SLASHED — core/macros.css
   @layer slashed.macros
   Reusable recipes: prose, surface, flow, cards, media, hero, scrims, clamps.
   Prefix: .sf-*; values resolve through macro tokens. */

@layer slashed.macros {

  
  .sf-prose {
    max-width:     var(--sf-container-prose);
    overflow-wrap: break-word;
  }

  .sf-prose > * + *              { margin-block-start: var(--sf-prose-paragraph); }
  .sf-prose :is(h2, h3, h4, h5, h6) + * { margin-block-start: var(--sf-prose-heading-gap); }
  .sf-prose li + li              { margin-block-start: var(--sf-prose-list-gap); }
  .sf-prose :is(blockquote, pre) { margin-block: var(--sf-prose-block-margin); }
  .sf-prose blockquote           { font-style: italic; padding-inline-start: var(--sf-prose-blockquote-padding); border-inline-start: var(--sf-prose-blockquote-border); }
  .sf-prose img                  { margin-block: var(--sf-prose-media-margin); border-radius: var(--sf-prose-media-radius); }
  .sf-prose :is(ul, ol)          { padding-inline-start: 1.25em; }
  .sf-prose > :is(ul, ol)        { margin-block-end: 0; }
  .sf-prose :is(ul, ol) :is(ul, ol) { margin-block-start: var(--sf-prose-nested-list-gap); }
  .sf-prose ul                   { list-style: disc; }
  .sf-prose ol                   { list-style: decimal; }
  .sf-prose ::marker             { color: var(--sf-prose-marker-color); }
  .sf-prose hr                   { margin-block: var(--sf-prose-hr-margin); }
  .sf-prose figure               { margin-block: var(--sf-prose-figure-margin); }
  .sf-prose figcaption           { font-size: var(--sf-prose-figcaption-size); color: var(--sf-color-text--muted); margin-block-start: var(--sf-prose-list-gap); }
  .sf-prose table                { margin-block: var(--sf-prose-block-margin); display: block; overflow-x: auto; overflow-inline: auto; inline-size: 100%; max-inline-size: 100%; }
  .sf-prose :is(th, td)          { padding: var(--sf-prose-table-pad); }
  .sf-prose video                { margin-block: var(--sf-prose-media-margin); border-radius: var(--sf-prose-media-radius); }

  /* Specificity 0-2-1 intentionally beats .sf-prose's 0-2-0 child selectors —
     documented exception. */
  .sf-prose .sf-not-prose > * + *    { margin-block-start: 0; }
  .sf-prose .sf-not-prose li + li    { margin-block-start: 0; }

  .sf-prose .sf-not-prose :is(ul,ol) { list-style: revert; padding-inline-start: revert; }
  .sf-prose .sf-not-prose ::marker   { color: inherit; }
  .sf-prose .sf-not-prose blockquote {
    margin-block: 0;
    padding-inline-start: 0;
    border-inline-start: 0;
    font-style: normal;
  }
  .sf-prose .sf-not-prose hr         { margin-block: 0; }
  .sf-prose .sf-not-prose img        { margin-block: 0; border-radius: 0; }
  .sf-prose .sf-not-prose figure     { margin-block: 0; }
  .sf-prose .sf-not-prose figcaption { font-size: inherit; color: inherit; margin-block-start: 0; }
  .sf-prose .sf-not-prose table      { margin-block: 0; display: revert; overflow-x: revert; overflow-inline: revert; inline-size: revert; max-inline-size: revert; }
  .sf-prose .sf-not-prose :is(th,td) { padding: revert; }
  .sf-prose .sf-not-prose video      { margin-block: 0; border-radius: 0; }

  .sf-flow > * + * {
    margin-block-start: var(--sf-flow-space);
  }

  .sf-truncate {
    overflow:      hidden;
    text-overflow: ellipsis;
    white-space:   nowrap;
  }

  .sf-line-clamp-2,
  .sf-line-clamp-3,
  .sf-line-clamp-N {
    display:            -webkit-box;
    /* stylelint-disable-next-line property-no-vendor-prefix -- de-facto standard; unprefixed CSS Overflow 4 line-clamp not yet shipped */
    -webkit-box-orient: vertical;
    overflow:           hidden;
  }

  /* stylelint-disable-next-line property-no-vendor-prefix -- see above */
  .sf-line-clamp-2 { -webkit-line-clamp: 2; }
  /* stylelint-disable-next-line property-no-vendor-prefix -- see above */
  .sf-line-clamp-3 { -webkit-line-clamp: 3; }
  .sf-line-clamp-N {
    /* stylelint-disable-next-line property-no-vendor-prefix -- see above */
    -webkit-line-clamp: var(--sf-line-clamp);
  }

  .sf-equal-height {
    display:     flex;
    align-items: stretch;
  }
  .sf-equal-height > * {
    block-size: auto;
    flex:       1 1 auto;
  }

  .sf-aspect {
    aspect-ratio: var(--sf-aspect, 16 / 9);
  }

  .sf-scroll-shadow {
    overflow-y:     auto;
    overflow-block: auto;
    /* stylelint-disable-next-line property-no-vendor-prefix -- required for Safari < 15.4 */
    -webkit-mask-image:
      linear-gradient(to bottom,
        transparent 0,
        black var(--sf-scroll-shadow-size),
        black calc(100% - var(--sf-scroll-shadow-size)),
        transparent 100%);
    mask-image:
      linear-gradient(to bottom,
        transparent 0,
        black var(--sf-scroll-shadow-size),
        black calc(100% - var(--sf-scroll-shadow-size)),
        transparent 100%);
  }

  .sf-scroll-snap {
    overflow-y:                auto;
    overflow-block:            auto;
    scroll-snap-type:          block mandatory;
    overscroll-behavior-block: contain;
  }
  .sf-scroll-snap > * {
    scroll-snap-align: start;
  }

  .sf-overflow-fade {
    overflow: hidden;
    /* stylelint-disable-next-line property-no-vendor-prefix -- required for Safari < 15.4 */
    -webkit-mask-image: linear-gradient(
      to right,
      black 0,
      black calc(100% - var(--sf-mask-scrim-end)),
      transparent 100%
    );
    mask-image: linear-gradient(
      to right,
      black 0,
      black calc(100% - var(--sf-mask-scrim-end)),
      transparent 100%
    );
  }

  .sf-overflow-fade--right {
    overflow: hidden;
    /* stylelint-disable-next-line property-no-vendor-prefix -- required for Safari < 15.4 */
    -webkit-mask-image: linear-gradient(
      to right,
      black 0,
      black calc(100% - var(--sf-mask-scrim-end)),
      transparent 100%
    );
    mask-image: linear-gradient(
      to right,
      black 0,
      black calc(100% - var(--sf-mask-scrim-end)),
      transparent 100%
    );
  }

  .sf-overflow-fade--left {
    overflow: hidden;
    /* stylelint-disable-next-line property-no-vendor-prefix -- required for Safari < 15.4 */
    -webkit-mask-image: linear-gradient(
      to right,
      transparent 0,
      black var(--sf-mask-scrim-start),
      black 100%
    );
    mask-image: linear-gradient(
      to right,
      transparent 0,
      black var(--sf-mask-scrim-start),
      black 100%
    );
  }

  .sf-overflow-fade--top {
    overflow: hidden;
    /* stylelint-disable-next-line property-no-vendor-prefix -- required for Safari < 15.4 */
    -webkit-mask-image: linear-gradient(
      to bottom,
      transparent 0,
      black var(--sf-mask-scrim-start),
      black 100%
    );
    mask-image: linear-gradient(
      to bottom,
      transparent 0,
      black var(--sf-mask-scrim-start),
      black 100%
    );
  }

  .sf-overflow-fade--bottom {
    overflow: hidden;
    /* stylelint-disable-next-line property-no-vendor-prefix -- required for Safari < 15.4 */
    -webkit-mask-image: linear-gradient(
      to bottom,
      black 0,
      black calc(100% - var(--sf-mask-scrim-end)),
      transparent 100%
    );
    mask-image: linear-gradient(
      to bottom,
      black 0,
      black calc(100% - var(--sf-mask-scrim-end)),
      transparent 100%
    );
  }

  .sf-overflow-fade--block {
    overflow: hidden;
    /* stylelint-disable-next-line property-no-vendor-prefix -- required for Safari < 15.4 */
    -webkit-mask-image: linear-gradient(
      to bottom,
      transparent 0,
      black var(--sf-mask-scrim-start),
      black calc(100% - var(--sf-mask-scrim-end)),
      transparent 100%
    );
    mask-image: linear-gradient(
      to bottom,
      transparent 0,
      black var(--sf-mask-scrim-start),
      black calc(100% - var(--sf-mask-scrim-end)),
      transparent 100%
    );
  }

  .sf-overflow-fade--inline {
    overflow: hidden;
    /* stylelint-disable-next-line property-no-vendor-prefix -- required for Safari < 15.4 */
    -webkit-mask-image: linear-gradient(
      to right,
      transparent 0,
      black var(--sf-mask-scrim-start),
      black calc(100% - var(--sf-mask-scrim-end)),
      transparent 100%
    );
    mask-image: linear-gradient(
      to right,
      transparent 0,
      black var(--sf-mask-scrim-start),
      black calc(100% - var(--sf-mask-scrim-end)),
      transparent 100%
    );
  }

  .sf-no-tap-highlight {
    /* stylelint-disable-next-line property-no-vendor-prefix -- no unprefixed equivalent */
    -webkit-tap-highlight-color: transparent;
  }

  .sf-surface { background: var(--sf-surface-color); }

  .sf-surface--primary   { --sf-surface-color: var(--sf-color-primary);   background: var(--sf-color-primary);   color: var(--sf-color-text--on-primary); }
  .sf-surface--secondary { --sf-surface-color: var(--sf-color-secondary); background: var(--sf-color-secondary); color: var(--sf-color-text--on-secondary); }
  .sf-surface--tertiary  { --sf-surface-color: var(--sf-color-tertiary);  background: var(--sf-color-tertiary);  color: var(--sf-color-text--on-tertiary); }
  .sf-surface--action    { --sf-surface-color: var(--sf-color-action);    background: var(--sf-color-action);    color: var(--sf-color-text--on-action); }
  .sf-surface--neutral   { --sf-surface-color: var(--sf-color-neutral);   background: var(--sf-color-neutral);   color: var(--sf-color-text--on-neutral); }
  .sf-surface--inverse   { --sf-surface-color: var(--sf-color-inverse);   background: var(--sf-color-inverse);   color: var(--sf-color-text--on-inverse); }
  .sf-surface--success   { --sf-surface-color: var(--sf-color-success);   background: var(--sf-color-success);   color: var(--sf-color-text--on-success); }
  .sf-surface--warning   { --sf-surface-color: var(--sf-color-warning);   background: var(--sf-color-warning);   color: var(--sf-color-text--on-warning); }
  .sf-surface--info      { --sf-surface-color: var(--sf-color-info);      background: var(--sf-color-info);      color: var(--sf-color-text--on-info); }
  .sf-surface--danger    { --sf-surface-color: var(--sf-color-danger);    background: var(--sf-color-danger);    color: var(--sf-color-text--on-danger); }

  @supports (color: oklch(from red l c h)) and (width: calc(1px * sign(1))) {
    :is(.sf-surface,
        .sf-surface--primary, .sf-surface--secondary, .sf-surface--tertiary,
        .sf-surface--action, .sf-surface--neutral, .sf-surface--inverse,
        .sf-surface--success, .sf-surface--warning,
        .sf-surface--info, .sf-surface--danger) {

      --sf-surface-contrast: oklch(from var(--sf-surface-color) clamp(0.1, sign(var(--sf-contrast-threshold) - l) * 999, 0.95) 0 0 / 1);
      background: var(--sf-surface-color);
      color: var(--sf-surface-contrast);
      --sf-color-text:              var(--sf-surface-contrast);
      --sf-color-heading:           var(--sf-surface-contrast);
      --sf-color-link:              oklch(from var(--sf-surface-contrast) l calc(c + 0.08) h);
      --sf-color-link--hover:       var(--sf-surface-contrast);
      --sf-color-link--underline:   oklch(from var(--sf-surface-contrast) l c h / 0.5);
      --sf-color-text--subtle:   oklch(from var(--sf-surface-contrast) l c h / 0.70);
      --sf-color-text--placeholder: oklch(from var(--sf-surface-contrast) l c h / 0.45);
      --sf-color-text--disabled:    oklch(from var(--sf-surface-contrast) l c h / 0.30);
      --sf-color-border:            oklch(from var(--sf-surface-contrast) l c h / 0.20);
      --sf-color-border--subtle:    oklch(from var(--sf-surface-contrast) l c h / 0.12);
      --sf-color-border--strong:    oklch(from var(--sf-surface-contrast) l c h / 0.35);
      --sf-focus-ring-color:        var(--sf-surface-contrast);
      --sf-caret-color:             var(--sf-surface-contrast);

      --sf-shadow-color:            oklch(from var(--sf-surface-color) var(--sf-shadow-lightness) c h);

      /* :root-only alias-overrides seal their value at the DECLARING element
         (var() resolves on :root), so a heading/body that reads the alias
         reads the frozen page value and ignores this surface's re-declared
         semantic token above — leaving headings dark on a dark surface.
         Re-declare the aliases here so they re-resolve against THIS surface's
         adapted --sf-color-heading / --sf-color-text. Same class of bug as the
         [data-theme] block in themes.css (#496), extended to heading/body. */
      --sf-heading-color:           var(--sf-color-heading);
      --sf-body-color:              var(--sf-color-text);

      /* INTERNAL flag (inherits): signals "inside an auto-contrast surface" so a
         nested .sf-card can restore surface-appropriate foreground for its own
         (non-adapting) background via a style query — see .sf-card in
         optional/components.css (#496). Not a public hook; read, don't set. */
      --sf-surface-active:          1;
    }
  }
  .sf-scrim {
    position:  relative;
    isolation: isolate;
  }
  .sf-scrim::before {
    content:        "";
    position:       absolute;
    inset:          0;
    z-index:        0;
    background:     var(--sf-scrim-gradient);
    pointer-events: none;
  }

  .sf-scrim > :not(img, picture, video, svg, canvas),
  .sf-scrim__content {
    position: relative;
    z-index:  1;
  }
  .sf-scrim--top    { --sf-scrim-direction: to bottom; }
  .sf-scrim--bottom { --sf-scrim-direction: to top; }
  .sf-scrim--full   { --sf-scrim-gradient: var(--sf-scrim-color); }

  /* Named background surface preset (issue #523). Composes the
     --sf-surface-bg-* token set (see tokens.macros.css) into one reusable,
     named background: base colour + image/gradient/pattern, sizing, an
     optional overlay layered above the image, and an optional animation.
     Inert until a preset feeds the tokens. The overlay is the first
     background-image layer so it paints ABOVE the image. */
  .sf-surface-bg {
    background-color:      var(--sf-surface-bg-color);
    background-image:      var(--sf-surface-bg-overlay), var(--sf-surface-bg-image);
    background-size:       var(--sf-surface-bg-size);
    background-position:   var(--sf-surface-bg-position);
    background-repeat:     var(--sf-surface-bg-repeat);
    background-attachment: var(--sf-surface-bg-attachment);
    animation:             var(--sf-surface-bg-animation);
  }

  .sf-text-protect {
    text-shadow: var(--sf-scrim-text-shadow);
  }

  .sf-text-gradient {
    background-image: var(--sf-gradient-primary);
    background-clip: text;
    color: transparent;
  }

  .sf-link-external::after {
    content:         var(--sf-link-external-marker);
    display:         inline-block;
    font-size:       0.85em;
    text-decoration: none;
  }
  /* Alt-text form (accessible name for the glyph) — gated so a UA that
     rejects the "value / string" grammar keeps the plain marker above
     instead of dropping the whole declaration. */
  @supports (content: "x" / "y") {
    .sf-link-external::after {
      content: var(--sf-link-external-marker) / var(--sf-link-external-label);
    }
  }

  .sf-content-auto {
    content-visibility:     auto;
    contain-intrinsic-size: auto var(--sf-content-intrinsic-size, 500px);
  }

  .sf-tabular-nums {
    font-variant-numeric: var(--sf-font-numeric, tabular-nums);
  }

  /* filter: drop-shadow() utilities — unlike box-shadow, follows the element's
     actual alpha shape (SVG icons, PNG cutouts, transparent logos). */
  .sf-drop-shadow-xs { filter: var(--sf-drop-shadow-xs); }
  .sf-drop-shadow-s  { filter: var(--sf-drop-shadow-s); }
  .sf-drop-shadow-m  { filter: var(--sf-drop-shadow-m); }
  .sf-drop-shadow-l  { filter: var(--sf-drop-shadow-l); }
  .sf-drop-shadow-xl { filter: var(--sf-drop-shadow-xl); }

  .sf-link--subtle:link,
  .sf-link--subtle:visited {
    text-decoration-line: none;
  }
  .sf-link--subtle:is(:link, :visited):hover,
  .sf-link--subtle:is(:link, :visited):focus-visible {
    text-decoration-line:  underline;
    text-decoration-color: currentcolor;
  }

  .sf-link--reverse:link,
  .sf-link--reverse:visited {
    text-decoration-line: underline;
  }
  .sf-link--reverse:is(:link, :visited):hover,
  .sf-link--reverse:is(:link, :visited):focus-visible {
    text-decoration-line: none;
  }

  /* Overlap recipe — one element intentionally overlapping the previous
     one (e.g. an image pulled up over the card below it), via a negative
     logical margin + a raised stacking context. Directional variants are
     standalone (margins are distinct properties, not a re-pointable knob). */
  .sf-overlap,
  .sf-overlap--down {
    position: relative;
    z-index:  var(--sf-z-raised);
  }
  .sf-overlap        { margin-block-start:  calc(-1 * var(--sf-overlap-pull)); }
  .sf-overlap--down  { margin-block-end:    calc(-1 * var(--sf-overlap-pull)); }

  /* Card-container recipe — the receiving side of an overlap: an isolated
     stacking context with block-start padding compensation so in-flow
     content clears an element intruding from above. The padding knob
     defaults to the pull knob so one override tunes both. */
  .sf-overlap-host {
    position:  relative;
    isolation: isolate;
    /* var() fallback resolved on this element, not pre-baked at :root —
       so a local --sf-overlap-pull override (even without setting
       --sf-overlap-host-pad) is honored (#496-class pitfall). */
    padding-block-start: var(--sf-overlap-host-pad, var(--sf-overlap-pull));
  }

}

/* ─── core/states.css ─── */
/* SLASHED — core/states.css
   @layer slashed.states
   Generic runtime states toggled by JS or ARIA: visibility, interactivity, selection, busy/current.
   Prefix: .sf-is-*; not utilities or component-specific variants.

   Reach for a native mechanism first — a pseudo-class (:disabled, :read-only,
   :empty, :user-invalid), a boolean/enumerated attribute ([hidden], [disabled],
   [readonly]), or an ARIA state ([aria-busy], [aria-current], [aria-expanded],
   [aria-selected], [aria-pressed]) — since the browser (and assistive tech)
   already understands it for free. Add a .sf-is-* class only for states with
   no native equivalent, or ones this file's own tokens/consumers need as a
   hook (see docs/states.md). See docs/states.md § "Prefer native state" for
   the full rationale and the [hidden] precedent that got .sf-is-hidden
   removed in favour of the attribute it duplicated. */

@layer slashed.states {

  /* No visibility classes here: .sf-is-hidden duplicated [hidden] (removed —
     core/reset.css hardens that attribute identically). .sf-invisible /
     .sf-visible moved to optional/utilities.css as single-property helpers
     (same call as Bulma's is-hidden/is-invisible living under "helpers",
     not component/state docs) — still the hook docs/roadmap.md's
     reveal-on-scroll pattern targets, just renamed and relocated. */

  /* -- INTERACTIVITY --
     No .sf-is-readonly here: it duplicated :read-only for real form controls,
     and its pointer-events:none blocked text selection that a genuinely
     read-only field should still allow — reach for :read-only, or
     [aria-readonly] on a non-native widget, instead. */

  /* cursor: not-allowed inherits to children with pointer-events: auto —
     intentional, part of the .sf-is-disabled API surface. */
  .sf-is-disabled {
    opacity:        var(--sf-opacity-disabled);
    pointer-events: none;

    cursor:         not-allowed;
    user-select:    none;
  }

  /* -- LOADING / ASYNC FEEDBACK -- */

  .sf-is-loading {
    color:          transparent !important;
    pointer-events: none;
    position:       relative;
  }

  .sf-is-loading > * {
    opacity: 0 !important;
  }

  .sf-is-loading::after {
    content:             "";
    position:            absolute;
    inset-block-start:   50%;
    inset-inline-start:  50%;
    inline-size:         1em;
    block-size:          1em;
    margin-block-start:  -0.5em;
    margin-inline-start: -0.5em;
    border:        var(--sf-border-width-2) solid var(--sf-color-border--strong);
    border-block-start-color: var(--sf-color-action);
    border-radius: var(--sf-radius-full);

    animation: var(--sf-animation-spin);
  }

  /* No .sf-is-busy / .sf-is-pending here: both were a couple of
     property-value declarations (cursor:progress, plus opacity for
     pending's "optimistic UI" dimming) with no consumer and no native gap
     they filled — [aria-busy="true"] covers the semantic, and the visual
     is two lines to hand-roll in your own component CSS if you want it. */

  /* Shimmer loading placeholder (was .sf-is-skeleton pre-Unreleased; renamed so
     "skeleton" names only the planned .sf-skeleton component, not a state —
     see docs/migration.md and roadmap.md #575). The class name now mirrors its
     driving token, --sf-animation-shimmer. */
  img.sf-is-shimmer,
  .sf-is-shimmer img {
    opacity: 0 !important;
  }

  .sf-is-shimmer {
    color:           transparent !important;
    background:      linear-gradient(
                       90deg,
                       var(--sf-color-inset) 25%,
                       var(--sf-color-raised) 50%,
                       var(--sf-color-inset) 75%
                     );
    background-size: 200% 100%;
    animation: var(--sf-animation-shimmer);
    border-radius:   var(--sf-radius-s);
    pointer-events:  none;
    user-select:     none;
  }

  /* -- SELECTED / HIGHLIGHTED --
     No .sf-is-active / .sf-is-open / .sf-is-collapsed / .sf-is-expanded /
     .sf-is-current / .sf-is-pressed here: these only ever set an
     inheritable --sf-is-* custom property for a hypothetical consumer
     calc() to branch on, with zero consumer anywhere in this codebase or
     its demo. Speculative "might be useful one day" surface, removed —
     see docs/states.md § "Prefer native state". Reach for [aria-current],
     [aria-expanded], [aria-pressed] as the styling hook instead; .sf-is-
     selected / .sf-is-highlighted below cover the two cases (selection,
     transient emphasis) that still need their own visual. */

  .sf-is-selected {
    background-color: var(--sf-color-bg--selected);
  }

  .sf-is-highlighted {
    background-color: var(--sf-color-bg--focus);
  }

  /* -- VALIDATION / FEEDBACK --
     No .sf-is-danger here: identical implementation to .sf-is-invalid/
     .sf-is-error (both only ever wrote --sf-field-*, so it never worked as
     the "destructive-action context" its own docs described outside a form
     field) and, per docs/migration.md, visual variants belong in your own
     component CSS anyway. Use .sf-is-invalid/.sf-is-error, or your own
     `--danger` variant class, instead. */

  .sf-is-valid,
  .sf-is-success {
    --sf-field-border-color: var(--sf-color-success);
    --sf-field-text-color:   var(--sf-color-success-strong);
  }

  .sf-is-invalid,
  .sf-is-error {
    --sf-field-border-color: var(--sf-color-danger);
    --sf-field-text-color:   var(--sf-color-danger-strong);
  }

  .sf-is-warning {
    --sf-field-border-color: var(--sf-color-warning);
    --sf-field-text-color:   var(--sf-color-warning-strong);
  }

  .sf-is-info {
    --sf-field-border-color: var(--sf-color-info);
    --sf-field-text-color:   var(--sf-color-info-strong);
  }

  /* -- DRAG & DROP -- */

  .sf-is-dragging {
    opacity: var(--sf-opacity-muted);
    cursor:  grabbing;
  }

  .sf-is-drop-target {
    outline:        var(--sf-focus-ring-width) dashed var(--sf-color-action);
    outline-offset: var(--sf-focus-ring-offset);
  }

  .sf-is-draggable {
    cursor: grab;
  }

  /* -- EMPTY STATE -- */

  .sf-is-empty:empty {
    display: none;
  }

  /* KEYFRAMES (used by states above) */

}

/* ─── core/motion.css ─── */
/* SLASHED — core/motion.css
   @layer slashed.motion
   Global transitions, view transitions, keyframes, and motion helpers.
   Respects prefers-reduced-motion and --sf-motion-scale. */

@layer slashed.motion {

  @media (prefers-reduced-motion: no-preference) {

    html {
      scroll-behavior: smooth;
    }

    a, button, input, select, textarea, summary {
      transition-property:
        color, background-color, border-color, text-decoration-color,
        fill, stroke, opacity, box-shadow, transform, scale, translate,
        filter, backdrop-filter;
      transition-duration:        var(--sf-duration-fast);
      transition-timing-function: var(--sf-ease-out);
    }

    @supports (view-transition-name: none) {
      ::view-transition-old(root),
      ::view-transition-new(root) {
        animation-duration:        var(--sf-duration-normal);
        animation-timing-function: var(--sf-ease-out);
      }
    }

    
    .sf-fade-in        { animation: var(--sf-animation-fade-in); }
    .sf-fade-out       { animation: var(--sf-animation-fade-out); }
    .sf-slide-in-up    { animation: var(--sf-animation-slide-in-up); }
    .sf-slide-in-down  { animation: var(--sf-animation-slide-in-down); }
    .sf-slide-in-left  { animation: var(--sf-animation-slide-in-left); }
    .sf-slide-in-right { animation: var(--sf-animation-slide-in-right); }
    .sf-scale-up       { animation: var(--sf-animation-scale-up); }
    .sf-scale-down     { animation: var(--sf-animation-scale-down); }

    
    /* animation-duration set unconditionally: without it, engines without
       animation-timeline snap to end state at 0s. */
    .sf-entrance--fade,
    .sf-entrance--fade-up,
    .sf-entrance--fade-down,
    .sf-entrance--fade-left,
    .sf-entrance--fade-right,
    .sf-entrance--scale-up {
      animation-duration:        var(--sf-duration-slow);
      animation-timing-function: var(--sf-ease-out);
      animation-fill-mode:       both;
    }
    @supports (animation-timeline: view()) {
      .sf-entrance--fade,
      .sf-entrance--fade-up,
      .sf-entrance--fade-down,
      .sf-entrance--fade-left,
      .sf-entrance--fade-right,
      .sf-entrance--scale-up {
        animation-fill-mode: forwards;
        animation-timeline: view();
        animation-range:    var(--sf-scroll-timeline-range-start, entry 0%)
                            var(--sf-scroll-timeline-range-end,   cover 30%);
      }
    }
    .sf-entrance--fade        { animation-name: sf-fade-in; }
    .sf-entrance--fade-up     { animation-name: sf-slide-in-up; }
    .sf-entrance--fade-down   { animation-name: sf-slide-in-down; }
    .sf-entrance--fade-left   { animation-name: sf-slide-in-left; }
    .sf-entrance--fade-right  { animation-name: sf-slide-in-right; }
    .sf-entrance--scale-up    { animation-name: sf-scale-up; }


    /* Exit counterpart of .sf-entrance--*: fires as the element scrolls
       out of view. Every declaration below lives inside @supports —
       unlike entrance (where a non-scroll fallback still ends visible),
       an unconditional exit animation would leave content permanently
       hidden on engines without animation-timeline: view() support, so
       there is deliberately no fallback declaration outside @supports. */
    @supports (animation-timeline: view()) {
      .sf-exit--fade,
      .sf-exit--fade-up,
      .sf-exit--fade-down,
      .sf-exit--fade-left,
      .sf-exit--fade-right,
      .sf-exit--scale-down {
        animation-duration:        var(--sf-duration-slow);
        animation-timing-function: var(--sf-ease-in);
        animation-fill-mode:       both;
        animation-timeline: view();
        animation-range:    var(--sf-scroll-timeline-range-exit-start, cover 70%)
                            var(--sf-scroll-timeline-range-exit-end,   exit 100%);
      }
      .sf-exit--fade        { animation-name: sf-fade-out; }
      .sf-exit--fade-up     { animation-name: sf-slide-out-up; }
      .sf-exit--fade-down   { animation-name: sf-slide-out-down; }
      .sf-exit--fade-left   { animation-name: sf-slide-out-left; }
      .sf-exit--fade-right  { animation-name: sf-slide-out-right; }
      .sf-exit--scale-down  { animation-name: sf-scale-down; }
    }

    /* Stagger — choreography, not the animation itself. Put .sf-stagger on a
       PARENT; every direct child gets an incrementing animation-delay so a
       time-based entrance (.sf-fade-in / .sf-slide-in-*) plays in sequence.
       Children WITHOUT an animation just carry an inert delay (no-op), so you
       opt in per child with no need to exclude anything. Pairs with time-based
       animations only — the .sf-entrance / .sf-exit families are scroll-driven
       (their rhythm is animation-range, not animation-delay).

       One formula, one knob (--sf-stagger-step); the two paths below only
       differ in how --sf-stagger-i (the child's index) is set. */
    .sf-stagger > * {
      animation-delay: calc(
        var(--sf-stagger-i, 0) * var(--sf-stagger-step, 75ms) * var(--sf-motion-scale, 1)
      );
    }

    /* Modern path: real, unbounded index straight from the DOM position. */
    @supports (animation-delay: calc(sibling-index() * 1ms)) {
      .sf-stagger > * { --sf-stagger-i: calc(sibling-index() - 1); }
    }

    /* Fallback (no sibling-index()): a bounded :nth-child ramp reusing the same
       formula. The default is the plateau value, so the 9th child onward shares
       the last rung's delay instead of growing without limit — 8 rungs cover a
       4-column grid's first two visible rows. Split into @supports not(...) so
       the modern override above wins cleanly without a specificity fight
       (:nth-child would otherwise out-specify the sibling-index rule). */
    @supports not (animation-delay: calc(sibling-index() * 1ms)) {
      .sf-stagger > *              { --sf-stagger-i: 8; }
      .sf-stagger > *:nth-child(1) { --sf-stagger-i: 0; }
      .sf-stagger > *:nth-child(2) { --sf-stagger-i: 1; }
      .sf-stagger > *:nth-child(3) { --sf-stagger-i: 2; }
      .sf-stagger > *:nth-child(4) { --sf-stagger-i: 3; }
      .sf-stagger > *:nth-child(5) { --sf-stagger-i: 4; }
      .sf-stagger > *:nth-child(6) { --sf-stagger-i: 5; }
      .sf-stagger > *:nth-child(7) { --sf-stagger-i: 6; }
      .sf-stagger > *:nth-child(8) { --sf-stagger-i: 7; }
    }

  }

  @keyframes sf-fade-in   { from { opacity: 0; } to { opacity: 1; } }
  @keyframes sf-fade-out  { from { opacity: 1; } to { opacity: 0; } }
  @keyframes sf-slide-in-up    { from { opacity: 0; translate: 0 1rem;  } to { opacity: 1; translate: 0 0; } }
  @keyframes sf-slide-in-down  { from { opacity: 0; translate: 0 -1rem; } to { opacity: 1; translate: 0 0; } }
  @keyframes sf-slide-in-left  { from { opacity: 0; translate: 1rem 0;  } to { opacity: 1; translate: 0 0; } }
  @keyframes sf-slide-in-right { from { opacity: 0; translate: -1rem 0; } to { opacity: 1; translate: 0 0; } }
  @keyframes sf-scale-up   { from { opacity: 0; scale: 0.92; } to { opacity: 1; scale: 1; } }
  @keyframes sf-scale-down { from { opacity: 1; scale: 1; } to { opacity: 0; scale: 0.92; } }

  @keyframes sf-slide-out-up    { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: 0 -1rem; } }
  @keyframes sf-slide-out-down  { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: 0 1rem;  } }
  @keyframes sf-slide-out-left  { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: -1rem 0; } }
  @keyframes sf-slide-out-right { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: 1rem 0;  } }

  @keyframes sf-spin    { to { rotate: 360deg; } }
  @keyframes sf-shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } }

  @keyframes sf-ping  { 75%, 100% { scale: 2; opacity: 0; } }
  @keyframes sf-blink { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }
  @keyframes sf-float { 0%, 100% { translate: 0 0; } 50% { translate: 0 -0.5rem; } }

  
  @supports (@property --x { syntax: "<color>"; inherits: false; initial-value: white; }) {
    @keyframes sf-color-pulse {
      0%   { --sf-color-primary-source-light: var(--sf-color-primary-source-light); }
      50%  { --sf-color-primary-source-light: oklch(from var(--sf-color-primary-source-light) clamp(0, calc(l + 0.25), 0.9) c h); }
      100% { --sf-color-primary-source-light: var(--sf-color-primary-source-light); }
    }

    @media (prefers-reduced-motion: no-preference) {
      .sf-color-pulse {
        animation: var(--sf-animation-color-pulse);
      }
    }
  }

  @supports (transition-behavior: allow-discrete) {
    dialog, [popover] {
      transition: var(--sf-transition-overlay);
    }
  }

}

/* ─── core/accessibility.css ─── */
/* SLASHED — core/accessibility.css
   @layer slashed.accessibility
   Focus rings, screen-reader helpers, skip links, and reduced-friction a11y utilities.
   Prefix: .sf-* plus native focus selectors. */

@layer slashed.accessibility {

  /* Focus */
  :focus:not(:focus-visible) {
    outline: none;
  }

  :focus-visible {
    outline:        var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-focus-ring-color) !important;
    outline-offset: var(--sf-focus-ring-offset) !important;
  }

  /* .sf-focus-shadow: replaces outline ring with box-shadow ring; !important required
     to take over the hardened :focus-visible outline (WCAG 2.4.7 still met). */
  .sf-focus-shadow:focus-visible {
    outline:    none                         !important;
    box-shadow: var(--sf-focus-ring-shadow)  !important;
  }

  /* forced-colors: box-shadow vanishes — restore outline ring for WCAG 2.4.7
     in Windows High Contrast. */
  @media (forced-colors: active) {
    .sf-focus-shadow:focus-visible {
      outline:        var(--sf-focus-ring-width) solid !important;
      outline-offset: var(--sf-focus-ring-offset) !important;
      box-shadow:     none !important;
    }
  }

  /* Reduced motion. Token override helps components that read */
  @media (prefers-reduced-motion: reduce) {
    :root {
      --sf-duration-instant: 0.01ms;
      --sf-duration-fast:    0.01ms;
      --sf-duration-normal:  0.01ms;
      --sf-duration-slow:    0.01ms;
      --sf-duration-slower:  0.01ms;
    }

    *,
    *::before,
    *::after {
      animation-duration:        0.01ms !important;
      animation-iteration-count: 1      !important;
      transition-duration:       0.01ms !important;
      scroll-behavior:           auto   !important;
    }
  }

  .no-motion,
  .no-motion *,
  .no-motion *::before,
  .no-motion *::after {
    animation-duration:        0.01ms !important;
    animation-iteration-count: 1      !important;
    transition-duration:       0.01ms !important;
    scroll-behavior:           auto   !important;
  }

  /* High contrast */
  @media (prefers-contrast: more) {
    :root {
      --sf-focus-ring-width: 3px;

      --sf-contrast-bias: 0.06;
    }

    hr {
      border-block-start-width: var(--sf-border-width-2);
    }
  }

  @media (prefers-reduced-transparency: reduce) {
    ::backdrop {
      background-color: var(--sf-color-neutral);
      opacity:          1;
    }
  }

  /* Reduced data */
  @media (prefers-reduced-data: reduce) {
    :root {
      --sf-shadow-strength: 0;
      --sf-blur: 0px;
    }
  }

  /* Minimum touch targets — a backstop for genuinely bare, unstyled controls.
     The floor targets only controls that carry NO class at all. The moment a
     control has any class it is considered *owned* — by SLASHED itself (.sf-btn
     owns a deliberate XS–XL size ladder via --sf-btn-min-height / --sf-size-*
     rungs), by the author, or by a third-party widget (a page-builder hamburger
     toggle, a plugin control) — and its sizing belongs to whoever put that class
     there, not to this blanket rule.

     Why so conservative: a blanket floor on *every* button/control silently
     overrode its owners. It collapsed .sf-btn's whole size ladder to one height
     on touch, and it stretched narrow custom widgets (e.g. a hamburger icon
     button) to 44px on BOTH axes — at a specificity higher than a single class,
     so the owner couldn't even override it without !important. Element-level
     selectors that reach for someone else's markup are the "too aggressive base
     style" trap; scoping the floor to classless controls keeps it from ever
     touching markup the framework doesn't own. It extends the "un-styled
     element" idiom already used for `a:not([class])` in base.

     "Un-styled" here means *no effective class* — no class attribute at all, OR
     an empty one (`class=""`), which templating/component renderers routinely
     emit for a control the author never styled. Both keep the floor. The test
     is written `:not([class]:not([class=""]))` — read it as "not (has a
     non-empty class)": it excludes only controls carrying a real class token.
     (A whitespace-only `class=" "` is treated as owned; that pathological case
     isn't worth widening the selector for.) `:where()` keeps the element list
     at zero specificity so the whole rule stays a low, easily-reasoned weight.

     Bare `<button>text</button>` and un-classed native controls — almost always
     the author's own quick markup — keep the WCAG 2.5.5 44px floor on both axes.
     To opt a control OUT, give it any class. To put the floor back ON an owned
     control, add the .sf-touch-target opt-in class below, set its own
     min-block/inline-size, or on .sf-btn use
     `--sf-btn-min-height: var(--sf-touch-target)`. */
  @media (pointer: coarse) {
    :where(
      button,
      input[type="button"], input[type="submit"], input[type="reset"],
      select, summary,
      input[type="checkbox"], input[type="radio"]
    ):not([class]:not([class=""])) {
      min-block-size:  var(--sf-touch-target);
      min-inline-size: var(--sf-touch-target);
    }
  }

  /* .sf-touch-target — the explicit opt-IN counterpart to the class-less floor
     above. Because the automatic floor deliberately never touches a control
     that carries a class, put this class on any control you own (a button with
     your own class, a third-party widget you can add a class to) to guarantee
     the WCAG 2.5.5 44px minimum hit area on both axes. Unlike the automatic
     floor it is NOT gated to a coarse pointer — an explicit opt-in is
     predictable everywhere. Size follows --sf-touch-target, so a wrapper (or
     the element itself) can retune it locally.

       <button class="my-menu-toggle sf-touch-target" aria-label="Menu">…</button>

     It sets ONLY the two min-size properties — deliberately no `display` — so it
     never strips a native affordance (e.g. a <summary>'s list-item marker, a
     control's baseline alignment). It therefore expects a target whose display
     already honours min-inline-size: buttons and native form controls
     (inline-block/flex) do. On a purely inline element (a bare <a>) add your own
     `display: inline-flex` (or inline-block) alongside it.

     No !important: it's a single class (specificity 0,1,0), so your own
     component rule can still override it when a specific control needs to opt
     back out. */
  .sf-touch-target {
    min-block-size:  var(--sf-touch-target);
    min-inline-size: var(--sf-touch-target);
  }

  /* Disabled states */
  [disabled],
  [aria-disabled="true"] {
    cursor:  not-allowed;
    opacity: var(--sf-opacity-disabled);
  }

  /* Screen-reader-only. Atomic contract: partial override either exposes hidden content
     or hides intended content — all-or-nothing, whole set hardened. */
  .sr-only,
  .sr-only-focusable:not(:focus, :focus-within) {
    position:    absolute            !important;
    width:       1px                 !important;
    height:      1px                 !important;
    padding:     0                   !important;
    margin:      -1px                !important;
    overflow:    hidden              !important;
    overflow:    clip                !important;
    clip-path:   inset(50%)          !important;
    white-space: nowrap              !important;
    border:      0                   !important;
  }

  /* Skip link */
  .skip-link {
    position:          absolute;
    inset-block-start: -100%;
    inset-inline-start: var(--sf-space-m);
    z-index:           var(--sf-z-tooltip);
    padding:           var(--sf-space-s) var(--sf-space-m);
    background:        var(--sf-color-bg);
    color:             var(--sf-color-text);
    border:            var(--sf-border-width-1) solid var(--sf-color-border);
    border-radius:     var(--sf-radius-m);
    transition:        inset-block-start var(--sf-duration-fast) var(--sf-ease-out);
  }
  .skip-link:focus {
    inset-block-start: var(--sf-space-m);
  }

  /* Focus-parent */

  .sf-focus-parent:focus-within {
    outline:        var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-focus-ring-color);
    outline-offset: var(--sf-focus-ring-offset);
  }

  /* Clickable-parent */
  /* Perf note (SL-002): the two selector groups below each combine multiple
     :has()-bearing branches, forcing subtree re-evaluation on DOM mutation
     inside .sf-clickable-parent. Fine for typical card/list-item usage; avoid
     applying this class to large (100+ node) grids or lists without profiling
     first. */
  .sf-clickable-parent {
    --sf-clickable-overlay-z: 1;
    position: relative;
    cursor:   pointer;
  }

  .sf-clickable-parent:not(:has([data-overlay-link], .sf-clickable-parent__overlay)) a:where(:any-link)::after,
  .sf-clickable-parent [data-overlay-link]::after,
  .sf-clickable-parent .sf-clickable-parent__overlay::after {
    content:        "";
    position:       absolute;
    inset:          0;
    z-index:        var(--sf-clickable-overlay-z);
    pointer-events: auto;
  }

  .sf-clickable-parent :is(
    button, input, textarea, select,
    summary, video, audio,
    [role="button"], [data-no-overlay]
  ),
  .sf-clickable-parent:has([data-overlay-link]) a:where(:any-link):not([data-overlay-link]),
  .sf-clickable-parent:has(.sf-clickable-parent__overlay) a:where(:any-link):not(.sf-clickable-parent__overlay),
  .sf-clickable-parent:not(:has([data-overlay-link], .sf-clickable-parent__overlay)) a:where(:any-link):not(:first-of-type) {
    position: relative;
    z-index:  calc(var(--sf-clickable-overlay-z) + 1);
  }

  .sf-clickable-parent:focus-within {
    outline:        var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-focus-ring-color);
    outline-offset: var(--sf-focus-ring-offset);
  }

  /* Forced colors (Windows High Contrast). Outline-based focus */
  @media (forced-colors: active) {
    :root {
      --sf-focus-ring-color: Highlight;
    }

    :focus-visible {
      outline-color: Highlight !important;
    }

    :where(*) {
      box-shadow:  none;
      text-shadow: none;
    }

    input,
    select,
    textarea {
      border: var(--sf-border-width-1) solid;
    }

    button,
    input[type="submit"],
    input[type="reset"],
    input[type="button"] {
      border-color: ButtonBorder !important;
    }

    .sf-text-gradient {
      background-image:         none !important;
      /* stylelint-disable-next-line property-no-vendor-prefix */
      -webkit-background-clip:  unset !important;
      background-clip:          unset !important;
      color:                    CanvasText !important;
    }

    .sf-is-loading::after {
      border-color:             ButtonText !important;
      border-block-start-color: Highlight !important;
    }
  }

}

/* ─── core/print.css ─── */
/* SLASHED — core/print.css
   @layer slashed.print
   Print-only visibility, page defaults, ink-saving resets, and URL disclosure.
   Prefix: .print-* for explicit print helpers. */

@layer slashed.print {

  
  .print-only {
    display: none;
  }

  @media print {

    /* --sf-print-page-size / -margin are read here so a :root override retunes
       the printed page box. Each fallback equals the historical literal, so the
       default print render is unchanged. Verified in Chromium that var() is
       substituted inside @page descriptors; engines lacking general custom-
       property substitution in @page fall back to the literal. */
    @page {

      size:   var(--sf-print-page-size, a4) portrait;
      margin: var(--sf-print-page-margin, 2cm);
    }

    body {
      font-size: var(--sf-print-base-size, 11pt);
    }

    *,
    *::before,
    *::after {
      box-shadow:  none;
      text-shadow: none;
    }

    a[href]:not([href^="#"], [href^="javascript:" i], [href^="mailto:" i], [href^="tel:" i], .sf-link-external)::after {
      content:    " (" attr(href) ")";
      font-size:  90%;
      word-break: break-all;
    }

    abbr[title]::after {
      content: " (" attr(title) ")";
    }

    img, svg, canvas, figure, table, pre, blockquote {
      break-inside: avoid;

    }

    thead {
      display: table-header-group;
    }

    tr {
      break-inside: avoid;
    }

    h1, h2, h3, h4, h5, h6 {
      break-after: avoid;

    }

    /* Fluid --sf-h*-size tokens resolve via 100vw clamp()s, which have no
       reliable meaning on a print page box. Pin print heading sizes to
       static pt values instead. */
    h1 { font-size: 24pt; }
    h2 { font-size: 20pt; }
    h3 { font-size: 16pt; }
    h4 { font-size: 14pt; }
    h5 { font-size: 12pt; }
    h6 { font-size: 11pt; }

    p {
      orphans: 3;
      widows:  3;
    }

    pre, blockquote {
      border:  var(--sf-border-width-1) solid var(--sf-color-border--strong);
      padding: var(--sf-space-s);
    }

    nav, aside, button, input, select, textarea,
    audio, video, dialog, [popover], .no-print {
      display: none !important;
    }

    .print-only {
      display: revert !important;
    }

    details { display: block; }
    details > summary { display: none !important; }
    details > :not(summary) { display: block; }

    
    .print-color-exact,
    .print-color-exact * {
      print-color-adjust:         exact !important;
      -webkit-print-color-adjust: exact !important;
    }

    .print-no-color,
    .print-no-color *,
    .print-no-color *::before,
    .print-no-color *::after {
      background:  transparent !important;
      color:       CanvasText  !important;
    }

  }

}

/* ─── optional/forms.css ─── */
/* SLASHED — optional/forms.css
   @layer slashed.forms
   Classless form controls: text inputs, selects, textareas, checks/radios, fieldsets, labels.
   Values resolve through core and optional component tokens. */

@layer slashed.forms {

  /* Shared base for text-like inputs, select, textarea */

  input:where(
    [type="text"], [type="email"], [type="password"], [type="url"],
    [type="tel"], [type="number"], [type="search"], [type="date"],
    [type="datetime-local"], [type="month"], [type="week"],
    [type="time"], :not([type])
  ),
  select,
  textarea {

    appearance: none;

    display: block;
    inline-size: 100%;
    /* Field metrics resolve through the component-tier field knobs first, then
       the core rhythm knob, then the literal — so overriding any of them retunes
       fields without touching global spacing/radius. Every fallback equals the
       shipped default, so the default render is unchanged. --sf-field-padding-*
       and --sf-field-radius live in optional/tokens.components.css (present in
       the -components/full bundles); --sf-field-block is the core-tier fallback
       so the knob still works in the forms-only bundles. */
    padding-block: var(--sf-field-padding-block, var(--sf-field-block, var(--sf-space-xs)));
    padding-inline: var(--sf-field-padding-inline, var(--sf-space-s));
    font-size: var(--sf-text-m);
    line-height: var(--sf-leading-normal);
    color: var(--sf-color-text);
    background-color: var(--sf-color-surface);
    border: var(--sf-border-width-1) solid var(--sf-field-border-color, var(--sf-color-border));
    border-radius: var(--sf-field-radius, var(--sf-radius-m));
    transition: var(--sf-transition-form-field);
  }

  /* Focus */
  input:where(
    [type="text"], [type="email"], [type="password"], [type="url"],
    [type="tel"], [type="number"], [type="search"], [type="date"],
    [type="datetime-local"], [type="month"], [type="week"],
    [type="time"], :not([type])
  ):focus-visible,
  select:focus-visible,
  textarea:focus-visible {
    border-color: var(--sf-color-border--focus);
    outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-color-border--focus);
    outline-offset: var(--sf-focus-ring-offset);
  }

  /* Disabled */
  input:disabled,
  select:disabled,
  textarea:disabled {
    background-color: var(--sf-color-bg--disabled);
    color: var(--sf-color-text--disabled);
    opacity: var(--sf-opacity-disabled);
    cursor: not-allowed;
  }

  /* Placeholder */
  input::placeholder,
  textarea::placeholder {
    color: var(--sf-color-text--placeholder);
    opacity: 1;
  }

  /* Autofill: strips Safari's own dark-mode autofill background; Chromium/Firefox honour
     the CSS background without this, but WebKit doesn't. */
  /* stylelint-disable selector-pseudo-class-no-unknown */
  input:-webkit-autofill {
    box-shadow: inset 0 0 0 1000px var(--sf-color-surface);
    -webkit-text-fill-color: var(--sf-color-text);
    caret-color: var(--sf-color-text);
    border-color: var(--sf-field-border-color, var(--sf-color-border));
  }
  input:autofill {
    box-shadow: inset 0 0 0 1000px var(--sf-color-surface);
    -webkit-text-fill-color: var(--sf-color-text);
    caret-color: var(--sf-color-text);
    border-color: var(--sf-field-border-color, var(--sf-color-border));
  }
  /* stylelint-enable selector-pseudo-class-no-unknown */

  /* Opt-in native validation feedback. Unconditional :user-invalid/:user-valid
     styling was removed in 0.8.0 (see docs/migration.md): it fires on simple
     focus+blur, so a still-empty required field could be marked invalid mid
     multi-field form-fill, before any submit was attempted — the pattern
     Bootstrap's own .was-validated gate exists to avoid. .sf-live-validate is
     that same gate: put it on a <form> or <fieldset> to scope the pivot to
     that subtree, so it only lights up on a real submit attempt (or explicit
     interaction) instead of applying everywhere by default.

     Safe to combine with manual validation: .sf-is-invalid/.sf-is-valid
     (core/states.css, @layer slashed.states) always win over the rules below
     regardless of selector specificity, because slashed.states is declared
     AFTER slashed.forms in the layer order (core/layers.css) — cascade layer
     order beats specificity. A field you've manually marked .sf-is-invalid
     (e.g. a server-side error) can't be silently un-coloured by these rules
     even if the browser considers it natively valid. */
  .sf-live-validate input:user-invalid,
  .sf-live-validate select:user-invalid,
  .sf-live-validate textarea:user-invalid {
    --sf-field-border-color: var(--sf-color-danger);
  }

  .sf-live-validate input:user-valid,
  .sf-live-validate select:user-valid,
  .sf-live-validate textarea:user-valid {
    --sf-field-border-color: var(--sf-color-success);
  }

  /* Numeric inputs */
  input[type="number"] {
    font-variant-numeric: var(--sf-font-numeric, tabular-nums);
  }

  /* Textarea specifics */
  textarea {
    min-block-size: 6rem;
    resize:         vertical;
  }

  @supports (field-sizing: content) {
    textarea {
      field-sizing: content;
    }
  }

  /* Select: chevron uses FieldText system color — custom properties don't propagate
     into data-URI SVG backgrounds. */
  select {
    padding-inline-end: calc(var(--sf-space-s) + 1.5em);
  }

  select:not([multiple], [size]) {
    background-repeat: no-repeat;
    background-position: right var(--sf-space-s) center;
    background-size: 1em;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='FieldText' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M2.5 4.5L6 8l3.5-3.5'/%3E%3C/svg%3E");
  }

  :dir(rtl) select:not([multiple], [size]) {
    background-position: left var(--sf-space-s) center;
  }

  /* Button / submit / reset. Scoped to :where(form, fieldset) so a bare
     <button> or <input type="submit"/"reset"/"button"> used elsewhere on
     the page (a page-builder's hamburger toggle, a modal close control, a
     third-party widget) is left alone — only controls living inside an
     actual form get the classless treatment. */
  :where(form, fieldset) button:not([class*="sf-"]),
  :where(form, fieldset) input[type="submit"]:not([class*="sf-"]),
  :where(form, fieldset) input[type="reset"]:not([class*="sf-"]),
  :where(form, fieldset) input[type="button"]:not([class*="sf-"]) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sf-space-xs);
    padding-block: var(--sf-space-xs);
    padding-inline: var(--sf-space-m);
    font-size: var(--sf-text-m);
    font-weight: var(--sf-font-weight-heading);
    line-height: var(--sf-leading-normal);
    color: var(--sf-color-text--on-action);
    background-color: var(--sf-color-action);
    border: var(--sf-border-width-1) solid transparent;
    border-radius: var(--sf-radius-m);
    cursor: pointer;
    transition: var(--sf-transition-colors);
    text-decoration: none;
  }

  :where(form, fieldset) button:not([class*="sf-"]):hover,
  :where(form, fieldset) input[type="submit"]:not([class*="sf-"]):hover,
  :where(form, fieldset) input[type="reset"]:not([class*="sf-"]):hover,
  :where(form, fieldset) input[type="button"]:not([class*="sf-"]):hover {

    background-color: var(--sf-color-action--hover, var(--sf-color-action));
  }

  :where(form, fieldset) button:not([class*="sf-"]):focus-visible,
  :where(form, fieldset) input[type="submit"]:not([class*="sf-"]):focus-visible,
  :where(form, fieldset) input[type="reset"]:not([class*="sf-"]):focus-visible,
  :where(form, fieldset) input[type="button"]:not([class*="sf-"]):focus-visible {
    outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-color-border--focus);
    outline-offset: var(--sf-focus-ring-offset);
  }

  :where(form, fieldset) button:not([class*="sf-"]):disabled,
  :where(form, fieldset) input[type="submit"]:not([class*="sf-"]):disabled,
  :where(form, fieldset) input[type="reset"]:not([class*="sf-"]):disabled,
  :where(form, fieldset) input[type="button"]:not([class*="sf-"]):disabled {
    opacity: var(--sf-opacity-disabled);
    cursor:  not-allowed;

  }

  /* Checkbox & radio */
  input[type="checkbox"],
  input[type="radio"] {
    inline-size:    1.125em;
    block-size:     1.125em;
    accent-color:   var(--sf-color-action);
    cursor:         pointer;
    vertical-align: middle;
  }

  /* File input */
  input[type="file"] {
    cursor: pointer;
  }
  input[type="file"]::file-selector-button {
    appearance: none;
    margin-inline-end: var(--sf-space-s);
    padding-block: var(--sf-space-xs);
    padding-inline: var(--sf-space-m);
    font-weight: var(--sf-font-weight-heading);
    color: var(--sf-color-text--on-action);
    background-color: var(--sf-color-action);
    border: var(--sf-border-width-1) solid transparent;
    border-radius: var(--sf-radius-m);
    cursor: pointer;
    transition: var(--sf-transition-colors);
  }
  input[type="file"]::file-selector-button:hover {
    background-color: var(--sf-color-action--hover, var(--sf-color-action));
  }

  /* Range */
  input[type="range"] {
    appearance: none;
    inline-size: 100%;
    background: transparent;
    cursor: pointer;
  }
  input[type="range"]::-webkit-slider-runnable-track {
    block-size: var(--sf-border-width-3, 0.25rem);
    background-color: var(--sf-color-border);
    border-radius: var(--sf-radius-full);
  }
  input[type="range"]::-moz-range-track {
    block-size: var(--sf-border-width-3, 0.25rem);
    background-color: var(--sf-color-border);
    border-radius: var(--sf-radius-full);
  }
  input[type="range"]::-webkit-slider-thumb {
    appearance: none;
    inline-size: 1em;
    block-size: 1em;
    margin-block-start: calc((var(--sf-border-width-3, 0.25rem) - 1em) / 2);
    background-color: var(--sf-color-action);
    border-radius: var(--sf-radius-full);
  }
  input[type="range"]::-moz-range-thumb {
    inline-size: 1em;
    block-size: 1em;
    background-color: var(--sf-color-action);
    border: none;
    border-radius: var(--sf-radius-full);
  }
  input[type="range"]:focus-visible {
    outline: var(--sf-focus-ring-width) var(--sf-focus-ring-style, solid) var(--sf-color-border--focus);
    outline-offset: var(--sf-focus-ring-offset);
  }

  /* Fieldset & legend */
  fieldset {
    border: var(--sf-border-width-1) solid var(--sf-color-border--subtle);
    border-radius: var(--sf-radius-m);
    padding: var(--sf-space-m);
  }

  legend {
    font-weight: var(--sf-font-weight-heading);
    padding-inline: var(--sf-space-xs);
  }

  /* Label */
  label {
    display: block;
    font-weight: var(--sf-font-weight-heading);
    margin-block-end: var(--sf-space-xs);
    color: var(--sf-field-text-color, inherit);
  }

  label:has(:required)::after,
  label:has(+ :required)::after,
  label:has(+ * :required)::after {
    content: var(--sf-field-required-marker, " *");
    color: var(--sf-color-danger);
  }

}
