/* ==========================================================================
   Sign in and create account - the pieces the workspace sheet does not carry

   Loaded AFTER /css/staff-dashboard-v2.css, which is the design system for
   these two pages exactly as it is for the staff and admin workspaces: the
   tokens, the shell, the card, the fields, the buttons, the checkbox and the
   validation treatment all come from there and are deliberately not repeated.

   The workspace sheet already describes a sign-in page - .shell--auth,
   .auth__card, .oauth-button, .auth__divider, .auth__row, .auth__alt and
   .footer-note--auth were all authored with it in mind but never had a page
   to sit on. This file is what the two real pages need on top of that: the
   lockout banner the login route can raise, the six-box code entry the OTP
   step uses, the strength meter on the password field, and the two-up name
   row. Nothing here introduces a colour, a radius or a shadow the system did
   not already have.
   ========================================================================== */

/* --- The card -------------------------------------------------------------

   Register asks for four fields where login asks for two, and an OTP step
   sits behind it. Register takes the wider card so the two-up name row is not
   a pair of stubs; login keeps the 396px the system sets, which is the width
   of the single decision it holds. */
.auth__card--wide {
  max-width: 428px;
}

/* --- Name row -------------------------------------------------------------
   First and last name on one line. They are one piece of information asked in
   two boxes, so they sit together rather than stacking into two separate rows
   the reader works through as two separate questions. */
.field-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
}

/* The pair is one field as far as the form rhythm is concerned, so the margin
   lives on the row and the two fields inside it drop their own. */
.field-pair .field {
  margin-bottom: 0;
}

.field-pair {
  margin-bottom: 16px;
}

/* Under about 400px the two boxes stop being usable side by side - the labels
   wrap and neither input is wide enough to show a typed name - so they stack
   and take their own margins back. */
@media (max-width: 400px) {
  .field-pair {
    grid-template-columns: 1fr;
    gap: 0;
    margin-bottom: 0;
  }

  .field-pair .field {
    margin-bottom: 16px;
  }
}

/* --- Password strength ----------------------------------------------------

   Four criteria, four segments. A single continuous bar that fills as the
   password improves says "keep going" without saying what is missing; four
   discrete segments plus the line beneath name the remaining work, which is
   the only thing the reader can act on.

   With no hue in the system, strength is carried by how much of the track is
   inked rather than by red-amber-green. A met criterion is near-black, an
   unmet one is the grey a field border takes - so the meter reads at a glance
   as "three of four" rather than as a verdict. */
.strength {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 2px;
}

.strength__track {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 4px;
}

.strength__seg {
  height: 3px;
  border-radius: 999px;
  background: var(--ink-150);
  transition: background 0.16s linear;
}

.strength__seg.is-on {
  background: var(--ink-900);
}

/* Empty until the field has something in it, and out of flow when empty so it
   never reserves a blank line under the input. */
.strength__note:empty {
  display: none;
}

.strength__note {
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--ink-500);
}

/* When every criterion is met the note stops being a list of outstanding work
   and becomes a confirmation, so it takes the ink the form text carries. */
.strength__note--met {
  color: var(--ink-700);
  font-weight: 500;
}

/* --- Lockout and unlock ---------------------------------------------------

   The login route can answer with ACCOUNT_LOCKED, which is not a field error -
   it is a state the whole form is in - so it is announced above the fields
   rather than under one of them. It reuses .settings-status, the block-level
   message the system already has, and adds only the action row that a
   self-service unlock needs. */
.auth__notice:empty {
  display: none;
}

/* A message the reader has to act on, rather than one that merely reports.
   The left rule is the device the audit log uses for a severity gutter: it
   marks the block as different in kind without introducing a colour. */
.auth__notice--strong {
  border-left: 3px solid var(--ink-900);
}

.auth__unlock {
  margin-bottom: 16px;
}

/* Ghost weight, deliberately. This is an action of last resort on a page
   whose primary commit is Sign in, and two filled buttons on one card leave
   the reader deciding which one is the way forward. */
