/* ═══════════════════════════════════════════════════════════════════
   Avalon Identity — Aegis-Quality OIDC Page Styles

   Login.cshtml injects --tenant-primary and --tenant-accent per-tenant
   from BrandingConfig via an inline <style> block. Other pages use the
   defaults below (Avalon brand colors). Future: pass branding through
   TempData or a shared partial if per-tenant theming is needed on
   MFA/consent/error pages.
   ═══════════════════════════════════════════════════════════════════ */

/* ── Design Tokens ────────────────────────────────────────────────── */
:root {
  /* Tenant branding defaults — Login.cshtml overrides per-tenant */
  --tenant-primary: #153448;
  --tenant-accent: #2d8a9e;

  /* Aegis palette */
  --frost-grey: #f8fafc;
  --silver-mist: #e2e8f0;
  --slate: #64748b;
  --text-primary: #334155;
  --text-secondary: rgb(80, 88, 103);
  --error: #a94442;
  --error-bg: #f8eaea;
  --success: #4a7052;
  --success-bg: #e9f3ec;
  --warning: #c67e3b;
  --warning-bg: #f7efe6;
  --blue-bg: #effbff;

  /* Typography */
  --font-size-xxs: 10px;
  --font-size-xs: 11px;
  --font-size-sm: 12px;
  --font-size-md: 13px;
  --font-size-base: 14px;
  --font-size-lg: 16px;
  --font-size-xl: 18px;
  --font-size-title: 24px;
  --font-size-xxl: 32px;

  /* Radii */
  --radius-xs: 4px;
  --radius-sm: 6px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-xl: 16px;
}

