/*!
 * jst-layout - CSS-first base + layout primitives for JST (opt-in, no runtime)
 * © Brent Jacobs · https://github.com/br3nt/jst · MIT
 *
 * Loads with no JavaScript: the <jst-*> primitives are styled purely as CSS
 * selectors. Custom properties + classless base + the full Every Layout primitive set;
 * component patterns live in jst-components.css. Design rationale and the
 * coverage audit: docs/design/component-library.md.
 *
 *   <link rel="stylesheet" href="jst-layout.css">
 *
 * Theme by overriding the --jst-* custom properties at :root. Structure stays put, the look
 * re-themes (the CSS Zen Garden bet).
 *
 * Everything lives in the `jst` cascade layer, so ANY unlayered app stylesheet
 * outranks these rules regardless of specificity: override with a plain
 * selector, no specificity games, or strip one rule locally with
 * `property: revert-layer`. Sub-layer order (weakest first) is declared here
 * once, and jst.components is filled by jst-components.css.
 */
@layer jst.theme, jst.base, jst.primitives, jst.components;

/* ============================================================================
 * jst.theme - theme variables (the theming contract)
 * ==========================================================================*/
@layer jst.theme {

:root {
  color-scheme: light dark;

  /* One modular space scale: one base + one ratio → derived steps. The base
     is deliberately compact: surfaces read dense-but-calm, not padded out.
     Loosen a whole app by raising --jst-space alone. */
  --jst-space: 0.625rem;
  --jst-ratio: 1.5;
  --jst-space-3xs: calc(var(--jst-space) / var(--jst-ratio) / var(--jst-ratio) / var(--jst-ratio));
  --jst-space-2xs: calc(var(--jst-space) / var(--jst-ratio) / var(--jst-ratio));
  --jst-space-xs:  calc(var(--jst-space) / var(--jst-ratio));
  --jst-space-s:   calc(var(--jst-space) / 1.2);
  --jst-space-m:   var(--jst-space);
  --jst-space-l:   calc(var(--jst-space) * var(--jst-ratio));
  --jst-space-xl:  calc(var(--jst-space) * var(--jst-ratio) * var(--jst-ratio));
  --jst-space-2xl: calc(var(--jst-space) * var(--jst-ratio) * var(--jst-ratio) * var(--jst-ratio));

  /* Measure (max line length), radius, border, fonts. Radius is deliberately
     tight; controls are compact. JST's own look is lean - the padded, rounded
     framework looks live in the theme skins, not the defaults. */
  --jst-measure: 60ch;
  --jst-radius: 0.1875rem;
  /* One radius knob drives three steps: -s for chips, badges and small inner
     elements, -l for containers (cards, dialogs, popovers, toasts). A skin that
     overrides --jst-radius scales all three coherently. */
  --jst-radius-s: calc(var(--jst-radius) * 0.6);
  --jst-radius-l: calc(var(--jst-radius) * 1.6);
  --jst-border-width: 1px;
  --jst-font: system-ui, sans-serif;
  --jst-font-mono: ui-monospace, monospace;
  --jst-font-size: 0.875rem;
  --jst-line: 1.45;
  --jst-control-size: 0.8rem;

  /* Motion: three durations for a coherent motion personality, plus one easing.
     fast = micro-interactions (hovers, toggles, fades); the base step = standard
     dismissals, drawers and accordions; slow = large, page-level movement. A
     skin re-times the whole UI by overriding these. */
  --jst-motion-fast: 125ms;
  --jst-motion: 250ms;
  --jst-motion-slow: 500ms;
  --jst-ease: ease;

  /* Color roles. light-dark() (Baseline 2024) gives free dark mode; the
     @media fallback below covers older engines. The neutral family's warmth is
     one knob: --jst-neutral-h (hue) and --jst-neutral-c (chroma) temperature all
     five greys at once, so one skin line shifts the whole grey family cool→warm.
     The two light values that ship pure (bg, surface) carry a -0.01 offset so at
     the default chroma they cancel to exactly 0 (unchanged white/grey), yet still
     warm once the knob is raised; a negative result clamps to 0 harmlessly. */
  --jst-neutral-h: 250;
  --jst-neutral-c: 0.01;
  --jst-bg:      light-dark(oklch(0.99 calc(var(--jst-neutral-c) - 0.01) var(--jst-neutral-h)), oklch(0.18 var(--jst-neutral-c) var(--jst-neutral-h)));
  --jst-surface: light-dark(oklch(0.96 calc(var(--jst-neutral-c) - 0.01) var(--jst-neutral-h)), oklch(0.23 var(--jst-neutral-c) var(--jst-neutral-h)));
  --jst-fg:      light-dark(oklch(0.22 var(--jst-neutral-c) var(--jst-neutral-h)), oklch(0.93 var(--jst-neutral-c) var(--jst-neutral-h)));
  --jst-muted:   light-dark(oklch(0.50 var(--jst-neutral-c) var(--jst-neutral-h)), oklch(0.70 var(--jst-neutral-c) var(--jst-neutral-h)));
  --jst-border:  light-dark(oklch(0.87 var(--jst-neutral-c) var(--jst-neutral-h)), oklch(0.35 var(--jst-neutral-c) var(--jst-neutral-h)));

  /* One accent → a derived shade ramp via relative color syntax (Baseline 2024).
     Override --jst-accent alone and the ramp follows. */
  --jst-accent:     oklch(0.55 0.18 255);
  --jst-accent-400: oklch(from var(--jst-accent) calc(l + 0.10) c h);
  --jst-accent-600: oklch(from var(--jst-accent) calc(l - 0.08) c h);
  --jst-accent-700: oklch(from var(--jst-accent) calc(l - 0.16) c h);

  /* Accessible foreground on the accent. Static fallback; upgraded to
     contrast-color() where supported (not yet Baseline). */
  --jst-accent-fg: white;

  /* Status roles, used by validation, alerts, badges, and toasts. Each has a
     foreground for text ON that color: a static fallback below, upgraded to
     contrast-color() where the engine can pick it (not yet Baseline). */
  --jst-ok: oklch(0.62 0.15 150);
  --jst-warn: oklch(0.75 0.15 80);
  --jst-error: oklch(0.55 0.19 25);
  --jst-ok-fg: white;
  --jst-warn-fg: oklch(0.25 0.05 80);
  --jst-error-fg: white;

  /* Elevation and overlay dimming, shared by every floating surface. */
  --jst-shadow: 0 0.25rem 0.75rem oklch(0 0 0 / 0.12);
  --jst-backdrop: oklch(0 0 0 / 0.45);

  /* Resting elevation for static surfaces (cards / boxes), distinct from the
     floating --jst-shadow used by dialogs, popovers and toasts. Default is flat:
     JST's own look separates surfaces by tone and border, not elevation. A skin
     that wants raised cards (material, daisy, bootstrap…) overrides this variable. */
  --jst-shadow-surface: none;

  /* Support text: hints, sublines, metadata under a control or heading. */
  --jst-font-small: 0.8rem;
}

/* Manual scheme override: set data-scheme on <html> (or any subtree) and the
   light-dark() variables follow. This is the whole dark-mode toggle. */
[data-scheme="light"] { color-scheme: light; }
[data-scheme="dark"]  { color-scheme: dark; }

@supports (color: contrast-color(red)) {
  :root {
    --jst-accent-fg: contrast-color(var(--jst-accent));
    --jst-ok-fg: contrast-color(var(--jst-ok));
    --jst-warn-fg: contrast-color(var(--jst-warn));
    --jst-error-fg: contrast-color(var(--jst-error));
  }
}

/* Fallback for engines without light-dark(): swap the role variables by scheme. */
@supports not (color: light-dark(white, black)) {
  :root {
    --jst-bg: oklch(0.99 calc(var(--jst-neutral-c) - 0.01) var(--jst-neutral-h)); --jst-surface: oklch(0.96 calc(var(--jst-neutral-c) - 0.01) var(--jst-neutral-h));
    --jst-fg: oklch(0.22 var(--jst-neutral-c) var(--jst-neutral-h)); --jst-muted: oklch(0.50 var(--jst-neutral-c) var(--jst-neutral-h));
    --jst-border: oklch(0.87 var(--jst-neutral-c) var(--jst-neutral-h));
  }
  @media (prefers-color-scheme: dark) {
    :root {
      --jst-bg: oklch(0.18 var(--jst-neutral-c) var(--jst-neutral-h)); --jst-surface: oklch(0.23 var(--jst-neutral-c) var(--jst-neutral-h));
      --jst-fg: oklch(0.93 var(--jst-neutral-c) var(--jst-neutral-h)); --jst-muted: oklch(0.70 var(--jst-neutral-c) var(--jst-neutral-h));
      --jst-border: oklch(0.35 var(--jst-neutral-c) var(--jst-neutral-h));
    }
  }
}

} /* @layer jst.theme */