.auth__unlock .ghost-button {
  width: 100%;
  justify-content: center;
}

.auth__unlock-msg:empty {
  display: none;
}

.auth__unlock-msg {
  margin-top: 8px;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--ink-500);
}

/* --- OTP step -------------------------------------------------------------

   The code step replaces the form inside the same card rather than opening a
   new page. The reader has just been sent somewhere else - their inbox - and
   coming back to a card that has visibly moved or resized would make them
   stop to check they are still in the right place. */

.otp__intro {
  margin-bottom: 18px;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--ink-500);
}

/* The address the code went to is the one thing on this screen the reader
   verifies, so it is lifted out of the sentence onto its own line at full
   ink - a mistyped address is the most common reason a code never lands. */
.otp__mail {
  display: block;
  margin-top: 4px;
  font-weight: 600;
  color: var(--ink-900);
  /* An address is one long token; letting it break anywhere is what makes a
     mistyped one possible to spot at this width. */
  overflow-wrap: anywhere;
}

/* Six boxes rather than one input. A code arrives as six separate characters
   and is checked back one character at a time, so the control is shaped like
   the thing being entered. */
.otp__boxes {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 8px;
}

.otp__box {
  width: 100%;
  padding: 11px 0;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 4.5px;
  font: inherit;
  font-size: 18px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-align: center;
  color: var(--ink-900);
  transition: border-color 0.12s linear, box-shadow 0.12s linear;
  /* A number spinner would put a pair of arrows inside a box 40px wide. */
  -moz-appearance: textfield;
  appearance: textfield;
}

.otp__box::-webkit-outer-spin-button,
.otp__box::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.otp__box:hover {
  border-color: var(--ink-400);
}

/* The same ring .field__input takes, for the same reason: an offset outline
   on a bordered box reads as a second box drawn around it. */
.otp__box:focus,
.otp__box:focus-visible {
  outline: none;
  border-color: var(--ink-900);
  box-shadow: 0 0 0 3px rgba(23, 21, 18, 0.09);
}

/* A box with a digit in it takes a stronger border, so the reader can see how
   far along the code is without reading the digits back. */
.otp__box.is-filled {
  border-color: var(--ink-600);
}

.otp__boxes.is-invalid .otp__box {
  border-color: var(--ink-900);
  box-shadow: 0 0 0 3px rgba(23, 21, 18, 0.07);
}

/* Under 400px six boxes plus their gaps no longer fit. The gap goes first,
   because it is the part that is not the control. */
@media (max-width: 400px) {
  .otp__boxes {
    gap: 5px;
  }

  .otp__box {
    padding: 10px 0;
    font-size: 16px;
  }
}

/* --- OTP actions ----------------------------------------------------------
   Resend and Back are both ways out of the step, so they share the row under
   the commit. Neither is a button in the system sense - they are text
   affordances, which is what .auth__link already is. */
.otp__actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-top: 16px;
}

/* A cooldown is not a failure, so a disabled link goes quiet rather than
   looking broken - and stops taking the pointer so it cannot be clicked at. */
.auth__link:disabled,
.auth__link:disabled:hover {
  color: var(--ink-300);
  text-decoration-color: transparent;
  cursor: default;
}

/* --- The sign-in button while the request is in flight --------------------

   `Auth.login()` in /js/auth.js owns this button during the request: it
   replaces the label with its own spinner markup and adds `.loading`. That
   function is shared by every page in the app, so it is left alone and the
   class it sets is given the treatment `.solid-button.is-busy` already
   carries - the label it swapped in goes transparent and the button draws the
   system spinner instead.

   The label is hidden rather than left to read "Logging in...", because the
   replacement string is wider than "Sign in" and the button would visibly
   grow under the pointer at the moment it was clicked. */
.solid-button.loading {
  color: transparent;
  position: relative;
  pointer-events: none;
}

/* The Font Awesome spinner in the markup it swaps in. These pages do not load
   that font, so the glyph is an empty box; the drawn spinner below replaces
   it. */