/* ── Reset ────────────────────────────────────────────────────────── */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html { height: 100%; }
body {
  font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  color: var(--text-primary);
  font-size: var(--font-size-base);
  min-height: 100%;
  margin: 0;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* ── Identity Container (full-viewport gradient) ─────────────────── */
.identity-container {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 100vh;
  padding: 2rem;
  position: relative;
  overflow: hidden;
  /* Background moved to body-class-gated rules below so the
     LoginBackground mode (gradient / solid / image) can actually
     take effect. An earlier revision set the gradient here
     unconditionally, which meant the body.identity-bg-solid and
     body.identity-bg-image overrides never won — this container
     fills the viewport and covered the body colour entirely. */
}

/* Default + explicit "gradient" mode — applies when body has no
   mode class, preserving the baseline look for unconfigured tenants. */
body:not(.identity-bg-solid):not(.identity-bg-image) .identity-container {
  background: linear-gradient(135deg, var(--tenant-accent) 0%, var(--tenant-primary) 100%);
}

/* Solid mode — flat tenant-primary fill. */
body.identity-bg-solid .identity-container {
  background: var(--tenant-primary);
}

/* Image mode — hero background. URL injected into --tenant-bg-image
   from the Razor page's nonce'd <style> block (already clamp-
   validated by BrandingResolver.ClampAssetUrl). */
body.identity-bg-image .identity-container {
  background-image: var(--tenant-bg-image);
  background-size: cover;
  background-position: center center;
  background-repeat: no-repeat;
}

.identity-container::before {
  content: '';
  position: absolute;
  top: -30%;
  right: -20%;
  width: 70%;
  height: 70%;
  background: radial-gradient(circle, rgba(255,255,255,0.06) 0%, transparent 70%);
  pointer-events: none;
}
.identity-container::after {
  content: '';
  position: absolute;
  bottom: -20%;
  left: -15%;
  width: 60%;
  height: 60%;
  background: radial-gradient(circle, rgba(255,255,255,0.04) 0%, transparent 70%);
  pointer-events: none;
}

/* ── Card ─────────────────────────────────────────────────────────── */
.identity-card {
  background: white;
  border-radius: var(--radius-xl);
  padding: 2.5rem 2rem 2rem;
  width: 100%;
  max-width: 440px;
  position: relative;
  z-index: 1;
  box-shadow:
    0 1px 3px rgba(15, 42, 61, 0.04),
    0 4px 12px rgba(15, 42, 61, 0.06),
    0 16px 40px rgba(15, 42, 61, 0.08);
  border: 1px solid rgba(226, 232, 240, 0.8);
}

.identity-card.wide {
  max-width: 520px;
}

/* ── Card Header / Branding ───────────────────────────────────────── */
.identity-header {
  text-align: center;
  margin-bottom: 1.75rem;
}

.identity-header img {
  max-height: 48px;
  max-width: 200px;
  margin-bottom: 12px;
}

.identity-logo {
  font-size: var(--font-size-xxl);
  font-weight: 700;
  color: var(--text-primary);
  letter-spacing: -1px;
  margin-bottom: 4px;
}

.identity-divider {
  width: 60px;
  height: 2px;
  background: var(--tenant-accent);
  margin: 12px auto;
}

.identity-subtitle {
  font-size: var(--font-size-xl);
  font-weight: 600;
  color: var(--tenant-accent);
  letter-spacing: 0.2em;
}

.identity-app-name {
  font-size: var(--font-size-xs);
  font-weight: 300;
  font-style: italic;
  color: var(--slate);
  letter-spacing: 0.08em;
  margin-top: 2px;
}

/* ── Page Titles (non-login pages) ────────────────────────────────── */
.identity-page-title {
  font-size: var(--font-size-title);
  font-weight: 500;
  color: var(--tenant-primary);
  margin-bottom: 0.25rem;
  letter-spacing: -0.02em;
  text-align: center;
}

.identity-page-desc {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  text-align: center;
  margin-bottom: 1.5rem;
  letter-spacing: 0.02em;
}

/* ── Form Elements ────────────────────────────────────────────────── */
.auth-form-group {
  margin-bottom: 1.75rem;
  position: relative;
}

.auth-form-label {
  display: block;
  font-size: var(--font-size-sm);
  font-weight: 600;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: var(--text-primary);
  margin-bottom: 8px;
}

.auth-form-input {
  width: 100%;
  padding: 10px 16px;
  border: 1px solid rgba(43, 95, 143, 0.15);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  background: white;
  font-size: var(--font-size-base);
  font-family: inherit;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
  outline: none;
}

.auth-form-input:focus {
  border-color: var(--tenant-primary);
  box-shadow: 0 0 0 3px rgba(30, 74, 99, 0.1);
}

.auth-form-input::placeholder {
  color: var(--slate);
  font-weight: 300;
}

/* Password field */
.password-wrapper { position: relative; }

.password-toggle {
  position: absolute;
  right: 12px;
  top: 50%;
  transform: translateY(-50%);
  background: none;
  border: none;
  padding: 6px;
  cursor: pointer;
  color: var(--text-secondary);
  border-radius: var(--radius-sm);
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
  justify-content: center;
}

.password-toggle:hover {
  color: var(--tenant-primary);
  background-color: var(--frost-grey);
  box-shadow: 0 0 0 2px rgba(30, 74, 99, 0.2);
}

/* ── OTP Code Entry ───────────────────────────────────────────────── */
.otp-container {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin: 1.5rem 0;
}

.otp-input {
  width: 44px;
  height: 52px;
  font-size: var(--font-size-title);
  font-weight: 600;
  font-family: inherit;
  text-align: center;
  border: 1px solid rgba(43, 95, 143, 0.15);
  border-radius: var(--radius-md);
  color: var(--text-primary);
  background: white;
  outline: none;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
  caret-color: var(--tenant-accent);
}

.otp-input:focus {
  border-color: var(--tenant-primary);
  box-shadow: 0 0 0 3px rgba(30, 74, 99, 0.1);
}

.otp-input.filled {
  border-color: var(--tenant-accent);
  background: rgba(45, 138, 158, 0.04);
}

/* Fallback single-input for non-JS */
.code-input-single {
  width: 100%;
  padding: 12px;
  border: 1px solid rgba(43, 95, 143, 0.15);
  border-radius: var(--radius-md);
  font-size: var(--font-size-xl);
  font-family: inherit;
  text-align: center;
  letter-spacing: 8px;
  color: var(--text-primary);
  background: white;
  outline: none;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.code-input-single:focus {
  border-color: var(--tenant-primary);
  box-shadow: 0 0 0 3px rgba(30, 74, 99, 0.1);
}

/* ── Buttons ──────────────────────────────────────────────────────── */
.btn-primary {
  width: 100%;
  padding: 12px 20px;
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: var(--font-size-base);
  font-weight: 600;
  color: white;
  background: var(--tenant-primary);
  border: none;
  cursor: pointer;
  transition: all 0.15s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  letter-spacing: 0.02em;
  /* .btn-primary is used on both <button> and <a>. Baking
     text-decoration: none into the class eliminates the need for
     inline style overrides on the anchor variants (which a strict CSP
     without style-src-attr 'unsafe-inline' would block). */
  text-decoration: none;
}

.btn-primary:hover { background: var(--tenant-accent); }
.btn-primary:active { transform: scale(0.98); }
.btn-primary:disabled { opacity: 0.6; cursor: not-allowed; }

/* Loading state — applied by JS on form submit so the user gets immediate
   visual feedback that their click registered while the server processes
   the request. The page navigates away on success/MFA/error, so there's
   no reset path — the next render starts fresh. */
.btn-primary.is-loading { cursor: wait; opacity: 0.92; }
.btn-primary.is-loading .btn-label::before {
  content: '';
  display: inline-block;
  width: 14px;
  height: 14px;
  margin-right: 10px;
  margin-bottom: -2px;
  border: 2px solid rgba(255, 255, 255, 0.4);
  border-top-color: white;
  border-radius: 50%;
  animation: btn-spin 0.6s linear infinite;
  vertical-align: middle;
}
@keyframes btn-spin { to { transform: rotate(360deg); } }

/* Honour prefers-reduced-motion (WCAG 2.3.3) — users with vestibular
   conditions or motion sensitivity opt out of continuous animations.
   Hide the spinner entirely rather than freezing it (a static unrotating
   circle reads as "broken"); the "Signing in…" label is sufficient
   feedback on its own. */
@media (prefers-reduced-motion: reduce) {
  .btn-primary.is-loading .btn-label::before { display: none; }
}

.btn-secondary {
  width: 100%;
  padding: 12px 20px;
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: var(--font-size-base);
  font-weight: 500;
  color: var(--text-primary);
  background: white;
  border: 1px solid var(--silver-mist);
  cursor: pointer;
  transition: all 0.15s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  /* Used on both <button> and <a>. Match .btn-primary's approach —
     strip anchor underline here so Cancel links don't need inline
     style="text-decoration: none". */
  text-decoration: none;
}

.btn-secondary:hover {
  background: var(--frost-grey);
  border-color: rgba(43, 95, 143, 0.25);
}

.btn-group {
  display: flex;
  gap: 12px;
  margin-top: 1.5rem;
}

.btn-group > * { flex: 1; }

.btn-stack {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 1.5rem;
}

/* Inline text button styled as a link.
   Two class names refer to the SAME rule:
     .btn-text — historical name (resend-code, inline-text-action callsites)
     .btn-link — newer name used at PasskeyInvite "Don't ask again",
                 Account/Phone + PhoneVerify Cancel anchors. Without this
                 alias, those three sites fell through to browser-default
                 button / anchor rendering (which produced a boxed look
                 on the <button> instances, defeating the link visual).
   Treat the two names as interchangeable when adding new callsites. */
.btn-text, .btn-link {
  background: none;
  border: none;
  color: var(--tenant-accent);
  font-size: var(--font-size-md);
  font-family: inherit;
  font-weight: 500;
  cursor: pointer;
  padding: 4px 0;
  transition: color 0.2s ease;
  /* Anchor variants (Phone / PhoneVerify Cancel links) rely on
     text-decoration: none here so the link doesn't show its
     default underline at rest — :hover re-adds the underline as
     the affordance signal, matching the .btn-secondary pattern of
     "underline on hover only". */
  text-decoration: none;
}

.btn-text:hover, .btn-link:hover {
  color: var(--tenant-primary);
  text-decoration: underline;
}

/* ── Status / Alert Boxes ─────────────────────────────────────────── */
.status-box {
  font-size: var(--font-size-sm);
  border-radius: var(--radius-md);
  padding: 10px 14px;
  margin-bottom: 1rem;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  line-height: 1.5;
}

.status-box i { margin-top: 2px; flex-shrink: 0; }

.status-box.error {
  color: var(--error);
  background: var(--error-bg);
  border: 1px solid rgba(169, 68, 66, 0.2);
}

.status-box.success {
  color: var(--success);
  background: var(--success-bg);
  border: 1px solid rgba(74, 112, 82, 0.2);
}

.status-box.warning {
  color: var(--warning);
  background: var(--warning-bg);
  border: 1px solid rgba(198, 126, 59, 0.2);
}

.status-box.info {
  color: var(--tenant-primary);
  background: var(--blue-bg);
  border: 1px solid rgba(45, 138, 158, 0.2);
}

/* ── Links ────────────────────────────────────────────────────────── */
.extra-link {
  font-weight: 500;
  font-size: var(--font-size-md);
  text-decoration: none;
  color: var(--tenant-primary);
  cursor: pointer;
  letter-spacing: 0.05em;
  transition: color 0.2s ease;
}

.extra-link:hover {
  color: var(--tenant-accent);
  text-decoration: underline;
}

/* Muted variant — used on recovery + passwordless CTAs to demote them
   below the primary Sign In button in the visual hierarchy. Still
   readable (var(--slate) is 7.06:1 contrast on white, AAA), but quiet
   enough that the eye lands on Sign In first. */
.extra-link--muted {
  color: var(--slate);
  font-size: var(--font-size-sm);
  font-weight: 400;
}
.extra-link--muted:hover {
  color: var(--tenant-accent);
  text-decoration: underline;
}
.extra-link--muted i { font-size: 11px; }

.back-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--font-size-sm);
  color: var(--slate);
  text-decoration: none;
  margin-bottom: 1.25rem;
  transition: color 0.2s ease;
  letter-spacing: 0.02em;
}

.back-link:hover { color: var(--tenant-accent); }

/* ── Back-link row ────────────────────────────────────────────────────
   Two back-links at top of Account subpages — "Back to account"
   (left, Avalon-internal navigation) and "Back to {AppName}" (right,
   exit to RP). Flex + space-between puts them at opposite ends so they
   don't cluster side-by-side looking like duplicates. Child .back-link
   margin-bottom is absorbed by the row's own margin-bottom so the
   page-title gap stays consistent.
   ─────────────────────────────────────────────────────────────────── */
