/* ==========================================================================
   LOGIN EFFECTS — pointer animation for the authentication screens only

   Loaded by the three unauthenticated screens (admin sign-in, admin access
   request, member portal sign-in) and by nothing else. Every rule is scoped
   under `.auth-fx`, a class that /js/login-effects.js puts on the login card
   or the individual sign-in stage — never on a dashboard, and never on the
   member portal's signed-in stage, which shares a container with the form.

   Nothing here changes a layout. The three effects are:

     1. a soft pointer-following glow, painted behind the form
     2. a small light travelling along an input's top and bottom edge
     3. the disabled half of the button sweep the site already has

   Colour comes from `var(--gold)` throughout rather than from a literal, so
   the same file follows the club's gold on the public site, the admin panel's
   blue-grey in its light theme, and both themes' dark variants, with no
   second set of rules and nothing to keep in step.
   ========================================================================== */

/* Registered so it can be *transitioned*. An unregistered custom property is
   a string as far as the animation engine is concerned and jumps between its
   values; registering it as a number lets the edge light fade in and out
   instead of snapping. Browsers without @property (Firefox before 128) simply
   get the snap, which is the effect without the polish rather than a break. */
@property --auth-edge {
  syntax: "<number>";
  inherits: false;
  initial-value: 0;
}

/* --------------------------------------------------------------------------
   1. The glow
   -------------------------------------------------------------------------- */

/* `position: relative` with no offsets moves nothing — it only makes the card
   the containing block for the glow layer below. */
.auth-fx { position: relative; }

/* The real content is lifted into its own stacking level so the glow, which
   is a positioned element, cannot paint over a label, an input or a button.
   `position: relative` without offsets again: no movement, only order. */
.auth-fx > *:not(.auth-fx-glow) {
  position: relative;
  z-index: 1;
}

/* The clip. Kept on this layer rather than as `overflow: hidden` on the card
   itself, because the card's own overflow would also clip the focus ring of
   an input sitting near its edge. */
.auth-fx-glow {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;          /* never in front of a click */
  opacity: 0;
  transition: opacity 0.4s ease;
}

/* Shown only where there is a pointer that hovers, and gated in CSS as well as
   in the script.
 *
 * The script decides once, at load. That is right for a phone, which is a
 * phone for the whole visit, and wrong for the two cases where the answer
 * changes underneath it: a window dragged onto a touchscreen, and a desktop
 * browser switched into device emulation. In both the element is already in
 * the page, and `pointerenter` fires on a *tap* as readily as on a hover — so
 * without this rule a finger would light a glow that then has no pointer to
 * follow and nothing to put it out. */
@media (hover: hover) and (pointer: fine) {
  .auth-fx.is-hot .auth-fx-glow { opacity: 1; }
}

/* The blob itself.
 *
 * A radial gradient rather than `filter: blur()` — a gradient is soft by
 * construction and costs one paint, while a blurred layer costs a filter pass
 * on every frame, which is what makes this kind of effect stutter on a phone.
 *
 * Moved with `transform` only. The element's own box never changes, so the
 * pointer moving produces no layout and no reflow, and the compositor can do
 * the work. `will-change` is deliberately absent: the layer is promoted while
 * it is animating anyway, and pinning it permanently costs memory on a page
 * that is idle most of the time.
 */
.auth-fx-glow::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 340px;
  height: 340px;
  margin: -170px 0 0 -170px;     /* centres the blob on the pointer */
  transform: translate3d(var(--auth-x, 50%), var(--auth-y, 50%), 0);
  /* The literal comes first as the fallback: a browser without color-mix()
     keeps a fixed gold rather than dropping the declaration and showing
     nothing. */
  background: radial-gradient(closest-side circle,
                              rgba(216, 178, 94, 0.30), transparent 100%);
  background: radial-gradient(closest-side circle,
                              color-mix(in srgb, var(--gold) 28%, transparent),
                              transparent 100%);
}

/* --------------------------------------------------------------------------
   2. The input edge light
   -------------------------------------------------------------------------- */

/* Two thin gradients laid over the input's own background, positioned at the
 * pointer's horizontal position.
 *
 * Painted as `background-image` rather than through a wrapper element, and
 * that is the important decision here: wrapping every field in a <span> to
 * hang pseudo-elements off it would put a new node between each input and the
 * form, which is exactly the sort of change that quietly breaks a password
 * manager, an `input + label` selector or a browser's autofill heuristics.
 * Nothing about the markup changes this way — no wrapper, no attribute, no id.
 *
 * `background-color` is left completely alone, so an autofilled field keeps
 * whatever the browser paints on it and stays readable.
 *
 * Only where there is a real pointer. On a touch screen there is nothing to
 * follow, and the rule is not even parsed into effect.
 */
