/* static/css/components/button.css
   The button component. Reads component tokens (--btn-*) defined on :root below,
   which resolve from tokens.css. Never hardcode a value in this file.
   Geometry (2026-08-18): 36px tall, --radius-xs 6px corner, --font-body at
   --fs-sm. Was 59.5px tall with 25px type — padding plus --fs-md overshot the
   44px --tap-min floor entirely, so the floor was never the binding constraint.
   Buttons never move on hover; --btn-transition carries colour only. */
:root{
  --btn-bg:var(--color-action);
  --btn-bg-hover:var(--color-action-hover);
  --btn-fg:var(--color-on-action);
  --btn-fg-hover:var(--btn-fg);   /* hover keeps fg unless a variant inverts (see --secondary) */
  --btn-fg-active:var(--color-on-action);
  --btn-border:transparent;
  --btn-border-width:1px;
  --btn-radius:var(--radius-xs);
  --btn-pad-block:var(--space-3xs);
  --btn-pad-inline:var(--space-s);
  --btn-font:var(--font-body);
  --btn-fs:var(--fs-sm);
  --btn-weight:var(--fw-medium);
  --btn-min-h:36px;
  --btn-line-height:1.1;
  --btn-transition:background-color var(--dur-quick) var(--ease), border-color var(--dur-quick) var(--ease);
  --btn-icon-size:2.25rem;
  --btn-icon-svg:1.25rem;
}
.th-btn{
  /* base.css has no global border-box reset, so without this, min-height would
     size the content box and padding/border would be added on top of it. */
  box-sizing:border-box;
  display:inline-flex; align-items:center; justify-content:center;
  gap:var(--space-2xs);
  min-height:var(--btn-min-h);
  padding:var(--btn-pad-block) var(--btn-pad-inline);
  border:var(--btn-border-width) solid var(--btn-border);
  border-radius:var(--btn-radius);
  font-family:var(--btn-font); font-size:var(--btn-fs); font-weight:var(--btn-weight);
  line-height:var(--btn-line-height); text-decoration:none; cursor:pointer; width:fit-content;
  background:var(--btn-bg); color:var(--btn-fg);
  transition:var(--btn-transition);

  &:hover{ background:var(--btn-bg-hover); color:var(--btn-fg-hover); }
  &:active{ color:var(--btn-fg-active); }
  &:focus-visible{ outline:3px solid var(--color-action); outline-offset:2px; }
}
/* Outline button: transparent + navy at rest, and it STAYS navy on hover — only
   the fill warms to --color-bg. The pre-2026-08-18 rule inverted to a solid
   cornflower fill, which now reads as a second primary next to the 6px geometry. */
.th-btn--secondary{
  --btn-bg:transparent; --btn-fg:var(--th-blue-900); --btn-border:var(--th-blue-900);
  --btn-bg-hover:var(--color-bg); --btn-fg-hover:var(--th-blue-900);
}
/* Tinted secondary: a filled-but-quiet alternative for when an outline is too
   faint and a primary too loud. The rest-state border is TRANSPARENT rather than
   absent on purpose — the hover border must not change the box size. */
.th-btn--tinted{
  --btn-bg:var(--color-action-tint); --btn-fg:var(--th-blue-900);
  --btn-border:transparent;
  --btn-bg-hover:var(--color-action-tint); --btn-fg-hover:var(--th-blue-900);
}
.th-btn--tinted:hover{ border-color:var(--color-action); }
/* On-dark: for a control sitting on --color-surface-dark (the navy panel on
   /about, the /users action bar, the login hero). White fill, navy text, and it
   STAYS that way on hover -- only the fill cools slightly, because a cornflower
   hover would read as a different button against navy. The border is set (not
   absent) so the hover state cannot change the box size, same reason as --tinted.
   Before this variant existed, pages/login.css and pages/user-management.css each
   hand-rolled their own on-dark control. */
