/* The pages an operator meets before the portal has earned any trust: sign in,
 * the code after it, the way back in when the phone is gone, and the handful of
 * pages that hand over or replace a credential.
 *
 * A separate sheet rather than another section of portal.css, because none of
 * this is reused by the hundred pages behind the sign-in and because portal.css
 * is the file everybody edits. A credential screen is also the one place in the
 * console where restraint is the whole brief: it is read once, quickly, often by
 * somebody who is already annoyed, and every ornament on it is another thing
 * between them and the fleet.
 *
 * Tokens only. Every colour, shadow, duration and easing below comes from the
 * block at the top of portal.css, so a change to the palette reaches these
 * pages without a second edit, and light mode needs no rules of its own.
 *
 * One gradient per screen. `--brand-gradient` is on the primary button and
 * nowhere else, which is why the mark beside the product name is flattened to
 * `--primary` here: two glowing things and the eye has nowhere to go.
 */

/* --- the column ----------------------------------------------------------- */

/* `.signed-out` centres with `place-items: center`, which makes the main
   element shrink to its content rather than filling the 420px column it was
   given. Without this a short card and a long one are two different widths,
   which reads as two different pieces of software rather than two steps. */
.signed-out main { width: 100%; }

.auth {
  display: grid;
  gap: 16px;
  width: 100%;
}

/* The pages with more than one card keep the portal's own page heading rather
   than folding the title into the first card. Its bottom margin would land on
   top of the grid gap and open a hole under the heading. */
.auth > .page-head,
.auth-account > .page-head { margin-bottom: 0; }

/* The product mark, above the card rather than inside it. A signed-out page has
   no sidebar, so this is the only thing on screen that says whose sign-in form
   this is, which matters on a page whose whole job is to be typed into. */
.auth-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 14px;
  margin: 0;
  font-size: 32px;
  font-weight: 700;
  letter-spacing: -0.02em;
}

.auth-mark .brand-logo {
  width: 72px;
  height: 72px;
  flex-basis: 72px;
}

/* --- the card ------------------------------------------------------------- */

.auth-card {
  padding: 26px 24px 24px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background-color: var(--surface-raised);
  /* The line of light along the top edge. `--sheen` fades out by 40 percent of
     the height, so it lifts the card off the ground without a second border and
     without a highlight colour of its own. */
  background-image: var(--sheen);
  box-shadow: var(--shadow);
}

/* Which step this is, above the heading. The second half of a two-step sign-in
   is otherwise a different page that appeared for no reason, and somebody who
   cannot see why they are being asked for a number is somebody who closes the
   tab. */