/* ============================================================================
 * jst.base - classless base (style elements directly, Pico-style)
 * ==========================================================================*/
@layer jst.base {

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

body {
  margin: 0;
  padding: var(--jst-space-l);
  background: var(--jst-bg);
  color: var(--jst-fg);
  font-family: var(--jst-font);
  font-size: var(--jst-font-size);
  line-height: var(--jst-line);
  -webkit-text-size-adjust: 100%;
  /* Native checkboxes, radios, ranges, and progress bars pick up the accent
     (Baseline 2022): themed form controls with zero component CSS. The caret
     and text selection derive from the same variable. */
  accent-color: var(--jst-accent);
  caret-color: var(--jst-accent);
}

::selection { background: color-mix(in oklch, var(--jst-accent) 22%, transparent); }

h1, h2, h3, h4, p, ul, ol, figure, blockquote { margin-block: 0; }
:where(h1, h2, h3, h4) { line-height: 1.15; text-wrap: balance; }
h1 { font-size: 1.4rem; }
h2 { font-size: 1.15rem; }
h3 { font-size: 1rem; }
h4 { font-size: 0.9rem; }
p { max-inline-size: var(--jst-measure); text-wrap: pretty; }

a { color: var(--jst-accent-600); }

:where(button, .jst-button) {
  font: inherit;
  font-size: var(--jst-control-size);
  line-height: 1.3;
  padding: 0.25em 0.65em;
  border: var(--jst-border-width) solid transparent;
  border-radius: var(--jst-radius);
  background: var(--jst-accent);
  color: var(--jst-accent-fg);
  cursor: pointer;
}
/* Derive the hover shade at use-site (not from the :root --jst-accent-600 custom property)
   so a button inside a re-themed subtree hovers to the right shade. See the
   relative-color gotcha in docs/design/component-library.md. */
:where(button, .jst-button):hover { background: oklch(from var(--jst-accent) calc(l - 0.08) c h); }

/* Button variants: the accent button is the default; everything else is a
   data-variant, so one attribute re-roles a button without new classes.
   quiet = bordered neutral, ghost = borderless, danger = destructive. */
:where(button, .jst-button)[data-variant="quiet"] {
  background: var(--jst-bg);
  color: var(--jst-fg);
  border-color: var(--jst-border);
}
:where(button, .jst-button)[data-variant="quiet"]:hover { background: var(--jst-surface); }
:where(button, .jst-button)[data-variant="ghost"] {
  background: transparent;
  color: var(--jst-muted);
}
:where(button, .jst-button)[data-variant="ghost"]:hover { background: var(--jst-surface); color: var(--jst-fg); }
:where(button, .jst-button)[data-variant="danger"] { background: var(--jst-error); color: var(--jst-error-fg); }
:where(button, .jst-button)[data-variant="danger"]:hover { background: oklch(from var(--jst-error) calc(l - 0.07) c h); }

:where(input, textarea, select) {
  font: inherit;
  font-size: var(--jst-control-size);
  line-height: 1.3;
  padding: 0.25em 0.5em;
  border: var(--jst-border-width) solid var(--jst-border);
  border-radius: var(--jst-radius);
  background: var(--jst-bg);
  color: var(--jst-fg);
}
:where(button, .jst-button, a, summary, input, textarea, select, [tabindex]):focus-visible {
  outline: 2px solid var(--jst-accent);
  outline-offset: calc(var(--jst-border-width) * -1);
}

/* One disabled treatment everywhere. */
:where(button, .jst-button, a, input, textarea, select)[disabled],
:where([aria-disabled="true"]) {
  opacity: 0.5;
  cursor: default;
  pointer-events: none;
}

/* Textareas grow with their content where the engine supports it (Baseline
   Newly 2026); elsewhere they stay ordinary resizable textareas. */
@supports (field-sizing: content) {
  textarea {
    field-sizing: content;
    min-block-size: 2.4rem;
    max-block-size: 60vb;
  }
}

/* Validation, from the platform's own constraint state: only after the user
   has interacted (:user-invalid, not :invalid), so pristine forms stay calm. */
:where(input, textarea, select):user-invalid { border-color: var(--jst-error); }
jst-field:has(:user-invalid) > label { color: var(--jst-error); }

::file-selector-button {
  font: inherit;
  margin-inline-end: var(--jst-space-s);
  padding: var(--jst-space-2xs) var(--jst-space-s);
  border: var(--jst-border-width) solid var(--jst-border);
  border-radius: var(--jst-radius);
  background: var(--jst-surface);
  color: var(--jst-fg);
  cursor: pointer;
}

kbd {
  font-family: var(--jst-font-mono);
  font-size: 0.85em;
  padding: 0.1em 0.4em;
  border: var(--jst-border-width) solid var(--jst-border);
  border-block-end-width: 2px;
  border-radius: var(--jst-radius-s);
  background: var(--jst-surface);
}

/* Prose: long-form flow. The global reset zeroes margins because app UIs
   space through <jst-stack>; inside .jst-prose, flowing text gets its rhythm
   back (the Every Layout flow owl), with more air before headings. */
.jst-prose > * + * { margin-block-start: var(--jst-space-m); }
.jst-prose > :where(h1, h2, h3, h4) { margin-block-start: var(--jst-space-xl); }
.jst-prose > :where(h1, h2, h3, h4) + * { margin-block-start: var(--jst-space-s); }
.jst-prose :where(ul, ol) { padding-inline-start: 1.2em; }
.jst-prose li + li { margin-block-start: var(--jst-space-3xs); }

/* Key-value pairs: the metadata block every record screen needs. */
.jst-kv {
  display: grid; grid-template-columns: max-content 1fr;
  gap: var(--jst-space-3xs) var(--jst-space-m);
  margin: 0;
}
.jst-kv dt {
  font-family: var(--jst-font-mono); font-size: 0.68rem; font-weight: 600;
  letter-spacing: 0.04em; text-transform: uppercase; color: var(--jst-muted);
  align-self: baseline;
}
.jst-kv dd { margin: 0; min-inline-size: 0; }

/* Skip link: visually hidden until focused, then a floating button. */
.jst-skip-link {
  position: absolute; inset-block-start: var(--jst-space-s); inset-inline-start: var(--jst-space-s);
  z-index: 100;
  padding: 0.3em 0.75em; border-radius: var(--jst-radius);
  background: var(--jst-accent); color: var(--jst-accent-fg); text-decoration: none;
  translate: 0 calc(-100% - var(--jst-space-l));
}
.jst-skip-link:focus-visible { translate: 0; }

/* Screen-reader-only content that stays in the accessibility tree. */
.jst-visually-hidden {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Micro-label: the small mono uppercase label over a control. .jst-eyebrow
   is the same voice used as a heading kicker. One rule. */
:where(label, legend), .jst-eyebrow {
  font-family: var(--jst-font-mono);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--jst-muted);
}

fieldset {
  border: var(--jst-border-width) solid var(--jst-border);
  border-radius: var(--jst-radius);
  padding: var(--jst-space-s) var(--jst-space-m) var(--jst-space-m);
  margin: 0;
  min-inline-size: 0;
}

code, pre { font-family: var(--jst-font-mono); }

} /* @layer jst.base */