.th-btn--on-dark{
  --btn-bg:var(--th-white); --btn-fg:var(--th-blue-900);
  --btn-border:var(--th-white);
  --btn-bg-hover:color-mix(in srgb, var(--th-white) 88%, var(--th-blue-200));
  --btn-fg-hover:var(--th-blue-900);
}
/* Danger keeps its red identity on hover (darker red), not the default cornflower. */
.th-btn--danger{
  --btn-bg:var(--color-error); --btn-fg:var(--color-on-action);
  --btn-bg-hover:color-mix(in srgb, var(--color-error) 86%, black);
}
/* Ghost stays quiet: a subtle neutral tint on hover, dark text unchanged. */
.th-btn--ghost{
  --btn-bg:transparent; --btn-fg:var(--color-text); --btn-border:transparent;
  --btn-bg-hover:color-mix(in srgb, var(--color-text) 10%, transparent); --btn-fg-hover:var(--color-text);
}
/* Small size: differs from the base only in its min-height floor (30px vs 36px).
   --btn-fs, --btn-pad-inline, and --btn-pad-block used to be set here too; all
   three either became the base value or would have made --sm render TALLER than
   the base under border-box, so they were dropped rather than left restating or
   fighting the base. */
.th-btn--sm{
  --btn-min-h:30px;
}
/* Square icon-only control (36px). Composes with any colour variant, e.g.
   `th-btn th-btn--ghost th-btn--icon`. Icon-only means an aria-label is
   mandatory at every call site — there is no visible text to read.
   Resets --btn-min-h like --sm does, though the base floor is now also 36px —
   equal to this control's own height — so this is belt-and-braces rather than
   load-bearing; kept in case the base floor ever changes independently. */
.th-btn--icon{
  width:var(--btn-icon-size); height:var(--btn-icon-size);
  padding:0; --btn-min-h:auto;
  flex:none;
}
.th-btn--icon svg{
  width:var(--btn-icon-svg); height:var(--btn-icon-svg);
  flex-shrink:0;
}
/* Disabled: keep the shape, read as inert. Covers both the native <button
   disabled> and links marked aria-disabled (an <a> can't be disabled).
   Was duplicated per page (pages/system.css) before Phase-D slice 3. */
.th-btn:disabled,
.th-btn[aria-disabled="true"]{
  opacity:0.5; cursor:not-allowed;
}
.th-btn[hidden]{ display:none; }   /* [hidden] must beat display:inline-flex */

/* ---------- Async action states ----------
   For buttons that fire a request and report the outcome in place (the purge
   controls on /users are the first caller). `is-loading` swaps the label for a
   spinner; `is-success` turns the whole control green. Both are component
   states so any future async action can reuse them. */
.th-btn.is-loading{
  pointer-events:none;
  position:relative;
  color:transparent;          /* hide the label, keep the button's width */
}
.th-btn.is-loading::after{
  /* No top/left offset needed: with no offsets set, the flex parent resolves
     this absolutely-positioned pseudo-element's static position as if it were
     the sole flex item, so align-items/justify-content center it for free. */
  content:"";
  position:absolute;
  width:1em; height:1em;
  border:2px solid var(--btn-fg);
  border-top-color:transparent;
  border-radius:var(--radius-circle);
  animation:th-btn-spin var(--dur-slow) linear infinite;
}
@keyframes th-btn-spin{ to{ transform:rotate(360deg); } }

/* Motion is decoration here: the ring stays visible in place of the label, so
   "working" still reads without it — it just stops rotating. */
@media (prefers-reduced-motion: reduce){
  .th-btn.is-loading::after{ animation:none; }
}

/* --btn-border must be re-pointed too, not just the fill: on `--secondary` it is
   --color-action, which would leave a cornflower ring around the green button. */
.th-btn.is-success{
  --btn-bg:var(--color-success);
  --btn-bg-hover:var(--color-success);
  --btn-fg:var(--color-on-action);
  --btn-border:var(--color-success);
  pointer-events:none;
}

/* `.icon-btn` (the old 36px square control from the styles.css monolith) was
   retired with the actions feature on 2026-07-27. Its replacement is the
   `.th-btn--icon` variant above — use that, do not revive a separate class. */