.back-link-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 1.25rem;
}
.back-link-row .back-link { margin-bottom: 0; }

/* ── Forgot-credential row ──────────────────────────────────────────
   Two peer recovery CTAs ("Forgot your {identifier}?" + "Forgot your
   password?") on the Login card, opposite ends of one row. Same layout
   shape as .back-link-row; kept as its own selector so the semantic
   name reads at the call site. Both anchors are .extra-link.extra-link--muted
   so the hierarchy demotes them below the primary Sign In button.
   gap + flex-wrap handle long tenant identifier labels gracefully —
   ("Forgot your Membership Number?") wraps to a two-row layout on
   narrow viewports instead of overlapping.
   ─────────────────────────────────────────────────────────────────── */
.forgot-links-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  margin-top: 1.25rem;
}

/* Accent the "exit to RP" link so it reads as a primary action
   distinct from the internal back-link.
   An earlier revision had the selector free-standing (.back-link-exit)
   so it would apply wherever the class was attached — breadcrumb
   .back-link on subpages, header-action .account-header-action-link
   on Index. That worked for the breadcrumb case, but the Index case
   broke because .account-header-action-link { color: slate } is sourced
   LATER in this file (line ~977) and wins on equal specificity
   (0,0,1,0 vs 0,0,1,0 → last one declared wins). Adding compound
   .account-header-action-link.back-link-exit raises specificity to
   0,0,2,0 and guarantees the exit styling applies regardless of the
   source-order of the base class. Same treatment for :hover. */
.back-link-exit,
.account-header-action-link.back-link-exit {
  color: var(--tenant-primary);
  font-weight: 500;
}
.back-link-exit:hover,
.account-header-action-link.back-link-exit:hover {
  color: var(--tenant-accent);
}

/* ── Dividers ─────────────────────────────────────────────────────── */
.form-divider {
  border: none;
  height: 1px;
  background: rgba(148, 163, 184, 0.18);
  margin: 1.5rem 0;
}