/* ============================================================================
 * jst.primitives - layout primitives (Every Layout recipes, CSS-only)
 * Set per-instance values with an inline custom property, e.g.
 *   <jst-stack style="--space: var(--jst-space-l)">
 * ==========================================================================*/
@layer jst.primitives {

/* Stack: even vertical rhythm between flow children. */
jst-stack {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: var(--space, var(--jst-space-m));
}

/* Cluster: a wrapping group of inline-ish things (tags, button rows). */
jst-cluster {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space, var(--jst-space-s));
  justify-content: var(--justify, flex-start);
  align-items: var(--align, center);
}

/* Grid: intrinsic responsive grid - as many --min-wide columns as fit, no
   media queries. min() keeps a single column from overflowing on narrow screens. */
jst-grid {
  display: grid;
  gap: var(--space, var(--jst-space-m));
  grid-template-columns: repeat(auto-fit, minmax(min(var(--min, 16rem), 100%), 1fr));
}

/* Sidebar: a sidebar next to a content area that wraps under it when the
   content would get narrower than --content-min. No breakpoint. */
jst-sidebar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space, var(--jst-space-m));
}
jst-sidebar > * {
  flex-grow: 1;
  flex-basis: var(--side-width, auto);
}
jst-sidebar > :not([data-side]) {
  flex-basis: 0;
  flex-grow: 999;
  min-inline-size: var(--content-min, 50%);
}

