:root {
  --panel-w: 420px;
  --panel-radius: 28px;

  --panel-border: rgba(255, 255, 255, 0.50);
  --panel-inner-border: rgba(255, 255, 255, 0.18);
  --panel-bg: rgba(255, 255, 255, 0.16);
  --panel-bg-2: rgba(255, 255, 255, 0.10);
  --panel-shadow: 0 18px 42px rgba(58, 85, 132, 0.14);

  --text-main: #29426c;
  --text-soft: #61789d;
  --field-text: #4c648b;
  --field-placeholder: #7a8fb1;

  --field-border: rgba(187, 203, 230, 0.48);
  --field-bg-1: rgba(255, 255, 255, 0.24);
  --field-bg-2: rgba(255, 255, 255, 0.16);
  --field-focus: rgba(50, 116, 255, 0.22);

  --button-from: #3a86ff;
  --button-to: #1f63ff;
  --button-shadow: 0 8px 18px rgba(40, 105, 255, 0.20);

  --link: #2e69da;
}

  * {
    box-sizing: border-box;
  }

  html,
  body {
    min-height: 100%;
  }

body {
  margin: 0;
  min-height: 100vh;
  font-family: "Noto Sans", "Roboto", "Geist", sans-serif;
  color: var(--text-main);
  background:
    radial-gradient(circle at 50% 15%, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.06) 22%, rgba(255, 255, 255, 0) 42%),
    radial-gradient(circle at 18% 52%, rgba(184, 211, 255, 0.18), rgba(184, 211, 255, 0) 34%),
    radial-gradient(circle at 84% 64%, rgba(204, 223, 255, 0.14), rgba(204, 223, 255, 0) 30%),
    url("/static/assets/covers/wallpaper.png") center / cover no-repeat fixed;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px 12px;
  overflow: hidden;
  position: relative;
  isolation: isolate;
}

/* L11 (CRITICAL, /login only): the shared body{overflow:hidden} above traps
   content on short viewports - the sign-in button, remember-me and the foot
   strip become unreachable by wheel or touch (live-confirmed defect F1).
   Scoped to body.login-page so /forgot-password, /reset-password, /register
   and the generic auth-message screen keep their shipped behaviour exactly
   as before; only /login (templates/index.html, body class added by this
   change) becomes a normal scrollable column. */
body.login-page {
  min-height: 100dvh;
  overflow-x: hidden;
  overflow-y: auto;
}

body::before,
body::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: -1;
}

body::before {
  background:
    radial-gradient(circle at 50% 18%, rgba(255, 255, 255, 0.10), rgba(255, 255, 255, 0.03) 24%, rgba(255, 255, 255, 0) 52%);
  backdrop-filter: blur(1px);
  -webkit-backdrop-filter: blur(1px);
}

body::after {
  background:
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.04), rgba(255, 255, 255, 0) 42%),
    radial-gradient(circle at 10% 82%, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0) 22%),
    radial-gradient(circle at 88% 84%, rgba(255, 255, 255, 0.12), rgba(255, 255, 255, 0) 22%);
  opacity: 0.70;
  mix-blend-mode: screen;
}

  .bg-fog,
  .bg-shimmer {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: -1;
  }

.bg-fog {
  background:
    radial-gradient(circle at 50% 18%, rgba(255, 255, 255, 0.14), rgba(255, 255, 255, 0) 24%),
    radial-gradient(circle at 30% 34%, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0) 14%),
    radial-gradient(circle at 72% 36%, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0) 14%);
  opacity: 0.36;
  filter: blur(8px);
  animation: fogShift 12s ease-in-out infinite alternate;
}

  .bg-shimmer {
    background:
      linear-gradient(110deg, rgba(255, 255, 255, 0) 18%, rgba(255, 255, 255, 0.08) 50%, rgba(255, 255, 255, 0) 82%);
    transform: translateX(-28%);
    opacity: 0.38;
    animation: shimmerSweep 16s linear infinite;
  }