/* ── "or" Divider (inline label between two CTAs) ─────────────────────
   Horizontal rule with a centered label ("or", "and", etc.). Used
   when two equally-weighted options need a clear visual separator
   (e.g. passkey | OTP on PasswordlessStart). The text comes from a
   <span> child so callers control the wording.

   Markup:
       <div class="auth-divider"><span>or</span></div>
*/
.auth-divider {
  display: flex;
  align-items: center;
  text-align: center;
  color: var(--text-secondary, #6b7280);
  font-size: 0.85rem;
  margin: 1.25rem 0;
}
.auth-divider::before,
.auth-divider::after {
  content: '';
  flex: 1;
  border-bottom: 1px solid var(--border-color, #e5e7eb);
}
.auth-divider span { padding: 0 0.75rem; }

/* ── Segmented Control (radiogroup-backed) ───────────────────────────
   Two-or-more side-by-side option chooser, semantically a WAI-ARIA
   radiogroup. The visible <label> elements are the styled segments;
   the underlying radio inputs stay sr-only-focusable so screen
   readers + keyboard users get native radiogroup semantics (Tab to
   enter, arrow keys to switch, Space to select), while sighted users
   see the styled chips. The :has(input:checked) selector mirrors the
   radio's :checked state into the label's visual treatment.

   Caller-side .is-checked class is a fallback mirror for engines
   that lag :has() support — JS can toggle .is-checked on change.
   Both selectors apply the same styling.

   Markup:
       <div class="auth-segmented-control" role="radiogroup" aria-label="…">
           <label class="auth-segmented-button">
               <input type="radio" name="…" value="a" class="sr-only" checked />
               Option A
           </label>
           <label class="auth-segmented-button">
               <input type="radio" name="…" value="b" class="sr-only" />
               Option B
           </label>
       </div>
*/
.auth-segmented-control {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
}

.auth-segmented-button {
  flex: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.65rem 0.9rem;
  border: 1px solid rgba(43, 95, 143, 0.15);
  border-radius: var(--radius-md);
  background: white;
  color: var(--text-secondary);
  font-size: var(--font-size-sm);
  font-weight: 500;
  cursor: pointer;
  transition: border-color 0.15s ease,
              background 0.15s ease,
              color 0.15s ease,
              box-shadow 0.15s ease;
}

.auth-segmented-button:hover {
  border-color: var(--tenant-primary);
  color: var(--text-primary);
}

/* Focus visibility for keyboard navigation. The ring sits on the
   label rather than the hidden radio so the visible focus indicator
   tracks the segment users actually see. */
.auth-segmented-button:focus-within {
  outline: none;
  border-color: var(--tenant-primary);
  box-shadow: 0 0 0 3px rgba(30, 74, 99, 0.15);
}

.auth-segmented-button:has(input:checked),
.auth-segmented-button.is-checked {
  border-color: var(--tenant-accent);
  background: rgba(45, 138, 158, 0.06);
  color: var(--text-primary);
  font-weight: 600;
}

.auth-segmented-button:has(input:disabled),
.auth-segmented-button.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* ── Inline Help Hint (subtle subtext under a CTA / above a form) ──
   For static helper copy that explains a non-obvious gate or escape
   hatch (e.g. "Some accounts aren't enabled for one-time codes…").
   Smaller than .identity-page-desc, lower contrast than body text,
   no icon. */
.auth-hint {
  font-size: 0.8rem;
  color: var(--text-secondary, #6b7280);
  line-height: 1.4;
  margin: 0.6rem 0 1rem;
  text-align: center;
}

/* ── Inline Form-Field Help (left-aligned, sits under a field) ──────
   For per-field helper copy that explains a server-side validation
   the user can't preview (e.g. breach-list check). Left-aligned so
   it reads naturally under a form input rather than centering like
   a CTA caption. Icon-leading layout matches .status-box. */
.auth-form-help {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  font-size: 0.8rem;
  color: var(--text-secondary, #6b7280);
  line-height: 1.4;
  margin: 0.6rem 0 0;
}

.auth-form-help i {
  flex-shrink: 0;
  margin-top: 0.15rem;
  color: var(--tenant-primary, #2b5f8f);
}

/* Error variant — used by the inline "passwords don't match" hint
   under the Confirm field. WCAG 3.3.1 (Error Identification, A) +
   3.3.3 (Error Suggestion, AA): a disabled-submit-without-message
   is the documented dark pattern; this surfaces the cause as soon
   as Confirm diverges from New. Toggled visible by
   /identity/js/password-rules.js via the `.is-hidden` companion. */
.auth-form-help.error {
  color: var(--error);
}
.auth-form-help.error i {
  color: var(--error);
}
.auth-form-help.is-hidden {
  display: none;
}

/* ── Breach-Check Warning (real-time client-side check) ────────────
   Inline status element under the new-password field. Driven by
   /identity/js/breach-check.js which sets one of four state
   classes:
     .is-hidden     — initial / cleared (no visible row)
     .is-checking   — debounced lookup in flight
     .is-safe       — corpus miss, password not in HIBP
     .is-compromised — corpus hit, password in HIBP (warn + block)
     .is-unavailable — proxy returned degraded, fall back to submit-
                       time server validation (neutral state)
   Same icon-leading layout as .auth-form-help with state-specific
   colour cues — tenant-neutral so branding overrides don't make
   the safety signals fail WCAG contrast against the bg. */
.breach-check-warning {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  font-size: 0.8rem;
  line-height: 1.4;
  margin: 0.6rem 0 0;
}

.breach-check-warning.is-hidden {
  display: none;
}

.breach-check-warning i {
  flex-shrink: 0;
  margin-top: 0.15rem;
}

.breach-check-warning.is-checking,
.breach-check-warning.is-unavailable {
  color: var(--text-secondary, #6b7280);
}
.breach-check-warning.is-checking i,
.breach-check-warning.is-unavailable i {
  color: var(--text-secondary, #6b7280);
}

.breach-check-warning.is-safe {
  color: #1b6e3a;
}
.breach-check-warning.is-safe i {
  color: #1b6e3a;
}

.breach-check-warning.is-compromised {
  color: #b42318;
}
.breach-check-warning.is-compromised i {
  color: #b42318;
}

/* ── OTP Countdown ─────────────────────────────────────────────────
   Inline expiry hint rendered under the OTP entry boxes. Reuses
   .auth-form-help for layout (icon-leading, small muted text) and
   layers state styling on top: the MM:SS value carries the tenant
   accent so the live tick is visually distinct, and .is-expired
   swaps the line to a tenant-neutral warning colour. Driven by
   /identity/js/otp-countdown.js; non-JS clients see "--:--" and
   the server-enforced expiry still applies on submit. */
/* Centre the row on the card's shared axis (title / cells / Verify /
   Resend are all centred) and add a breathing-room gap above the
   Verify button — .auth-form-help's `margin: 0.6rem 0 0` was designed
   for tight-under-a-field hints (Reset, Account/Password) and leaves
   zero bottom margin, which collapses against .btn-primary in this
   page's stack. 1rem matches the .status-box / .timer-wrapper rhythm
   used elsewhere on the same card. */
.otp-countdown {
  justify-content: center;
  margin-bottom: 1rem;
}

.otp-countdown .otp-countdown-value {
  color: var(--tenant-primary, #2b5f8f);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.otp-countdown.is-expired {
  color: #b42318;
}

.otp-countdown.is-expired i {
  color: #b42318;
}

/* ── Timer (OTP) ──────────────────────────────────────────────────── */
.timer-wrapper {
  text-align: center;
  margin-top: 1rem;
  font-size: var(--font-size-md);
  color: var(--text-secondary);
}

.timer-wrapper .timer {
  color: var(--tenant-primary);
  font-weight: 600;
}

/* ── Method Selector Cards ────────────────────────────────────────── */
.method-list { list-style: none; }

.method-card {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px;
  border: 1px solid rgba(43, 95, 143, 0.12);
  border-radius: var(--radius-lg);
  cursor: pointer;
  transition: all 0.2s ease;
  background: white;
  width: 100%;
  font: inherit;
  text-align: left;
  color: inherit;
  text-decoration: none;
}

.method-card:hover {
  border-color: var(--tenant-accent);
  background: rgba(45, 138, 158, 0.03);
  box-shadow: 0 2px 8px rgba(34, 86, 130, 0.06);
}

.method-card + .method-card,
.method-list li + li { margin-top: 10px; }

.method-card-icon {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-md);
  background: rgba(45, 138, 158, 0.08);
  color: var(--tenant-accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--font-size-lg);
  flex-shrink: 0;
}

.method-card-content { flex: 1; min-width: 0; }

.method-card-title {
  font-size: var(--font-size-base);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 2px;
}

.method-card-desc {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.4;
}

.method-card-arrow {
  color: var(--slate);
  font-size: var(--font-size-sm);
  flex-shrink: 0;
  transition: transform 0.2s ease;
}

.method-card:hover .method-card-arrow {
  transform: translateX(3px);
  color: var(--tenant-accent);
}

/* ── Large Icon (passkey, error, logout) ──────────────────────────── */
.icon-large {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32px;
  margin: 0 auto 1.5rem;
}

.icon-large.accent {
  background: rgba(45, 138, 158, 0.08);
  color: var(--tenant-accent);
}

.icon-large.error {
  background: var(--error-bg);
  color: var(--error);
}

.icon-large.success {
  background: var(--success-bg);
  color: var(--success);
}

/* Passkey pulse */
.passkey-pulse {
  animation: passkey-pulse 2s ease-in-out infinite;
}

@keyframes passkey-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(45, 138, 158, 0.2); }
  50% { box-shadow: 0 0 0 16px rgba(45, 138, 158, 0); }
}

/* ── Error Page ───────────────────────────────────────────────────── */
.error-code {
  font-size: var(--font-size-xs);
  font-family: 'Courier New', monospace;
  color: var(--slate);
  text-align: center;
  padding: 8px 12px;
  background: var(--frost-grey);
  border-radius: var(--radius-sm);
  margin-top: 1rem;
  letter-spacing: 0.03em;
}

/* ── Footer ───────────────────────────────────────────────────────── */
.identity-footer {
  text-align: center;
  margin-top: 1.5rem;
  padding-top: 1rem;
  border-top: 1px solid rgba(148, 163, 184, 0.12);
}

.identity-footer-text {
  font-size: var(--font-size-xs);
  color: var(--slate);
  letter-spacing: 0.02em;
}

.identity-footer-text a {
  color: var(--tenant-accent);
  text-decoration: none;
}

.identity-footer-text a:hover { text-decoration: underline; }

/* ── Status Text (passkey) ────────────────────────────────────────── */
#status {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  text-align: center;
  margin-top: 12px;
  letter-spacing: 0.02em;
}

/* ── Responsive ───────────────────────────────────────────────────── */
@media (max-width: 576px) {
  .identity-container {
    padding: 1rem;
    align-items: flex-start;
    padding-top: 2rem;
  }

  .identity-card {
    padding: 1.75rem 1.25rem 1.5rem;
    border-radius: var(--radius-lg);
  }

  .identity-card.wide { max-width: 100%; }

  .identity-logo { font-size: var(--font-size-title); }

  .identity-page-title { font-size: var(--font-size-xl); }

  .otp-container { gap: 6px; }

  .otp-input {
    width: 38px;
    height: 44px;
    font-size: var(--font-size-xl);
  }

  .method-card {
    padding: 12px;
    gap: 12px;
  }

  .method-card-icon {
    width: 38px;
    height: 38px;
    font-size: var(--font-size-base);
  }

  .btn-group { flex-direction: column; }
}

/* ── Dark Mode — DELIBERATELY ABSENT ──────────────────────────────
   Hosted identity pages do NOT honour `prefers-color-scheme: dark`.
   BrandingConfig is the single source of truth for how these pages
   look per tenant. Matches the convention of every major enterprise
   IdP hosted login (Auth0, Okta, Entra, AWS Cognito) — the page
   renders per tenant configuration, not per end-user OS preference.

   Rationale (for the deliberate absence of OS dark-mode honouring):
     * Three dimensions (prefers-color-scheme × tenant-primary ×
       background-mode) is intractable for WCAG contrast. Example
       failure mode: a tenant with a dark-navy PrimaryColor + solid
       background mode + user on OS dark mode → card (#1e293b)
       and background (tenant-primary) collapse into the same tonal
       band, card stops reading as a floating panel.
     * Tenant branding silently overridden by OS preference is the
       OPPOSITE of what per-tenant branding is for — two tenants with
       identical branding would render differently depending on the
       user's laptop setting.
     * Hosted-login is a ~5-second brand surface, not a productivity
       app. The ergonomic win of dark mode doesn't outweigh the
       branding-predictability cost.

   If tenant-controlled dark mode is later needed, add an explicit
   `LoginTheme: "light" | "dark"` field to BrandingConfig — so the
   TENANT decides, not the end-user's OS. ────────────────────────── */

/* ── Utility ──────────────────────────────────────────────────────── */
/* Screen-reader only (visually hidden, accessible to AT) */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Reset fieldset used for OTP grouping — strip the browser's
   default fieldset chrome but keep the base .otp-container
   { margin: 1.5rem 0 } rhythm so the Verify button below has
   breathing room instead of butting against the digit boxes. */
fieldset.otp-container {
  border: none;
  padding: 0;
}

.text-center { text-align: center; }
.mt-1 { margin-top: 0.5rem; }
.mt-2 { margin-top: 1rem; }
.mt-3 { margin-top: 1.5rem; }
.mb-1 { margin-bottom: 0.5rem; }
.hidden { display: none !important; }

/* ============================================================
   Utility classes — CSP compatibility
   Replace inline style="" attributes that a strict nonce-only CSP
   (no style-src-attr 'unsafe-inline') would block. Named with the
   u- prefix to distinguish from component classes.
   ============================================================ */
.u-flex-end    { display: flex; justify-content: flex-end; }
.u-disabled    { opacity: 0.5; cursor: default; }
.u-d-contents  { display: contents; }
.u-d-inline    { display: inline; }
.u-d-none      { display: none; }
.u-text-error  { color: var(--error); }
.u-text-left   { text-align: left; }
.u-text-center { text-align: center; }
.u-no-underline { text-decoration: none; }

/* Margin utilities (extends the existing .mt-1/.mt-2/.mt-3/.mb-1
   set) — needed to strip inline `style="margin-bottom:0"` and
   `style="margin-top:…"` variants used in account subpages. */
.u-mb-0        { margin-bottom: 0; }
.u-mb-small    { margin-bottom: 0.75rem; }
.u-mt-1        { margin-top: 1rem; }

/* Empty-state icon + text — used when a list section has zero items
   (no trusted devices, no passkeys, no MFA methods enrolled, no
   security activity yet, etc.). Replaces the repeated inline
   `style="font-size:40px;color:var(--slate);margin-bottom:10px;"`
   pattern across 5+ Account subpages. Two size variants because
   the design uses 48px for the Passkeys page (single prominent
   icon) and 40px elsewhere (smaller secondary icon under a subtitle). */
.u-empty-state-icon {
  font-size: 40px;
  color: var(--slate);
  margin-bottom: 10px;
}

.u-empty-state-icon-lg {
  font-size: 48px;
  color: var(--slate);
  margin-bottom: 12px;
}

/* Section heading — bold + left-aligned. Used in MFA.cshtml to mark
   sub-sections within the page (Authenticator apps / Backup codes).
   Variants for with/without top-margin; both share bold + left-align. */
.u-section-heading {
  font-weight: 600;
  text-align: left;
}

.u-section-heading.u-mt-1 {
  margin-top: 1rem;
}

/* Caption text — smaller secondary-coloured text centred below an
   element (MFA enrol QR code hint). */
.u-caption-center {
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
  text-align: center;
}

/* QR code container — white bg + small padding so a tenant's image
   background doesn't interfere with QR scanning. */
.u-qr-box {
  padding: 8px;
  background: white;
}

/* Stepup modal size variants — the base .stepup-modal rule sets a
   width floor; these variants widen it for specific contexts
   (MFA enrol QR ~440px, backup-code grid ~460px). */
.stepup-modal.stepup-modal-md { max-width: 440px; }
.stepup-modal.stepup-modal-lg { max-width: 460px; }

/* ═══════════════════════════════════════════════════════════════════
   Self-service identity pages

   Ported from /GUID/identity mockup after user sign-off. Additive only:
   no existing rule above this block is touched. Every class below is
   scoped to the new self-service screens:
     /Identity/Account/*  — Profile, Password, Passkeys, MFA, Sessions,
                            TrustedDevices, LoginPreferences, Security
     /Identity/Forgot     — password-reset request
     /Identity/Reset      — password-reset consume
     /Identity/VerifyEmail — email-verification landing

   Tokens: the source mockup uses --brand-* names (Aegis palette).
   They are rewritten here to --tenant-* so per-tenant branding set
   via BrandingConfig → Login.cshtml's inline <style> block overrides
   them at runtime. Only --tenant-primary + --tenant-accent are
   per-tenant; other tokens (radii, typography, text colours) remain
   global.
   ═══════════════════════════════════════════════════════════════════ */

/* ── Identity page subtitle (below .identity-page-title) ────────── */
.identity-page-subtitle {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  text-align: center;
  margin-bottom: 1.5rem;
  letter-spacing: 0.02em;
}

/* ── Input error state (forgot.html / reset.html etc.) ──────────── */
.auth-form-input.has-error {
  border-color: var(--error);
  background: var(--error-bg);
}

/* ── Main large CTA button (primary action on account-* pages) ──── */
.main-large-btn {
  width: 100%;
  padding: 12px 20px;
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: var(--font-size-base);
  font-weight: 500;
  color: white;
  background: var(--tenant-primary);
  border: none;
  cursor: pointer;
  transition: all 0.15s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.main-large-btn:hover {
  background: var(--tenant-accent);
}

.main-large-btn:active {
  transform: translateY(1px);
}

.main-large-btn:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.main-large-btn .btn-loader { display: none; }
.main-large-btn.loading .btn-text { display: none; }
.main-large-btn.loading .btn-loader { display: inline-flex; }

/* ── Secondary outline button (non-primary actions) ─────────────── */
.secondary-btn {
  width: 100%;
  padding: 12px 20px;
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: var(--font-size-base);
  font-weight: 500;
  color: var(--text-primary);
  background: white;
  border: 1px solid var(--silver-mist);
  cursor: pointer;
  transition: all 0.15s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  text-decoration: none;
}

.secondary-btn:hover {
  background: var(--frost-grey);
  border-color: rgba(43, 95, 143, 0.3);
}

/* ── Danger button (destructive — revoke all, delete account, etc) */
.danger-btn {
  width: 100%;
  padding: 12px 20px;
  border-radius: var(--radius-md);
  font-family: inherit;
  font-size: var(--font-size-base);
  font-weight: 500;
  color: white;
  background: var(--error);
  border: none;
  cursor: pointer;
  transition: all 0.15s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.danger-btn:hover {
  background: #8a3837;
}

/* ── Text-link button — destructive action styled as a quiet text link
   for two-tier visual weight next to a primary destructive button. The
   primary button (.danger-btn) carries the affordance signal; this
   variant demotes itself so the eye lands on the primary first and the
   secondary doesn't compete. Destructive intent is conveyed via the
   copy + the confirm() dialog the click handler invokes — the visual
   restraint avoids "two equally-prominent red buttons" decision-
   paralysis on a page where only one of the two actions matches what
   the user actually came here to do. Used by /Identity/Account/Sessions
   for the "Sign out everywhere, including this device" affordance
   sitting below the primary "Sign out of all other devices" button. */
.text-link-btn {
  background: none;
  border: none;
  padding: 6px 0;
  margin: 8px auto 0;
  font-family: inherit;
  cursor: pointer;
  color: var(--slate);
  font-size: var(--font-size-sm);
  font-weight: 400;
  letter-spacing: 0.05em;
  transition: color 0.2s ease;
  display: block;
}
.text-link-btn:hover {
  color: var(--tenant-accent);
  text-decoration: underline;
}
.text-link-btn:focus-visible {
  outline: 2px solid var(--tenant-primary);
  outline-offset: 2px;
}

/* ── Form divider with inline label text (e.g. "OR") ────────────── */
.form-divider-text {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 1.25rem 0;
  color: var(--text-secondary);
  font-size: var(--font-size-xs);
  letter-spacing: 0.1em;
  text-transform: uppercase;
}

.form-divider-text::before,
.form-divider-text::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--silver-mist);
}

/* ── Wider card variant for account-* screens ───────────────────── */
/* 640px instead of the default 440px login card — list content like
   sessions + trusted devices needs horizontal room. */
.identity-card.account-wide { max-width: 640px; }

/* ── Account header (shown on every account-*.html) ─────────────── */
.account-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1.5rem;
}

.account-header-user {
  display: flex;
  align-items: center;
  gap: 10px;
}

.account-avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--tenant-accent), var(--tenant-primary));
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: var(--font-size-md);
}