.solid-button.loading > i {
  display: none;
}

.solid-button.loading::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 15px;
  height: 15px;
  margin: -7.5px 0 0 -7.5px;
  border: 2px solid rgba(253, 252, 250, 0.35);
  border-top-color: var(--surface);
  border-radius: 999px;
  animation: button-spin 0.62s linear infinite;
}

/* --- Step swap ------------------------------------------------------------
   The form and the code step occupy the same slot. `hidden` is what the
   controller toggles, and the workspace sheet [hidden] rule already carries
   the !important that beats the display these two would otherwise take. */
.auth__step[hidden] {
  display: none !important;
}

/* --- Alerts ---------------------------------------------------------------

   `Auth.showAlert()` in /js/auth.js is shared by every page in the app and
   builds its own markup into #global-alert-container. Rewriting it would
   change behaviour on pages this redesign is not touching, so it is restyled
   here instead: the same DOM, given the workspace toast treatment, so a
   message means the same thing and looks the same as one raised anywhere else
   in the product.

   The <i> it injects is a Font Awesome glyph. These two pages no longer load
   Font Awesome - 100KB of icon font for one mark - so it is hidden and the
   mark is drawn from the class the alert already carries. */
#global-alert-container {
  position: fixed;
  top: 18px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 90;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: max-content;
  max-width: min(420px, calc(100vw - 32px));
  pointer-events: none;
}

.global-alert {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 11px 13px;
  background: var(--ink-900);
  color: var(--surface);
  border: 0;
  border-radius: 8px;
  box-shadow: 0 4px 10px rgba(23, 21, 18, 0.14), 0 14px 32px -12px rgba(23, 21, 18, 0.32);
  font-size: 13px;
  font-weight: 500;
  line-height: 1.4;
  pointer-events: auto;
  animation: toast-in 0.32s cubic-bezier(0.22, 0.61, 0.36, 1);
  transition: opacity 0.18s linear, transform 0.22s cubic-bezier(0.4, 0, 1, 1);
}

/* The injected Font Awesome glyph, replaced by the drawn mark below. */
.global-alert > i {
  display: none;
}

/* A ring for a report, a filled disc for a confirmation. Two states is all
   this surface needs: something happened, or something went wrong - and the
   copy already says which. */
.global-alert::before {
  content: '';
  flex-shrink: 0;
  width: 15px;
  height: 15px;
  border: 2px solid var(--surface);
  border-radius: 999px;
  opacity: 0.85;
}

.global-alert.success::before {
  background: var(--surface);
  opacity: 0.9;
}

.global-alert .alert-message {
  min-width: 0;
}

/* Present for the keyboard and for a message that outstays its welcome, but
   never the thing the eye lands on - the toast clears itself. */
.global-alert .alert-close {
  flex-shrink: 0;
  padding: 0 0 0 4px;
  background: none;
  border: 0;
  font-size: 16px;
  line-height: 1;
  color: var(--surface);
  opacity: 0.55;
  cursor: pointer;
  transition: opacity 0.12s linear;
}

.global-alert .alert-close:hover {
  opacity: 1;
}

@media (max-width: 560px) {
  #global-alert-container {
    left: 16px;
    right: 16px;
    transform: none;
    width: auto;
    max-width: none;
  }
}

/* --- Reset link sent ------------------------------------------------------

   The confirmation on the forgot-password card. It replaces the form rather
   than sitting above it: the form has nothing left to do, and leaving it on
   screen invites a second submit the rate limiter would only refuse.

   The mark is line art at ink weight rather than a green tick. Nothing has
   succeeded in a way the reader can verify - the wording is deliberately "if
   that address has an account" - so a celebratory mark would be claiming more
   than the page actually knows. */
.sent {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 14px 15px;
  margin-bottom: 14px;
  background: var(--page);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.sent__mark {
  flex: none;
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: 999px;
  background: var(--ink-900);
  color: var(--surface);
}

.sent__text {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--ink-700);
}

