/* ============================================================================
   login.css — Apex Dash Royal Sports, entry surface

   Layout for login.html only. Consumes tokens.css; introduces no colour, type
   step, radius or shadow of its own. app.css still loads first and owns the
   base, the skip link, .apex-btn, .apex-iconbtn and .apex-field — this file
   composes those and adds the stage/rail split and the sign-in form.

   Cascade note, same as app.css: everything here is UNLAYERED, so it beats
   Tailwind's layered utilities and matches app.css on import order. A rule in
   this file that competes with one in app.css for the same property wins by
   being later, not by being more specific — which is why the field height and
   padding below are written as plain class rules.

   The page is the Midnight Field family end to end — a top-to-bottom sky from
   field-bright into field, not a flat fill. That is not a dark theme: DESIGN.md's
   own hero is full-bleed Midnight Field and the locked light theme governs the
   surfaces, not the field. The only light surface here is the card.

   The lighter top is the one thing on this page that has to be watched: the white
   card (10.1:1 across it), the ember dot field (2.96:1) and the mark (3.16:1) all
   separate from --apex-field-bright by less than they do from --apex-field. All
   three still clear their floors, but the mark against the top of the sky is the
   tightest number on the page. Darken --apex-field-bright to buy any of it back.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   1. Field
   --------------------------------------------------------------------------- */

.apex-login-body {
  background-color: var(--apex-field);
  /* app.css's body colour is --apex-ink, which is a light-surface ink. On this
     page's Midnight Field field it measures 1.6:1, so the page default has to
     be set here even though every visible run of text below also names its own
     colour — an uncoloured string in the stage would otherwise be unreadable. */
  color: var(--apex-on-field);
}

.apex-login {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  min-height: 100dvh;
  /* The field layer is absolutely positioned against this box, so this element
     has to be the containing block. */
  position: relative;
  background-color: var(--apex-field);
  /* The field is a sky: field-bright at the top through field-mid to field at
     the bottom, with the key-light hotspot painted over it. Top layer first.

     The hotspot is `--apex-field-veil`, an alpha, and that is load-bearing: the
     lift used to fade to the OPAQUE field colour, which is fine when the field is
     flat but hides the gradient everywhere outside its own radius. Fading to
     transparent lets the sky through and leaves the lift doing its one job —
     saying where the key light is.

     It sits at 66% because the key is at (5, 10, 7), upper right: a field glowing
     from one side while the object is lit from the other is the kind of thing
     nobody names and everybody feels. The linear gradient runs top to bottom for
     the same reason — the light comes from above.

     It deliberately stays here, UNDER the dot field, rather than moving above it
     to survive the field's opaque canvas: the field is mounted transparent
     instead (see dots.js), so the lift, the mark and the card all sit over dots
     rather than the dots sitting over a repainted flat navy. */
  background-image:
    radial-gradient(72% 66% at 66% 42%, var(--apex-field-veil) 0%, transparent 70%),
    linear-gradient(
      180deg,
      var(--apex-field-bright) 0%,
      var(--apex-field-mid) 55%,
      var(--apex-field) 100%
    );
}

/* Vanta's canvas, and only its canvas, lives in here. Empty when the effect is
   unavailable — no three.js, no Vanta, no WebGL context, or a reader who asked
   for reduced motion — and the field below is then exactly the page it was
   before this layer existed, which is the point of giving it its own element.

   The opacity is a direct user instruction and it is deliberately high. At 0.25
   the dots composited to roughly rgb(66 49 54) and read as a texture; at 0.9 a
   dot composites to roughly rgb(219 105 50), which is the brand ember itself, so
   the field is an ember wash rather than a constellation. That is the wanted
   effect, and it is also the one thing on this page that pushes against the One
   Voice Rule — the mark and the single button no longer have the field to
   themselves. AGENTS.md requires the ember share of the viewport to be
   re-measured whenever this number moves; PLAN.md §19.4 carries the count.

   pointer-events: none is safe. The effect listens on window for mousemove and
   touchmove and measures against its own canvas rect, so it still tracks the
   pointer — but the layer can never swallow a click meant for the card.

   The name is __dots and not __field on purpose: __field is already the form
   field wrapper (section 4 below), and a second owner of that class would make
   this layer's position/pointer-events/opacity apply to the two inputs. */
.apex-login__dots {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  opacity: 0.9;
}

/* ---------------------------------------------------------------------------
   2. Stage
   --------------------------------------------------------------------------- */

