/* ============================================================
   button.css — kapy.ch v2 · Task 5
   Variants: primary | secondary | ghost
   Sizes:    sm | md | lg (md default; mobile bumps to 48px touch-target)
   States:   hover, focus-visible, active, disabled, loading
   Shape:    optional "pill" modifier
   ============================================================ */

.btn {
  /* Defaults are MD size — overridden by --sm / --lg modifiers */
  --btn-min-h: 44px;
  --btn-py: var(--space-2);
  --btn-px: var(--space-5);
  --btn-fs: var(--fs-body-md);
  --btn-radius: var(--radius-lg);
  --btn-gap: var(--space-2);

  /* Layout */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--btn-gap);
  min-height: var(--btn-min-h);
  padding: var(--btn-py) var(--btn-px);

  /* Typography */
  font-family: inherit;
  font-size: var(--btn-fs);
  font-weight: var(--fw-medium);
  line-height: 1;
  letter-spacing: 0;
  white-space: nowrap;
  text-decoration: none;

  /* Surface */
  border: 1px solid transparent;
  border-radius: var(--btn-radius);
  background: transparent;
  color: inherit;

  /* Interaction */
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;

  /* Motion (minimal — 150ms max) */
  transition:
    background-color var(--dur-fast) var(--ease-out),
    border-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out),
    box-shadow var(--dur-normal) var(--ease-out),
    transform 80ms var(--ease-out);
}

/* ---------- Focus-Visible — 3px ring, 2px offset, brand ---------- */

.btn:focus-visible {
  outline: none;
  /* 2px gap matches canvas, then 3px brand ring */
  box-shadow:
    0 0 0 2px var(--bg-canvas),
    0 0 0 5px var(--brand-500);
}

/* On surfaces other than canvas, offset color should adapt — but for now
   the canvas-color offset is the most common case (nav + cards above canvas). */

/* ---------- Active (Press-Scale) ---------- */

.btn:active:not([aria-disabled='true']):not(:disabled):not([aria-busy='true']) {
  transform: scale(0.97);
}

/* ---------- Disabled ---------- */

.btn:disabled,
.btn[aria-disabled='true'] {
  cursor: not-allowed;
  opacity: 0.55;
  box-shadow: none;
}

.btn:disabled:hover,
.btn[aria-disabled='true']:hover {
  background-color: inherit;
  border-color: inherit;
}

/* ---------- Sizes ---------- */

.btn--sm {
  --btn-min-h: 36px;
  --btn-py: var(--space-1);
  --btn-px: var(--space-4);
  --btn-fs: var(--fs-body-sm);
  --btn-radius: var(--radius-md);
  --btn-gap: var(--space-1);
}

/* MD is default — values already on .btn */

.btn--lg {
  --btn-min-h: 52px;
  --btn-py: var(--space-3);
  --btn-px: var(--space-6);
  --btn-fs: var(--fs-body-lg);
  --btn-radius: var(--radius-lg);
}

/* Mobile touch-target bump for MD/LG */
@media (max-width: 767px) {
  .btn--md,
  .btn--lg {
    --btn-min-h: 48px;
  }
}

/* ---------- Shape Modifier: Pill ---------- */

.btn--pill {
  --btn-radius: var(--radius-pill);
}

/* ---------- Full-Width Modifier ---------- */

.btn--full {
  width: 100%;
}

/* ============================================================
   VARIANTS
   ============================================================ */

/* ---------- Primary ---------- */

.btn--primary {
  background-color: var(--action-primary-bg);
  color: var(--action-primary-text);
  box-shadow: var(--shadow-xs);
}

@media (hover: hover) {
  .btn--primary:hover:not(:disabled):not([aria-disabled='true']):not([aria-busy='true']) {
    background-color: var(--action-primary-hover);
    box-shadow: var(--shadow-sm), var(--glow-brand);
  }
}

.btn--primary:active:not(:disabled):not([aria-disabled='true']):not([aria-busy='true']) {
  background-color: var(--action-primary-active);
  box-shadow: var(--shadow-xs);
}

/* ---------- Secondary ---------- */

.btn--secondary {
  background-color: var(--action-secondary-bg);
  color: var(--action-secondary-text);
  border-color: var(--action-secondary-border);
}

@media (hover: hover) {
  .btn--secondary:hover:not(:disabled):not([aria-disabled='true']):not([aria-busy='true']) {
    background-color: var(--action-secondary-hover);
    border-color: var(--neutral-500);
  }
  [data-theme='dark'] .btn--secondary:hover:not(:disabled):not([aria-disabled='true']):not([aria-busy='true']) {
    border-color: oklch(40% 0.024 270);
  }
}

@media (hover: hover) and (prefers-color-scheme: dark) {
  :root:not([data-theme='light'])
    .btn--secondary:hover:not(:disabled):not([aria-disabled='true']):not([aria-busy='true']) {
    border-color: oklch(40% 0.024 270);
  }
}

/* ---------- Ghost ---------- */

.btn--ghost {
  background-color: var(--action-ghost-bg);
  color: var(--action-ghost-text);
}

@media (hover: hover) {
  .btn--ghost:hover:not(:disabled):not([aria-disabled='true']):not([aria-busy='true']) {
    background-color: var(--action-ghost-hover);
  }
}

/* ============================================================
   LOADING STATE — CSS-only spinner
   Label stays in flow (visibility:hidden) → no width change → no CLS
   ============================================================ */

.btn[aria-busy='true'] {
  cursor: progress;
  position: relative;
  pointer-events: none;
}

.btn[aria-busy='true'] .btn__label {
  /* visibility:hidden preserves width but removes text from a11y tree.
     The macro mirrors `label` into the button's aria-label when state=loading,
     so the accessible name is preserved via aria-label instead.
     axe-core skips visibility:hidden elements for color-contrast. */
  visibility: hidden;
}

.btn[aria-busy='true']::after {
  content: '';
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  width: 1em;
  height: 1em;
  margin-block-start: -0.5em;
  margin-inline-start: -0.5em;
  border: 2px solid currentColor;
  border-block-start-color: transparent;
  border-radius: 50%;
  animation: btn-spin 700ms linear infinite;
}

@keyframes btn-spin {
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: reduce) {
  .btn[aria-busy='true']::after {
    animation: none;
    border-block-start-color: currentColor;
    opacity: 0.6;
  }
}

/* ============================================================
   LABEL — base styles for the inner span
   (kept minimal so the label can be replaced by callers via slot)
   ============================================================ */

.btn__label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}