.account-user-name {
  font-size: var(--font-size-md);
  font-weight: 600;
  color: var(--text-primary);
  line-height: 1.2;
}

.account-user-email {
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
}

/* Shared base for every header-row action anchor (Sign out,
   Back-to-{AppName}). Neutral slate colour + inline-flex layout;
   each concrete variant (.account-signout-link, .back-link-exit) adds
   its own hover colour. An earlier revision applied .account-signout-link
   to the "Back to {AppName}" link as well, which forced an override
   to suppress the red hover — unwanted coupling between unrelated
   semantic roles. Splitting the base out lets each link carry its own
   hover without fighting another class. */
.account-header-action-link {
  font-size: var(--font-size-sm);
  color: var(--slate);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  transition: color 0.2s ease;
}

/* Sign-out link — same shape as the base header-action link, plus
   error-red hover. Kept standalone (not extending the base class via
   HTML) for backwards-compat with existing markup and because sign-out
   has a distinct semantic that warrants an explicit class. */
.account-signout-link {
  font-size: var(--font-size-sm);
  color: var(--slate);
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  transition: color 0.2s ease;
}

.account-signout-link:hover { color: var(--error); }

/* Cluster for the top-right action group on /Account (hub)
   — Back-to-{AppName} (when RP-launched) + Sign out. Flex-row with
   gap so the two links sit neatly beside each other. Each link now
   carries its own hover colour via the class that semantically fits
   its role (.back-link-exit → tenant-accent; .account-signout-link →
   error red). The cross-class override that previously lived here
   was removed — see .account-header-action-link rationale above. */