/* The address on its own line at full ink: it is the one thing on this screen
   the reader checks, and a typo in it is the reason the mail never lands. */
.sent__mail {
  display: block;
  margin-top: 3px;
  font-weight: 600;
  color: var(--ink-900);
  overflow-wrap: anywhere;
}

.sent__hint {
  margin-bottom: 16px;
}

/* --- Arrival --------------------------------------------------------------

   The account, drawn the way the workspace header draws it. This is the first
   sight the reader gets of who they are signed in as, and it uses the same
   disc, the same initials and the same name-over-role stack that will sit in
   the top right of every page after this one - so the control they are about
   to go looking for is already familiar by the time they get there.

   It is also what makes this page worth its four seconds. Saying "signed in"
   over a generic card is a loading screen; showing the identity that was just
   established is the sign-in completing. */

.arrive {
  display: flex;
  align-items: center;
  gap: 14px;
  padding-bottom: 18px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--border);
}

.arrive__id {
  min-width: 0;
}

/* The name is the <h1>. It is the largest thing on the card because it is the
   answer to the only question this page exists to settle - who am I signed in
   as - and it is set at the card title's weight rather than a display size,
   because the page is a step in a flow and not a landing. */
.arrive__name {
  font-size: 17px;
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1.25;
  /* A long name wraps rather than pushing the disc off the card. */
  overflow-wrap: anywhere;
}

.arrive__role {
  margin-top: 1px;
  font-size: 12.5px;
  color: var(--ink-400);
}

.arrive__line {
  margin-bottom: 18px;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--ink-500);
}

/* The card head is gone on this page - the identity block is the heading - so
   the body carries the padding the head would have contributed. */
.auth__body--arrive {
  padding-top: 22px;
}

/* --- Signed in ------------------------------------------------------------

   The page between signing in and the workspace. It is on screen for four
   seconds and the reader is not deciding anything, so it says what happened,
   shows how long the wait is, and offers the way out of waiting.

   Deliberately the same card as the sign-in page it follows. The old version
   was a separate design - green seal, pulsing rings, a serif italic name, a
   22px radius, a gradient wash - which made signing in feel like it handed you
   to a different product for four seconds and then to a third. Same card, same
   ink, same radius: the sequence now reads as one workspace opening. */

.signed {
  margin: 0 0 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--page);
}

/* A label-and-value pair per line rather than the three-column strip the old
   page used. The strip could not survive a phone - it was simply hidden below
   600px, which took the account and the method with it. Rows fit any width. */
.signed__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  padding: 9px 13px;
  border-bottom: 1px solid var(--border);
}

.signed__row:last-child {
  border-bottom: 0;
}

.signed__key {
  flex: none;
  font-size: 12px;
  color: var(--ink-500);
}

.signed__val {
  min-width: 0;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-900);
  text-align: right;
  /* A timestamp changing width mid-render would shift the row. */
  font-variant-numeric: tabular-nums;
}

/* The method row carries a mark before its label. */
.signed__val--mark {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 6px;
}

.signed__val--mark svg {
  flex: none;
}

/* The workspace sheet resets `a.ghost-button` but has no `a.solid-button`,
   because until this page no view used a filled button as a link - every
   primary commit in the workspace is a real <button>. Here the primary action
   is a navigation, so it is an anchor, and without this it renders with the
   underline and link colour the anchor carries by default. */
a.solid-button {
  text-decoration: none;
  color: var(--surface);
}

/* --- The wait -------------------------------------------------------------

   One bar, filling once, over a known duration. The fill is driven by a CSS
   transition to its final width - set once, in a single write - rather than by
   a per-frame `style.width` assignment. The old page did the latter *and* put
   an 80ms transition on the same property, so every frame started a new 80ms
   animation toward a target the next frame moved again: the bar visibly
   stuttered and lagged the countdown it was meant to match. Handing the whole
   travel to the compositor in one declaration is both smoother and cheaper. */

.signed__wait {
  margin-bottom: 18px;
}

.signed__track {
  height: 3px;
  border-radius: 999px;
  background: var(--ink-150);
  overflow: hidden;
}

