/* buttons.css — All button variants. km- prefix. */

/* `[hidden]` is a user-agent-origin rule; any author rule (even non-!important, even less
   specific) beats it in the cascade, so `.km-btn { display: inline-flex }` below silently
   defeated `<button hidden>` on every button across the whole app. This restores it. */
.km-btn[hidden] { display: none; }

/* Default size matches the gz wizard's step-footer buttons (Suivant/
   Précédent) — bumped from the original .55/1/.85rem because those read as
   too small/cramped. Still fully controlled by --km-density (Réglages'
   "Density" slider, #densityRange in index.php) since every value here is
   density-relative, not a fixed px — dragging that slider scales every
   default-sized button in the app, this one included. --sm/--lg/--icon
   stay deliberately smaller/larger for compact contexts (table rows,
   toolbars, icon-only actions) — only the unmodified default size changed. */
.km-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .45rem;
  padding: calc(var(--km-density) * .7) calc(var(--km-density) * 1.3);
  border-radius: var(--km-radius);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text);
  font-size: .95rem;
  font-weight: 600;
  font-family: var(--km-font);
  transition: all var(--km-trans);
  flex-shrink: 0;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
}
/* Focus ring is a darker shade of the button's own color — never white or a
   mismatched accent. Outline stays visible against both the page and the
   button, so the focused control is always clearly identifiable. */
