/* ============================================================
   components/buttons.css — .dd-btn
   Variants (primary grad-coral+glow, secondary outline, tertiary
   ghost), sizes, full-width, loading (dot-loader), disabled, and a
   magnetic-hover hook (JS in components.js).
   ============================================================ */

.dd-btn {
  /* Geometry knobs, so a size variant moves one number and everything that
     derives from padding — the icon-only square below — follows. Same idea as
     `.dd-choice`'s two knobs (components/choice.css). */
  --dd-btn-pad-y: var(--space-4);
  --dd-btn-pad-x: var(--space-8);
  --dd-btn-icon: 1.25em;          /* `.dd-icon`'s own height */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55em;
  padding: var(--dd-btn-pad-y) var(--dd-btn-pad-x);
  border: 2px solid transparent;
  border-radius: var(--radius-pill);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 600;
  line-height: 1;
  text-transform: lowercase;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  background: transparent;
  color: var(--text);
  position: relative;
  isolation: isolate;
  transition: transform var(--dur-base) var(--ease-dreamy),
              box-shadow var(--dur-base) var(--ease-dreamy),
              background-color var(--dur-base) var(--ease-soft),
              border-color var(--dur-base) var(--ease-soft),
              color var(--dur-base) var(--ease-soft);
}

/* `display` above beats the UA `[hidden]` rule, so a button rendered `hidden`
   stays on screen — and it fails silently, because the markup says hidden and
   only the pixels disagree. Two containers had patched that locally before
   this rule existed (the bulk actions bar; the asset modals' upload row, where
   "clear" had been showing with nothing to clear since #62). The guard belongs
   next to the declaration that causes it, not in each container that trips
   over it — found via #175. */
.dd-btn[hidden] {
  display: none;
}

.dd-btn:focus-visible {
  outline: var(--focus-outline);
  outline-offset: 2px;
}

/* ---- variants ---- */
.dd-btn--primary {
  background: var(--grad-coral);
  color: var(--text-on-coral);
  box-shadow: var(--shadow-glow);
}
.dd-btn--primary:hover {
  transform: translateY(-2px);
}

.dd-btn--secondary {
  background: var(--surface);
  color: var(--text);
  border-color: var(--coral-200);
}
.dd-btn--secondary:hover {
  background: var(--coral-50);
  border-color: var(--accent);
  color: var(--text);          /* fixes the old invisible-label hover */
  transform: translateY(-2px);
}

.dd-btn--tertiary {
  background: transparent;
  color: var(--link);
  border-color: transparent;
}
.dd-btn--tertiary:hover {
  background: var(--tint-soft);
  color: var(--link);
  transform: translateY(-2px);
}

/* ---- sizes / layout ---- */
.dd-btn--sm {
  --dd-btn-pad-y: var(--space-2);
  --dd-btn-pad-x: var(--space-5);
  font-size: var(--fs-small);
}
.dd-btn--lg {
  --dd-btn-pad-y: var(--space-5);
  --dd-btn-pad-x: var(--space-10);
  font-size: var(--fs-lead);
}

.dd-btn--block { width: 100%; }

/* Full width on a phone only. This is `style.css`'s old global rule — every
   `.btn` on the site went 100% wide under 768px — turned into something a
   button opts into (#243). It had to become opt-in to migrate at all: as a
   global it would have caught all 200-odd `.dd-btn`s the moment the legacy
   name went away, and a card's two action pills would have stacked. */
@media (max-width: 768px) {
  .dd-btn--block-sm { width: 100%; }
}

/* An icon is 1.25em tall against a 1em line box, so a button carrying one is
   otherwise taller than the button beside it that does not — 57px against 53px,
   which is the bug this whole issue opened on, one row up. Pull the difference
   back into the padding the icon is already sitting in: every `.dd-btn` is
   2·pad-y + 1em + border tall, icon or no icon, at every size. A row where one
   control is taller than the rest reads as a mistake (owner, 2026-09-01). */
.dd-btn .dd-icon {
  margin-block: calc((1em - var(--dd-btn-icon)) / 2);
}

/* Icon-only — the same height as a text button of the same size (the rule
   above is what makes that true), squared off by `aspect-ratio`. Deriving it
   is the point: the version that did not is how `.action-btn-icon` ended up
   with a hand-tuned 34px that then had to be kept in step by hand (#243). */
.dd-btn--icon {
  padding: var(--dd-btn-pad-y);
  border-radius: var(--radius-pill);
  aspect-ratio: 1;
  gap: 0;
}
/* …and the same correction on the inline axis, which only an icon-only button
   wants: `aspect-ratio` takes its side from the width, so an icon 0.25em wider
   than the line box would square the button up 4px larger than the pill beside
   it. In a button that also has a label, the inline gap is the gap's job. */
.dd-btn--icon .dd-icon {
  margin-inline: calc((1em - var(--dd-btn-icon)) / 2);
}

/* The add button — the "+" that opens a create modal. A round coral target
   with a moving gradient and a glow pulse on hover, so it reads as ADD rather
   than as one more pill in the row. Geometry is `--icon`'s, so it matches
   whatever size the row it sits in uses; the border stays and turns
   transparent rather than being removed, because the gradient paints under it
   and losing 2px of box would make it the odd one out again. */
