/* ==========================================================
   IROZON DESIGN SYSTEM — Button (canonical, v2)
   Public API: <button class="iz-button" data-variant="..." data-size="...">
   Depends only on Irozon design tokens (color/typography/radius/motion) —
   see packages/design-tokens. No JS is required for this component to
   render correctly, including its loading state.
   ========================================================== */

.iz-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  border: 1px solid transparent;
  /* 12px, not a --r-* token: the scale (8/10/16/24) has no rung there.
     Matches Signature CTA's own literal 12px exactly — that's this
     size class's established corner for a default-size Irozon CTA, not
     a token gap to fill. Small/Large keep their own explicit radius
     below; Large's still-unset radius intentionally inherits this. */
  border-radius: 12px;
  font-family: var(--font-label);
  font-weight: 500;
  font-size: var(--fs-body-sm);
  line-height: 1;
  block-size: 48px;
  padding-inline: 24px;
  padding-block: 0;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  position: relative;
  background: none;
  transition:
    background-color var(--motion-duration-fast) var(--motion-ease-standard),
    border-color var(--motion-duration-fast) var(--motion-ease-standard),
    color var(--motion-duration-fast) var(--motion-ease-standard),
    transform var(--motion-duration-instant) var(--motion-ease-standard);
}
/* React groups the icon and label in a span to preserve width while loading.
   Lay out that content too; block-level SVGs must stay beside the label. */
.iz-button > span:not(.iz-button__spinner) {
  display: inline-flex;
  align-items: center;
  gap: inherit;
}
.iz-button svg {
  inline-size: 18px;
  block-size: 18px;
  flex: none;
}
.iz-button:focus-visible {
  outline: 2px solid var(--color-focus);
  outline-offset: 2px;
}
.iz-button:active {
  transform: scale(0.97);
}
@media (prefers-reduced-motion: reduce) {
  .iz-button {
    transition:
      background-color var(--motion-duration-fast) var(--motion-ease-standard),
      border-color var(--motion-duration-fast) var(--motion-ease-standard),
      color var(--motion-duration-fast) var(--motion-ease-standard);
  }
  .iz-button:active {
    transform: none;
  }
}

/* ---------- Sizes ---------- */
/* Default requires no data-size attribute. */
.iz-button[data-size="small"] {
  block-size: 36px;
  padding-inline: 14px;
  gap: 6px;
  font-size: var(--fs-label);
  border-radius: var(--r-xs);
}
.iz-button[data-size="small"] svg {
  inline-size: 16px;
  block-size: 16px;
}
.iz-button[data-size="large"] {
  block-size: 52px;
  padding-inline: 26px;
  gap: 10px;
  font-size: var(--fs-body);
}
.iz-button[data-size="large"] svg {
  inline-size: 20px;
  block-size: 20px;
}

/* ---------- Icon-only ---------- */
/* Square hit area. Accessible name is required via aria-label on the
   button element — never conveyed by icon shape alone. */
.iz-button[data-icon-only] {
  padding-inline: 0;
  inline-size: 48px;
  gap: 0;
}
.iz-button[data-icon-only][data-size="small"] {
  inline-size: 36px;
}
.iz-button[data-icon-only][data-size="large"] {
  inline-size: 52px;
}

/* ---------- Variants ---------- */
/* Primary — one per decision area, and theme-adaptive rather than a
   fixed brand color: Ink on light surfaces, Green on dark surfaces
   (see --color-action-primary in design-tokens for the rationale).
   The strongest action always resolves to whichever anchor color has
   the most presence against the current surface. */
.iz-button[data-variant="primary"] {
  background: var(--color-action-primary);
  color: var(--color-on-action-primary);
}
.iz-button[data-variant="primary"]:hover {
  background: var(--color-action-primary-hover);
}
.iz-button[data-variant="primary"]:active {
  background: var(--color-action-primary-active);
}

/* Secondary — neutral, clearly actionable via a real border. */
.iz-button[data-variant="secondary"] {
  background: var(--color-surface);
  color: var(--color-text-primary);
  border-color: var(--color-border-strong);
}
.iz-button[data-variant="secondary"]:hover {
  background: var(--color-surface-subtle);
  border-color: var(--color-text-primary);
}
.iz-button[data-variant="secondary"]:active {
  background: var(--color-border);
}