.login-main {
  width: 100%;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 36px;
  position: relative;
}

  .logo-wrap {
    width: min(100%, 760px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding-inline: 12px;
    pointer-events: none;
  }

  .logo {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    opacity: 0;
    transform: translateY(-18px) scale(0.985);
    filter: blur(14px);
    animation: logoReveal 1.1s cubic-bezier(.22,.7,.2,1) forwards;
    will-change: transform, opacity, filter;
  }

.logo img {
  display: block;
  max-height: 120px;
  max-width: min(100%, 320px);
  object-fit: contain;
  filter: drop-shadow(0 4px 6px rgba(88, 88, 88, 1.2));  
}
.logo .brand-wordmark {
  display: block;
  color: var(--text-primary, #162033);
  font-family: var(--app-theme-font-family, inherit);
  font-size: var(--app-theme-font-size-lg, 22px);
  font-weight: 700;
  letter-spacing: 0;
}

.login-shell {
  position: relative;
  width: min(100%, var(--panel-w));
  animation: shellFloat 8s ease-in-out 1.25s infinite;
  will-change: transform;
}

  .login-shell::before {
    content: "";
    position: absolute;
    inset: auto 11% -20px;
    height: 42px;
    border-radius: 50%;
    background: radial-gradient(circle, rgba(66, 97, 153, 0.22), rgba(66, 97, 153, 0) 72%);
    filter: blur(18px);
    opacity: 0.58;
    z-index: 0;
    animation: shadowPulse 8s ease-in-out 1.25s infinite;
  }

/* L12 (/login only): the infinite vertical bob and its shadow pulse are
   removed - the panel currently drifts +-7px forever, including while the
   user is aiming at a field. Scoped so /register, /forgot-password,
   /reset-password and /auth-message (which also render .login-shell) keep
   their shipped motion unchanged. */
body.login-page .login-shell,
body.login-page .login-shell::before {
  animation: none;
}

.login-box {
  position: relative;
  z-index: 1;
  width: 100%;
  padding: 22px 24px 0;
  border-radius: var(--panel-radius);
  border: 2px solid var(--panel-border);
  background:
    linear-gradient(
      180deg,
      rgba(255, 255, 255, 0.16) 0%,
      rgba(255, 255, 255, 0.12) 52%,
      rgba(255, 255, 255, 0.09) 100%
    );
  box-shadow:
    var(--panel-shadow),
    inset 0 1px 0 rgba(255, 255, 255, 0.30);
  backdrop-filter: blur(14px) saturate(112%);
  -webkit-backdrop-filter: blur(14px) saturate(112%);
  opacity: 0;
  transform: translateY(18px) scale(0.985);
  filter: blur(14px);
  animation: panelReveal 0.95s cubic-bezier(.2,.76,.18,1) 0.18s forwards;
  transition: box-shadow 0.28s ease, filter 0.35s ease, opacity 0.35s ease;
  overflow: hidden;
  isolation: isolate;
}

.login-box--auth-page {
  padding-bottom: 26px;
}

  .login-box::before,
  .login-box::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    border-radius: inherit;
  }

  .login-box::before {
    background:
      radial-gradient(circle at 50% 0%, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0.06) 32%, rgba(255, 255, 255, 0) 58%);
    z-index: -2;
  }

  .login-box::after {
    inset: 1px;
    border-radius: calc(var(--panel-radius) - 1px);
    box-shadow:
      inset 0 0 0 1px var(--panel-inner-border),
      inset 0 1px 0 rgba(255, 255, 255, 0.22);
    z-index: -1;
  }

  .login-box:hover {
    box-shadow:
      0 28px 70px rgba(58, 85, 132, 0.18),
      inset 0 1px 0 rgba(255, 255, 255, 0.38);
  }

.login-title {
  margin: 0 0 18px;
  text-align: center;
  font-size: clamp(42px, 4.2vw, 58px);
  line-height: 1;
  font-weight: 800;
  letter-spacing: -0.04em;
  color: #485569;
  text-shadow: 0 4px 8px rgba(55, 78, 126, 0.16);
}

.login-title--compact {
  font-size: clamp(34px, 3.4vw, 44px);
  line-height: 1.12;
  letter-spacing: 0;
  /* /register, /forgot-password, /reset-password and /auth-message pair this
     modifier with .login-title and must keep the shipped colour/shadow above
     - restated here so the :not() rule below (login only) can safely change
     colour/shadow without affecting them. */
  color: #485569;
  text-shadow: 0 4px 8px rgba(55, 78, 126, 0.16);
}

/* L1 (/login only): adopts the approved register auth-identity title values.
   .login-title--compact is used by every other auth screen, so excluding it
   guarantees this only ever matches /login's bare .login-title h1. */
.login-title:not(.login-title--compact) {
  margin: 0;
  font-size: 34px;
  letter-spacing: -0.02em;
  color: #37507a;
  text-shadow: 0 3px 8px rgba(55, 78, 126, 0.14);
}

.login-form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.login-form[hidden] {
  display: none !important;
}

  .field {
    position: relative;
  }

