/**
 * The signup corrections, drawn entirely in CSS.
 *
 * Everything visible here is CSS rather than injected elements, because the
 * first version of this feature inserted nodes into React's own tree and the
 * two sides fought until the application never finished loading. CSS is not
 * something React manages, so it cannot be undone and cannot start a loop.
 *
 * Every message below is a `::before` or `::after` on an element the bundle
 * already renders, switched on by a data attribute that JavaScript sets on the
 * form. No element here is ever created.
 */

@property --sd-at {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 0%;
}

/* ── Colours ──────────────────────────────────────────────────────────────
   Named once and answered again for a dark host, so a message never lands as
   dark red on a dark ground. The page is rendered inside somebody else's
   colour scheme, and both have to read. */
.auth-overlay {
  --sd-bad: #c0392b;
  --sd-warn: #a8620a;
  --sd-good: #12805c;
  --sd-note: #4b5a72;
  --sd-track: rgba(15, 30, 60, .12);
}
html[data-theme="dark"] {
  .auth-overlay {
    --sd-bad: #ff9a8f;
    --sd-warn: #f5bf5a;
    --sd-good: #6fdcae;
    --sd-note: #a9b8cf;
    --sd-track: rgba(150, 180, 230, .18);
  }
}

/* "Continue with Google / GitHub" are disabled from JavaScript. The label and
   the explanation are drawn here, on the elements themselves. */