.auth-step {
  margin: 0 0 6px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 650;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.auth-card > h1 {
  margin: 0 0 8px;
  font-size: 22px;
  letter-spacing: -0.01em;
}

.auth-card > h2 {
  margin: 0 0 8px;
  font-size: 16px;
}

.auth-lede {
  margin: 0;
  max-width: 48ch;
  color: var(--muted);
  font-size: 14px;
}

/* --- the fields ----------------------------------------------------------- */

.auth-form { margin: 0; }

.auth-form .field { margin-bottom: 14px; }
.auth-form .field:last-of-type { margin-bottom: 0; }

.auth-form .field > label {
  margin-bottom: 5px;
  font-size: 13px;
  font-weight: 600;
}

/* Sunk rather than level with the card, so the part that takes typing is the
   part that looks like it does. */
.auth-form input[type="text"],
.auth-form input[type="password"],
.auth-form input[type="email"] {
  padding: 10px 12px;
  border-radius: 9px;
  background: var(--surface-sunk);
  transition: border-color var(--dur-micro) var(--ease-out);
}

.auth-form input:hover { border-color: var(--border-strong); }
.auth-form input:focus { border-color: var(--primary); }

/* Nothing here for `.field-invalid` or `.field-error`. Both are styled in
   components.css, which loads after this file, and a credential form is the
   last place that should have its own idea of what a rejected field looks
   like. */

.auth-form .help {
  margin-top: 5px;
  font-size: 13px;
}

/* --- the one action ------------------------------------------------------- */

/* Stacked and full width rather than a row. There is one thing to do on these
   pages, the primary is first in the source and first down the screen, and on a
   phone held one-handed a full-width target does not need aiming at. */
.auth-actions {
  display: grid;
  gap: 10px;
  margin: 20px 0 0;
}

.auth-submit,
.auth-actions .button {
  justify-content: center;
  padding: 11px 16px;
  border-radius: 9px;
  font-size: 15px;
}

/* The one gradient on the screen, and the only lit thing on it.
 *
 * The label is 19px and bold for emphasis now, not for contrast. It was
 * originally sized up to buy the 3:1 large-text threshold, because white on
 * the gradient was 4.23:1 and 3.53:1 at the time and no darker token existed.
 * `--brand-gradient` is the accessible pair since, at 5.70:1 and 4.60:1, so
 * body-sized text on it would pass and this size is a free choice: the single
 * action on a sign-in card is worth being the biggest thing on it.
 *
 * `background-size` is wider than the button so the run stays in the violet half
 * and the pink arrives as a hint at the trailing edge rather than a second
 * colour competing with the first. */
/* Scoped through `.auth-actions` to outrank the sizing rule above it, which
   matches the same button through its own descendant selector and otherwise
   keeps the label at 15px however loudly this block asks for 19. */
.auth-actions .auth-submit,
.auth-actions .auth-submit:hover {
  border-color: transparent;
  background-color: var(--primary);
  background-image: var(--brand-gradient);
  background-size: 220% 100%;
  background-position: 0 0;
  color: var(--primary-text);
  font-size: 19px;
  font-weight: 700;
}

.auth-submit {
  box-shadow: var(--glow-primary);
  transition:
    transform var(--dur-micro) var(--ease-out),
    box-shadow var(--dur-micro) var(--ease-out);
}

.auth-submit:hover {
  transform: translateY(-1px);
  box-shadow: var(--glow-primary), var(--shadow);
}

.auth-submit:active { transform: translateY(0); }

/* --- the refusal ---------------------------------------------------------- */

/* Space for a refusal is held open whether or not there is one to show, so the
   fields sit at the same height on the second attempt as on the first. Two lines
   are reserved because that is what the one refusal these pages can show comes
   to inside a 420px card, measured rather than guessed; one line looked tidier
   and moved everything down twenty pixels the moment it was wrong. */
.auth-alert-slot {
  display: grid;
  align-content: center;
  min-height: 62px;
  margin: 14px 0 2px;
}

/* Held-open space is a gap that looks like a mistake, so it holds a rule between
   the explanation and the form instead, and the alert takes the rule's place. */
.auth-alert-slot::before {
  content: "";
  height: 1px;
  background-color: var(--border);
}

.auth-alert-slot:has(.auth-alert)::before { display: none; }

.auth-alert {
  display: flex;
  gap: 9px;
  align-items: flex-start;
  margin: 0;
  padding: 10px 12px;
  border-radius: 9px;
  font-size: 14px;
  line-height: 1.45;
  animation: auth-alert-in var(--dur-enter) var(--ease-spring) both;
}

.auth-alert p { margin: 0; }
.auth-alert p + p { margin-top: 5px; }

/* A mark as well as the colour, because roughly one man in twelve reads this
   panel as grey text in a slightly different grey box. */
.auth-alert-glyph {
  flex: 0 0 auto;
  font-size: 15px;
  line-height: 1.5;
}

/* --- the six digits ------------------------------------------------------- */

/* One field, not six cells. A cell per digit looks precise and breaks the first
   thing anybody does with a code they have just read off a phone, which is
   paste it. The field is sized and spaced to say "six digits" instead. */
.auth-code input[type="text"] {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 30px;
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: 0.34em;
  text-align: center;
  /* Tracking is added after the last digit as well as between them, so the run
     sits half a space left of centre without this correction. */
  text-indent: 0.17em;
  padding: 12px 10px;
}

/* A recovery code is thirty-odd characters with dashes in it, so it gets the
   monospace and the tracking but not the poster size. */
.auth-code-long input[type="text"] {
  font-size: 20px;
  letter-spacing: 0.12em;
  text-indent: 0.06em;
}

/* --- the quiet second panel ---------------------------------------------- */

/* What to do when the code will not work, the warning about spending a recovery
   code, where to keep the paper. All of it has to stay on the page and none of
   it should compete with the field. Sunk, smaller, and below. */
.auth-aside {
  padding: 16px 18px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface-sunk);
  font-size: 14px;
}

.auth-aside > h2 {
  margin: 0 0 8px;
  font-size: 14px;
  font-weight: 650;
}

.auth-aside p { margin: 0 0 10px; }
.auth-aside > :last-child { margin-bottom: 0; }

/* Under the card, in the small print position. Kept ranged left rather than
   centred under the centred card, because three lines of centred prose is
   harder to read than it is pretty. */
.auth-note {
  margin: 0;
  color: var(--muted);
  font-size: 13px;
}

/* A banner inside the flow of a card, for the two occasions where the page has
   to say something before the form makes sense: a forced password change, and a
   set of codes that replaces one an operator may have taped inside a cupboard.
   The bar is `currentcolor`, so it takes the tone of whichever message class is
   on the element. */
.auth-banner {
  margin: 16px 0;
  padding: 12px 14px;
  border-left: 3px solid currentcolor;
  border-radius: 4px 10px 10px 4px;
  font-weight: 400;
}

.auth-banner:first-child { margin-top: 0; }
.auth-banner p { margin: 0 0 8px; }
.auth-banner > :last-child { margin-bottom: 0; }

/* The path an operator was heading for, which can be longer than the column.
   Broken anywhere rather than allowed to run off the card, because a truncated
   URL is exactly the part of this panel that has to be readable. */