.km-btn:focus-visible   { outline: 2px solid var(--accent); outline-offset: 2px; }
.km-btn--primary:focus-visible,
.km-btn--outline-primary:focus-visible,
.km-btn--ghost:focus-visible,
.km-btn--link:focus-visible { outline-color: color-mix(in srgb, var(--accent) 65%, #000); }
.km-btn--success:focus-visible,
.km-btn--outline-success:focus-visible { outline-color: color-mix(in srgb, var(--positive) 65%, #000); }
.km-btn--danger:focus-visible,
.km-btn--outline-danger:focus-visible,
.km-btn--ghost-danger:focus-visible { outline-color: color-mix(in srgb, var(--negative) 65%, #000); }
.km-btn--warning:focus-visible { outline-color: color-mix(in srgb, var(--warning) 65%, #000); }
.km-btn:disabled        { opacity: .45; cursor: not-allowed; pointer-events: none; }

/* Variants */
.km-btn--primary   { background: var(--accent);   border-color: var(--accent);   color: #fff; }
.km-btn--secondary { background: var(--surface);  border-color: var(--border);   color: var(--text); }
.km-btn--success   { background: var(--positive); border-color: var(--positive); color: #fff; }
.km-btn--danger    { background: var(--negative); border-color: var(--negative); color: #fff; }
.km-btn--warning   { background: var(--warning);  border-color: var(--warning);  color: #fff; }
.km-btn--ghost         { border-color: transparent; background: transparent; color: var(--accent); }
.km-btn--ghost-danger  { border-color: transparent; background: transparent; color: var(--negative); }
.km-btn--outline-primary { border-color: var(--accent);   color: var(--accent);   background: transparent; }
.km-btn--outline-danger  { border-color: var(--negative); color: var(--negative); background: transparent; }
.km-btn--outline-success { border-color: var(--positive); color: var(--positive); background: transparent; }
.km-btn--link { border-color: transparent; background: transparent; color: var(--accent); padding-left: 0; padding-right: 0; }
/* Scan button — every "Scanner…" button (opens the camera/QR scanner). See
   --km-scan-* in css/variables.css. The faint green-tinted border keeps it
   visible on the dark theme too. */
.km-btn--scan { background: var(--km-scan-bg); border-color: color-mix(in srgb, var(--km-scan-icon) 30%, var(--km-scan-bg)); color: var(--km-scan-fg); }
.km-btn--scan i { color: var(--km-scan-icon); }
/* Retour — the same look everywhere (2026-10-06): coral, distinct from
   primary (blue), scan (anthracite) and danger (red). Tokens --km-back-* in
   css/variables.css (dark override in css/themes.css). */
.km-btn--back { background: var(--km-back-bg); border: 1.5px solid var(--km-back-border); color: var(--km-back-fg); font-weight: 600; }
/* .km-btn.km-btn--x (2 classes) on purpose (fix 2026-10-06): the generic
   `.km-btn:hover` / `.km-btn:active` further down come later in the file with
   the same specificity, so a single-class rule here lost to them — hovering
   a scan button turned it light grey with its white text unreadable. */
.km-btn.km-btn--back:hover { background: color-mix(in srgb, var(--km-back-border) 18%, var(--km-back-bg)); color: var(--km-back-fg); }
.km-btn.km-btn--back:active { background: color-mix(in srgb, var(--km-back-border) 28%, var(--km-back-bg)); color: var(--km-back-fg); }
.km-btn.km-btn--scan:hover { background: color-mix(in srgb, var(--km-scan-bg) 85%, #fff); }
.km-btn.km-btn--scan:active { background: color-mix(in srgb, var(--km-scan-bg) 75%, #fff); }

/* ── Hover — real per-type darkening, mouse only ──
   `.km-btn:hover { background: var(--surface-2) }` used to sit on the base
   rule and, since --primary/--success/--danger/--warning's own :hover only
   set `filter`, that grey background cascaded through underneath the
   filter on every colored variant — hovering a solid accent button
   suddenly washed it out to a flat neutral grey ("hover looks white").
   Fixed by giving every variant its own explicit hover background, each a
   genuinely darker shade of ITS OWN color (color-mix toward black) instead
   of one generic grey for all of them.
   Wrapped in @media (hover: hover): touchscreens report `hover: none`, so
   without this a tap leaves the button visually "stuck" hovered until the
   user taps elsewhere (no mouseleave event ever fires on touch) — exactly
   the "still white after I release it" symptom. Real tap feedback for
   touch comes from :active below instead, which naturally releases on
   touchend. */
@media (hover: hover) {
  .km-btn:hover               { background: var(--surface-2); }
  .km-btn--primary:hover      { background: color-mix(in srgb, var(--accent) 82%, #000); }
  .km-btn--secondary:hover    { background: var(--surface-2); }
  .km-btn--success:hover      { background: color-mix(in srgb, var(--positive) 82%, #000); }
  .km-btn--danger:hover       { background: color-mix(in srgb, var(--negative) 82%, #000); }
  .km-btn--warning:hover      { background: color-mix(in srgb, var(--warning) 82%, #000); }
  .km-btn--ghost:hover        { background: var(--accent-soft); }
  .km-btn--ghost-danger:hover { background: color-mix(in srgb, var(--negative) 12%, transparent); }
  .km-btn--outline-primary:hover { background: var(--accent-soft); }
  .km-btn--outline-danger:hover  { background: color-mix(in srgb, var(--negative) 12%, transparent); }
  .km-btn--outline-success:hover { background: color-mix(in srgb, var(--positive) 12%, transparent); }
  .km-btn--link:hover { text-decoration: underline; background: transparent; }
}

/* ── Active (press) — works identically on mouse and touch, and always
   releases on mouseup/touchend, so it can never get stuck the way :hover
   does on touch. A darker press-state per type, one step further than the
   hover shade above. */
.km-btn:active               { background: var(--surface-2); }
.km-btn--primary:active      { background: color-mix(in srgb, var(--accent) 70%, #000); }
.km-btn--secondary:active    { background: var(--surface-2); }
.km-btn--success:active      { background: color-mix(in srgb, var(--positive) 70%, #000); }
.km-btn--danger:active       { background: color-mix(in srgb, var(--negative) 70%, #000); }
.km-btn--warning:active      { background: color-mix(in srgb, var(--warning) 70%, #000); }
.km-btn--ghost:active        { background: var(--accent-soft); }
.km-btn--ghost-danger:active { background: color-mix(in srgb, var(--negative) 18%, transparent); }
.km-btn--outline-primary:active { background: var(--accent-soft); }
.km-btn--outline-danger:active  { background: color-mix(in srgb, var(--negative) 18%, transparent); }
.km-btn--outline-success:active { background: color-mix(in srgb, var(--positive) 18%, transparent); }

/* Press scale — on top of the fill-darkening above, so a tap reads as a
   physical "push" (2026-08-23, explicit request: the fill alone wasn't
   registering as an obvious click cue). Snaps down fast (60ms, faster than
   --km-trans) so the press itself feels immediate; releasing still eases
   back out over the base --km-trans via the transition already declared on
   .km-btn. Skipped for --link (underline-only, no button "body" to press). */
.km-btn:active:not(.km-btn--link) {
  transform: scale(.95);
  transition: transform .06s ease, background var(--km-trans);
}

/* Click "wave" — a soft flat-color band sweeps diagonally top-left → bottom-
   right across the element, then fades (2026-08-23, explicit request:
   replaces the earlier brightness-flash version, which didn't read as a
   directional "fill"). Generic, reusable mechanism — `.km-clickwave` is the
   opt-in marker (any clickable element can add it, not just .km-btn), driven
   by the SAME delegated 'click' handler as before (js/app.js's
   initBtnPulse(), now selecting `.km-btn, .km-clickwave`), which toggles
   `.km-clickwave--play`. Fires on 'click' rather than :active so the full
   sweep always plays even on a fast tap.

   Color is a custom property, `--km-wave`, so one keyframe/rule works for
   every variant/module — default here is a translucent white (right for
   any solid-color fill: primary/success/danger/warning already have white
   text, so a white sweep reads as "this button lightened", not a color
   clash). Anything with a light/neutral background (secondary/outline/
   ghost, or a non-.km-btn element like `.km-aff2-step`) overrides
   `--km-wave` to a translucent tint of ITS OWN color instead — see the
   per-variant overrides below and lv/aff2/i.css's `.km-aff2-step--*`. */
.km-btn, .km-clickwave {
  --km-wave: rgba(255,255,255,.4);
  position: relative;
  overflow: hidden;
  isolation: isolate;
}
.km-btn--secondary, .km-btn--outline-primary, .km-btn--outline-danger,
.km-btn--outline-success, .km-btn--ghost, .km-btn--ghost-danger {
  --km-wave: color-mix(in srgb, var(--accent) 35%, transparent);
}
.km-btn::after, .km-clickwave::after {
  content: '';
  position: absolute;
  inset: -60% -60%;
  background: linear-gradient(135deg, transparent 42%, var(--km-wave) 50%, transparent 58%);
  transform: translate(-60%, -60%);
  opacity: 0;
  pointer-events: none;
  z-index: 1;
}
@keyframes km-clickwave {
  0%   { transform: translate(-60%, -60%); opacity: 1; }
  100% { transform: translate(60%, 60%);   opacity: 0; }
}
.km-clickwave--play::after {
  animation: km-clickwave .45s ease-out;
}

/* Sizes — rescaled to stay clearly smaller/larger than the new bigger
   default above (the old --lg was actually smaller than the old default's
   padding, an inconsistency this fixes too). */
.km-btn--sm   { padding: calc(var(--km-density) * .35) calc(var(--km-density) * .65); font-size: .82rem; font-weight: 500; }
.km-btn--lg   { padding: calc(var(--km-density) * .85) calc(var(--km-density) * 1.6); font-size: 1.05rem; }
.km-btn--block { width: 100%; }

/* Icon-only */
.km-btn--icon { width: 34px; height: 34px; padding: 0; border-radius: var(--km-radius); }
.km-btn--icon.km-btn--sm { width: 28px; height: 28px; }
.km-btn--icon.km-btn--lg { width: 42px; height: 42px; }

/* Loading state (2026-08-30, fixed — see below) — label stays visible,
   spinner appends after it instead of replacing it.
   Was `color: transparent !important` (hides the label) with the spinner
   drawn via `border: 2px solid currentColor` — but currentColor resolves
   through the SAME element's `color`, which that rule had just forced to
   transparent, so the spinner border was invisible too (fully
   transparent RGBA has no alpha for `filter: invert()` to restore — invert
   only flips RGB channels, never alpha). Net effect for every button using
   this class app-wide: no visible spinner, just the label vanishing and
   reappearing once the request finished — exactly the "hides the text
   until it's done" bug reported live. Fixed by leaving `color` alone (the
   label keeps its normal color and stays visible) and switching the
   spinner to `display: inline-block` after the label in normal flow —
   works whether the button's base class is flex (`.km-btn`, gap already
   spaces it) or plain inline/block (any other module's own button class
   that also gets this state via kmBtnLoading()/kmBtnLoadingDone(),
   js/app.js) without needing to know that button's padding. */
.km-btn--loading { pointer-events: none; }
.km-btn--loading::after {
  content: '';
  display: inline-block;
  vertical-align: middle;
  margin-left: 6px;
  width: 14px; height: 14px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: km-spin .7s linear infinite;
}

/* Button group */
.km-btn-group { display: inline-flex; }
.km-btn-group .km-btn { border-radius: 0; margin-left: -1px; }
.km-btn-group .km-btn:first-child { border-radius: var(--km-radius) 0 0 var(--km-radius); margin-left: 0; }
.km-btn-group .km-btn:last-child  { border-radius: 0 var(--km-radius) var(--km-radius) 0; }
.km-btn-group .km-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; z-index: 1; }

/* Button row */
.km-btn-row { display: flex; gap: .5rem; flex-wrap: wrap; align-items: center; }

/* Spinner */
.km-spinner {
  width: 13px; height: 13px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  animation: km-spin .7s linear infinite;
  display: inline-block;
}

@keyframes km-spin { to { transform: rotate(360deg); } }

/* ── Admin-tier action marker (2026-08-30, explicit request — simplified
   same day: "no need for that style any more, we will make it later, just
   turn the buttons as they was, and add small tag on the top left of the
   button called ADMIN in purple or black small") ──────────────────────────
   Border + animated sheen from the earlier iteration removed — purely a
   small top-left tag now, everything else about the button (its own
   color class, border, etc.) stays exactly as it already was. */
.km-btn--admin {
  position: relative;
  padding-left: calc(var(--km-density) * 1.3 + 1.5rem);
}
.km-btn--admin::after {
  content: 'ADMIN';
  position: absolute;
  top: 0; left: 0;
  font-size: .5rem;
  font-weight: 800;
  letter-spacing: .04em;
  line-height: 1;
  padding: 2px 5px 2px;
  background: #4c1d95;
  color: #fff;
  border-bottom-right-radius: calc(var(--km-radius) / 2);
}

/* "Suivant" briefly locked after a step change (kmNextLock, js/app.js). */
.km-btn.km-btn--hold { opacity: .55; cursor: wait; }