/* Center: a centered column capped at the measure, with optional gutters. */
jst-center {
  display: block;
  box-sizing: content-box;
  max-inline-size: var(--measure, var(--jst-measure));
  margin-inline: auto;
  padding-inline: var(--gutters, 0);
}

/* Box: a padded surface with a border and matching radius. A container, so it
   takes the larger container radius. */
jst-box {
  display: block;
  padding: var(--space, var(--jst-space-m));
  border: var(--jst-border-width) solid var(--jst-border);
  border-radius: var(--jst-radius-l);
  background: var(--surface, var(--jst-surface));
}

/* Switcher: children sit side by side sharing the row equally, and ALL flip to
   stacked once the container is narrower than --threshold. The flex-basis
   trick: (threshold - 100%) is negative when the container is wider than the
   threshold (basis clamps to 0, so flex-grow shares the row) and hugely
   positive when narrower (each child fills the row). */
jst-switcher {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space, var(--jst-space-m));
}
jst-switcher > * {
  flex-grow: 1;
  flex-basis: calc((var(--threshold, 30rem) - 100%) * 999);
}
/* Optional limit: cap items per row; overflow wraps to full-width rows.
   A custom property can't drive a selector, so the cap is an attribute. */
jst-switcher[limit="2"] > :nth-last-child(n + 3),
jst-switcher[limit="2"] > :nth-last-child(n + 3) ~ *,
jst-switcher[limit="3"] > :nth-last-child(n + 4),
jst-switcher[limit="3"] > :nth-last-child(n + 4) ~ *,
jst-switcher[limit="4"] > :nth-last-child(n + 5),
jst-switcher[limit="4"] > :nth-last-child(n + 5) ~ * {
  flex-basis: 100%;
}