.account-header-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* ── Account hub grid (account.html) ────────────────────────────── */
/* 2-up grid of navigation cards. Reuses .method-card styling for the
   card body but lays them in a grid rather than a vertical list. */
.account-hub-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px;
}

@media (max-width: 480px) {
  .account-hub-grid { grid-template-columns: 1fr; }
}

/* ── Filter chips (Security activity page) ─────────────────────── */
/* Horizontal pill-shaped filter bar — compact alternative to the
   full-width .secondary-btn stack that filter-chip extends. Earlier
   iterations used inline `style="width:auto;padding:6px 12px"` on
   each button but the nonce-based CSP (`style-src 'self' 'nonce-...'`)
   strips inline style attributes, so those overrides never applied
   and every chip rendered at .secondary-btn's `width: 100%`, taking
   a full vertical row per filter. Moving the overrides here puts
   them under the `'self'` stylesheet allowance so CSP permits them.
   Specificity: .secondary-btn.filter-chip is 0,0,2,0 — wins over the
   bare .secondary-btn rule regardless of source order. */
#filter-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 1.25rem;
}

.secondary-btn.filter-chip {
  width: auto;
  flex: 0 0 auto;
  padding: 6px 14px;
  font-size: var(--font-size-sm);
  border-radius: 999px;          /* pill shape */
}

.secondary-btn.filter-chip.selected {
  background: var(--tenant-accent);
  color: white;
  border-color: var(--tenant-accent);
}

.secondary-btn.filter-chip.selected:hover {
  background: var(--tenant-accent);
  /* keep selected hover identical to selected resting — users expect
     the active filter to not invert on hover like a normal chip would */
}

/* ── Password complexity rules (reset.html, account-password.html) */
.password-rules {
  margin-top: 10px;
  padding: 10px 14px;
  background: var(--frost-grey);
  border-radius: var(--radius-sm);
  list-style: none;
}

.password-rules li {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  padding: 3px 0;
  display: flex;
  align-items: center;
  gap: 8px;
  transition: color 0.2s ease;
}

.password-rules li::before {
  content: '\f00d';                 /* fa-xmark */
  /* The vendored Font Awesome bundle declares
     font-family: var(--fa-style-family, "Font Awesome 6 Pro").
     Hardcoding "Font Awesome 6 Free" silently tofu'd every chip
     marker because the loaded webfont doesn't register under
     that family name. Defer to the same CSS variable the FA
     bundle reads so the chip survives FA version upgrades. */
  font-family: var(--fa-style-family, 'Font Awesome 6 Pro');
  font-weight: 900;
  /* 14px (was 10px) gives the icon enough scale that the
     tri-state legend (xmark unmet / check met / info server-only)
     registers at-glance — WCAG 1.4.1 non-colour redundancy is
     only effective if the redundancy is actually perceptible. */
  font-size: 14px;
  color: var(--slate);
  width: 16px;
  text-align: center;
  transition: color 0.2s ease;
}

.password-rules li.ok { color: var(--success); }

.password-rules li.ok::before {
  content: '\f00c';                 /* fa-check */
  color: var(--success);
}

/* Server-only rules (e.g. password-history reuse) — checked by
   the server at submit, not by the client live evaluator. The
   default rule appearance uses an x-mark "not satisfied yet"
   marker which would be misleading here (the rule isn't
   "unsatisfied" so much as "not yet checkable"). Replace the
   marker with a neutral info icon. Text colour stays inherited
   from .password-rules li (var(--text-secondary)) since the
   default already matches the desired neutral treatment for an
   unverified rule. */
.password-rules li.server-only::before {
  content: '\f05a';                 /* fa-info-circle */
  color: var(--text-secondary);
}

/* ── Sessions list (account-sessions.html, trusted-devices, passkeys) */
.session-card {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  padding: 14px 16px;
  border: 1px solid rgba(43, 95, 143, 0.12);
  border-radius: var(--radius-lg);
  margin-bottom: 10px;
  background: white;
  transition: border-color 0.2s ease;
}

.session-card.current {
  border-color: var(--tenant-accent);
  background: rgba(45, 138, 158, 0.04);
}

.session-card-icon {
  width: 38px;
  height: 38px;
  border-radius: var(--radius-md);
  background: rgba(45, 138, 158, 0.08);
  color: var(--tenant-accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--font-size-lg);
  flex-shrink: 0;
}

.session-card-body { flex: 1; min-width: 0; }