@media (hover: hover) and (pointer: fine) {
  .auth-fx input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="submit"]) {
    background-image:
      radial-gradient(42px 3px at var(--auth-ix, 50%) 0%,
                      rgba(216, 178, 94, calc(var(--auth-edge, 0) * 0.85)),
                      transparent 70%),
      radial-gradient(42px 3px at var(--auth-ix, 50%) 100%,
                      rgba(216, 178, 94, calc(var(--auth-edge, 0) * 0.85)),
                      transparent 70%);
    background-repeat: no-repeat;
    /* The site's own input transition is repeated here on purpose. This is a
       shorthand and would otherwise replace it, taking the focus ring's fade
       with it. */
    transition:
      border-color 0.25s ease,
      box-shadow 0.25s ease,
      background 0.25s ease,
      --auth-edge 0.28s ease;
  }

  /* Same two gradients in the club's live gold, for browsers that can mix.
     Split from the block above so the fallback survives if this one is not
     understood. */
  @supports (color: color-mix(in srgb, red 50%, transparent)) {
    .auth-fx input:not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="submit"]) {
      background-image:
        radial-gradient(42px 3px at var(--auth-ix, 50%) 0%,
                        color-mix(in srgb, var(--gold)
                                  calc(var(--auth-edge, 0) * 85%), transparent),
                        transparent 70%),
        radial-gradient(42px 3px at var(--auth-ix, 50%) 100%,
                        color-mix(in srgb, var(--gold)
                                  calc(var(--auth-edge, 0) * 85%), transparent),
                        transparent 70%);
    }
  }
}

/* --------------------------------------------------------------------------
   3. The button sweep — the half that was missing
   -------------------------------------------------------------------------- */

/* The diagonal shine, the hover lift and the gold focus ring are already in
 * style.css and apply to every .btn on the site, these screens included. They
 * are deliberately left exactly as they are.
 *
 * What was missing is the other half of the rule: a button that is disabled
 * because a request is in flight — "Sending…", "Submitting…", "Verifying…" —
 * still lifted and still swept when the pointer crossed it, which reads as
 * "press me again" at the one moment pressing again is wrong.
 */
.auth-fx .btn:disabled::before,
.auth-fx button:disabled::before { content: none; }

.auth-fx .btn:disabled:hover,
.auth-fx button:disabled:hover { transform: none; }

.auth-fx .btn:disabled,
.auth-fx button:disabled { cursor: default; }

/* --------------------------------------------------------------------------
   Reduced motion
   -------------------------------------------------------------------------- */

/* Everything above is decoration. With the setting on, all of it goes and the
   forms keep every part that carries meaning — the focus ring, the disabled
   state, the field colours, the validation. */
@media (prefers-reduced-motion: reduce) {
  .auth-fx-glow { display: none; }

  .auth-fx input { background-image: none; }

  .auth-fx .btn::before,
  .auth-fx button::before { content: none; }

  .auth-fx .btn:hover,
  .auth-fx button:hover { transform: none; }
}

/* ==========================================================================
   PART B ADDITIONS — motions taken from the sign-in-card-2 reference

   Three things from that reference that were not already here. What is
   deliberately NOT taken from it: its purple gradient, its black glass card,
   its Google button, its sign-up link and its "StyleMe" wording. This project
   has its own branding, its own fields and no social authentication, and the
   reference was supplied as an animation reference only.

   The reference's travelling border light is not repeated here either — it is
   already provided, better, by /css/border-beam.css, which puts a real conic
   ring on these same cards.
   ========================================================================== */

/* ---------- 1. entrance ----------
   The card arrives rather than appearing. 18px is enough to read as movement
   and small enough that nothing reflows around it — the transform is composited
   and the element's box never changes. */
@keyframes auth-rise {
  from { opacity: 0; transform: translate3d(0, 18px, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

.auth-enter {
  animation: auth-rise 0.55s cubic-bezier(0.22, 1, 0.36, 1) both;
}

/* ---------- 2. the tilt ----------
   Applied to the compact sign-in cards only, and only where there is a mouse.
   The angles come from the script as two custom properties; the element keeps
   a single transform so nothing fights over it.

   Kept to a few degrees on purpose: the reference uses ±10°, which on a card
   the member is about to type into makes the fields feel like they are moving
   away from the pointer. */
@media (hover: hover) and (pointer: fine) {
  .auth-tilt {
    transform:
      perspective(1100px)
      rotateX(var(--auth-rx, 0deg))
      rotateY(var(--auth-ry, 0deg));
    transform-style: preserve-3d;
    /* Only on the way back to rest. During the movement the script writes
       every frame, and a transition there would lag behind the pointer. */
    transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .auth-tilt.is-tilting { transition: none; }
}

/* ---------- 3. the loading state ----------
   A ring that spins while a request is in flight, beside whatever wording the
   existing script has put in the button — "Sending…", "Verifying…",
   "Preparing…". The text is never touched: this is a ::after on the button, so
   the script owns the label and this owns the indicator.

   Only inside an authentication card, and only while genuinely disabled. */
.auth-fx .btn:disabled::after,
.auth-fx button:disabled::after {
  content: "";
  display: inline-block;
  width: 0.85em;
  height: 0.85em;
  margin-left: 0.55em;
  vertical-align: -0.12em;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  opacity: 0.75;
  animation: auth-spin 0.7s linear infinite;
}

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

/* ---------- reduced motion ----------
   The entrance and the tilt go; the spinner keeps turning, because it is not
   decoration — it is the only thing telling the member the request is still
   running. Its motion is small, steady and confined to one 12px ring. */
@media (prefers-reduced-motion: reduce) {
  .auth-enter { animation: none; }
  .auth-tilt { transform: none !important; transition: none; }
}