.auth-banner .mono { overflow-wrap: anywhere; }

/* --- codes shown once ---------------------------------------------------- */

/* The one page in this set where the content is the point. Each code is its own
   chip on the sunk panel, so ten of them are ten things rather than a block of
   text, and each chip selects as a unit on a single click.

   No counter on the chips. A number in a `::before` is copied along with the
   code by some browsers, and pasting "3 K7F2-9QB4" into a password manager is
   the same failure as breaking the clipboard on the code field. */
.auth-codes {
  gap: 10px;
  padding: 14px;
  grid-template-columns: repeat(auto-fit, minmax(186px, 1fr));
  font-size: 17px;
}

.auth-codes li {
  padding: 9px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  text-align: center;
  user-select: all;
}

/* The instruction that matters most on that page, at the size of the thing it
   is warning about. */
.auth-once {
  font-size: 15px;
  font-weight: 400;
}

/* The wait on the lockout page, which is the only fact on the screen anybody can
   act on. Emphasised in place rather than pulled out onto its own line, because
   the sentence around it is what explains that the clock restarts on every
   further attempt. Nothing else on that page is emphasised at all: whoever is
   reading it has just failed to sign in several times and does not need the
   page to agree with them about it. */
.auth-wait {
  font-size: 22px;
  font-weight: 600;
  letter-spacing: 0.03em;
}

/* --- links on these surfaces --------------------------------------------- */

/* `--primary` is 4.1:1 against `--surface-raised` and 4.5:1 against
   `--surface`, which is at or under the floor for a 14px link. `--primary-hover`
   is the same hue lighter and clears it at 6.4:1. The underline stays on, so
   the link is never marked by colour alone. */
.auth a:not(.button),
.auth-account a:not(.button) { color: var(--primary-hover); }

.auth a:not(.button):hover,
.auth-account a:not(.button):hover { color: var(--text); }

/* --- your own account, inside the shell ---------------------------------- */

/* This one is signed in, so it renders beside the sidebar and must not centre
   itself or assume a bare background. It is a column of cards that had no space
   between them at all, since `.card` carries no margin and nothing here was
   setting a gap. */
.auth-account {
  display: grid;
  gap: 16px;
  max-width: 900px;
}

.auth-account .card { padding: 18px; }

.auth-account dl {
  display: grid;
  grid-template-columns: minmax(140px, auto) 1fr;
  gap: 6px 16px;
  margin: 0 0 12px;
}

.auth-account dt {
  color: var(--muted);
  font-weight: 600;
}

.auth-account dd { margin: 0; }

@media (max-width: 640px) {
  .auth-account dl { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .auth-account dd { margin-bottom: 8px; }
}

.auth-account h2 { margin: 0 0 8px; }

.account-subhead {
  margin: 20px 0 6px;
  font-size: 15px;
  font-weight: 650;
}

.account-inline { margin: 0 0 12px; }

.account-confirm { margin-top: 12px; }

/* QR needs a light ground to scan, even on a dark page. The white is the
   scannable surface, not a theme token: a scanner cannot read a dark square. */
.totp-setup {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 16px 20px;
  align-items: start;
  margin: 12px 0;
}

.totp-qr-frame {
  margin: 0;
  padding: 12px;
  background: #fff;
  border-radius: 10px;
  width: fit-content;
}

.totp-qr {
  display: block;
  width: 220px;
  height: 220px;
}

@media (max-width: 640px) {
  .totp-setup { grid-template-columns: minmax(0, 1fr); }
}

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

/* The card arrives with a short rise and the rest of the column fades with it.

   `main` already animates in portal.css, and a translate inside a translate
   reads as two separate slides, so the page-level animation is swapped for a
   plain fade of the same length and the card does the only moving. Transform
   and opacity only, which costs one composited frame and never reflows the form
   somebody is about to type into. */
main:has(> .auth) { animation-name: auth-fade; }

.auth-card { animation: auth-rise var(--dur-enter) var(--ease-out) both; }

@keyframes auth-fade {
  from { opacity: 0; }
}

@keyframes auth-rise {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
}

@keyframes auth-alert-in {
  from {
    opacity: 0;
    transform: translateY(-4px);
  }
}

@media (prefers-reduced-motion: reduce) {
  main:has(> .auth),
  .auth-card,
  .auth-alert { animation: none; }

  .auth-submit:hover,
  .auth-submit:active { transform: none; }
}

/* --- narrow ------------------------------------------------------------- */

/* A phone, during an incident, in a car park. The card keeps its shape and
   loses padding rather than reflowing into something else. */
@media (max-width: 480px) {
  .auth-card { padding: 22px 18px 20px; }
  .auth-card > h1 { font-size: 20px; }
  .auth-aside { padding: 14px 16px; }
  .auth-code input[type="text"] { font-size: 26px; letter-spacing: 0.28em; text-indent: 0.14em; }
  .auth-wait { font-size: 20px; }
  .auth-codes { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
}