.session-card-title {
  font-size: var(--font-size-base);
  font-weight: 600;
  color: var(--text-primary);
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 4px;
}

.session-badge {
  font-size: var(--font-size-xxs);
  font-weight: 600;
  color: var(--tenant-accent);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  background: rgba(45, 138, 158, 0.1);
  padding: 2px 8px;
  border-radius: 10px;
}

.session-card-meta {
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
  line-height: 1.5;
}

.session-card-meta .meta-row { display: block; }

.session-revoke-btn {
  background: none;
  border: none;
  color: var(--error);
  font-size: var(--font-size-sm);
  font-family: inherit;
  cursor: pointer;
  padding: 6px 10px;
  border-radius: var(--radius-sm);
  transition: background 0.2s ease;
  flex-shrink: 0;
}

.session-revoke-btn:hover { background: var(--error-bg); }

/* ── Security audit timeline (account-security.html) ────────────── */
.audit-timeline {
  list-style: none;
  position: relative;
}

.audit-timeline::before {
  content: '';
  position: absolute;
  left: 17px;
  top: 10px;
  bottom: 10px;
  width: 1px;
  background: rgba(148, 163, 184, 0.2);
}

.audit-event {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 10px 0;
  position: relative;
}

.audit-event-icon {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--frost-grey);
  color: var(--slate);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--font-size-sm);
  flex-shrink: 0;
  z-index: 1;
  border: 2px solid white;
}

.audit-event-icon.login    { background: var(--success-bg); color: var(--success); }
.audit-event-icon.password { background: var(--blue-bg); color: var(--tenant-primary); }
.audit-event-icon.passkey  { background: rgba(45, 138, 158, 0.12); color: var(--tenant-accent); }
.audit-event-icon.mfa      { background: rgba(45, 138, 158, 0.12); color: var(--tenant-accent); }
.audit-event-icon.denied   { background: var(--error-bg); color: var(--error); }

.audit-event-body { flex: 1; min-width: 0; padding-top: 4px; }

.audit-event-title {
  font-size: var(--font-size-sm);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 2px;
}

.audit-event-meta {
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
  line-height: 1.5;
}

/* ── MFA device list + QR enrollment (account-mfa.html) ─────────── */
.mfa-device-list { list-style: none; margin-bottom: 1rem; }

.mfa-device {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px;
  border: 1px solid rgba(43, 95, 143, 0.12);
  border-radius: var(--radius-md);
  margin-bottom: 8px;
}

.mfa-device-icon {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-sm);
  background: rgba(45, 138, 158, 0.08);
  color: var(--tenant-accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--font-size-base);
  flex-shrink: 0;
}

.mfa-device-body { flex: 1; min-width: 0; }

.mfa-device-title {
  font-size: var(--font-size-base);
  font-weight: 500;
  color: var(--text-primary);
}

.mfa-device-meta {
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
}

/* QR-code + manual-key panel during TOTP enrollment. */
.mfa-qr-panel {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 20px;
  background: var(--frost-grey);
  border-radius: var(--radius-lg);
  margin: 1rem 0;
}

/* Intentionally white in BOTH light and dark modes — authenticator-app
   QR scanners expect dark modules on a light background. No dark-mode
   override for this rule. */
.mfa-qr-code {
  width: 180px;
  height: 180px;
  background: white;
  border: 1px solid var(--silver-mist);
  border-radius: var(--radius-sm);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--slate);
  font-size: var(--font-size-xs);
  letter-spacing: 0.05em;
}

.mfa-manual-key {
  font-family: 'Courier New', monospace;
  font-size: var(--font-size-sm);
  background: white;
  padding: 6px 10px;
  border: 1px solid var(--silver-mist);
  border-radius: var(--radius-sm);
  letter-spacing: 0.15em;
  color: var(--text-primary);
  user-select: all;
}

/* Backup codes — 5×2 grid of monospace tokens. */
.backup-codes-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 6px;
  margin: 0.75rem 0;
}

.backup-code {
  font-family: 'Courier New', monospace;
  font-size: var(--font-size-sm);
  background: var(--frost-grey);
  padding: 8px 10px;
  border-radius: var(--radius-sm);
  text-align: center;
  letter-spacing: 0.1em;
  user-select: all;
}

/* ── Step-up auth modal (delete passkey, remove MFA, etc) ───────── */
.stepup-backdrop {
  position: fixed; inset: 0;
  background: rgba(15, 42, 61, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 1rem;
}

.stepup-modal {
  background: white;
  border-radius: var(--radius-xl);
  padding: 1.75rem 1.5rem 1.25rem;
  width: 100%;
  max-width: 380px;
  box-shadow: 0 20px 60px rgba(15, 42, 61, 0.2);
}

.stepup-modal-title {
  font-size: var(--font-size-xl);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 0.25rem;
  text-align: center;
}

.stepup-modal-desc {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  text-align: center;
  margin-bottom: 1.25rem;
}

/* ── Verify-email state badges (verify-email.html) ──────────────── */
.verify-email-status {
  text-align: center;
  padding: 1.5rem 0 0.5rem;
}

.verify-email-status .icon-large {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 30px;
  margin: 0 auto 1rem;
}

.verify-email-status.pending .icon-large {
  background: var(--blue-bg);
  color: var(--tenant-primary);
}

.verify-email-status.success .icon-large {
  background: var(--success-bg);
  color: var(--success);
}

.verify-email-status.failed .icon-large {
  background: var(--error-bg);
  color: var(--error);
}

.verify-email-status-title {
  font-size: var(--font-size-xl);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 0.25rem;
}

.verify-email-status-desc {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.5;
  margin-bottom: 1.25rem;
}

/* Large logout/redirect-state icon (also reused by Forgot "email
   sent" confirmation screen). */
.logout-icon-large {
  width: 72px;
  height: 72px;
  border-radius: 50%;
  background: var(--blue-bg);
  color: var(--tenant-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 30px;
  margin: 0 auto 1rem;
}

/* ── Login preferences — grouping + radio cards + toggle switch ─── */
/* Grouping wrapper: one per preference block (factor, OTP channel,
   passwordless). */
.pref-group {
  padding: 14px 0;
  border-bottom: 1px solid rgba(148, 163, 184, 0.15);
}
.pref-group:last-child { border-bottom: none; }

.pref-group-title {
  font-size: var(--font-size-base);
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 4px;
  display: flex;
  align-items: center;
  gap: 8px;
}

.pref-group-desc {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.5;
  margin-bottom: 12px;
}

/* "Coming soon" chip for roadmap-only controls. Distinguishes stubs
   from functional controls. */
.soon-chip {
  display: inline-block;
  font-size: var(--font-size-xxs);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--warning);
  background: var(--warning-bg);
  padding: 2px 8px;
  border-radius: 10px;
}

/* Radio-card selector — stack of full-width rows; selected row gets
   accent border + subtle fill. */
.radio-card {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  padding: 12px 14px;
  border: 1px solid rgba(43, 95, 143, 0.12);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease;
  margin-bottom: 8px;
  background: white;
}

.radio-card:hover {
  border-color: rgba(43, 95, 143, 0.3);
}

.radio-card.selected {
  border-color: var(--tenant-accent);
  background: rgba(45, 138, 158, 0.04);
}

.radio-card.disabled {
  opacity: 0.55;
  cursor: not-allowed;
}

.radio-card input[type="radio"] {
  margin-top: 3px;
  accent-color: var(--tenant-accent);
}

.radio-card-body { flex: 1; min-width: 0; }

.radio-card-title {
  font-size: var(--font-size-base);
  font-weight: 500;
  color: var(--text-primary);
  display: flex;
  align-items: center;
  gap: 8px;
}

.radio-card-meta {
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
  margin-top: 2px;
}

.radio-card-meta a { color: var(--tenant-accent); }

/* Toggle switch (on/off) — for security-alerts pref. */
.pref-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 0;
}

