// Shared SCSS mixins for the library. Consume from a component stylesheet with:
//   @use '../../styles/mixins' as ea;
//   .ea-foo__close { @include ea.icon-button; }

// WCAG 2.2 SC 2.5.8 Target Size (Minimum), Level AA. Any pointer target the
// library ships has to reach this in at least one dimension pair, or qualify
// for one of the SC's exceptions (inline, spacing, essential).
$target-size-min: 24px;

// Standard focus indicator. The ring is a soft box-shadow halo, but box-shadow
// is dropped in forced-colors (Windows High Contrast) mode, which would leave
// keyboard users with no visible focus. Pair the halo with a real outline that
// only renders in forced-colors so the ring survives there. Pass the full
// box-shadow value to compose the ring with an existing shadow.
@mixin focus-ring($ring: var(--shadow-focus-ring)) {
  box-shadow: $ring;

  @media (forced-colors: active) {
    outline: 2px solid Highlight;
    outline-offset: 2px;
  }
}

// Floating surfaces (menus, dialogs, popovers, toasts) lean on elevation shadow
// for their boundary, but shadows are dropped in forced-colors mode. Add a
// hairline border there so the surface stays separated from the content behind.
@mixin elevated-surface-border {
  @media (forced-colors: active) {
    border: 1px solid CanvasText;
  }
}

// A readonly field takes no input, so it offers a pointer nothing: no ring on
// click, no icon hover, no pointer cursor. Keyboard focus still has to be
// visible, so the border alone picks up the focus colour, without the halo.
@mixin readonly-focus-cue {
  border-color: var(--color-border-focus);
  box-shadow: none;

  // Border colour is unreliable in forced-colors, so the ring's outline stands
  // in there, matching what `focus-ring` paints for an editable field
  @media (forced-colors: active) {
    outline: 2px solid Highlight;
    outline-offset: 2px;
  }
}

@mixin readonly-field {
  &:has(:focus-visible) {
    @include readonly-focus-cue;
  }

  button {
    cursor: default;

    &:hover {
      background-color: transparent;
      color: var(--color-text-secondary);
    }
  }
}

// `readonly-field` for a control whose root is the focusable element itself: it
// keeps the quiet keyboard cue but stops offering to be pressed or typed into.
@mixin readonly-control {
  cursor: default;

  &:focus-visible {
    @include readonly-focus-cue;
  }
}

// Hides an element visually while keeping it in the accessibility tree, so
// screen readers still announce it. The clip pattern is used instead of
// `display: none` or `visibility: hidden`, which would remove it entirely.
@mixin visually-hidden {
  position: absolute;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  clip-path: inset(50%);
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  white-space: nowrap;
  border: 0;
}

// Standard icon button: clear, close/dismiss, password-toggle, nav, etc. The box
// is `--ea-icon-button-size` (em) so it scales with the host component's size and
// is consistent across components at a given size tier. The glyph is enlarged so
// it reads clearly inside the box regardless of how much padding the icon's own
// viewBox carries (the feather `x`, for instance, only fills its middle half).
@mixin icon-button {
  // Anchors the grown pointer target below
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: var(--ea-icon-button-size, 1.75em);
  height: var(--ea-icon-button-size, 1.75em);
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--color-text-secondary);
  cursor: pointer;
  transition: var(--transition-colors);

  // Glyph sized via font-size (not width) so it overrides the icon's inline 1em
  // host size and still scales with the box.
  > * {
    font-size: 1.25em;
  }

  // The visible box is deliberately small on the dense tiers (17.5px at 2xs,
  // 21px at xs), which is under the 24px WCAG 2.2 SC 2.5.8 target minimum.
  // SC 2.5.8 measures the interactive target rather than the painted control,
  // so the target is grown here and the visuals are left alone.
  //
  // A layout that stacks or abuts these buttons closer than the minimum must
  // opt out via `--ea-icon-button-target: 0`, because grown targets that
  // overlap steal each other's clicks, which is worse than the undersized
  // targets the SC's spacing exception already covers.
  &::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: max(100%, var(--ea-icon-button-target, #{$target-size-min}));
    height: max(100%, var(--ea-icon-button-target, #{$target-size-min}));
    transform: translate(-50%, -50%);
  }

  &:hover {
    background-color: var(--color-state-hover);
    color: var(--color-text-primary);
  }

  &:focus-visible {
    outline: none;
    @include focus-ring;
  }

  &:disabled {
    cursor: default;
    opacity: 0.5;
  }
}

// Hairline rule for a pseudo-element, so it spans its container's inner edges
// instead of being inset by the content's own padding. `$inset` pulls the rule
// in from those edges, matching a list that pads its rows.
@mixin divider-rule($edge: top, $inset: 0) {
  position: absolute;
  #{$edge}: 0;
  inset-inline-end: $inset;
  inset-inline-start: $inset;
  height: var(--border-width-thin);
  background-color: var(--color-divider);
  content: '';
}

// Section wrapper in a grouped select-like option list, and the rule an
// unlabelled section carries in place of a heading. The rule stays out of the
// option list entirely.
@mixin option-group {
  position: relative;

  &--ruled {
    padding-top: 0.25em;
    margin-top: 0.25em;

    &::before {
      @include divider-rule;
    }
  }
}

// Heading above a labelled section, sized in em so it scales with the host
// component's `size`.
@mixin option-group-label {
  display: block;
  padding: 0.5em 0.75em 0.125em;
  font-size: 0.8125em;
  font-weight: var(--font-weight-semibold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-text-tertiary);
  user-select: none;
}

// Strips the geometry the UA stylesheet forces on a `[popover]` element (a
// centred, viewport-filling, scrollable box) so a surface promoted into the top
// layer keeps the box it has in the normal layer. Colour and spacing chrome
// (`padding`, `border`, `background`, `color`) is left alone: a surface that
// declares its own already wins, and one that does not resets it alongside the
// include.
@mixin top-layer-reset {
  inset: auto;
  overflow: visible;
  width: auto;
  height: auto;
}