/* Tertiary — quiet, but never borderless at rest. --color-border is
   too close to --color-surface to register as a line at 1px (that's
   what shipped in the previous pass, and manual QA could still see
   the Cancel button vanish into a white dialog) — the same weak token
   that keeps default Cards from looking heavy is exactly wrong for a
   control that has to read as clickable with zero other affordance.
   --color-border-strong is the one neutral token with real separation
   from every surface tone in both themes, so Tertiary rests at it —
   equal to Secondary's own resting border. The two stay visually
   distinct anyway: Secondary is additionally a filled surface, so its
   border reads as the edge of a solid control, while Tertiary stays
   transparent, so the same border reads as a light outline around
   empty space — quieter by construction, not by a fainter, invisible
   color. Hover/active escalate through fill only, same as before. */
.iz-button[data-variant="tertiary"] {
  background: transparent;
  color: var(--color-text-primary);
  border-color: var(--color-border-strong);
}
.iz-button[data-variant="tertiary"]:hover {
  background: var(--color-surface-subtle);
}
.iz-button[data-variant="tertiary"]:active {
  background: var(--color-border);
}

/* Destructive — reuses the canonical error token and the same tint
   pattern as status/badge error. No new red is introduced. */
.iz-button[data-variant="destructive"] {
  background: transparent;
  color: var(--color-error);
  border-color: var(--color-error);
}
.iz-button[data-variant="destructive"]:hover {
  background: color-mix(in srgb, var(--color-error) 12%, transparent);
}
.iz-button[data-variant="destructive"]:active {
  background: color-mix(in srgb, var(--color-error) 20%, transparent);
}

/* ---------- Disabled ----------
   `disabled` (native): the action is genuinely unavailable right now —
   browser removes it from the tab order and blocks all interaction for
   free, no JS needed.
   `aria-disabled="true"`: the control must stay focusable/announced
   (e.g. so a screen-reader user can reach it and learn *why* it's
   unavailable, via aria-describedby, instead of it silently vanishing
   from the tab order). Both are supported; pick per case, don't force
   one universal rule. */
.iz-button:disabled,
.iz-button[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
}
.iz-button:disabled:hover,
.iz-button:disabled:active,
.iz-button[aria-disabled="true"]:hover,
.iz-button[aria-disabled="true"]:active {
  transform: none;
  background: initial;
}
.iz-button[data-variant="primary"]:disabled,
.iz-button[data-variant="primary"][aria-disabled="true"] {
  background: var(--color-action-primary);
}
.iz-button[data-variant="secondary"]:disabled,
.iz-button[data-variant="secondary"][aria-disabled="true"] {
  background: var(--color-surface);
}
.iz-button[data-variant="destructive"]:disabled,
.iz-button[data-variant="destructive"][aria-disabled="true"] {
  background: transparent;
}

/* ---------- Directional icon hint ----------
   Reserved for icons that are actually directional (e.g. "Continue" →
   forward arrow). Opt in with data-icon-hint="end". */
.iz-button[data-icon-hint="end"] svg:last-child {
  transition: transform var(--motion-duration-fast) var(--motion-ease-standard);
}
.iz-button[data-icon-hint="end"]:hover svg:last-child {
  transform: translateX(3px);
}
.iz-button[data-icon-hint="end"]:dir(rtl):hover svg:last-child {
  transform: translateX(-3px);
}
@media (prefers-reduced-motion: reduce) {
  .iz-button[data-icon-hint="end"] svg:last-child {
    transition: none;
  }
  .iz-button[data-icon-hint="end"]:hover svg:last-child {
    transform: none;
  }
}

/* ---------- Loading ----------
   aria-busy="true" is the only thing required to render this state —
   no JS. The button's internal loading indicator is an .iz-loader (see
   loader.css) at data-size="small", marked aria-hidden since the
   button's own aria-busy already carries the semantics; it inherits
   currentColor, so it's always the button's own foreground — never
   the Brand loader. The label stays in the DOM and merely turns
   invisible (visibility, not display), so its layout box keeps holding
   the button's width — nothing needs to measure or lock width in JS. */
.iz-button__spinner {
  display: none;
}
.iz-button[aria-busy="true"] {
  cursor: default;
}
.iz-button[aria-busy="true"] > :not(.iz-button__spinner) {
  visibility: hidden;
}
.iz-button[aria-busy="true"] .iz-button__spinner {
  /* Centered via inset:0 + margin:auto, not transform — the loader's
     own rotation animation sets `transform` on this same element, and
     an animated transform replaces the computed value outright rather
     than composing with a static one. A translate(-50%,-50%) here
     would get clobbered by the animation's rotate() every frame,
     which is what caused the spinner to lose its centering and appear
     to sink/drift instead of spinning in place. */
  display: inline-flex;
  position: absolute;
  inset: 0;
  margin: auto;
}
