/* DICOMetrics auth screens.
   Ported from APOM templates/login.html inline styles, adapted to be CSP-clean
   (no inline <style>/<script>, no style="" attributes) and self-hosted. Relies on
   the vendored Bootstrap 5.3 (html[data-bs-theme="dark"]) for the --bs-* palette and
   base .form-control/.btn/.alert; the Bootstrap fallbacks below keep the look intact
   even if the vendor file is absent. */

/* Auth backdrop, per theme. This was ONE hardcoded gradient used in both themes,
   so the "light" auth screens were actually dark blue -- the theme toggle did
   nothing to the page behind the card. Declared as a token so the value lives in
   one place and the dark override is a single line. */
:root {
  /* Light: a real tint, not near-white. Measured -- a paler backdrop left the
     card at 1.07:1 against it, so the card stopped reading as a surface at all. */
  --auth-bg: linear-gradient(135deg, #a9c1e8 0%, #cfdcf2 100%);
  /* Ink for anything sitting DIRECTLY on the backdrop (the HIPAA badge). This is
     a token because the badge used a hardcoded translucent WHITE: correct on the
     dark blue, invisible (1.28:1, measured) the moment the light theme stopped
     being dark blue too. */
  --auth-on-bg: rgba(15, 23, 42, 0.82);
}

:root[data-bs-theme="dark"] {
  --auth-bg: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
  --auth-on-bg: rgba(255, 255, 255, 0.75);
}

.auth-body {
  background: var(--auth-bg, linear-gradient(135deg, #1e3c72 0%, #2a5298 100%));
  color: var(--bs-body-color, #dee2e6);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

.login-container {
  max-width: 400px;
  width: 100%;
  margin: 2rem auto;
  padding: 0 1rem;
}

.login-card {
  /* Theme-reactive surface (matches every other card in the app --
     .panel/.idle-modal-card/.app-modal-card all use --bs-tertiary-bg). This
     used to be Bootstrap's static "dark" theme color, which never changes
     between light/dark; in light mode it collided with .form-label's
     --bs-body-color (also #212529 in light mode), making labels/helper
     text/headings invisible. */
  background: var(--bs-tertiary-bg, #2b3035);
  border: 1px solid var(--bs-border-color, #495057);
  border-radius: 12px;
  padding: 1.5rem;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
}

.logo-container {
  text-align: center;
  margin-bottom: 0.5rem;
}

.logo-container img {
  max-width: 250px;
  height: auto;
  margin-bottom: 0.5rem;
  border-radius: 24px;
  /* No border plate. It existed for one stated reason -- "no transparent variant
     exists" for the wordmark -- and logo_new_dark.png now does, so each theme
     gets art that already suits its own card. */
}

/* Exactly one wordmark per theme. Scoped to .logo-container and placed after the
   shared rule above, per the incident recorded in app.css: a bare `--dark`
   selector lost to a later `display` rule and rendered BOTH logos at once. */
.logo-container .auth-logo--dark {
  display: none;
}

:root[data-bs-theme="dark"] .logo-container .auth-logo--light {
  display: none;
}

:root[data-bs-theme="dark"] .logo-container .auth-logo--dark {
  display: inline-block;
}

.form-group {
  margin-bottom: 1.5rem;
}

.form-label {
  display: flex;
  align-items: center;
  color: var(--bs-body-color, #dee2e6);
  font-weight: 500;
  margin-bottom: 0.5rem;
}

.form-control {
  background: var(--bs-secondary-bg, #2b3035);
  border: 1px solid var(--bs-border-color, #495057);
  color: var(--bs-body-color, #dee2e6);
  padding: 0.75rem 1rem;
  border-radius: 8px;
  transition: all 0.2s ease;
}

.form-control:focus {
  background: var(--bs-secondary-bg, #2b3035);
  border-color: #667eea;
  box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
  color: var(--bs-body-color, #dee2e6);
  outline: none;
}

.btn-login {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  border: none;
  color: #fff;
  padding: 0.75rem 2rem;
  border-radius: 8px;
  font-weight: 600;
  width: 100%;
  cursor: pointer;
  transition: all 0.2s ease;
}

.btn-login:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4);
  color: #fff;
}

.btn-login:focus-visible {
  outline: 2px solid #b3bdf5;
  outline-offset: 2px;
}

.forgot-row {
  text-align: center;
  margin-bottom: 1.25rem;
  font-size: 0.9rem;
}

.forgot-link,
.register-link a {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  color: #667eea;
  text-decoration: none;
  font-weight: 500;
}

.forgot-link:hover,
.register-link a:hover {
  color: #764ba2;
  text-decoration: underline;
}

.register-link {
  text-align: center;
  margin-top: 1.5rem;
  color: var(--bs-secondary-color, #adb5bd);
}

.alert {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  border: none;
  border-radius: 8px;
  padding: 0.75rem 1rem;
  margin-bottom: 1.5rem;
}

.alert-danger {
  background: rgba(220, 53, 69, 0.15);
  color: #dc3545;
  border-left: 4px solid #dc3545;
}

.alert-success {
  background: rgba(25, 135, 84, 0.15);
  color: #198754;
  border-left: 4px solid #198754;
}

.alert-warning {
  background: rgba(255, 193, 7, 0.15);
  color: #ffc107;
  border-left: 4px solid #ffc107;
}

.alert-info {
  background: rgba(13, 202, 240, 0.15);
  color: #0dcaf0;
  border-left: 4px solid #0dcaf0;
}

.hipaa-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  text-align: center;
  color: var(--auth-on-bg, rgba(255, 255, 255, 0.75));
  font-size: 0.8rem;
  margin-top: 1rem;
}

.icon {
  flex: 0 0 auto;
  vertical-align: -0.125em;
}

.form-label .icon {
  margin-right: 0.5rem;
}

.auth-lead {
  text-align: center;
  color: var(--bs-secondary-color, #adb5bd);
  font-size: 0.9rem;
  margin-bottom: 1.25rem;
}

.field-hint {
  margin-top: 0.4rem;
  margin-bottom: 0;
  font-size: 0.8rem;
  color: var(--bs-secondary-color, #adb5bd);
}

.pw-rules {
  margin: 0.4rem 0 0;
  padding-left: 1.1rem;
  font-size: 0.8rem;
  color: var(--bs-secondary-color, #adb5bd);
}

.pw-rules li {
  margin: 0.1rem 0;
}

.pw-match {
  margin: 0.4rem 0 0;
  min-height: 1em;
  font-size: 0.8rem;
}
.pw-match-ok {
  color: #75b798;
}
.pw-match-bad {
  color: #ea868f;
}

.spinner {
  display: inline-block;
  width: 0.9em;
  height: 0.9em;
  border: 2px solid rgba(255, 255, 255, 0.5);
  border-top-color: #fff;
  border-radius: 50%;
  animation: auth-spin 0.6s linear infinite;
  vertical-align: -0.15em;
}

@keyframes auth-spin {
  to {
    transform: rotate(360deg);
  }
}

.toast-hide {
  opacity: 0;
  transition: opacity 0.4s ease;
}

.btn-resend {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  width: 100%;
  margin-top: 0.75rem;
  padding: 0.6rem 1rem;
  background: transparent;
  color: var(--bs-secondary-color, #adb5bd);
  border: 1px solid var(--bs-border-color, #495057);
  border-radius: 8px;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s ease;
}

.btn-resend:hover {
  color: var(--bs-body-color, #dee2e6);
  border-color: #667eea;
}

/* `.auth-admin-hint` lived here: the "Administrator sign-in" banner 7-A put on
   the shared login screen when an operator arrived via /admin/login. It went
   with the banner -- administrators have their own page now, so the only people
   it could still appear for were providers. Deleted rather than left behind:
   dead CSS outlives the markup that explained it. */

/* "Platform Administration" pill on the admin login (APOM's `.admin-badge`).

   The markup reuses `.admin-header-band`, but that class is styled in app.css
   and the auth pages only load auth.css -- so the identity was in the DOM and
   invisible on screen, with the shield glued to the text. This is the auth-page
   treatment: APOM's pill, in our accent rather than its hardcoded gradient. */
.admin-header-band--auth {
  display: flex;
  justify-content: center;
  margin: 0 0 1rem;
}

.admin-header-band--auth .admin-header-title {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.4rem 1rem;
  border-radius: 999px;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  /* Literal fallback, not a bare `var(--dm-accent)`: that token is defined in
     app.css, which the auth pages do not load, so the background resolved to
     nothing and left white text on a white card -- invisible. The fallback is
     app.css's own light-theme accent value. */
  background: var(--dm-accent, #4c51bf);
  color: #fff;
}

/* The button label sits against its icon without this -- the auth stylesheet
   has no equivalent of the app shell's icon gap. */
.auth-form .btn svg,
.auth-alt a svg {
  margin-right: 0.35rem;
  vertical-align: -0.15em;
}

/* --- Show/hide password toggle ------------------------------------------------
   Echoes `.field`/`.field-status` (app.css) for structure and
   `.search-submit`/`.icon-btn` for looks -- but reimplemented here rather than
   reused, because auth/base.html loads ONLY vendored Bootstrap + this file.
   app.css is not on these pages, so its classes and its `--dm-*` tokens do not
   exist. Every custom property below therefore carries a literal fallback; a
   bare `var(--dm-accent)` here resolves to nothing, which this stylesheet has
   been bitten by before. */
.password-field {
  position: relative;
  display: block;
}

/* Room for the button so a long password cannot slide underneath it. */
.password-field .form-control {
  padding-right: 3rem;
  width: 100%;
}

.password-toggle {
  position: absolute;
  top: 50%;
  right: 0.5rem;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: 8px;
  color: var(--bs-secondary-color, #adb5bd);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease;
}

.password-toggle:hover {
  color: var(--bs-body-color, #dee2e6);
  border-color: var(--bs-border-color, #495057);
}

.password-toggle:focus-visible {
  outline: 2px solid #b3bdf5;
  outline-offset: 2px;
}

/* Both glyphs are in the DOM; CSS decides which is visible. That is what lets
   auth.js avoid innerHTML entirely -- see the (5) block there. Default state is
   hidden-password, so the EYE ("show") is the offered action. */
.password-toggle-hide {
  display: none;
}

.password-field.is-revealed .password-toggle-show {
  display: none;
}

.password-field.is-revealed .password-toggle-hide {
  display: inline-flex;
}

/* Item 6: `.auth-heading` is used by team/join.html and
   enterprise/contact_sales.html and had NO rule anywhere, so the <h1> inherited
   left alignment while `.auth-lead` beneath it was already centred -- the card
   read as broken rather than as a choice. */
/* Q3: APOM stacks the invitation actions vertically --
   `templates/accept_invitation.html:62-66`,
   `.action-buttons { display: flex; flex-direction: column; gap: 1rem }`.

   IN auth.css, not app.css. The auth chrome (`auth/base.html`) loads only
   bootstrap + this file, so a rule in app.css never reaches this page -- which
   is exactly how the first attempt at this fix passed its test and left the
   buttons side by side on screen. The integration test now reads the
   stylesheet the page actually links.

   Self-contained rather than a modifier on `.btn-row`: that class is not in
   auth.css either, so there is no flex container here to modify. The buttons
   were sitting side by side simply because `.btn` is inline-block. */
.btn-row-stacked {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.75rem;
  margin-top: 1rem;
}

.auth-heading {
  text-align: center;
  /* Q3: this rule set ONLY `text-align`, so the <h1> fell through to
     Bootstrap's default -- 2.5rem, and `h1` is responsive so it renders larger
     still on a wide viewport -- sitting directly above `.auth-lead` at 0.9rem.
     The card read as a shout followed by a whisper.

     1.5rem is APOM's own emphasis size on this exact screen
     (`templates/accept_invitation.html` `.team-name`, :53), so this is parity
     rather than a number chosen to look about right.

     Note the shape of the original defect: the rule was added by an earlier fix
     that scoped itself to alignment alone. Same too-narrow scoping that made
     the spinal-region labels need doing twice. */
  font-size: 1.5rem;
  line-height: 1.3;
  margin-bottom: 0.5rem;
}

/* --- Passkeys ------------------------------------------------------------------

   Sign-in with a passkey sits directly BELOW the email field and above the
   password field, so it reads as the alternative to typing a password rather than
   as a second submit. `.btn-passkey` therefore overrides `.btn-login`'s gradient
   with an outline treatment: the gradient is the primary action on this screen and
   there can only be one of those.

   It used to sit above the whole form, which put the control above the input it
   depends on -- Cognito issues no WEB_AUTHN challenge without a username -- and
   made an empty-field click look like a broken button.

   Every one of these controls is authored `hidden` and revealed by passkey.js only
   when the ceremony can actually run, so none of this is ever visible on a browser
   that cannot use it. */
.btn-passkey {
  background: transparent;
  border: 1px solid #667eea;
  color: #667eea;
  /* Sits inside the form now, between two fields, so it owns its own breathing
     room -- `.form-group`'s margin only spaces the fields from each other. */
  margin-bottom: 0.75rem;
}

.btn-passkey:hover:not(:disabled) {
  background: rgba(102, 126, 234, 0.08);
  color: #764ba2;
  border-color: #764ba2;
  /* No lift: `.btn-login:hover` translates and casts a shadow, which on a
     secondary action competes with the primary button right below it. */
  transform: none;
  box-shadow: none;
}

/* TWO disabled meanings, two cursors. "Nothing to act on yet" (no email typed) is
   not the same state as "working on it", and one cursor for both told the user the
   page was busy when it was in fact waiting for them. `aria-busy` is the flag
   passkey.js already sets for the second, so it does the styling too. */
.btn-passkey:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

.btn-passkey[aria-busy="true"] {
  cursor: progress;
  /* Less faded than the idle-disabled state: this button is doing something and
     is the most important thing on the screen while it does. */
  opacity: 0.85;
}

/* The shared `.spinner` is hard-coded white, which is right on `.btn-login`'s
   gradient and wrong here -- this button is an outline treatment with #667eea
   text, so a white ring read as a stray element rather than as part of the label.
   `currentColor` inherits whatever the button's text is, including the hover
   shift to #764ba2. The shared rule is deliberately NOT touched: it is used by the
   submit button on every auth screen. */
.btn-passkey .spinner {
  border-color: currentColor;
  border-top-color: transparent;
}

/* Announced, never shown. The live region that carries passkey progress to a
   screen reader: the busy label sits on the button, which is only announced while
   focus is there, and during a platform sheet it is not.

   The clip-rect idiom rather than `display:none` or `visibility:hidden` -- both of
   those remove the element from the accessibility tree, which would make this
   announce nothing at all. `white-space: nowrap` stops a long phrase wrapping into
   a 1px box and being reported oddly by some readers. This class did not exist
   anywhere in the stylesheets before; a template using it would have been a silent
   no-op that showed the text in the middle of the form. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Idle-unavailable is styled through `aria-disabled` now, not `:disabled`. The
   property took the button out of the tab order, so a keyboard user never found
   it; the ARIA attribute states the same thing and stays focusable. `:disabled`
   is still matched below because the BUSY state does use the real property. */
.btn-passkey[aria-disabled="true"] {
  opacity: 0.6;
  cursor: not-allowed;
}

/* The reason the button is off, shown only while it is. Sits immediately under it
   and points back up at the field that unlocks it. */
.passkey-hint {
  margin: -0.25rem 0 1rem;
  font-size: 0.8rem;
  color: var(--bs-secondary-color, #adb5bd);
  text-align: center;
}

/* "or" divider between the passkey button and the form. A rule either side of the
   word, drawn with borders on the pseudo-elements so there is no extra markup. */
.passkey-or {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 1.25rem 0;
  color: var(--bs-secondary-color, #adb5bd);
  font-size: 0.85rem;
}

.passkey-or::before,
.passkey-or::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--bs-border-color, rgba(255, 255, 255, 0.15));
}

/* Shown only when a ceremony FAILED -- never when the user simply dismissed the
   platform sheet, which passkey.js classifies as a cancel. Deliberately quiet for
   that reason: the password form directly below is the way forward, so this
   explains rather than alarms. */
.passkey-error {
  margin: 0 0 1rem;
  font-size: 0.85rem;
  color: var(--bs-warning-text-emphasis, #ffc107);
  text-align: center;
}

/* The passkey-as-recovery hint on /auth/forgot. Same voice as `.register-link`,
   which sits just below it. */
.auth-hint {
  text-align: center;
  margin-top: 1.25rem;
  font-size: 0.85rem;
  color: var(--bs-secondary-color, #adb5bd);
}

.auth-hint a {
  color: #667eea;
  text-decoration: none;
  font-weight: 500;
}

.auth-hint a:hover {
  color: #764ba2;
  text-decoration: underline;
}