.field input[type="text"],
.field input[type="password"],
.field input[type="email"] {
  width: 100%;
  height: 58px;
  padding: 0 18px 0 56px;
  border-radius: 18px;
  border: 1.5px solid var(--field-border);
  background: linear-gradient(180deg, var(--field-bg-1), var(--field-bg-2));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.38),
    0 3px 12px rgba(70, 96, 148, 0.04);
  font-size: 18px;
  color: var(--field-text);
  outline: none;
  transition: border-color 0.22s ease, box-shadow 0.22s ease, background-color 0.22s ease, transform 0.22s ease;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  position: relative;
  z-index: 1;
}

  .field input::placeholder {
    color: var(--field-placeholder);
  }

.field--password input[type="password"],
.field--password input[type="text"] {
  padding-right: 58px;
}

.field input:-webkit-autofill,
.field input:-webkit-autofill:hover,
.field input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--field-text);
  caret-color: var(--field-text);
  transition: background-color 9999s ease-out;
  -webkit-box-shadow:
    0 0 0 1000px rgba(255, 255, 255, 0.22) inset,
    inset 0 1px 0 rgba(255, 255, 255, 0.38),
    0 3px 12px rgba(70, 96, 148, 0.04);
}

.field--password input::-ms-reveal,
.field--password input::-ms-clear {
  display: none;
  width: 0;
  height: 0;
}

.field--password input::-webkit-credentials-auto-fill-button,
.field--password input::-webkit-strong-password-auto-fill-button,
.field--password input::-webkit-textfield-decoration-container {
  display: none !important;
  visibility: hidden;
  pointer-events: none;
}

.field::before {
  content: "";
  position: absolute;
  left: 18px;
  top: 50%;
  width: 22px;
  height: 22px;
  transform: translateY(-50%);
  opacity: 0.76;
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  pointer-events: none;
  z-index: 2;
}

  .field--login::before {
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="none" stroke="%23687ea2" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M20 21a8 8 0 0 0-16 0"/><circle cx="12" cy="7" r="4.2"/></svg>');
  }

  .field--password::before {
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="none" stroke="%23687ea2" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><rect x="5" y="11" width="14" height="9" rx="2.5"/><path d="M8 11V8.6A4.1 4.1 0 0 1 12 4.5 4.1 4.1 0 0 1 16 8.6V11"/></svg>');
  }

  .field--email::before {
    background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="none" stroke="%23687ea2" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><rect x="3.8" y="5.5" width="16.4" height="13" rx="2.5"/><path d="m5.2 7.3 6.8 5.4 6.8-5.4"/></svg>');
  }

  .field input:focus {
    border-color: rgba(72, 132, 255, 0.34);
    box-shadow:
      0 0 0 4px rgba(72, 132, 255, 0.10),
      inset 0 1px 0 rgba(255, 255, 255, 0.48),
      0 8px 18px rgba(72, 132, 255, 0.06);
    background: linear-gradient(180deg, rgba(255, 255, 255, 0.34), rgba(255, 255, 255, 0.24));
    transform: translateY(-1px);
  }

.field-password-toggle {
  position: absolute;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.34);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.36);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  z-index: 3;
  transition: background-color 0.2s ease, box-shadow 0.2s ease;
}

.field-password-toggle img {
  width: 20px;
  height: 20px;
  display: block;
  opacity: 0.82;
  transition: opacity 0.2s ease;
}

.field-password-toggle:hover {
  background: rgba(255, 255, 255, 0.50);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.44),
    0 2px 8px rgba(70, 96, 148, 0.08);
}

.field-password-toggle:hover img {
  opacity: 1;
}

.field-password-toggle:focus,
.field-password-toggle:focus-visible {
  outline: none;
  box-shadow:
    0 0 0 3px rgba(72, 132, 255, 0.12),
    inset 0 1px 0 rgba(255, 255, 255, 0.44);
}

/* L10 (/login only): the 12%-alpha box-shadow above is effectively invisible
   on the glass panel (A3). .field-password-toggle is shared with
   /reset-password, so the fix is scoped to body.login-page instead of
   changing the shared rule above - mirrors the pattern register.css already
   uses for its own instance of this same control. */
body.login-page .field-password-toggle:focus,
body.login-page .field-password-toggle:focus-visible {
  outline: 2px solid var(--info, #2563eb);
  outline-offset: 2px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.44);
}

.remember {
  display: flex;
  margin-left: 20px;
  align-items: center;
  gap: 10px;
  margin-top: 2px;
  color: #5d7398;
  font-size: 15px;
  user-select: none;
}

.remember input[type="checkbox"] {
  width: 18px;
  height: 18px;
  margin: 0;
  appearance: none;
  -webkit-appearance: none;
  border: 1.5px solid rgba(146, 171, 212, 0.9);
  border-radius: 6px;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.34), rgba(255, 255, 255, 0.18));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.42),
    0 1px 3px rgba(58, 85, 132, 0.08);
  cursor: pointer;
  flex: 0 0 auto;
  transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
}