.apex-login__stage {
  position: relative;
  /* Above the dot field. The field is absolutely positioned and these are grid
     items, so without a z-index here and on the rail below, painting order would
     put the field's canvas over the card. */
  z-index: 1;
  /* Tall enough that the mark reads as the subject of the band rather than an
     ornament in it: the stage is wide and short under 1024px, so the mark's
     size is bound by this height. */
  min-height: 38dvh;
}

/* The bloom the mark sits in. A pseudo-element, so it is the stage's first child
   and paints below the canvas and the fallback without either of them knowing
   about it — which also means the blocked-CDN PNG gets the same glow as the
   live scene.

   Sized as an ellipse a little wider than it is tall, because the mark is: the
   glow has to read as light coming off the object rather than as a spotlight the
   object happens to stand in. It is not an animation, so reduced motion leaves
   it alone. */
.apex-login__stage::before {
  content: "";
  position: absolute;
  inset: 22% 16%;
  background-image: radial-gradient(closest-side, var(--apex-ember-veil), transparent 100%);
  pointer-events: none;
}

.apex-login__scene {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

/* Shown only when three.js or the mask is unavailable. Sized so the mark lands
   near the scale the scene frames it at, so the composition does not change
   shape when the CDN is blocked. The vh term tracks scene.js's FIT: it was 26vh
   against FIT 0.52, and 22vh of 26 is 0.44 of 0.52. Change one and change the
   other, or the blocked-CDN page stops matching the live one. */
.apex-login__fallback {
  position: absolute;
  top: 50%;
  left: 50%;
  width: clamp(128px, 22vh, 280px);
  height: auto;
  transform: translate(-50%, -50%);
}

/* ---------------------------------------------------------------------------
   3. Rail and card
   --------------------------------------------------------------------------- */

.apex-login__rail {
  display: grid;
  align-content: center;
  justify-items: center;
  gap: var(--apex-s4);
  padding: var(--apex-s6) var(--apex-s5);
  /* Positioned so the z-index below is not ignored — see the stage. */
  position: relative;
  z-index: 1;
}

.apex-login__card {
  width: 100%;
  max-width: 400px;
  padding: var(--apex-s6);
  border-radius: var(--apex-r-card);
  background-color: var(--apex-white);
  /* The card is a light surface again, so the ink flips back here rather than
     on each of the eleven runs of type inside it. */
  color: var(--apex-ink);
  box-shadow: var(--apex-sh-ambient);
}

.apex-login__brand {
  display: flex;
  align-items: center;
  gap: var(--apex-s3);
}

.apex-login__brand-name {
  display: block;
  color: var(--apex-field);
  font-family: var(--apex-font-display);
  font-size: var(--apex-fs-subhead);
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.2;
}

.apex-login__brand-sub {
  display: block;
  color: var(--apex-muted);
  font-size: var(--apex-fs-micro);
  font-weight: 800;
  letter-spacing: var(--apex-ls-micro);
  text-transform: uppercase;
}

.apex-login__title {
  margin-top: var(--apex-s5);
  color: var(--apex-field);
  font-size: var(--apex-fs-metric);
  font-weight: 800;
  letter-spacing: -0.01em;
  line-height: 1.2;
}

.apex-login__lead {
  margin-top: var(--apex-s2);
  color: var(--apex-muted-deep);
  font-size: var(--apex-fs-body);
  line-height: 1.55;
  text-wrap: pretty;
}

/* ---------------------------------------------------------------------------
   4. Form
   --------------------------------------------------------------------------- */

.apex-login__form {
  display: grid;
  gap: var(--apex-s4);
  margin-top: var(--apex-s6);
}

.apex-login__field {
  display: grid;
  gap: var(--apex-s2);
}

.apex-login__label {
  color: var(--apex-muted-deep);
  font-size: var(--apex-fs-label);
  font-weight: 800;
  letter-spacing: var(--apex-ls-label);
  text-transform: uppercase;
}

.apex-login__control {
  position: relative;
  display: flex;
  align-items: center;
}

.apex-login__control > svg.feather {
  position: absolute;
  left: var(--apex-s4);
  color: var(--apex-muted);
  pointer-events: none;
}

/* 48px, not the 44px floor: the reveal toggle is a 44px target sitting inside
   the pill, and it needs an inset to read as a control rather than as the whole
   field. 16px text also keeps mobile Safari from zooming the viewport on focus. */
.apex-login__input {
  width: 100%;
  height: 48px;
  padding-left: calc(var(--apex-s4) + 20px + var(--apex-s3));
  padding-right: var(--apex-s4);
  font-size: 1rem;
}

.apex-login__input--secret {
  padding-right: calc(var(--apex-s2) + var(--apex-tap));
}

.apex-login__reveal {
  position: absolute;
  top: 50%;
  right: var(--apex-s1);
  transform: translateY(-50%);
  color: var(--apex-muted);
}

.apex-login__reveal-icon {
  display: inline-grid;
  place-items: center;
}

.apex-login__error {
  display: flex;
  align-items: flex-start;
  gap: var(--apex-s2);
  color: var(--apex-error-text);
  font-size: var(--apex-fs-small);
  font-weight: 600;
  line-height: 1.45;
}

.apex-login__error svg.feather {
  flex: 0 0 auto;
  margin-top: 1px;
}

/* An invalid field takes the error hue on its border and its focus ring. Left
   as ember it would look like an invitation to act, which is the opposite of
   what the message under it says. */
.apex-login__input[aria-invalid="true"] {
  border-color: var(--apex-error-border);
}

.apex-login__input[aria-invalid="true"]:focus-visible {
  border-color: var(--apex-error-text);
  box-shadow: 0 0 0 3px var(--apex-error-ring);
}

.apex-login__submit {
  width: 100%;
  min-height: 48px;
  margin-top: var(--apex-s1);
}

.apex-login__note {
  display: flex;
  align-items: flex-start;
  gap: var(--apex-s2);
  margin-top: var(--apex-s5);
  padding-top: var(--apex-s4);
  border-top: 1px solid var(--apex-line-70);
  color: var(--apex-muted);
  font-size: var(--apex-fs-small);
  line-height: 1.5;
}

.apex-login__note svg.feather {
  flex: 0 0 auto;
  margin-top: 1px;
}

.apex-login__noscript {
  width: 100%;
  max-width: 400px;
  border-radius: var(--apex-r-card);
}

/* ---------------------------------------------------------------------------
   5. Browser surfaces
   The parts no design system draws, themed from the palette. A sign-in form is
   the field most likely to be autofilled, and the UA's pale slab would land
   inside a white pill.
   --------------------------------------------------------------------------- */

.apex-login__input:-webkit-autofill,
.apex-login__input:-webkit-autofill:hover,
.apex-login__input:-webkit-autofill:focus {
  /* An inset shadow is the only thing that outranks the UA's autofill
     background; a background-color on the input loses to it every time. */
  box-shadow: 0 0 0 1000px var(--apex-white) inset;
  -webkit-text-fill-color: var(--apex-ink);
}

.apex-login__input:-webkit-autofill:focus-visible {
  box-shadow: 0 0 0 1000px var(--apex-white) inset,
    0 0 0 3px var(--apex-ember-ring);
}

.apex-login__input[aria-invalid="true"]:-webkit-autofill:focus-visible {
  box-shadow: 0 0 0 1000px var(--apex-white) inset,
    0 0 0 3px var(--apex-error-ring);
}

/* Edge paints its own reveal eye into a password field, which would sit beside
   this page's toggle and hide the same thing twice. One control, or none. */
.apex-login__input::-ms-reveal {
  display: none;
}

/* ---------------------------------------------------------------------------
   6. Adapt
   --------------------------------------------------------------------------- */

@media (min-width: 1024px) {
  .apex-login {
    grid-template-columns: minmax(0, 1fr) clamp(400px, 32vw, 448px);
    /* The stage is the page. Locking the row means the field never scrolls
       behind the mark; if a short viewport cannot fit the card, the rail
       scrolls inside itself instead. */
    height: 100dvh;
    min-height: 0;
    overflow: hidden;
  }

  .apex-login__stage {
    min-height: 0;
  }

  .apex-login__rail {
    /* The 1px hairline that used to separate stage from rail is gone. It was
       drawn to divide two panels; with the dot field running full-bleed behind
       both, there is no seam left for it to mark, and a rule laid over a dot
       field reads as a glitch rather than as structure. The card's own
       elevation separates it. */
    overflow-y: auto;
    padding: var(--apex-s6);
  }
}

@media (max-width: 479px) {
  .apex-login__stage {
    min-height: 34dvh;
  }

  .apex-login__card {
    padding: var(--apex-s5);
  }
}
