/* ═══════════════════════════════════════════════════════════════════
   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; }

.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;
}

.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; }

/* 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);
}

/* ── 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. */
.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;
}

/* ── 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 */
  font-family: 'Font Awesome 6 Free';
  font-weight: 900;
  font-size: 10px;
  color: var(--slate);
  width: 14px;
  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 passwordless / 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. */

/* ── 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. ───── */