.remember input[type="checkbox"]:hover {
  border-color: rgba(96, 136, 206, 0.92);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.44), rgba(255, 255, 255, 0.24));
}

.remember input[type="checkbox"]:focus,
.remember input[type="checkbox"]:focus-visible {
  /* L10: .remember is exclusive to /login (no other auth screen renders a
     "remember me" control), so the fix replaces the rule directly instead
     of adding a scoped override - the 12%-alpha box-shadow was effectively
     invisible on the glass panel (A3). */
  outline: 2px solid var(--info, #2563eb);
  outline-offset: 2px;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.42);
}

.remember input[type="checkbox"]:checked {
  border-color: rgba(47, 124, 255, 0.96);
  background:
    url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18"><path d="M4.3 9.2l2.9 2.9 6-6.4" fill="none" stroke="%23ffffff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>') center / 12px 12px no-repeat,
    linear-gradient(180deg, #4a8fff, #2f7cff);
  box-shadow:
    0 6px 12px rgba(47, 124, 255, 0.18),
    inset 0 1px 0 rgba(255, 255, 255, 0.24);
}

  .remember label {
    cursor: pointer;
  }

.login-submit {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 82%;
  height: 52px;
  border: 0;
  margin: 8px auto 0;
  align-self: center;
  border-radius: 18px;
  background: linear-gradient(90deg, var(--button-from), var(--button-to));
  box-shadow:
    var(--button-shadow),
    inset 0 1px 0 rgba(255, 255, 255, 0.34);
  color: #ffffff;
  text-align: center;
  text-decoration: none;
  font-size: 18px;
  font-weight: 800;
  letter-spacing: 0.01em;
  cursor: pointer;
  transition: transform 0.22s ease, box-shadow 0.22s ease, filter 0.22s ease;
}

  .login-submit:hover {
    transform: translateY(-1px);
    box-shadow:
      0 14px 30px rgba(40, 105, 255, 0.26),
      inset 0 1px 0 rgba(255, 255, 255, 0.40);
    filter: saturate(1.03);
  }

  .login-submit:active {
    transform: translateY(0);
    box-shadow:
      0 8px 18px rgba(40, 105, 255, 0.20),
      inset 0 1px 0 rgba(255, 255, 255, 0.28);
  }

.login-help {
  margin-top: 18px;
  padding-top: 18px;
  border-top: 1px solid rgba(189, 204, 229, 0.24);
  color: var(--text-soft);
  font-size: 15px;
  line-height: 1.45;
  text-align: center;
  text-wrap: balance;
}

  .login-help a {
    color: var(--link);
    text-decoration: none;
    font-weight: 800;
  }

  .login-help a:hover {
    text-decoration: underline;
  }

  .login-help a.login-submit {
    color: #ffffff;
    text-decoration: none;
  }

  .login-help a.login-submit:hover {
    text-decoration: none;
  }

.login-forgot-link {
  margin: 16px 0 0;
  text-align: center;
  font-size: 15px;
  line-height: 1.45;
  text-wrap: balance;
}

  .login-forgot-link a {
    color: var(--link);
    text-decoration: none;
    font-weight: 800;
  }

  .login-forgot-link a:hover {
    text-decoration: underline;
  }

.login-bottom-links {
  margin: 16px -24px 0;
  padding: 16px 18px 18px;
  border-top: 1px solid rgba(189, 204, 229, 0.24);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0.09));
  display: flex;
  justify-content: center;
  gap: 6px;
  flex-wrap: wrap;
  font-size: 14px;
  color: #61789d;
}

  .login-bottom-links a {
    color: var(--link);
    text-decoration: none;
    font-weight: 700;
  }

  .login-bottom-links a:hover {
    text-decoration: underline;
  }

  .login-bottom-links span {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    white-space: nowrap;
  }

.login-bottom-links span::before {
  content: "";
  width: 14px;
  height: 14px;
  border-radius: 4px;
  background: linear-gradient(180deg, rgba(94, 124, 181, 0.14), rgba(94, 124, 181, 0.05));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.46),
    0 1px 2px rgba(52, 69, 108, 0.05);
  flex: 0 0 auto;
}

.login-footer {
  width: 100%;
  text-align: center;
  color: rgba(255, 255, 255, 0.80);
  text-shadow: 0 1px 2px rgba(35, 58, 96, 0.18);
  font-size: 16px;
  padding: 6px 0 10px;
  opacity: 0;
  animation: footerFade 1s ease 0.6s forwards;
}

