/*
Skeleton

A placeholder shape that pulses while content loads. Two uses:

  <Skeleton class="w-..." />          a sized shape on its own
  <Skeleton loading={x}>content</Skeleton>
                                      wraps the real content and keeps its
                                      box: the content is laid out (hidden)
                                      while loading, so nothing moves when
                                      it appears.

  .skeleton[data-shape="block" | "text" | "circle"]
  .skeleton[data-loading]             the pulse and the muted fill
  .skeleton-line                      one bar of a text skeleton
*/

/* The package's layer order, repeated in every sheet so it holds whichever sheet loads first. */
@layer reset, tokens, base, components, templates, utilities, overrides;

@layer templates {

.skeleton {
  box-sizing:    border-box;
  display:       block;
  border-radius: var(--radius-item);
}

.skeleton.is-inline {
  display:        inline-block;
  vertical-align: bottom;   /* centered in the line, never taller than it */
}

.skeleton[data-loading] {
  color:      transparent;   /* bare text (<Skeleton>Loading</Skeleton>) is not painted */
  background: var(--bg-muted);
  animation:  skeleton-pulse calc(var(--duration-loop) * 4) var(--ease-in-out) infinite;
  cursor:     progress;
}

/* Inline around text: the skeleton flows with
   the text, so each line of it gets its own bar and the paragraph keeps
   its exact shape. The text is transparent while loading. */
.skeleton.is-inline:not(:empty) {
  display:                    inline;
  vertical-align:             baseline;
  box-decoration-break:       clone;
  -webkit-box-decoration-break: clone;
}

.skeleton.is-inline[data-loading]:not(:empty) {
  color:       transparent;
  user-select: none;
}

/* One wrapped control (a switch, a button, an avatar): the wrapper is a
   flex box as wide as the control, with no stretch, so the placeholder is
   exactly the control's box, loaded or loading. A block wrapper would be as
   wide as the row and as tall as the text strut. Everything else keeps
   block layout, so the wrapper never changes the layout of what it wraps:
   a block child already fills the row, and any other lone element may sit
   in a sentence ("Plan <Badge>Pro</Badge> renews today") and has to flow
   with it. A sized skeleton (width, or a class) still wins. */
.skeleton[data-shape="block"]:not(.is-inline):has(> :only-child:is(button, .button, .switch, .checkbox, .avatar, .avatar-group, .spinner, input:is([type="checkbox"], [type="radio"]), progress, meter)) {
  display:     flex;
  align-items: flex-start;
}

/* The width sits at the specificity of .skeleton alone, so every sizing
   rule (a responsive width, a class, an inline width) still wins. */
.skeleton:where([data-shape="block"]:not(.is-inline):has(> :only-child:is(button, .button, .switch, .checkbox, .avatar, .avatar-group, .spinner, input:is([type="checkbox"], [type="radio"]), progress, meter))) {
  width: fit-content;
}

/* Wrapped content keeps its layout and hides while loading. */
.skeleton[data-loading] > :not(.skeleton-line) {
  visibility: hidden;
}

.skeleton[data-shape="circle"] {
  aspect-ratio:  1;
  border-radius: var(--radius-full);
}

/* A shape with no content and no size of its own: one line of body text.
   Zero specificity, so a caller's class that sizes it (a height, or
   height: auto with an aspect-ratio for media) always wins. Only a height
   of its own counts: min-height and max-height bound the line, they do not
   replace it (an inline style starts with the property or follows a space
   or a semicolon, so "-height" never matches). */
:where(.skeleton[data-shape="block"]:empty:not([style^="height"], [style*=" height"], [style*=";height"])) {
  height: 1lh;
}

.skeleton[data-shape="text"][data-loading] {
  display:        flex;
  flex-direction: column;
  gap:            calc(1lh - 1em);
  background:     none;
  animation:      none;
  border-radius:  0;
}

.skeleton-line {
  height:        1em;
  border-radius: var(--radius-item);
  background:    var(--bg-muted);
  animation:     skeleton-pulse calc(var(--duration-loop) * 4) var(--ease-in-out) infinite;
}

.skeleton-line:last-child:not(:first-child) {
  width: 60%;
}

/* Responsive values: a responsive width, height, bound or margin (a per
   breakpoint object) writes --skeleton-<prop>-<breakpoint> and names it in
   data-responsive. Breakpoints are the Elements ones (--bp-sm .. --bp-2xl,
   elements man style/tokens/scale); media queries cannot read tokens, so
   their rem values are written here. */

.skeleton[data-responsive~="width-initial"] { width: var(--skeleton-width-initial); }
.skeleton[data-responsive~="height-initial"] { height: var(--skeleton-height-initial); }
.skeleton[data-responsive~="min-width-initial"] { min-width: var(--skeleton-min-width-initial); }
.skeleton[data-responsive~="max-width-initial"] { max-width: var(--skeleton-max-width-initial); }
.skeleton[data-responsive~="min-height-initial"] { min-height: var(--skeleton-min-height-initial); }
.skeleton[data-responsive~="max-height-initial"] { max-height: var(--skeleton-max-height-initial); }
.skeleton[data-responsive~="margin-top-initial"] { margin-top: var(--skeleton-margin-top-initial); }
.skeleton[data-responsive~="margin-right-initial"] { margin-right: var(--skeleton-margin-right-initial); }
.skeleton[data-responsive~="margin-bottom-initial"] { margin-bottom: var(--skeleton-margin-bottom-initial); }
.skeleton[data-responsive~="margin-left-initial"] { margin-left: var(--skeleton-margin-left-initial); }

@media (min-width: 40rem) {
  .skeleton[data-responsive~="width-sm"] { width: var(--skeleton-width-sm); }
  .skeleton[data-responsive~="height-sm"] { height: var(--skeleton-height-sm); }
  .skeleton[data-responsive~="min-width-sm"] { min-width: var(--skeleton-min-width-sm); }
  .skeleton[data-responsive~="max-width-sm"] { max-width: var(--skeleton-max-width-sm); }
  .skeleton[data-responsive~="min-height-sm"] { min-height: var(--skeleton-min-height-sm); }
  .skeleton[data-responsive~="max-height-sm"] { max-height: var(--skeleton-max-height-sm); }
  .skeleton[data-responsive~="margin-top-sm"] { margin-top: var(--skeleton-margin-top-sm); }
  .skeleton[data-responsive~="margin-right-sm"] { margin-right: var(--skeleton-margin-right-sm); }
  .skeleton[data-responsive~="margin-bottom-sm"] { margin-bottom: var(--skeleton-margin-bottom-sm); }
  .skeleton[data-responsive~="margin-left-sm"] { margin-left: var(--skeleton-margin-left-sm); }
}

@media (min-width: 48rem) {
  .skeleton[data-responsive~="width-md"] { width: var(--skeleton-width-md); }
  .skeleton[data-responsive~="height-md"] { height: var(--skeleton-height-md); }
  .skeleton[data-responsive~="min-width-md"] { min-width: var(--skeleton-min-width-md); }
  .skeleton[data-responsive~="max-width-md"] { max-width: var(--skeleton-max-width-md); }
  .skeleton[data-responsive~="min-height-md"] { min-height: var(--skeleton-min-height-md); }
  .skeleton[data-responsive~="max-height-md"] { max-height: var(--skeleton-max-height-md); }
  .skeleton[data-responsive~="margin-top-md"] { margin-top: var(--skeleton-margin-top-md); }
  .skeleton[data-responsive~="margin-right-md"] { margin-right: var(--skeleton-margin-right-md); }
  .skeleton[data-responsive~="margin-bottom-md"] { margin-bottom: var(--skeleton-margin-bottom-md); }
  .skeleton[data-responsive~="margin-left-md"] { margin-left: var(--skeleton-margin-left-md); }
}

@media (min-width: 64rem) {
  .skeleton[data-responsive~="width-lg"] { width: var(--skeleton-width-lg); }
  .skeleton[data-responsive~="height-lg"] { height: var(--skeleton-height-lg); }
  .skeleton[data-responsive~="min-width-lg"] { min-width: var(--skeleton-min-width-lg); }
  .skeleton[data-responsive~="max-width-lg"] { max-width: var(--skeleton-max-width-lg); }
  .skeleton[data-responsive~="min-height-lg"] { min-height: var(--skeleton-min-height-lg); }
  .skeleton[data-responsive~="max-height-lg"] { max-height: var(--skeleton-max-height-lg); }
  .skeleton[data-responsive~="margin-top-lg"] { margin-top: var(--skeleton-margin-top-lg); }
  .skeleton[data-responsive~="margin-right-lg"] { margin-right: var(--skeleton-margin-right-lg); }
  .skeleton[data-responsive~="margin-bottom-lg"] { margin-bottom: var(--skeleton-margin-bottom-lg); }
  .skeleton[data-responsive~="margin-left-lg"] { margin-left: var(--skeleton-margin-left-lg); }
}

@media (min-width: 80rem) {
  .skeleton[data-responsive~="width-xl"] { width: var(--skeleton-width-xl); }
  .skeleton[data-responsive~="height-xl"] { height: var(--skeleton-height-xl); }
  .skeleton[data-responsive~="min-width-xl"] { min-width: var(--skeleton-min-width-xl); }
  .skeleton[data-responsive~="max-width-xl"] { max-width: var(--skeleton-max-width-xl); }
  .skeleton[data-responsive~="min-height-xl"] { min-height: var(--skeleton-min-height-xl); }
  .skeleton[data-responsive~="max-height-xl"] { max-height: var(--skeleton-max-height-xl); }
  .skeleton[data-responsive~="margin-top-xl"] { margin-top: var(--skeleton-margin-top-xl); }
  .skeleton[data-responsive~="margin-right-xl"] { margin-right: var(--skeleton-margin-right-xl); }
  .skeleton[data-responsive~="margin-bottom-xl"] { margin-bottom: var(--skeleton-margin-bottom-xl); }
  .skeleton[data-responsive~="margin-left-xl"] { margin-left: var(--skeleton-margin-left-xl); }
}

@media (min-width: 96rem) {
  .skeleton[data-responsive~="width-2xl"] { width: var(--skeleton-width-2xl); }
  .skeleton[data-responsive~="height-2xl"] { height: var(--skeleton-height-2xl); }
  .skeleton[data-responsive~="min-width-2xl"] { min-width: var(--skeleton-min-width-2xl); }
  .skeleton[data-responsive~="max-width-2xl"] { max-width: var(--skeleton-max-width-2xl); }
  .skeleton[data-responsive~="min-height-2xl"] { min-height: var(--skeleton-min-height-2xl); }
  .skeleton[data-responsive~="max-height-2xl"] { max-height: var(--skeleton-max-height-2xl); }
  .skeleton[data-responsive~="margin-top-2xl"] { margin-top: var(--skeleton-margin-top-2xl); }
  .skeleton[data-responsive~="margin-right-2xl"] { margin-right: var(--skeleton-margin-right-2xl); }
  .skeleton[data-responsive~="margin-bottom-2xl"] { margin-bottom: var(--skeleton-margin-bottom-2xl); }
  .skeleton[data-responsive~="margin-left-2xl"] { margin-left: var(--skeleton-margin-left-2xl); }
}

@media (prefers-reduced-motion: reduce) {
  .skeleton[data-loading],
  .skeleton-line {
    animation: none;
  }
}

@keyframes skeleton-pulse {
  50% { opacity: 0.5; }
}

}