.auth-socials button[disabled] {
  opacity: .55;
  cursor: not-allowed;
  position: relative;
}
.auth-socials button[disabled]::after {
  content: "Soon";
  margin-left: 7px;
  padding: 2px 6px;
  border-radius: 999px;
  background: var(--sd-track);
  font-size: 10px;
  font-weight: 650;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.auth-socials::after {
  content: "Signing in with Google or GitHub is not available yet. Use your email address.";
  display: block;
  grid-column: 1 / -1;
  margin-top: 8px;
  font-size: 12.5px;
  opacity: .68;
  text-align: center;
}

/* ── Field messages ───────────────────────────────────────────────────────
   Only on the signup form: `:has(input[name="confirmPassword"])` is what tells
   it apart from sign-in, which shares the component with fewer fields.

   The room underneath is reserved whether or not anything is showing, so the
   form does not shuffle every time a message appears — a form that jumps while
   being typed into is the reason people lose their place in it.

   Absolutely positioned, because these labels are flex containers: a message in
   normal flow becomes a flex item and sits beside the field instead of under
   it, which is how the terms line first came out wrong. */
.auth-form-wrap form:has(input[name="confirmPassword"]) label:has(input[name="email"]),
.auth-form-wrap form:has(input[name="confirmPassword"]) label:has(input[name="password"]),
.auth-form-wrap form:has(input[name="confirmPassword"]) label:has(input[name="confirmPassword"]) {
  position: relative;
  margin-bottom: 19px;
}

.auth-form-wrap form label:has(input[name="email"])::after,
.auth-form-wrap form label:has(input[name="password"])::after,
.auth-form-wrap form label:has(input[name="confirmPassword"])::after {
  position: absolute;
  top: calc(100% + 5px);
  left: 1px;
  right: 0;
  font-size: 12px;
  font-weight: 500;
  line-height: 1.35;
  letter-spacing: 0;
  /* The label's own text is a heading and is often uppercased or spaced; the
     message is a sentence and must not inherit that. */
  text-transform: none;
}

/* The address. "Not an address yet" while it is half typed would be nagging, so
   JavaScript withholds a verdict until the field has been left or filled. */
form[data-sd-email="bad"] label:has(input[name="email"])::after {
  content: "That is not a complete email address — it needs a name, an @, and a domain.";
  color: var(--sd-bad);
}
/* A likely misspelling of a big provider. Named, not merely flagged, because
   the whole point is that the address looks right until you see the correct one
   beside it. It does not block: the domain is real and does accept mail, so
   nothing further down the line can catch it either. */
form[data-sd-email="typo"] label:has(input[name="email"])::after {
  content: "Did you mean " var(--sd-typo, "another domain") "? Check the spelling — the code goes to this address.";
  color: var(--sd-warn);
}
form[data-sd-email="ok"] label:has(input[name="email"])::after {
  content: "We will send your code here.";
  color: var(--sd-good);
  opacity: .85;
}

/* The password. Below the minimum is a refusal; everything above it is advice. */
form[data-sd-pass="short"] label:has(input[name="password"])::after {
  content: "At least 8 characters. Keep going.";
  color: var(--sd-bad);
}
form[data-sd-pass="obvious"] label:has(input[name="password"])::after {
  content: "That is one of the first passwords anybody guesses. Please choose another.";
  color: var(--sd-bad);
}
form[data-sd-pass="weak"] label:has(input[name="password"])::after {
  content: "Long enough. Mixing in a capital, a number or a symbol would make it much harder to guess.";
  color: var(--sd-warn);
}
form[data-sd-pass="fair"] label:has(input[name="password"])::after {
  content: "Good password.";
  color: var(--sd-good);
  opacity: .85;
}
form[data-sd-pass="strong"] label:has(input[name="password"])::after {
  content: "Strong password.";
  color: var(--sd-good);
  opacity: .85;
}

/* The strength bar, drawn on the label itself: a track the full width, filled
   from the left by a gradient with one hard stop. One element, no nodes. */
form[data-sd-pass]:not([data-sd-pass=""]) label:has(input[name="password"])::before {
  content: "";
  position: absolute;
  top: calc(100% + 25px);
  left: 1px;
  right: 0;
  height: 3px;
  border-radius: 999px;
  background:
    linear-gradient(to right, var(--sd-fill) 0 var(--sd-at), transparent var(--sd-at) 100%),
    var(--sd-track);
  transition: --sd-at .18s ease;
}
form[data-sd-pass="short"] label:has(input[name="password"])::before   { --sd-fill: var(--sd-bad);  --sd-at: 18%; }
form[data-sd-pass="obvious"] label:has(input[name="password"])::before { --sd-fill: var(--sd-bad);  --sd-at: 18%; }
form[data-sd-pass="weak"] label:has(input[name="password"])::before    { --sd-fill: var(--sd-warn); --sd-at: 45%; }
form[data-sd-pass="fair"] label:has(input[name="password"])::before    { --sd-fill: var(--sd-good); --sd-at: 72%; }
form[data-sd-pass="strong"] label:has(input[name="password"])::before  { --sd-fill: var(--sd-good); --sd-at: 100%; }
/* Room for the bar as well as the sentence, on the password field only. */
.auth-form-wrap form:has(input[name="confirmPassword"]) label:has(input[name="password"]) {
  margin-bottom: 26px;
}

/* The match. This is the one the whole request was about: it used to be found
   out about after submitting, from the server, by which time the form had been
   sent and the answer came back as a red banner at the top. */
form[data-sd-match="no"] label:has(input[name="confirmPassword"])::after {
  content: "These two do not match yet.";
  color: var(--sd-bad);
}
form[data-sd-match="yes"] label:has(input[name="confirmPassword"])::after {
  content: "Both passwords match.";
  color: var(--sd-good);
}

/* Caps Lock — the quiet reason two passwords disagree, and the reason a
   password typed correctly today is refused tomorrow. */
form[data-sd-caps="on"] label:has(input[name="password"])::after,
form[data-sd-caps="on"] label:has(input[name="confirmPassword"])::after {
  content: "Caps Lock is on.";
  color: var(--sd-warn);
}

/* ── The line above the button ────────────────────────────────────────────
   A disabled button with no explanation is how this page came to read as
   broken in the first place. It now names the first thing in the way, in the
   order the fields are filled in. */
.auth-form-wrap form[data-sd-block]:not([data-sd-block=""]) .auth-check {
  margin-bottom: 22px;
}
.auth-form-wrap form[data-sd-block]:not([data-sd-block=""]) .auth-check + * {
  position: relative;
}
.auth-form-wrap form[data-sd-block]:not([data-sd-block=""]) .auth-check + *::before {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(100% + 6px);
  font-size: 12.5px;
  font-weight: 500;
  line-height: 1.4;
  /* Stated, never inherited. This note hangs off the submit button, whose text
     is white on a coloured fill — inheriting that painted the sentence white on
     the white page, where it had been sitting unread all along. */
  color: var(--sd-note);
  text-align: left;
  text-transform: none;
  letter-spacing: 0;
}
form[data-sd-block="name"] .auth-check + *::before {
  content: "Enter the name this account should be in.";
}
form[data-sd-block="email"] .auth-check + *::before {
  content: "Enter the email address the account should belong to.";
}
form[data-sd-block="password"] .auth-check + *::before {
  content: "Choose a password of at least 8 characters.";
}
form[data-sd-block="obvious"] .auth-check + *::before {
  content: "Choose a password that is not on every guessing list.";
}
form[data-sd-block="match"] .auth-check + *::before {
  content: "The two passwords need to match.";
}
form[data-sd-block="terms"] .auth-check + *::before {
  content: "Agree to the Terms and Privacy Policy to create your account.";
}

/* A disabled primary button has to look disabled, or the page reads as broken
   a second time — which is the thing this whole file exists to stop. */
.auth-form-wrap button[disabled],
.auth-primary[disabled] {
  opacity: .5;
  cursor: not-allowed;
  filter: grayscale(.35);
}


/* ── What the tick box actually covers ─────────────────────────────────────
   The row reads "I agree to the Terms and Privacy Policy", and the Terms now
   carry a Data Processing Agreement with them — the contract for the people a
   customer will go on to email. Somebody agreeing to a contract about other
   people's personal data should be told that is what they are doing, and not
   have to find it by following a link out of a document they have not read.

   Drawn here rather than added to the row: that row is rendered by React, and
   putting a node inside it is what once left this page stuck on its skeleton.
   Only on the registration form — sign-in and recovery share the component. */
.auth-form-wrap form:has(input[name="confirmPassword"]) .auth-check {
  position: relative;
}
.auth-form-wrap form:has(input[name="confirmPassword"]) .auth-check::after {
  content: "The Terms include our Data Processing Agreement, which covers the people you send to.";
  position: absolute;
  /* Tight under the checkbox. The line telling you why the button is shut is
     positioned against the button below, so the two share this gap — this one
     takes the top of it, that one the bottom. They overlapped at first, and two
     sentences printed over each other say less than either would alone. */
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  font-size: 11.5px;
  line-height: 1.4;
  font-weight: 400;
  color: var(--sd-note);
  text-transform: none;
  letter-spacing: 0;
}
/* Room for both lines, and reserved whether or not the second one is showing,
   so the button does not jump as it appears and goes. */
.auth-form-wrap form:has(input[name="confirmPassword"]) .auth-check,
.auth-form-wrap form[data-sd-block]:not([data-sd-block=""]):has(input[name="confirmPassword"]) .auth-check {
  margin-bottom: 52px;
}