.pref-toggle-label { flex: 1; min-width: 0; }

.pref-toggle-title {
  font-size: var(--font-size-base);
  font-weight: 500;
  color: var(--text-primary);
  display: flex;
  align-items: center;
  gap: 8px;
}

.pref-toggle-desc {
  font-size: var(--font-size-xs);
  color: var(--text-secondary);
  line-height: 1.4;
  margin-top: 2px;
}

.pref-switch {
  position: relative;
  display: inline-block;
  width: 42px;
  height: 24px;
  flex-shrink: 0;
}

.pref-switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

.pref-switch-slider {
  position: absolute;
  inset: 0;
  background: rgba(148, 163, 184, 0.35);
  border-radius: 12px;
  cursor: pointer;
  transition: background 0.2s ease;
}

.pref-switch-slider::before {
  content: '';
  position: absolute;
  width: 18px;
  height: 18px;
  left: 3px;
  top: 3px;
  background: white;
  border-radius: 50%;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
  transition: transform 0.2s ease;
}

.pref-switch input:checked + .pref-switch-slider {
  background: var(--tenant-accent);
}

.pref-switch input:checked + .pref-switch-slider::before {
  transform: translateX(18px);
}

.pref-switch input:disabled + .pref-switch-slider {
  opacity: 0.5;
  cursor: not-allowed;
}

/* Keyboard focus indicator — the <input type="checkbox"> is visually
   hidden (0×0, opacity 0) so the browser's default focus ring lives
   where nobody can see it. Mirror focus to the slider via sibling
   combinator so tab-stops on this toggle remain visible.
   WCAG 2.1 AA — 2.4.7 Focus Visible. */
.pref-switch input:focus-visible + .pref-switch-slider {
  outline: 2px solid var(--tenant-accent);
  outline-offset: 2px;
  box-shadow: 0 0 0 3px rgba(45, 138, 158, 0.2);
}

/* ── Consent screen (/Identity/Consent) ─────────────────────────── */
/* Used by Consent.cshtml — the explicit-consent screen the IdP shows
   when a ConsentType=explicit OIDC client requests authorization and
   the user has no covering valid OidcAuthorization. Visual goals:
     * Render the requested scopes prominently — the user must be able
       to see exactly what they're granting before clicking Allow.
     * Match the rest of the identity-card visual language so users
       don't experience a brand-shift mid-flow.
     * Keep the button row consistent with the .btn-group / .btn-primary
       / .btn-secondary primitives used elsewhere — no consent-specific
       button variants. */
.consent-scopes {
  margin: 1.5rem 0;
  padding: 1rem 1.25rem;
  background: var(--frost-grey, #f7f9fc);
  border-radius: var(--radius-md);
  border: 1px solid rgba(43, 95, 143, 0.08);
}

.consent-scopes-heading {
  margin: 0 0 0.75rem 0;
  font-size: var(--font-size-md);
  font-weight: 600;
  color: var(--text-primary);
}

.consent-scope-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.consent-scope-item {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
}

.consent-scope-icon {
  color: var(--tenant-accent);
  margin-top: 4px;
  flex-shrink: 0;
}

.consent-scope-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.consent-scope-name {
  font-weight: 500;
  color: var(--text-primary);
  font-size: var(--font-size-base);
}

.consent-scope-description {
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  line-height: 1.4;
}

.consent-footer-note {
  margin-top: 1.5rem;
  font-size: var(--font-size-sm);
  color: var(--text-secondary);
  text-align: center;
}

.consent-footer-note a {
  color: var(--tenant-accent);
  text-decoration: none;
}

.consent-footer-note a:hover {
  text-decoration: underline;
}

/* ── Utility additions ──────────────────────────────────────────── */
.mb-2 { margin-bottom: 1rem; }

/* .fa-spin and @keyframes spin DELIBERATELY absent — the vendor-provided
   /identity/vendor/fontawesome/css/all.min.css defines .fa-spin with its
   own fa-spin keyframes + CSS custom properties (--fa-animation-duration,
   --fa-animation-direction, etc.) + prefers-reduced-motion handling.
   Overriding it here would clobber all of that and block future FA
   upgrades. Rely on the vendor definition. If we ever need a
   project-specific spin utility, use a different class name (e.g.
   .u-spin) — don't shadow vendor classes. */

/* ── Trust-device opt-in checkbox ───────────────────────────────────
   The "Trust this device for N days" checkbox that appears on
   MfaChallenge + PasskeyChallenge post-verification. Native inputs
   only (matches identity's no-invented-primitives style), but the
   label needs a two-line layout — bold label above muted help text —
   which browser defaults do not give. Under the identity CSP
   (`style-src 'self' 'nonce-...'` with no `unsafe-inline`), inline
   `style="..."` attributes on individual elements are stripped, so
   the layout MUST live in this file.

   Structure:
     <label class="auth-trust-check">
       <input type="checkbox" ... />
       <span class="auth-trust-check-body">
         <span class="auth-trust-check-title">Trust this device for N days</span>
         <span class="auth-trust-check-hint">Skip verification…</span>
       </span>
     </label>
*/
.auth-trust-check {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  margin: 1rem 0;
  cursor: pointer;
  font-size: 0.95em;
}

.auth-trust-check > input[type="checkbox"] {
  /* Small vertical nudge so the box sits on the title's baseline,
     not the top of the label bounding box. flex-shrink stops it from
     collapsing when the hint line wraps. */
  margin-top: 0.2em;
  flex-shrink: 0;
}

.auth-trust-check-body {
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}

.auth-trust-check-title {
  font-weight: 500;
  color: var(--text-primary, #1a1a1a);
}

.auth-trust-check-hint {
  font-size: 0.85em;
  color: var(--text-muted, #666);
  line-height: 1.35;
}

/* ── "This device" badge on the Trusted-Devices management page ──
   Pill badge appended to the DeviceName heading on the row whose
   device_token_hash matches the current browser's `tdv` cookie —
   populated by DbUp 581 + the ISessionsAdminService overload. */
.trust-device-current-badge {
  display: inline-block;
  margin-left: 0.5rem;
  padding: 0.125rem 0.55rem;
  border-radius: 999px;
  font-size: 0.75em;
  font-weight: 500;
  color: var(--success, #4a7052);
  background: var(--success-bg, #e9f3ec);
  vertical-align: middle;
}
.trust-device-current-badge i {
  margin-right: 0.25rem;
}

/* ── Dark Mode — DELIBERATELY ABSENT (see earlier comment block) ──
   The self-service late-defined components (session-card / radio-card /
   mfa-manual-key / stepup-modal / secondary-btn / audit-event-icon)
   used to have a matching end-of-file @media (prefers-color-scheme:
   dark) block here. Removed — see rationale above. ───── */