.dd-btn--add {
  background: linear-gradient(135deg, var(--coral-300) 0%, var(--coral-500) 100%);
  background-size: 220% 220%;
  background-position: 0% 50%;
  border-color: transparent;
  color: var(--text-on-coral);
  box-shadow: var(--shadow-glow-tight);
}
.dd-btn--add:hover,
.dd-btn--add:focus-visible {
  transform: translateY(-1px) scale(1.06);
  color: var(--text-on-coral);
  animation: dd-add-pulse 1.6s var(--ease-soft) infinite;
}
@keyframes dd-add-pulse {
  0%   { background-position: 0% 50%;   box-shadow: var(--shadow-glow-tight); }
  50%  { background-position: 100% 50%; box-shadow: var(--shadow-glow-lift); }
  100% { background-position: 0% 50%;   box-shadow: var(--shadow-glow-tight); }
}
@media (prefers-reduced-motion: reduce) {
  .dd-btn--add:hover,
  .dd-btn--add:focus-visible { animation: none; }
}

/* ---- quiet ---- */
/* The quiet pill: a small uppercase label in a thin outline, muted at rest and
   tinted when it is on (#301). It is for a control that sits beside the thing
   it works on — the lyrics studio's toolbar, a bar over a sheet — where even
   `--secondary` would shout over the words.

   "On" is the ARIA state the markup already carries: `aria-pressed` for a
   toggle, `aria-expanded` for a disclosure. There is no class to keep in step
   with it. The label there is `--link`, because `--accent-strong` fails AA at
   11px (design-language §1).

   Height is still the kit's sum, 2·pad-y + 1em + border; the padding is what
   lands it on the height the studio's pill already had. */
.dd-btn--quiet {
  --dd-btn-pad-y: 0.5em;
  --dd-btn-pad-x: 0.85em;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--text-muted);
  background-color: transparent;
  border-width: 1.5px;
  border-color: var(--border);
  transition-duration: var(--dur-fast);
}
.dd-btn--quiet:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--text);
}

/* On top of `--quiet`: the one a bar leads with — the thing you meant to do —
   beside a plain `--quiet` for the way back out of it (restore / back to
   current, approve all / clear all). */
.dd-btn--quiet-accent {
  color: var(--link);
  background-color: var(--surface);
  border-color: var(--accent);
}
.dd-btn--quiet-accent:hover:not(:disabled) {
  color: var(--link);
  background-color: var(--tint-20);
}

.dd-btn--quiet[aria-pressed="true"],
.dd-btn--quiet[aria-expanded="true"] {
  color: var(--link);
  background-color: var(--tint-20);
  border-color: var(--accent);
}

/* A native `<select>` that has to sit in a row of them — the studio's audio
   version. The control stays native; only its arrow is drawn. */
select.dd-btn--quiet {
  appearance: none;
  -webkit-appearance: none;
  padding-right: 1.8em;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6' fill='none' stroke='%237A6F6B' stroke-width='1.5'%3E%3Cpath d='M1 1l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.7em center;
}

/* ---- disabled ---- */
.dd-btn:disabled,
.dd-btn[aria-disabled="true"],
.dd-btn.is-disabled {
  cursor: not-allowed;
  opacity: 0.5;
  transform: none;
  box-shadow: none;
}

/* ---- loading (label hidden, dot-loader shown; size preserved) ---- */
.dd-btn__label {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
}
.dd-btn__spinner {
  position: absolute;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
}
.dd-btn.is-loading {
  pointer-events: none;
  transform: none;
}
.dd-btn.is-loading .dd-btn__label { visibility: hidden; }
.dd-btn.is-loading .dd-btn__spinner { display: flex; }

/* The same state, htmx's way: there is no JS call to make, so the trigger is
   the `htmx-request` class htmx puts on the element itself. Opt-in, because a
   button whose label vanishes on every request it makes is a decision, not a
   default. Wrap the label in `.dd-btn__label` exactly as `setLoading` does —
   one set of internals, two ways of entering the state. The generic
   `.htmx-indicator` show/hide wiring lives in dashboard.css. */
.dd-btn--loadable.htmx-request .dd-btn__label { visibility: hidden; }
.dd-btn--loadable .dd-loader.htmx-indicator {
  position: absolute;
  inset: 0;
  justify-content: center;
  align-items: center;
}
.dd-btn--primary.dd-btn--loadable .dd-loader { color: var(--text-on-coral); }

/* Spinner color tracks the variant (since the label area is hidden). */
.dd-btn--primary .dd-btn__spinner { color: var(--text-on-coral); }
.dd-btn--secondary .dd-btn__spinner,
.dd-btn--tertiary .dd-btn__spinner { color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .dd-btn { transition: background-color var(--dur-base) var(--ease-soft),
                     border-color var(--dur-base) var(--ease-soft),
                     color var(--dur-base) var(--ease-soft); }
  .dd-btn:hover { transform: none; }
  .dd-btn--quiet { transition: none; }
}
