/* -- Indicator component ------------------------------------------ */

@layer components {
  /* .indicator wraps any element (a button, avatar, card, input, image)
     and pins one or more .indicator-item on it. Each item is CENTERED on an
     anchor point of the wrapper's edge: data-position picks the point -
     vertical top / middle / bottom + horizontal start / center / end
     (default top-end). A single word sets one axis - "start" / "center" /
     "end" horizontal, "top" / "middle" / "bottom" vertical - so "middle-center"
     is the middle of the element. start / end are logical: in RTL, start is
     the right edge. */
  .indicator {
    position: relative;
    display: inline-flex;
    width: max-content;
    max-width: 100%;
  }
  /* the wrapped element fills the wrapper: a no-op at the default
     max-content width, but a wrapper given a width passes it on - a card
     (a size container, no intrinsic width) would otherwise collapse to 0 */
  .indicator > :not(.indicator-item) {
    flex: 1 1 auto;
    min-width: 0;
  }

  .indicator-item {
    /* --_h / --_v: 0 = start / top, 0.5 = center / middle, 1 = end / bottom */
    --_h: 1;
    --_v: 0;
    position: absolute;
    z-index: 1;
    /* --_in: data-inset pulls the item toward the element's center by this
       much per axis (a corner item moves diagonally) - for an icon inside a
       padded button, whose box corner sits well away from the glyph;
       --_dir flips the horizontal pull in RTL */
    --_in: 0px;
    --_dir: 1;
    inset-block-start: calc(var(--_v) * 100%);
    left: calc(var(--_h) * 100%);
    translate:
      calc(-50% + (1 - 2 * var(--_h)) * var(--_in) * var(--_dir))
      calc(-50% + (1 - 2 * var(--_v)) * var(--_in));
    white-space: nowrap;

    &:dir(rtl) { left: calc((1 - var(--_h)) * 100%); --_dir: -1; }
    &[data-inset] { --_in: 0.5rem; }
    &[data-inset="sm"] { --_in: 0.25rem; }
    &[data-inset="lg"] { --_in: 0.75rem; }

    &[data-position^="middle"] { --_v: 0.5; }
    &[data-position^="bottom"] { --_v: 1; }
    &[data-position$="start"] { --_h: 0; }
    &[data-position$="center"] { --_h: 0.5; }

    /* responsive placement (DaisyUI's sm: / md: / lg: / xl:): the same
       values in data-position-sm / -md / -lg / -xl apply from that viewport
       width up (40 / 48 / 64 / 80rem) */
    @media (min-width: 40rem) {
      &[data-position-sm^="top"] { --_v: 0; }
      &[data-position-sm^="middle"] { --_v: 0.5; }
      &[data-position-sm^="bottom"] { --_v: 1; }
      &[data-position-sm$="start"] { --_h: 0; }
      &[data-position-sm$="center"] { --_h: 0.5; }
      &[data-position-sm$="end"] { --_h: 1; }
    }
    @media (min-width: 48rem) {
      &[data-position-md^="top"] { --_v: 0; }
      &[data-position-md^="middle"] { --_v: 0.5; }
      &[data-position-md^="bottom"] { --_v: 1; }
      &[data-position-md$="start"] { --_h: 0; }
      &[data-position-md$="center"] { --_h: 0.5; }
      &[data-position-md$="end"] { --_h: 1; }
    }
    @media (min-width: 64rem) {
      &[data-position-lg^="top"] { --_v: 0; }
      &[data-position-lg^="middle"] { --_v: 0.5; }
      &[data-position-lg^="bottom"] { --_v: 1; }
      &[data-position-lg$="start"] { --_h: 0; }
      &[data-position-lg$="center"] { --_h: 0.5; }
      &[data-position-lg$="end"] { --_h: 1; }
    }
    @media (min-width: 80rem) {
      &[data-position-xl^="top"] { --_v: 0; }
      &[data-position-xl^="middle"] { --_v: 0.5; }
      &[data-position-xl^="bottom"] { --_v: 1; }
      &[data-position-xl$="start"] { --_h: 0; }
      &[data-position-xl$="center"] { --_h: 0.5; }
      &[data-position-xl$="end"] { --_h: 1; }
    }
  }

  /* -- Status dot ----------------------------------------------------
     A small colored dot for presence and state (DaisyUI's status): on its
     own inline, or as an .indicator-item. data-variant colors it,
     data-size sizes it, data-animate="ping" sends a ring pulsing out of it
     (an ::after copy scaling up and fading), "bounce" hops it, "pulse"
     fades it. The dot is currentColor, so a variant is one color line.
     Success / warning / info are literals by design: the tweakcn token set
     has no such pairs (AGENTS.md token rule, like the avatar badge). */
  .indicator-dot {
    position: relative;
    display: inline-block;
    flex-shrink: 0;
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 9999px;
    color: var(--muted-foreground);
    background-color: currentColor;
    vertical-align: middle;

    &[data-variant="neutral"] { color: var(--foreground); }
    &[data-variant="primary"] { color: var(--primary); }
    /* the tweakcn --secondary / --accent are pale SURFACES - too faint for a
       dot: secondary / accent take the theme's vivid chart colors instead */
    &[data-variant="secondary"] { color: var(--chart-2); }
    &[data-variant="accent"] { color: var(--chart-4); }
    &[data-variant="destructive"], &[data-variant="error"] { color: var(--destructive); }
    &[data-variant="success"] { color: #16a34a; }
    &[data-variant="warning"] { color: #f59e0b; }
    &[data-variant="info"] { color: #0ea5e9; }

    &[data-size="xs"] { width: 0.25rem; height: 0.25rem; }
    &[data-size="sm"] { width: 0.375rem; height: 0.375rem; }
    &[data-size="md"] { width: 0.5rem; height: 0.5rem; }
    &[data-size="lg"] { width: 0.75rem; height: 0.75rem; }
    &[data-size="xl"] { width: 1rem; height: 1rem; }

    &[data-animate="ping"]::after {
      content: '';
      position: absolute;
      inset: 0;
      border-radius: inherit;
      background-color: currentColor;
      animation: indicator-ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
    }
    &[data-animate="bounce"] { animation: indicator-bounce 1s infinite; }
    &[data-animate="pulse"] { animation: indicator-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; }
  }

  /* a dot pinned as an indicator stays absolutely positioned (the dot's own
     position: relative comes later in the cascade); a bounce must not fight
     the centering translate, so it moves the pinned dot with margin instead */
  .indicator-item.indicator-dot {
    position: absolute;

    &[data-animate="bounce"] { animation-name: indicator-bounce-margin; }
  }

  @keyframes indicator-ping {
    75%, 100% { scale: 2.25; opacity: 0; }
  }
  @keyframes indicator-bounce {
    0%, 100% { translate: 0 -25%; animation-timing-function: cubic-bezier(0.8, 0, 1, 1); }
    50% { translate: 0 0; animation-timing-function: cubic-bezier(0, 0, 0.2, 1); }
  }
  @keyframes indicator-bounce-margin {
    0%, 100% { margin-top: -0.25em; animation-timing-function: cubic-bezier(0.8, 0, 1, 1); }
    50% { margin-top: 0; animation-timing-function: cubic-bezier(0, 0, 0.2, 1); }
  }
  @keyframes indicator-pulse {
    50% { opacity: 0.5; }
  }

  /* -- Accessibility ------------------------------------------ */
  @media (prefers-reduced-motion: reduce) {
    .indicator-dot[data-animate], .indicator-item.indicator-dot[data-animate] { animation: none; }
    .indicator-dot[data-animate]::after { animation: none; opacity: 0; }
  }

  /* forced colors would repaint the dot with Canvas and erase it: the
     color carries the state, so the dot keeps its own */
  @media (forced-colors: active) {
    .indicator-dot { forced-color-adjust: none; }
  }
}