/* Cover: a block at least --min-height tall whose principal child (marked
   data-principal) is vertically centered; anything before/after it is pushed
   to the edges, like a hero with a header and footer. */
jst-cover {
  display: flex;
  flex-direction: column;
  gap: var(--space, var(--jst-space-m));
  min-block-size: var(--min-height, 100vh);
  padding: var(--space, var(--jst-space-m));
}
jst-cover > [data-principal] { margin-block: auto; }

/* Frame: crop media (or center anything else) to a fixed aspect ratio. */
jst-frame {
  display: flex;
  justify-content: center;
  align-items: center;
  aspect-ratio: var(--ratio, 16 / 9);
  overflow: hidden;
}
jst-frame > :where(img, video) {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

/* Reel: a horizontal scroller for overflowing content (cards, thumbnails).
   Children keep their intrinsic or --item-width size instead of shrinking. */
jst-reel {
  display: flex;
  gap: var(--space, var(--jst-space-m));
  block-size: var(--height, auto);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-color: var(--jst-muted) transparent;
  /* Opt into snapping per instance: --snap: x mandatory (or x proximity). */
  scroll-snap-type: var(--snap, none);
}
jst-reel > * { flex: 0 0 var(--item-width, auto); scroll-snap-align: start; }
jst-reel > img { block-size: 100%; flex-basis: auto; inline-size: auto; }

/* Imposter: an element positioned over its nearest positioning container,
   centered by default. The :has() rule makes the direct parent that container
   automatically; content larger than the container scrolls inside. */
:where(:has(> jst-imposter)) { position: relative; }
jst-imposter {
  position: absolute;
  inset-block-start: var(--block, 50%);
  inset-inline-start: var(--inline, 50%);
  transform: translate(-50%, -50%);
  max-inline-size: calc(100% - (var(--margin, 0px) * 2));
  max-block-size: calc(100% - (var(--margin, 0px) * 2));
  overflow: auto;
}
jst-imposter[fixed] { position: fixed; }

/* Field: one labelled control - label, control, then an optional <small>
   hint or error line - spaced as a single unit. */
jst-field {
  display: flex;
  flex-direction: column;
  gap: var(--jst-space-3xs);
}
jst-field > small { color: var(--jst-muted); font-size: var(--jst-font-small); }
jst-field > small[data-error] { color: var(--jst-error); }

/* Form row: a single-line form. Fields share the row (each at least
   --field-min wide) and wrap when tight; buttons and anything marked
   data-actions keep their natural size, aligned to the control baseline. */
jst-form-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space, var(--jst-space-s));
  align-items: flex-end;
}
jst-form-row > * { flex: 1 1 var(--field-min, 9rem); min-inline-size: 0; }
jst-form-row > :where(button, [data-actions]) { flex: 0 0 auto; }