.signed__fill {
  width: 0;
  height: 100%;
  border-radius: 999px;
  background: var(--ink-900);
}

/* Applied on the frame after paint, so the transition has a 0% start to run
   from. The duration is set from the controller to match the countdown. */
.signed__fill.is-running {
  width: 100%;
}

.signed__count {
  margin-top: 9px;
  font-size: 11.5px;
  color: var(--ink-400);
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* The bar is a decorative restatement of the countdown text beside it, so it
   is the part that goes when motion is not wanted. The text still counts
   down, which is the information. */
@media (prefers-reduced-motion: reduce) {
  .signed__fill {
    transition: none !important;
  }
}

/* --- A document page ------------------------------------------------------

   The privacy policy, and anything else that is read rather than acted on.
   The sign-in pages centre a card on the short axis because they hold one
   decision; a policy is a column of prose and wants the opposite - it starts
   at the top and is measured for reading, not for deciding.

   Same shell, same header, same card, same ink. It shares the header's
   view-transition name with the auth pages, so following the link under the
   sign-in card holds the brand block still on the way here. */

.doc {
  flex: 1;
  width: 100%;
  /* 66ch at this size is a comfortable measure; the card's own padding brings
     the text itself to roughly 62. Wider and the eye loses the line return. */
  max-width: 660px;
  margin: 0 auto;
  padding: 30px 0 48px;
}

.doc__head {
  margin-bottom: 20px;
}

.doc__title {
  font-size: 23px;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.doc__meta {
  margin-top: 5px;
  font-size: 12.5px;
  color: var(--ink-400);
}

.doc__card {
  box-shadow: var(--shadow);
}

.doc__body {
  padding: 26px 28px 28px;
}

/* Sections are separated by space rather than by rules. A rule under every
   heading in a six-part document turns the page into a stack of boxes and
   makes the whole thing read as heavier than it is. */
.doc__section + .doc__section {
  margin-top: 26px;
}

.doc__heading {
  margin-bottom: 9px;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--ink-900);
}

.doc__text {
  font-size: 13.5px;
  line-height: 1.68;
  color: var(--ink-700);
}

.doc__text + .doc__text {
  margin-top: 10px;
}

.doc__list {
  margin: 9px 0 0;
  padding-left: 18px;
  font-size: 13.5px;
  line-height: 1.68;
  color: var(--ink-700);
}

.doc__list li + li {
  margin-top: 4px;
}

/* The marker is quieter than the text it introduces - at full ink a column of
   bullets reads as a second, competing column down the left edge. */
.doc__list li::marker {
  color: var(--ink-300);
}

.doc__link {
  color: var(--ink-900);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: var(--ink-200);
  transition: text-decoration-color 0.12s linear;
}

.doc__link:hover {
  text-decoration-color: var(--ink-600);
}

.doc__foot {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 20px;
}

/* The workspace sheet caps this at the 396px of a sign-in card. Under a
   660px document that leaves the copyright line wrapping into three
   centred fragments, so here it takes the document's own measure. */
.doc .footer-note--auth {
  max-width: none;
  margin-top: 28px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
}

@media (max-width: 560px) {
  .doc {
    padding: 22px 0 36px;
  }

  .doc__title {
    font-size: 20px;
  }

  .doc__body {
    padding: 20px 18px 22px;
  }

  /* Two buttons side by side at this width leave neither with a readable
     label, so they stack and take the full measure. */
  .doc__foot .ghost-button {
    flex: 1 1 100%;
    justify-content: center;
  }
}

/* --- Footer --------------------------------------------------------------
   The sign-off under the card, matching .footer-note--auth in the workspace
   sheet. Only the link inside it is new, because no workspace footer has
   one. */
.footer-note--auth a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: var(--ink-200);
  transition: color 0.12s linear, text-decoration-color 0.12s linear;
}

.footer-note--auth a:hover {
  color: var(--ink-700);
  text-decoration-color: var(--ink-400);
}