/* ============================================================================
   /login redesign (docs/gui-redesign/login/) — everything below this banner
   is new. Shared classes that /forgot-password, /reset-password, /register
   and /auth-message also render are always scoped under body.login-page so
   those screens keep rendering exactly as shipped; classes below that are
   exclusive to templates/index.html (.login-head, .login-subtitle,
   .login-alert*, .login-field*, .login-submit__spinner,
   .login-page-footer*) are written unscoped since nothing else can match
   them, matching the approach static/css/register.css already established
   for its own new classes.
   ========================================================================= */

/* ---- L7 (/login only): repurpose the shared tinted foot strip for the
   register cross-link; drop the meaningless decorative square (L12/F4).
   /register renders the same .login-bottom-links markup for its own
   "Already have an account?" strip and must keep that square untouched. ---- */
body.login-page .login-bottom-links span::before {
  display: none;
}

/* ---- L6, L9, L12: submit becomes full width with a busy/spinner state.
   .login-submit is shared with /forgot-password, /reset-password and
   /auth-message, so every change here is scoped to /login only. ---- */
body.login-page .login-submit {
  position: relative;
  width: 100%;
  height: 52px;
  gap: 10px;
  color: var(--text-on-accent, #ffffff);
}

body.login-page .login-submit[aria-busy="true"] {
  cursor: progress;
}

body.login-page .login-submit:disabled {
  /* the action is in progress, not disallowed - keep the gradient during the
     busy state rather than switching to a greyed/disabled treatment */
  opacity: 0.86;
  cursor: progress;
}

.login-submit__spinner {
  display: none;
  width: 18px;
  height: 18px;
  border: 2px solid rgba(255, 255, 255, 0.45);
  border-top-color: var(--text-on-accent, #ffffff);
  border-radius: 50%;
  animation: loginSubmitSpin 0.8s linear infinite;
}

body.login-page .login-submit[aria-busy="true"] .login-submit__spinner {
  display: block;
}

@keyframes loginSubmitSpin {
  to {
    transform: rotate(360deg);
  }
}

/* ---- L2: header stack (title -> subtitle only; #loginNote is deliberately
   not rendered per the approved revision). Exclusive to /login. ---- */
.login-head {
  margin-bottom: 20px;
  text-align: center;
}

.login-subtitle {
  margin: 8px 0 0;
  color: var(--text-main);
  font-size: var(--app-theme-font-size-lg, 17px);
  font-weight: 700;
  line-height: 1.3;
  text-wrap: balance;
}

/* ---- L5: form-level credentials error, replacing the invalid-credentials
   modal. Exclusive to /login; openModal() stays wired for genuine
   connection errors only (auth_lang_dropdown.js). ---- */
.login-alert {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin: 0 0 4px;
  padding: 11px 13px;
  border: 1px solid color-mix(in srgb, var(--danger, #dc2626) 30%, transparent);
  border-left: 3px solid var(--danger, #dc2626);
  border-radius: var(--radius-lg, 18px);
  background: var(--danger-bg, #fef2f2);
  color: var(--danger, #dc2626);
  font-size: var(--app-theme-font-size-sm, 14px);
  font-weight: 600;
  line-height: 1.45;
  text-align: left;
}

.login-alert[hidden] {
  display: none;
}

.login-alert__icon {
  display: block;
  flex: 0 0 auto;
  width: 18px;
  height: 18px;
  margin-top: 1px;
}

/* ---- L3/L4: labelled field pattern. Named .login-field* (not .field*) so it
   cannot collide with the shared .field/.field--* rules above, which
   /forgot-password and /reset-password still use for their own unrestyled
   fields (58px height, 18px radius). Grid layout lets the recovery link (L4)
   render on the password label row while staying after the input in DOM/tab
   order via grid-area placement. Exclusive to /login. ---- */
.login-field {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "label aside"
    "shell shell";
  align-items: baseline;
  column-gap: 12px;
  row-gap: 6px;
  min-width: 0;
}

.login-field__label {
  grid-area: label;
  color: var(--text-main);
  font-size: var(--app-theme-font-size-xs, 13px);
  font-weight: 700;
  line-height: 1.25;
}

.login-field__aside {
  grid-area: aside;
  justify-self: end;
  color: var(--link);
  font-size: var(--app-theme-font-size-xs, 13px);
  font-weight: 700;
  line-height: 1.25;
  text-decoration: none;
}

.login-field__aside:hover {
  text-decoration: underline;
}

.login-field__aside:focus-visible {
  outline: 2px solid var(--info, #2563eb);
  outline-offset: 2px;
  border-radius: 4px;
}

.login-field__shell {
  grid-area: shell;
  position: relative;
}

.login-field__shell::before {
  content: "";
  position: absolute;
  left: 14px;
  top: 50%;
  z-index: 2;
  width: 20px;
  height: 20px;
  transform: translateY(-50%);
  opacity: 0.76;
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  pointer-events: none;
}

/* Same drawing language/stroke colour/weight as the shared .field--login and
   .field--password icons above - duplicated rather than shared so the two
   field systems stay fully independent. */
.login-field--login .login-field__shell::before {
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="none" stroke="%23687ea2" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><path d="M20 21a8 8 0 0 0-16 0"/><circle cx="12" cy="7" r="4.2"/></svg>');
}

.login-field--password .login-field__shell::before {
  background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="none" stroke="%23687ea2" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24"><rect x="5" y="11" width="14" height="9" rx="2.5"/><path d="M8 11V8.6A4.1 4.1 0 0 1 12 4.5 4.1 4.1 0 0 1 16 8.6V11"/></svg>');
}

.login-field__input {
  width: 100%;
  height: var(--login-field-h, 48px);
  padding: 0 16px 0 44px;
  border: 1.5px solid var(--field-border);
  border-radius: var(--radius-md, 14px);
  background: linear-gradient(180deg, var(--field-bg-1), var(--field-bg-2));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.38),
    0 3px 12px rgba(70, 96, 148, 0.04);
  color: var(--field-text);
  font-size: var(--app-theme-font-size-md, 15px);
  outline: none;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: border-color 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease;
}

.login-field--password .login-field__input {
  padding-right: 46px;
}

.login-field__input::placeholder {
  color: var(--field-placeholder);
}

.login-field__input:hover {
  border-color: rgba(150, 175, 214, 0.68);
}

.login-field__input:focus {
  border-color: var(--info, #2563eb);
  box-shadow:
    0 0 0 4px color-mix(in srgb, var(--info, #2563eb) 16%, transparent),
    inset 0 1px 0 rgba(255, 255, 255, 0.48);
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.34), rgba(255, 255, 255, 0.24));
}

/* Autofill must not repaint over the leading icon or the toggle - the
   shipped rule set for .field, duplicated here for .login-field__input. */
.login-field__input:-webkit-autofill,
.login-field__input:-webkit-autofill:hover,
.login-field__input:-webkit-autofill:focus {
  -webkit-text-fill-color: var(--field-text);
  caret-color: var(--field-text);
  transition: background-color 9999s ease-out;
  -webkit-box-shadow:
    0 0 0 1000px rgba(255, 255, 255, 0.22) inset,
    inset 0 1px 0 rgba(255, 255, 255, 0.38),
    0 3px 12px rgba(70, 96, 148, 0.04);
}

.login-field__input::-ms-reveal,
.login-field__input::-ms-clear {
  display: none;
  width: 0;
  height: 0;
}

.login-field__input::-webkit-credentials-auto-fill-button,
.login-field__input::-webkit-strong-password-auto-fill-button,
.login-field__input::-webkit-textfield-decoration-container {
  display: none !important;
  visibility: hidden;
  pointer-events: none;
}

/* L5: both fields carry the invalid state together, because POST /login
   never names a field in its {ok:false} response. */
.login-field.is-invalid .login-field__input,
.login-field__input[aria-invalid="true"] {
  border-color: var(--danger, #dc2626);
  box-shadow:
    0 0 0 3px color-mix(in srgb, var(--danger, #dc2626) 16%, transparent),
    inset 0 1px 0 rgba(255, 255, 255, 0.38);
}

/* L10: the password toggle keeps its shipped 32x32/20px shell (README_CSS.md
   "Auth form controls") but /login pins it to a constant position/size
   across every breakpoint instead of the shared rule's scale-up-on-shrink
   behaviour below, to match the new field padding. */
body.login-page .login-field__shell .field-password-toggle {
  right: 8px;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-sm, 10px);
}

body.login-page .login-field__shell .field-password-toggle img {
  width: 20px;
  height: 20px;
}

/* ---- L8: support contact + copyright, out of the panel and into the page
   footer, no longer aria-hidden. Exclusive to /login; .login-footer itself
   (the copyright line) is reused unchanged from the shared rule above. ---- */
.login-page-footer {
  width: 100%;
  padding: 4px 0 6px;
  text-align: center;
}

.login-page-footer p {
  margin: 0;
}

.login-page-footer__support {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 2px 8px;
  color: rgba(255, 255, 255, 0.92);
  font-size: var(--app-theme-font-size-sm, 14px);
  text-shadow: 0 1px 3px rgba(35, 58, 96, 0.42);
}

/* white + underline, not colour-only: --link blue would fail contrast on the
   wallpaper. */
.login-page-footer__support a {
  color: #ffffff;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 3px;
}

.login-page-footer__support a:focus-visible {
  outline: 2px solid #ffffff;
  outline-offset: 3px;
  border-radius: 4px;
}

  html.lang-switching .login-box {
    filter: blur(4px);
    opacity: 0.92;
    transform: scale(0.992);
  }

  html.lang-switching .logo {
    filter: blur(10px);
    opacity: 0.82;
  }

  html.lang-switching .login-shell {
    animation-play-state: paused;
  }

  html.lang-switching body::before {
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
  }

  @keyframes logoReveal {
    0% {
      opacity: 0;
      transform: translateY(-18px) scale(0.985);
      filter: blur(14px);
    }
    62% {
      opacity: 1;
    }
    100% {
      opacity: 1;
      transform: translateY(0) scale(1);
      filter: blur(0);
    }
  }

  @keyframes panelReveal {
    0% {
      opacity: 0;
      transform: translateY(18px) scale(0.985);
      filter: blur(14px);
    }
    62% {
      opacity: 1;
    }
    100% {
      opacity: 1;
      transform: translateY(0) scale(1);
      filter: blur(0);
    }
  }

  @keyframes shellFloat {
    0%, 100% { transform: translateY(0); }
    50% { transform: translateY(-7px); }
  }

  @keyframes shadowPulse {
    0%, 100% { transform: scaleX(1); opacity: 0.58; }
    50% { transform: scaleX(0.95); opacity: 0.44; }
  }

  @keyframes fogShift {
    0% { transform: translate3d(0, 0, 0) scale(1); }
    100% { transform: translate3d(0, -10px, 0) scale(1.02); }
  }

  @keyframes shimmerSweep {
    0% { transform: translateX(-32%); }
    100% { transform: translateX(32%); }
  }

  @keyframes footerFade {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: translateY(0); }
  }

  @media (max-width: 980px) {
    .login-box {
      padding: 28px 28px 0;
      border-radius: 28px;
    }

    .login-box--auth-page {
      padding-bottom: 28px;
    }

    .login-title {
      font-size: clamp(44px, 8vw, 62px);
      margin-bottom: 22px;
    }

    .login-title--compact {
      font-size: clamp(34px, 6vw, 46px);
      margin-bottom: 18px;
    }

    .field input[type="text"],
    .field input[type="password"],
    .field input[type="email"] {
      height: 66px;
      font-size: 20px;
      border-radius: 20px;
      padding-left: 64px;
    }

    .field--password input[type="password"],
    .field--password input[type="text"] {
      padding-right: 64px;
    }

    .field::before {
      left: 22px;
      width: 26px;
      height: 26px;
    }

    .field-password-toggle {
      right: 18px;
      width: 36px;
      height: 36px;
      border-radius: 12px;
    }

    .field-password-toggle img {
      width: 22px;
      height: 22px;
    }

    .login-submit {
      width: 82%;
      height: 58px;
      font-size: 22px;
      border-radius: 18px;
    }

    .login-help {
      font-size: 18px;
      padding-top: 22px;
      margin-top: 22px;
    }

    .login-forgot-link {
      margin-top: 18px;
      font-size: 18px;
    }

    .login-bottom-links {
      margin-inline: -28px;
      padding: 18px 18px 20px;
      font-size: 16px;
    }
  }

  @media (max-width: 640px) {
    body {
      padding: 16px 12px 14px;
    }

    .logo img {
      max-height: 110px;
      max-width: 44vw;
    }

    .login-main {
      gap: 20px;
    }

    .login-shell {
      width: min(100%, 430px);
    }

    .login-box {
      padding: 22px 18px 0;
      border-radius: 24px;
    }

    .login-box--auth-page {
      padding-bottom: 24px;
    }

    .login-title {
      margin-bottom: 18px;
      font-size: 48px;
    }

    .login-title--compact {
      font-size: 36px;
      line-height: 1.12;
    }

    .field input[type="text"],
    .field input[type="password"],
    .field input[type="email"] {
      height: 58px;
      border-radius: 18px;
      font-size: 18px;
      padding-left: 56px;
    }

    .field--password input[type="password"],
    .field--password input[type="text"] {
      padding-right: 58px;
    }

    .field::before {
      left: 18px;
      width: 22px;
      height: 22px;
    }

    .field-password-toggle {
      right: 14px;
      width: 32px;
      height: 32px;
      border-radius: 10px;
    }

    .remember {
      font-size: 16px;
    }

    .login-submit {
      width: 100%;
      height: 54px;
      font-size: 20px;
      border-radius: 16px;
    }

    .login-help {
      font-size: 16px;
      margin-top: 18px;
      padding-top: 18px;
    }

    .login-forgot-link {
      margin-top: 16px;
      font-size: 16px;
    }

    .login-bottom-links {
      margin-inline: -18px;
      gap: 10px;
      font-size: 13px;
      padding: 14px 14px 16px;
    }

    .login-bottom-links span::before {
      width: 14px;
      height: 14px;
      border-radius: 4px;
    }

    .login-footer {
      font-size: 18px;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .logo,
    .login-box,
    .login-shell,
    .login-shell::before,
    .bg-fog,
    .bg-shimmer,
    .login-footer {
      animation: none !important;
    }

    .logo,
    .login-box,
    .login-footer {
      opacity: 1;
      transform: none;
      filter: none;
    }

    .login-submit__spinner {
      animation: none !important;
    }
  }

  /* ==========================================================================
     /login redesign responsive ladder (design-brief.md section 9/10, L9).
     Field height 48/52/50, matching the approved /register ladder, instead of
     the shared .field selector's 58px/66px-at-768 scale-up bug (F3) - that
     shared selector is untouched above since /forgot-password and
     /reset-password still use it unmodified. Everything here is scoped to
     body.login-page (or already exclusive to /login markup) so no other auth
     screen's layout changes.
     ========================================================================= */

  body.login-page .logo img {
    max-height: 108px;
  }

  @media (max-width: 1024px) {
    body.login-page {
      --login-field-h: 52px;
    }
  }

  @media (max-width: 900px) {
    .login-title:not(.login-title--compact) {
      font-size: 32px;
    }
  }

  @media (max-width: 480px) {
    body.login-page {
      --login-field-h: 50px;
    }

    .login-title:not(.login-title--compact) {
      font-size: 28px;
    }
  }

  /* Very narrow / long localized "Forgot password?" strings: the recovery
     link drops below the field instead of crushing the label - which is
     also its DOM order, so tab order does not change. */
  @media (max-width: 340px) {
    .login-field {
      grid-template-columns: minmax(0, 1fr);
      grid-template-areas:
        "label"
        "shell"
        "aside";
    }

    .login-field__aside {
      justify-self: start;
    }
  }

  /* L11 (CRITICAL): short viewports (landscape phone, small desktop window).
     The page already scrolls (body.login-page above); this keeps the
     credentials above the fold instead of spending a quarter of a 420px-tall
     viewport on the logo. */
  @media (max-height: 560px) {
    body.login-page .login-main {
      justify-content: flex-start;
      gap: 12px;
    }

    body.login-page .logo img {
      max-height: 56px;
    }

    body.login-page .login-head {
      margin-bottom: 14px;
    }
  }

  /* softer central glow */
body {
  background:
    radial-gradient(
      circle at 50% 15%,
      rgba(255, 255, 255, 0.10),
      rgba(255, 255, 255, 0.035) 20%,
      rgba(255, 255, 255, 0) 40%
    ),
    radial-gradient(circle at 18% 52%, rgba(184, 211, 255, 0.14), rgba(184, 211, 255, 0) 34%),
    radial-gradient(circle at 84% 64%, rgba(204, 223, 255, 0.10), rgba(204, 223, 255, 0) 30%),
    url("/static/assets/covers/wallpaper.png") center / cover no-repeat fixed;
}

body::before {
  background:
    radial-gradient(
      circle at 50% 18%,
      rgba(255, 255, 255, 0.055),
      rgba(255, 255, 255, 0.018) 22%,
      rgba(255, 255, 255, 0) 48%
    );
  backdrop-filter: blur(0.8px);
  -webkit-backdrop-filter: blur(0.8px);
}

body::after {
  background:
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, 0.025), rgba(255, 255, 255, 0) 42%),
    radial-gradient(circle at 10% 82%, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0) 22%),
    radial-gradient(circle at 88% 84%, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0) 22%);
  opacity: 0.52;
}

.bg-fog {
  background:
    radial-gradient(circle at 50% 18%, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0) 22%),
    radial-gradient(circle at 30% 34%, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0) 14%),
    radial-gradient(circle at 72% 36%, rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0) 14%);
  opacity: 0.22;
  filter: blur(6px);
}

.bg-shimmer {
  opacity: 0.18;
}

.login-box {
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.13) 0%,
    rgba(255, 255, 255, 0.10) 52%,
    rgba(255, 255, 255, 0.08) 100%
  );
  box-shadow:
    0 16px 36px rgba(58, 85, 132, 0.12),
    inset 0 1px 0 rgba(255, 255, 255, 0.24);
  backdrop-filter: blur(12px) saturate(108%);
  -webkit-backdrop-filter: blur(12px) saturate(108%);
}

.login-box::before {
  background:
    radial-gradient(
      circle at 50% 0%,
      rgba(255, 255, 255, 0.10),
      rgba(255, 255, 255, 0.025) 30%,
      rgba(255, 255, 255, 0) 56%
    );
}
