/* SLASHED — optional/utilities.css
   @layer slashed.utilities
   Static opt-in utility helpers.
   Prefix: .sf-*; keep dynamic state in .sf-is-* classes.

   Most definitions below are STAGED (commented out) — SLASHED is BEM-first
   by design and ships only a curated subset of utility classes; uncomment
   a block to activate it. The visibility, heading, text-size,
   hover-transform, list-reset, marker-colour, alternate-selection, and
   sticky blocks are active. */

@layer slashed.utilities {

  /* Text wrap — per-element override of the global text-wrap defaults
     (--sf-heading-text-wrap / --sf-body-text-wrap). */
  /* .sf-balance { text-wrap: balance; } */
  /* .sf-pretty  { text-wrap: pretty; } */

  /* Object fit / position — per-element control of replaced media (img,
     video) without touching the global --sf-object-* tokens. */
  /* .sf-object-cover   { object-fit: cover; } */
  /* .sf-object-contain { object-fit: contain; } */

  /* .sf-object-top    { object-position: top; } */
  /* .sf-object-center { object-position: center; } */
  /* .sf-object-bottom { object-position: bottom; } */

  /* ============================================================
   * VISIBILITY HELPERS (.sf-invisible / .sf-visible)
   * Single-property visibility overrides — not a "state" (core/states.css)
   * since neither reacts to anything on its own; toggle from JS to hide
   * something without pulling it out of layout (.sf-invisible), or to
   * force-show an element that inherited visibility:hidden from a parent
   * (.sf-visible). This is the hook docs/roadmap.md's "reveal on scroll"
   * pattern targets — pair with an IntersectionObserver in your own code.
   * ============================================================ */
  .sf-invisible { visibility: hidden; }
  .sf-visible   { visibility: visible; }

  /* ============================================================
   * HEADING HELPER CLASSES (.sf-h1 – .sf-h6)
   * Heading-level typography on any element without changing
   * document semantics. Carries the typographic scale from the
   * h1–h6 rules in core/base.css, including overflow-wrap; margins
   * are intentionally NOT reset here (a visual class shouldn't clear
   * the caller's spacing the way the base element reset does).
   *   <div class="sf-h2">Visually H2, semantically a div</div>
   * ============================================================ */
  .sf-h1,
  .sf-h2,
  .sf-h3,
  .sf-h4,
  .sf-h5,
  .sf-h6 {
    font-family:   var(--sf-heading-font-family, var(--sf-font-heading));
    font-weight:   var(--sf-font-weight-heading);
    color:         var(--sf-heading-color, var(--sf-color-heading));
    text-wrap:     var(--sf-heading-text-wrap);
    overflow-wrap: break-word;
  }
  .sf-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); }
  .sf-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); }
  .sf-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); }
  .sf-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); }
  .sf-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); }
  .sf-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); }

  /* ============================================================
   * TEXT-SIZE UTILITIES (.sf-text-2xs – .sf-text-4xl)
   * Apply a full fluid text-size role — size, line-height, weight,
   * tracking, and measure (max-inline-size) — to any element in one
   * class, mirroring the per-size token sets in core/tokens.css.
   *   <p class="sf-text-l">Larger body copy</p>
   * ============================================================ */
  .sf-text-2xs { font-size: var(--sf-text-2xs); line-height: var(--sf-text-2xs-line-height); font-weight: var(--sf-text-2xs-font-weight); letter-spacing: var(--sf-text-2xs-letter-spacing); max-inline-size: var(--sf-text-2xs-max-width); }
  .sf-text-xs  { font-size: var(--sf-text-xs);  line-height: var(--sf-text-xs-line-height);  font-weight: var(--sf-text-xs-font-weight);  letter-spacing: var(--sf-text-xs-letter-spacing);  max-inline-size: var(--sf-text-xs-max-width); }
  .sf-text-s   { font-size: var(--sf-text-s);   line-height: var(--sf-text-s-line-height);   font-weight: var(--sf-text-s-font-weight);   letter-spacing: var(--sf-text-s-letter-spacing);   max-inline-size: var(--sf-text-s-max-width); }
  .sf-text-m   { font-size: var(--sf-text-m);   line-height: var(--sf-text-m-line-height);   font-weight: var(--sf-text-m-font-weight);   letter-spacing: var(--sf-text-m-letter-spacing);   max-inline-size: var(--sf-text-m-max-width); }
  .sf-text-l   { font-size: var(--sf-text-l);   line-height: var(--sf-text-l-line-height);   font-weight: var(--sf-text-l-font-weight);   letter-spacing: var(--sf-text-l-letter-spacing);   max-inline-size: var(--sf-text-l-max-width); }
  .sf-text-xl  { font-size: var(--sf-text-xl);  line-height: var(--sf-text-xl-line-height);  font-weight: var(--sf-text-xl-font-weight);  letter-spacing: var(--sf-text-xl-letter-spacing);  max-inline-size: var(--sf-text-xl-max-width); }
  .sf-text-2xl { font-size: var(--sf-text-2xl); line-height: var(--sf-text-2xl-line-height); font-weight: var(--sf-text-2xl-font-weight); letter-spacing: var(--sf-text-2xl-letter-spacing); max-inline-size: var(--sf-text-2xl-max-width); }
  .sf-text-3xl { font-size: var(--sf-text-3xl); line-height: var(--sf-text-3xl-line-height); font-weight: var(--sf-text-3xl-font-weight); letter-spacing: var(--sf-text-3xl-letter-spacing); max-inline-size: var(--sf-text-3xl-max-width); }
  .sf-text-4xl { font-size: var(--sf-text-4xl); line-height: var(--sf-text-4xl-line-height); font-weight: var(--sf-text-4xl-font-weight); letter-spacing: var(--sf-text-4xl-letter-spacing); max-inline-size: var(--sf-text-4xl-max-width); }

  /* ============================================================
   * Z-INDEX UTILITIES (.sf-z-*)
   * Named z-index scale from core/tokens.css. Requires a
   * positioning context (position: relative / absolute / fixed /
   * sticky) to have any effect.
   * ============================================================ */
  /* .sf-z-below    { z-index: var(--sf-z-below); }
  .sf-z-base      { z-index: var(--sf-z-base); }
  .sf-z-raised    { z-index: var(--sf-z-raised); }
  .sf-z-sticky    { z-index: var(--sf-z-sticky); }
  .sf-z-fixed     { z-index: var(--sf-z-fixed); }
  .sf-z-dropdown  { z-index: var(--sf-z-dropdown); }
  .sf-z-overlay   { z-index: var(--sf-z-overlay); }
  .sf-z-modal     { z-index: var(--sf-z-modal); }
  .sf-z-toast     { z-index: var(--sf-z-toast); }
  .sf-z-tooltip   { z-index: var(--sf-z-tooltip); } */

  /* ============================================================
   * DECORATIVE ANIMATION UTILITIES (.sf-spin / .sf-ping / etc.)
   * Backed by keyframes in core/motion.css; animation shorthand
   * tokens live in core/tokens.css. Wrapped in reduced-motion
   * media query — elements are invisible/static otherwise.
   *
   * .sf-shimmer: override --sf-shimmer-base and
   * --sf-shimmer-highlight to match your surface if needed.
   * ============================================================ */
  /* @media (prefers-reduced-motion: no-preference) {
    .sf-spin  { animation: var(--sf-animation-spin); }
    .sf-ping  { animation: var(--sf-animation-ping); }
    .sf-blink { animation: var(--sf-animation-blink); }
    .sf-float { animation: var(--sf-animation-float); }
    .sf-shimmer {
      --sf-shimmer-base:      var(--sf-color-inset);
      --sf-shimmer-highlight: var(--sf-color-raised);
      background: linear-gradient(
        90deg,
        var(--sf-shimmer-base)      0%,
        var(--sf-shimmer-highlight) 50%,
        var(--sf-shimmer-base)      100%
      );
      background-size: 200% 100%;
      animation: var(--sf-animation-shimmer);
    }
  } */

  /* ============================================================
   * HOVER-TRANSFORM UTILITIES (.sf-hover-*)
   * Simple, composable hover-triggered transforms — apply directly to
   * any element (card, image, icon, button). Duration/easing reuse the
   * same tokens as --sf-transition-transform (core/tokens.css), but the
   * transition-property targets scale/translate directly: these rules
   * change the `scale` and `translate` properties (CSS Transforms
   * Level 2), which a `transition: transform ...` shorthand does not
   * cover — transform is a distinct property from its scale/translate/
   * rotate components and won't pick up their changes. The global
   * duration kill-switch in core/accessibility.css neutralizes these
   * under prefers-reduced-motion without needing a separate guard here.
   * On a/button/input/select/textarea/summary, core/motion.css's global
   * transition (slashed.motion, layered after slashed.utilities) also
   * covers scale/translate, so --sf-duration-fast wins there instead of
   * --sf-duration-normal — the class selector's higher specificity still
   * keeps these rules in control of *which* properties transition.
   * ============================================================ */
  .sf-hover-grow, .sf-hover-shrink, .sf-hover-float, .sf-hover-sink,
  .sf-hover-slide-start, .sf-hover-slide-end {
    transition-property:        scale, translate;
    transition-duration:        var(--sf-duration-normal);
    transition-timing-function: var(--sf-ease-out);
  }
  .sf-hover-grow:hover    { scale: var(--sf-hover-grow-scale, 1.05); }
  .sf-hover-shrink:hover  { scale: var(--sf-hover-shrink-scale, 0.95); }
  .sf-hover-float:hover   { translate: 0 calc(-1 * var(--sf-hover-lift, 0.25em)); }
  .sf-hover-sink:hover    { translate: 0 var(--sf-hover-lift, 0.25em); }
  .sf-hover-slide-start:hover { translate: calc(-1 * var(--sf-hover-slide, 0.5em)) 0; }
  .sf-hover-slide-end:hover   { translate: var(--sf-hover-slide, 0.5em) 0; }
  /* translate is physical (X axis), but the -start/-end names are logical —
     flip the horizontal direction under RTL so "start"/"end" stay correct. */
  :where([dir="rtl"]) .sf-hover-slide-start:hover { translate: var(--sf-hover-slide, 0.5em) 0; }
  :where([dir="rtl"]) .sf-hover-slide-end:hover   { translate: calc(-1 * var(--sf-hover-slide, 0.5em)) 0; }

  /* ============================================================
   * LIST RESET (.sf-list-none)
   * Drops marker + inline start-padding in one class, on any
   * <ul>/<ol> (or element with list-style applied).
   * ============================================================ */
  .sf-list-none {
    list-style: none;
    padding-inline-start: 0;
  }

  /* ============================================================
   * LIST MARKER COLOUR (.sf-marker--*)
   * Colour the ::marker of an arbitrary list. Brand/action families
   * only — for any other colour set `::marker { color }` yourself.
   * The :not(.sf-prose *) guard keeps this utility from reaching into
   * a .sf-prose subtree, which owns its markers via
   * --sf-prose-marker-color (utilities are layered after macros, so
   * without the guard this would win). Covers the element's own marker
   * and descendant list-item markers.
   * ============================================================ */
  .sf-marker--primary :not(.sf-prose *)::marker, .sf-marker--primary:not(.sf-prose *)::marker { color: var(--sf-color-primary); }
  .sf-marker--secondary :not(.sf-prose *)::marker, .sf-marker--secondary:not(.sf-prose *)::marker { color: var(--sf-color-secondary); }
  .sf-marker--tertiary :not(.sf-prose *)::marker, .sf-marker--tertiary:not(.sf-prose *)::marker { color: var(--sf-color-tertiary); }
  .sf-marker--action :not(.sf-prose *)::marker, .sf-marker--action:not(.sf-prose *)::marker { color: var(--sf-color-action); }

  /* ============================================================
   * ALTERNATE SELECTION COLOUR (.sf-selection--alt)
   * Overrides the ::selection custom properties (core/base.css) on
   * the element and its descendants, for surfaces where the default
   * selection colours land with poor contrast (e.g. a dark card on a
   * light page). Backed by --sf-color-selection-*--alt (core/tokens.css).
   * ============================================================ */
  .sf-selection--alt {
    --sf-color-selection-bg:   var(--sf-color-selection-bg--alt);
    --sf-color-selection-text: var(--sf-color-selection-text--alt);
  }

  /* ============================================================
   * STICKY POSITIONING (.sf-sticky)
   * The sticky mechanism — position: sticky at the header-aware
   * --sf-sticky-offset. Offset modifiers (--s/-m/-l) add extra gap
   * on top of that fluid offset. (position: sticky is already
   * conditional by nature, so there is no separate runtime-toggled
   * state class.)
   * ============================================================ */
  .sf-sticky {
    position: sticky;
    inset-block-start: var(--sf-sticky-offset, 0);
    z-index: var(--sf-z-sticky);
  }
  .sf-sticky--s { inset-block-start: calc(var(--sf-sticky-offset, 0) + var(--sf-space-s)); }
  .sf-sticky--m { inset-block-start: calc(var(--sf-sticky-offset, 0) + var(--sf-space-m)); }
  .sf-sticky--l { inset-block-start: calc(var(--sf-sticky-offset, 0) + var(--sf-space-l)); }

  /* ============================================================
   * CONTENT-WIDTH UTILITIES (.sf-width-*)
   * Constrain an element to a fraction of the content column
   * (--sf-content-width) and centre it in normal flow. inline-size:100%
   * lets it shrink below the cap on a narrower parent; margin-inline:auto
   * absorbs the leftover space to centre it. The keyword variants map to
   * CSS width keywords: full = 100% uncapped, plus auto / fit-content /
   * min-content / max-content. Logical properties throughout, so they
   * follow the writing mode. Centring assumes normal flow — inside a
   * flex/grid parent, alignment is governed by the container instead.
   *   <div class="sf-width-50">Half the content width, centred</div>
   * ============================================================ */
  .sf-width-10, .sf-width-20, .sf-width-30, .sf-width-40, .sf-width-50,
  .sf-width-60, .sf-width-70, .sf-width-80, .sf-width-90 {
    inline-size:   100%;
    margin-inline: auto;
  }
  .sf-width-10 { max-inline-size: calc(var(--sf-content-width) * 0.1); }
  .sf-width-20 { max-inline-size: calc(var(--sf-content-width) * 0.2); }
  .sf-width-30 { max-inline-size: calc(var(--sf-content-width) * 0.3); }
  .sf-width-40 { max-inline-size: calc(var(--sf-content-width) * 0.4); }
  .sf-width-50 { max-inline-size: calc(var(--sf-content-width) * 0.5); }
  .sf-width-60 { max-inline-size: calc(var(--sf-content-width) * 0.6); }
  .sf-width-70 { max-inline-size: calc(var(--sf-content-width) * 0.7); }
  .sf-width-80 { max-inline-size: calc(var(--sf-content-width) * 0.8); }
  .sf-width-90 { max-inline-size: calc(var(--sf-content-width) * 0.9); }

  .sf-width-full { inline-size: 100%; max-inline-size: none; }
  .sf-width-auto { inline-size: auto; }
  .sf-width-fit  { inline-size: fit-content; }
  .sf-width-min  { inline-size: min-content; }
  .sf-width-max  { inline-size: max-content; }

  /* ============================================================
   * OVERFLOW-DEBUG UTILITY (.sf-debug-overflow)
   * Dev-only diagnostic: apply to <body> (or any container) to outline
   * every descendant, making the element causing horizontal page
   * overflow visually obvious. Not a production utility — remove
   * before shipping.
   * ============================================================ */
  /* .sf-debug-overflow,
  .sf-debug-overflow *,
  .sf-debug-overflow *::before,
  .sf-debug-overflow *::after {
    outline: 1px solid oklch(0.6 0.25 30 / 0.6);
  } */

}