/* Carousel: a reel that snaps one child per view. Progressive: engines with
   CSS carousels (Chromium) get scroll buttons and clickable markers with zero
   JS; everywhere else it is a smooth snapping reel. */
jst-reel[data-carousel] {
  position: relative;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
}
jst-reel[data-carousel] > * { flex: 0 0 100%; scroll-snap-align: center; }
@supports selector(::scroll-marker) {
  jst-reel[data-carousel] {
    scroll-marker-group: after;
    /* Explicit anchor for the scroll buttons; anchor-scope keeps each
       carousel's name to itself so several can share a page. */
    anchor-name: --jst-carousel;
    anchor-scope: --jst-carousel;
  }
  jst-reel[data-carousel]::scroll-marker-group {
    display: flex; justify-content: center; gap: var(--jst-space-2xs);
    padding-block: var(--jst-space-2xs);
  }
  jst-reel[data-carousel] > *::scroll-marker {
    content: "";
    inline-size: 0.5em; block-size: 0.5em; border-radius: 50%;
    background: var(--jst-border); cursor: pointer;
  }
  jst-reel[data-carousel] > *::scroll-marker:target-current { background: var(--jst-accent); }
  /* The buttons implicitly anchor to the scroll container; anchor() insets
     overlay them just inside its edges (their containing block is NOT the
     reel, so plain insets would escape it). */
  jst-reel[data-carousel]::scroll-button(left),
  jst-reel[data-carousel]::scroll-button(right) {
    position: absolute; z-index: 1;
    position-anchor: --jst-carousel;
    inset-block-start: anchor(center); translate: 0 -50%;
    display: grid; place-items: center;
    border: var(--jst-border-width) solid var(--jst-border); border-radius: 50%;
    inline-size: 1.8em; block-size: 1.8em; padding: 0;
    background: var(--jst-bg); color: var(--jst-fg); cursor: pointer;
  }
  jst-reel[data-carousel]::scroll-button(left)  { content: "‹"; inset-inline-start: calc(anchor(start) + var(--jst-space-2xs)); }
  jst-reel[data-carousel]::scroll-button(right) { content: "›"; inset-inline-end: calc(anchor(end) + var(--jst-space-2xs)); }
  jst-reel[data-carousel]::scroll-button(*):disabled { opacity: 0.3; }
}

/* Opt-in container-query context: the subtree of any [data-container] element
   can respond to ITS width with @container, not the viewport's. */
[data-container] { container-type: inline-size; }

/* Reveal-on-scroll: scroll-driven animation, zero JS. Progressive (Chromium;
   Firefox behind a flag) and disabled for reduced-motion users. */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    .jst-reveal {
      animation: jst-reveal both;
      animation-timeline: view();
      animation-range: entry 0% entry 60%;
    }
    @keyframes jst-reveal { from { opacity: 0; translate: 0 1rem; } }
  }
}

/* Icon: an inline icon sized and aligned to the surrounding text, with an
   even gap when a label follows. 1cap tracks the font's capital height where
   supported; the em fallback is close enough elsewhere. */
jst-icon {
  display: inline-flex;
  align-items: baseline;
  gap: var(--space, var(--jst-space-3xs));
}
jst-icon > svg {
  align-self: center;
  block-size: 0.75em;
  inline-size: auto;
  /* A sprite reference without a viewBox has no intrinsic ratio, so
     inline-size: auto would collapse to 0 (Safari). Fall back to square. */
  aspect-ratio: auto 1 / 1;
  fill: currentColor;
}
@supports (block-size: 1cap) {
  jst-icon > svg { block-size: 1cap; }
}

} /* @layer jst.primitives */
