/* Trinora ID auth pages — Immos design system (login redesign handoff,
   2026-09-28). Hand-written, no build step: the immos-design-system tokens
   and components from the Sign In mockup, recreated for server-rendered
   Django templates. The pre-2026 class vocabulary (btn, field, links, …)
   is kept so every auth page re-skins through this file alone.

   Deliberate deviation from the mockup: inputs rest on --border-strong
   (≈3:1 against the card) instead of --border-default — the field-contrast
   decision from plan/01 §4 Step 9 carries over. Dark mode follows
   prefers-color-scheme with the design system's dark palette. */

@import url('https://fonts.googleapis.com/css2?family=Archivo:wght@400;500;600;700;800&display=swap');
@import url('https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;0,700;1,400&display=swap');

:root {
  /* Basalt neutrals (cool slate ramp) */
  --basalt-50: #f6f7f9;
  --basalt-100: #eceef2;
  --basalt-200: #d8dde4;
  --basalt-300: #b9c0cc;
  --basalt-400: #8b94a3;
  --basalt-500: #66707f;
  --basalt-600: #4b5563;
  --basalt-700: #363e4a;
  --basalt-800: #232a33;
  --basalt-900: #161b21;
  --basalt-950: #0d1116;
  --white: #ffffff;

  /* Teal — signature primary */
  --teal-100: #c3ede6;
  --teal-300: #5accbb;
  --teal-500: #0e9c8a;
  --teal-600: #0a7e70;
  --teal-700: #0a645a;

  /* Semantic hues */
  --green-100: #d9f2e3; --green-500: #1e9e5a; --green-600: #158048;
  --amber-100: #fbeecb; --amber-600: #b47f0e;
  --red-100: #fbdedd;   --red-500: #d83a3a;   --red-600: #bd2c2c; --red-700: #96201f;
  --blue-100: #dbe7fb;  --blue-600: #2159c1;

  /* Accent (default palette: teal) */
  --accent-tint: #c3ede6;
  --accent: #0e9c8a;
  --accent-hover: #0a7e70;
  --accent-strong: #0a645a;
  --accent-on: #ffffff;
  --accent-ring: rgba(14, 156, 138, 0.4);

  /* Semantic aliases — surfaces, text, borders, status */
  --surface-page: var(--basalt-50);
  --surface-card: var(--white);
  --surface-sunken: var(--basalt-100);
  --text-strong: var(--basalt-900);
  --text-body: var(--basalt-700);
  --text-muted: var(--basalt-500);
  --text-subtle: var(--basalt-400);
  --text-link: var(--accent-hover);
  --border-subtle: var(--basalt-200);
  --border-default: var(--basalt-300);
  --border-strong: var(--basalt-400);
  --divider: var(--basalt-200);
  --status-success: var(--green-600); --status-success-bg: var(--green-100);
  --status-warning: var(--amber-600); --status-warning-bg: var(--amber-100);
  --status-danger:  var(--red-600);   --status-danger-bg:  var(--red-100);
  --status-info:    var(--blue-600);  --status-info-bg:    var(--blue-100);

  /* Elevation — subtle, slate-tinted; borders over drop shadows */
  --shadow-sm: 0 1px 2px rgba(13, 17, 22, 0.06), 0 2px 4px rgba(13, 17, 22, 0.05);
  --shadow-lg: 0 4px 8px rgba(13, 17, 22, 0.06), 0 14px 28px rgba(13, 17, 22, 0.12);
  --shadow-focus: 0 0 0 3px var(--accent-ring);

  /* Type */
  --font-display: "Archivo", ui-sans-serif, system-ui, sans-serif;
  --font-sans: "IBM Plex Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;

  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root {
    --surface-page: var(--basalt-950);
    --surface-card: var(--basalt-900);
    --surface-sunken: #0a0d11;
    --text-strong: var(--basalt-50);
    --text-body: var(--basalt-200);
    --text-muted: var(--basalt-400);
    --text-subtle: var(--basalt-500);
    --text-link: var(--teal-300);
    --border-subtle: #2a333e;
    --border-default: var(--basalt-700);
    --border-strong: var(--basalt-600);
    --divider: #2a333e;
    --status-success: #4ec27e; --status-success-bg: #133021;
    --status-warning: #e8b84a; --status-warning-bg: #33280d;
    --status-danger:  #ec6a6a; --status-danger-bg:  #331616;
    --status-info:    #6f9ff0; --status-info-bg:    #14243f;
    --accent-tint: #133038;
    --shadow-lg: 0 4px 8px rgba(0, 0, 0, 0.35), 0 14px 28px rgba(0, 0, 0, 0.5);
    color-scheme: dark;
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--surface-page);
  color: var(--text-body);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* Text links tint on hover; component links (nav tabs, buttons, …) keep
   their own colors. :where() zeroes the specificity so a bare `a:hover`
   cannot out-vote class rules — it used to recolor the white nav-tab
   text/icon into the teal bar's hover shade (≈1.2:1) on hover. */
:where(a) { color: var(--text-link); text-decoration: none; }
:where(a:hover) { color: var(--accent-strong); text-decoration: underline; text-underline-offset: 2px; }
@media (prefers-color-scheme: dark) {
  :where(a:hover) { color: var(--text-link); }
}

/* Page shell: centered column, footer pinned to the bottom. */
.wrap {
  min-height: 100vh;
  display: grid;
  grid-template-rows: 1fr auto;
  box-sizing: border-box;
  padding: 24px;
}
.center { display: grid; place-items: center; }
.box {
  width: 100%;
  max-width: 400px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.foot {
  display: flex;
  justify-content: center;
  /* Baseline (not the default stretch): the switcher's summary carries
     vertical padding for its focus ring, which would otherwise sit its
     text ~2px lower than the Help/Privacy/Terms links. */
  align-items: baseline;
  gap: 20px;
  font-size: 13px;
  color: var(--text-muted);
  flex-wrap: wrap;
}
.foot a { color: var(--text-muted); }
.foot-nav { display: contents; }
/* Anonymous language switcher (plan/09 §1.4): a footer <details> disclosure
   collapsed to the current language's endonym; the menu drops UP (the footer
   sits at the viewport bottom). :where() keeps specificity below every
   authoring rule (as ever). */
.foot-lang { position: relative; }
.foot-lang-summary {
  display: inline-flex;
  align-items: center;
  padding: 2px 6px;
  border-radius: 6px;
  color: var(--text-muted);
  cursor: pointer;
  list-style: none;
}
.foot-lang-summary::-webkit-details-marker { display: none; }
:where(.foot-lang-summary):hover { color: var(--text-body); }
:where(.foot-lang-summary):focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}
.foot-lang[open] .foot-lang-summary { color: var(--text-body); }
.foot-lang-caret {
  width: 6px;
  height: 6px;
  margin-inline-start: 7px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg);
  transition: transform 0.15s ease;
}
.foot-lang[open] .foot-lang-caret { transform: rotate(-135deg); }
.foot-lang-menu {
  position: absolute;
  bottom: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  min-width: 11rem;
  padding: 6px;
  display: grid;
  gap: 2px;
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: 10px;
  box-shadow: var(--shadow-lg);
  z-index: 10;
}
.foot-lang-btn {
  background: none;
  border: 0;
  padding: 7px 10px;
  font: inherit;
  text-align: left;
  color: var(--text-body);
  border-radius: 7px;
  cursor: pointer;
}
:where(.foot-lang-btn):hover {
  background: var(--surface-sunken);
  color: var(--text-strong);
}
:where(.foot-lang-btn):focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}
:where(.foot-lang-btn)[aria-current="true"] {
  color: var(--text-strong);
  font-weight: 600;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 18px;
  color: var(--text-strong);
}
/* The seal (design handoff "Seal Refined") — the artwork carries its own
   palette, so no tile background; sized square like the T-tile it replaced. */
.brand-mark {
  display: block;
  width: 32px;
  height: 32px;
}
/* Wordmark per the handoff lockup: "immos" in text-strong, "id" in the
   accent; dark mode lightens the accent half to teal-300 for contrast.
   The auth shell keeps the word space; the topbar lockup sits flush
   (no space in its markup — user call, 2026-10-03). */
.brand-name { letter-spacing: -0.02em; }
.brand-id { color: var(--accent); }
@media (prefers-color-scheme: dark) {
  .brand-id { color: var(--teal-300); }
}

/* The card sits below the brand row; the 3px accent top is the brand moment. */
.card {
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-top: 3px solid var(--accent);
  border-radius: 12px;
  padding: 32px;
  color: var(--text-body);
  box-shadow: var(--shadow-sm);
}
.card > :last-child { margin-bottom: 0; }

h1 {
  font-family: var(--font-display);
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--text-strong);
  margin: 0;
}
h2 { font-size: 1rem; margin: 1.5rem 0 0.5rem; color: var(--text-strong); }

p { margin: 0 0 1rem; }
.muted { color: var(--text-muted); font-size: 14px; margin: 6px 0 0; text-wrap: pretty; }
.hint, .footnote { color: var(--text-muted); font-size: 14px; }
.page-head { margin-bottom: 24px; }
.page-head h1 { margin-bottom: 6px; }
.page-head p { margin: 0; }

/* Session messages render as design-system alerts. */
.messages { list-style: none; margin: 0 0 1rem; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.messages li {
  position: relative;
  border: 1px solid transparent;
  border-radius: 8px;
  padding: 12px 12px 12px 40px;
  font-size: 14px;
  line-height: 1.5;
  overflow-wrap: anywhere;
}
/* The flash icons are Lucide marks inlined as CSS masks (CSS can't read the
   sprite) — path data mirrors the vendored files: lucide-static 1.55.0
   circle-alert / circle-check. Masks paint alpha only; the stroke color
   literal is irrelevant, recoloring happens via background: currentColor. */
.messages li::before {
  content: "";
  position: absolute;
  left: 12px;
  top: 12px;
  width: 16px;
  height: 16px;
  background: currentColor;
  mask: center / contain no-repeat
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cline x1='12' x2='12' y1='8' y2='12'/%3E%3Cline x1='12' x2='12.01' y1='16' y2='16'/%3E%3C/svg%3E");
}
.messages .success {
  background: var(--status-success-bg);
  border-color: rgba(21, 128, 72, 0.3);
  color: var(--status-success);
}
.messages .success::before {
  mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='m16 9-5.5 5.5L8 12'/%3E%3C/svg%3E");
}
.messages .error {
  background: var(--status-danger-bg);
  border-color: rgba(189, 44, 44, 0.3);
  color: var(--status-danger);
}
.messages .warning {
  background: var(--status-warning-bg);
  border-color: rgba(180, 127, 14, 0.35);
  color: var(--status-warning);
}
.messages .info, .messages .debug {
  background: var(--status-info-bg);
  border-color: rgba(33, 89, 193, 0.3);
  color: var(--status-info);
}

.banner {
  border-radius: 8px;
  padding: 12px 16px;
  font-size: 14px;
  margin-bottom: 1rem;
}
.banner.warning { background: var(--status-warning-bg); color: var(--status-warning); }

/* Fields. Forms are the rhythm-keeper: 16px between blocks, 8px inside. */
form { display: flex; flex-direction: column; gap: 16px; }
form .field { min-width: 0; width: 100%; }
.field { display: flex; flex-direction: column; gap: 8px; margin: 0; }
.field label { font-size: 14px; font-weight: 500; color: var(--text-strong); }
/* A hint's paragraph margin would stack on the field gap below it (its box
   extends the .field the grid gap then adds to), doubling the row rhythm. */
.field .hint { margin: 0; }

input[type="text"], input[type="email"], input[type="password"],
input[type="url"], input[type="tel"], input[type="number"],
select, textarea {
  width: 100%;
  min-width: 0;
  height: 40px;
  padding: 0 12px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--surface-card);
  color: var(--text-strong);
  font: inherit;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
  -webkit-appearance: none;
  appearance: none;
}
/* Long preset labels ("Automatic (25 Sep 2026)") clip in narrow columns —
   degrade to an ellipsis, never a sliced glyph. */
select { text-overflow: ellipsis; }
textarea { height: auto; padding: 8px 12px; }
select {
  padding-right: 36px;
  /* Lucide chevron-down (lucide-static 1.55.0), inlined: background-image
     can't reach the sprite, and the color literal stands in for a fixed
     muted gray — selects can't recolor a background image. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2366707f' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 16px;
}
::placeholder { color: var(--text-subtle); opacity: 1; }
::selection { background: var(--accent-tint); color: var(--text-strong); }

input:hover, select:hover, textarea:hover { border-color: var(--basalt-500); }

input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--shadow-focus);
}

.has-error input, .has-error select, .has-error textarea {
  border-color: var(--status-danger);
  box-shadow: 0 0 0 3px rgba(189, 44, 44, 0.3);
}
.field-error { color: var(--status-danger); font-size: 12px; margin: 0; }
.field--mono input { font-family: var(--font-mono); letter-spacing: 0.08em; }

/* Password field with the show/hide affordance inside the input's corner. */
.pw-input { position: relative; display: flex; }
.pw-input input { padding-right: 44px; }
.pw-eye {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: none;
  border-radius: 8px;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
  padding: 0;
}
.pw-eye:hover { background: var(--surface-sunken); color: var(--text-strong); }
.pw-eye:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.pw-eye svg { display: block; }
.pw-eye .icon-eye-off { display: none; }
.pw-showing .icon-eye { display: none; }
.pw-showing .icon-eye-off { display: block; }

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 0 16px;
  height: 40px;
  border: 1px solid transparent;
  border-radius: 8px;
  font: inherit;
  font-size: 16px;
  font-weight: 600;
  line-height: 1;
  text-align: center;
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  transition: background 0.15s ease, box-shadow 0.15s ease, transform 0.05s ease;
  -webkit-appearance: none;
  appearance: none;
}
.btn:hover { text-decoration: none; }
.btn:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.btn[disabled], .btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }
/* The display above would beat the UA's [hidden] rule — author styles win
   on origin, not specificity — so hiding must be spelled out again. */
.btn[hidden] { display: none; }

/* Submit-in-progress (the busy guard in base.html): while the form's POST is
   in flight the submitter carries .is-busy + disabled. The spinner is a
   pseudo-element rather than markup — the resend-countdown rewrites its
   button's textContent, which would wipe a child element. currentColor keeps
   it legible on every variant through the disabled dimming. */
.btn.is-busy { cursor: progress; }
.btn.is-busy::before,
.btn-link.is-busy::before {
  content: "";
  flex: none;
  border: 2px solid currentColor;
  border-inline-end-color: transparent;
  border-radius: 50%;
  animation: btn-busy-spin 0.7s linear infinite;
}
.btn.is-busy::before { width: 16px; height: 16px; }
/* .btn-link is a plain inline button — flex it so the spinner sits in the
   link's own gap instead of relying on whitespace in the label. */
.btn-link.is-busy { display: inline-flex; align-items: center; gap: 6px; }
.btn-link.is-busy::before { width: 12px; height: 12px; }
@keyframes btn-busy-spin { to { transform: rotate(1turn); } }
@media (prefers-reduced-motion: reduce) {
  .btn.is-busy::before,
  .btn-link.is-busy::before { animation-duration: 1.6s; }
}

.btn-primary { background: var(--accent); color: var(--accent-on); }
.btn-primary:hover { background: var(--accent-hover); }
.btn-primary:active { transform: translateY(1px); }

.btn-secondary {
  background: var(--surface-card);
  color: var(--text-strong);
  border-color: var(--border-default);
}
.btn-secondary:hover { background: var(--surface-sunken); }

.btn-danger { background: var(--status-danger); color: #fff; width: auto; }
.btn-danger:hover { background: var(--red-700); }

.btn-lg { height: 48px; font-size: 18px; padding: 0 24px; }

.buttons { display: flex; gap: 12px; }
.buttons .btn { flex: 1; width: auto; }
.buttons form { flex: 1; display: flex; }
.buttons + .buttons { margin-top: 8px; }
/* Paragraph margins are bottom-only, so text after a button row would sit
   flush against it — give it the same 1rem paragraphs give each other. */
.buttons + p { margin-top: 1rem; }

/* OTP step (login wizard): Verify is the form's full-width primary; the
   backup-code switch keeps its own row below it (12px, never flush against
   Verify) and Back reads as a plain link at the panel's bottom left — it is
   a POST goto_step, so a .btn-link submit rather than an anchor. */
.step-alt { margin-top: 12px; }
.step-back { margin-top: 16px; }
/* Forms are column flex (form { display:flex }), which would stretch the
   link to full width with its label centered — pin it left, shrink-wrapped. */
.step-back .btn-link { align-self: flex-start; }

/* Row under the password: keep-signed-in checkbox + forgot-password link. */
.lblrow {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 13px;
}
.lblrow-end { justify-content: flex-end; }

/* hCaptcha challenge (plan/12): center the widget iframe inside the field
   block; the token input itself is hidden. */
.hcaptcha .h-captcha { display: flex; justify-content: center; }

/* Custom checkbox (the design system's Checkbox). */
.check {
  display: inline-flex;
  align-items: flex-start;
  gap: 8px;
  font-size: 16px;
  color: var(--text-body);
  cursor: pointer;
  user-select: none;
  position: relative;
}
.check input { position: absolute; opacity: 0; width: 0; height: 0; }
.check-box {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  border: 1.5px solid var(--border-strong);
  border-radius: 4px;
  background: var(--surface-card);
  color: #fff;
  transition: background 0.15s ease, border-color 0.15s ease;
}
.check-box svg { opacity: 0; transform: scale(0.6); transition: opacity 0.12s ease, transform 0.12s ease; }
.check input:checked + .check-box { background: var(--accent); border-color: var(--accent); }
.check input:checked + .check-box svg { opacity: 1; transform: scale(1); }
.check input:focus-visible + .check-box { box-shadow: var(--shadow-focus); }
.check-label { padding-top: 1px; line-height: 1.25; }

.divider {
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--text-muted);
  font-size: 13px;
  /* The form rhythm's 16px on both sides — the rule floats clear of the
     blocks above and below it (login v2, 2026-10-13). */
  margin: 16px 0;
}
.divider::before, .divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--divider);
}

/* Login tiles ("Sign In v2" handoff, 2026-10-13): the passkey and the
   social providers as square icon tiles under the "or continue with"
   divider. Each tile is its own POST form (allauth 65 starts OAuth on POST
   only); the grid ships three columns so the Microsoft cell waits empty
   until its credentials land. */
.auth-tiles {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
}
.auth-tiles form { display: flex; }
.btn-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  aspect-ratio: 1 / 1;
  padding: 8px;
  background: var(--surface-card);
  border: 1px solid var(--border-default);
  border-radius: 8px;
  color: var(--text-strong);
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, transform 0.05s ease;
  -webkit-appearance: none;
  appearance: none;
}
.btn-tile:hover { background: var(--surface-sunken); border-color: var(--border-strong); }
.btn-tile:active { transform: translateY(1px); }
.btn-tile:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
  border-color: var(--accent);
}
.btn-tile[disabled] { opacity: 0.5; cursor: not-allowed; }
.btn-tile .tile-ico { width: 26px; height: 26px; color: var(--accent-strong); }
@media (prefers-color-scheme: dark) {
  .btn-tile .tile-ico { color: var(--teal-300); }
}

/* 1rem top margin: after a form/buttons (no bottom margin) it opens the gap
   to the action row; after a text paragraph it collapses with that p's 1rem
   bottom margin, so those pages are unchanged. */
.links {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 14px;
  margin-top: 1rem;
}

.alt {
  display: flex;
  justify-content: center;
  gap: 6px;
  font-size: 14px;
  color: var(--text-muted);
  /* Same 16px the divider gets, so its rule sits equidistant between the
     tiles above and the prompt below (login v2, 2026-10-13). */
  margin-top: 16px;
  padding-top: 20px;
  border-top: 1px solid var(--divider);
}

.secure {
  /* Block, not flex: a wrapped line inside the login card left the icon
     stranded at the margin while the anonymous text item stretched to the
     full width — inline flow keeps the icon glued to the text's first line. */
  font-size: 13px;
  color: var(--text-muted);
  text-align: center;
}
.secure svg { vertical-align: -2px; margin-right: 6px; }

.meta { color: var(--text-muted); font-size: 14px; margin-bottom: 1rem; }
.meta strong { color: var(--text-strong); }

.qr { display: flex; justify-content: center; margin: 1rem 0; }
.qr svg, .qr img { border: 1px solid var(--border-subtle); border-radius: 8px; }

.backup-codes {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 8px;
  font-family: var(--font-mono);
  font-size: 14px;
  background: var(--surface-sunken);
  border-radius: 8px;
  padding: 12px 16px;
}

.inline-form { display: inline; }

@media (max-width: 480px) {
  .wrap { padding: 16px; }
  .card { padding: 24px 20px; }
}

/* ===== Account dashboard (Account Home handoff, 2026-09-28) — the profile
   page's chrome: dark topbar, teal tab nav, wide overview. Icons are Lucide
   marks referenced through the generated <use> sprite
   (accounts/templatetags/icons.py); stroke properties live here so the
   vendored symbols stay bare. ===== */
.i {
  display: block;
  width: 18px;
  height: 18px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.i [fill="currentColor"] { fill: currentColor; }
.i-14 { width: 14px; height: 14px; }
.i-16 { width: 16px; height: 16px; }

/* Top bar */
.tb {
  background: var(--basalt-900);
  color: var(--basalt-100);
  /* Same pixel height as immos-manager-webapp's .topbar (portal.css). */
  height: 52px;
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 0 24px;
  font-size: 14px;
}
.tb-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 16px;
  color: #fff;
}
.mark {
  display: block;
  width: 26px;
  height: 26px;
}
/* The topbar is dark in both schemes — the "id" half always takes the
   lightened accent (the handoff's dark-lockup treatment). */
.tb-brand .brand-id { color: var(--teal-300); }
.tb-sp { flex: 1; }
.tb form { display: flex; }  /* Sign out is a POST form wearing a tb-link */
.tb-link {
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--basalt-300);
  background: none;
  border: 0;
  font: inherit;
  cursor: pointer;
  padding: 6px 8px;
  border-radius: 8px;
  text-decoration: none;
  white-space: nowrap;
}
.tb-link:hover { color: #fff; background: var(--basalt-800); text-decoration: none; }

/* User menu — the reference app's <details> dropdown (portal.css .dd):
   the summary is the trigger, the menu drops below. dd--plain drops the
   trigger's border box, dd--right pins the panel to the bar's right edge.
   The panel is light against the dark bar, so it re-enters the light-scheme
   token vocabulary (portal's --surface-raised is --surface-card here). */
.dd { position: relative; }
.dd > summary {
  display: flex;
  align-items: center;
  gap: 8px;
  background: none;
  border: 1px solid var(--basalt-700);
  color: #fff;
  font: inherit;
  font-size: 14px;
  padding: 6px 10px;
  border-radius: 8px;
  cursor: pointer;
  list-style: none;
  white-space: nowrap;
}
.dd > summary::-webkit-details-marker { display: none; }
:where(.dd > summary):hover { background: var(--basalt-800); }
:where(.dd > summary):focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.dd--plain > summary { border-color: transparent; padding: 4px 8px 4px 4px; }
.dd-menu {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  min-width: 240px;
  background: var(--surface-card);
  color: var(--text-body);
  border: 1px solid var(--border-subtle);
  border-radius: 12px;
  box-shadow: var(--shadow-lg);
  padding: 6px;
  z-index: 70;
}
.dd--right .dd-menu { left: auto; right: 0; }
.dd-item {
  display: flex;
  width: 100%;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  background: none;
  border: 0;
  font: inherit;
  font-size: 14px;
  color: var(--text-strong);
  padding: 8px 10px;
  border-radius: 6px;
  cursor: pointer;
  text-align: left;
  text-decoration: none;
}
:where(.dd-item):hover { background: var(--surface-sunken); }
.dd-item-main { display: flex; align-items: center; gap: 10px; min-width: 0; }
.dd-head {
  padding: 10px 10px 12px;
  border-bottom: 1px solid var(--divider);
  margin-bottom: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.dd-head .who { font-weight: 600; color: var(--text-strong); }
.dd-head .mail { font-family: var(--font-mono); font-size: 12px; color: var(--text-muted); overflow-wrap: anywhere; }
/* This app's .muted carries paragraph rhythm; inside the head it's a label. */
.dd-head .muted { margin: 0; }
.avatar {
  width: 28px;
  height: 28px;
  border-radius: 999px;
  background: var(--accent-tint);
  color: var(--accent-strong);
  display: grid;
  place-items: center;
  font-weight: 600;
  font-size: 12px;
  flex: none;
}
@media (prefers-color-scheme: dark) {
  .avatar { color: var(--teal-300); }
}

/* Tab nav — only implemented surfaces are tabs (Overview/Security/Linked). */
.nav {
  background: var(--accent-hover);
  display: flex;
  padding: 0 16px;
  overflow-x: auto;
}
.nav-tab {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  min-width: 96px;
  padding: 12px 14px 10px;
  color: var(--accent-on);
  font-size: 13px;
  position: relative;
  text-decoration: none;
  transition: background 0.15s;
}
.nav-tab:hover { background: color-mix(in srgb, var(--accent-strong) 60%, transparent); text-decoration: none; }
.nav-tab[aria-current="page"] { background: var(--accent-strong); font-weight: 600; }
.nav-i { width: 20px; height: 20px; }
.dotc {
  position: absolute;
  top: 8px;
  right: 28px;
  width: 8px;
  height: 8px;
  border-radius: 999px;
  background: var(--status-warning);
  box-shadow: 0 0 0 2px var(--accent-hover);
}

/* Overview main column */
.dash-main {
  max-width: 1200px;
  width: 100%;
  margin: 0 auto;
  padding: 28px 24px 64px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.dash-main .messages { margin: 0; }
.dash-main .messages a { color: inherit; text-decoration: underline; }
.ph { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.dash-main h1 { font-size: 28px; margin: 0; }
.eyebrow {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 6px;
}
.dash-main .btn { width: auto; }

/* The step-up challenge (plan/17 §1.1) is an auth moment, not a dashboard:
   the login card's 400px column (.box), centered the way .center centers it
   on the auth shell. */
.narrow {
  width: 100%;
  max-width: 400px;
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.narrow .links { margin: 0; }

/* Panels (dashboard cards; only the welcome panel wears the accent top) */
.panel {
  background: var(--surface-card);
  border: 1px solid var(--border-subtle);
  border-radius: 12px;
  padding: 24px;
  box-shadow: var(--shadow-sm);
  color: var(--text-body);
}
.panel-accent { border-top: 3px solid var(--accent); }
.panel h2 {
  font-family: var(--font-display);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--text-strong);
  margin: 0;
}
.chead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding-bottom: 14px;
  margin-bottom: 16px;
  border-bottom: 1px solid var(--divider);
}

/* Welcome panel: intro + security checklist + quick actions */
.welcome { display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) minmax(0, 1fr); gap: 32px; }
.welcome-intro { display: flex; flex-direction: column; gap: 14px; align-items: flex-start; }
.welcome-title { font-size: 20px; }
.welcome-intro .muted { margin: 0; }
.welcome h3 { font-size: 14px; font-weight: 600; color: var(--text-strong); margin: 0 0 12px; }
.protected { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-muted); margin: 0; }
.checks { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.checks li { display: flex; align-items: center; gap: 10px; font-size: 14px; }
.ok { color: var(--status-success); }
.warn { color: var(--status-warning); }
.qa { display: flex; flex-direction: column; gap: 10px; font-size: 14px; align-items: flex-start; }
.qa a { display: flex; align-items: center; gap: 10px; }

/* Two-column body: profile form left, security/linked stack right */
.grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 20px; align-items: start; }
.col { display: flex; flex-direction: column; gap: 20px; }
/* A body that loses its second column (e.g. the email page with adding
   disabled) must not leave a phantom hole — span the full width. */
.grid > .col:only-child { grid-column: 1 / -1; }
.dash-form { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; }
.dash-form .full { grid-column: 1 / -1; }
.dash-form .field { margin: 0; min-width: 0; }
.factions {
  grid-column: 1 / -1;
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 12px;
  margin-top: 4px;
}

/* Row lists (security card, linked accounts) */
.row { display: flex; align-items: center; gap: 14px; padding: 12px 0; border-top: 1px solid var(--divider); }
.chead + .row { border-top: 0; padding-top: 0; }
/* A row with no row before it — the list opening its container — draws its
   divider against empty space, so the first row skips the rule entirely
   (security-key list, 2026-10-13). */
.row:first-child { border-top: 0; padding-top: 0; }
/* Passwordless-capable key marker (security-key list): a muted open lock
   after the key's name; the span's title attribute carries the tooltip
   (the .dotc precedent). */
.key-unlock { color: var(--text-muted); cursor: help; }
.key-unlock .i { display: inline-block; vertical-align: -2px; margin-left: 4px; }
.row .ico {
  width: 36px;
  height: 36px;
  border-radius: 8px;
  background: var(--surface-sunken);
  display: grid;
  place-items: center;
  color: var(--text-body);
  flex: none;
}
.row .t { flex: 1; min-width: 0; }
.row .t b { display: block; font-size: 14px; color: var(--text-strong); font-weight: 600; }
.row .t span { font-size: 13px; color: var(--text-muted); }
/* Linked-account rows carry the remote identity, often an email — let the
   unbreakable string wrap inside the narrow profile column. */
.linked .row .t b { overflow-wrap: anywhere; }

/* Connected accounts: selectable rows (radio + provider identity) */
.conn-list { display: flex; flex-direction: column; }
.conn {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 0;
  border-top: 1px solid var(--divider);
  cursor: pointer;
}
.conn:first-child { border-top: 0; padding-top: 0; }
.conn input[type="radio"] { width: 16px; height: 16px; accent-color: var(--accent); flex: none; }
/* Provider brand mark, same dressing as the .row icon squares. The mark
   itself keeps its official fills; the color here only feeds the fallback
   link icon's stroke. */
.conn .ico {
  width: 36px;
  height: 36px;
  border-radius: 8px;
  background: var(--surface-sunken);
  display: grid;
  place-items: center;
  color: var(--text-body);
  flex: none;
}
.conn .t { flex: 1; min-width: 0; }
.conn .t b { display: block; font-size: 14px; color: var(--text-strong); font-weight: 600; }
.conn .t > span { font-size: 13px; color: var(--text-muted); }

/* Badges and small row buttons */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 24px;
  padding: 0 10px;
  border-radius: 999px;
  font-size: 12px;
  font-weight: 600;
  background: var(--surface-sunken);
  color: var(--text-body);
  flex: none;
}
.badge .dot { width: 6px; height: 6px; border-radius: 999px; background: currentColor; }
.badge-success { background: var(--status-success-bg); color: var(--status-success); }
.btn-sm { height: 32px; padding: 0 12px; font-size: 14px; flex: none; }

@media (max-width: 900px) {
  .welcome, .grid, .dash-form { grid-template-columns: minmax(0, 1fr); }
  .tb .hide-sm { display: none; }
}
@media (max-width: 600px) {
  .tb { padding: 0 16px; gap: 12px; }
  .dash-main { padding: 20px 16px 48px; }
  .panel { padding: 20px; }
}

/* ===== Devices & activity (plan/06, handoff "Devices - Activity" mockup,
   2026-09-29): signed-in devices card + attempts table. Same token
   vocabulary as the dashboard above; every link hover is scoped under its
   component class (the bare a:hover specificity bug precedent). ===== */

.mono { font-family: var(--font-mono); font-size: 12px; }

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* Danger alert banner (new-location warning) with .lnk actions. */
.banner-danger { background: var(--status-danger-bg); color: var(--status-danger); }
.banner-danger p { margin: 0; }
.banner-act { display: flex; gap: 20px; margin-top: 10px; flex-wrap: wrap; }

/* Button dressed as a link (banner actions). */
.lnk {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 14px;
  color: var(--text-link);
  cursor: pointer;
  text-align: left;
}
.lnk:hover { color: var(--accent-strong); text-decoration: underline; text-underline-offset: 2px; }
@media (prefers-color-scheme: dark) {
  .lnk:hover { color: var(--text-link); }
}

/* Ghost + accent variants the dashboard didn't need until now. */
.btn-ghost { background: none; border-color: transparent; color: var(--text-link); }
.btn-ghost:hover { background: var(--surface-sunken); text-decoration: none; }
.badge-accent { background: var(--accent-tint); color: var(--accent-strong); }
.badge-danger { background: var(--status-danger-bg); color: var(--status-danger); }
@media (prefers-color-scheme: dark) {
  .badge-accent { color: var(--teal-300); }
}

/* Signed-in devices: two-column row grid, collapsing under 900px. */
.dev { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0 32px; }
.dev .row:first-child,
.dev .row:nth-child(2) { border-top: 0; padding-top: 0; }
.dev .row .t b { display: block; }
.dev .plat, .dev .dev-loc { display: block; }

/* Filter row: pill result tabs (links) + method select (GET form). */
.filters { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 12px; }
.ftabs { display: flex; gap: 6px; flex-wrap: wrap; }
.ftabs a {
  display: inline-flex;
  align-items: center;
  padding: 6px 12px;
  border-radius: 999px;
  font-size: 13px;
  color: var(--text-muted);
  text-decoration: none;
  white-space: nowrap;
}
.ftabs a:hover { color: var(--text-strong); background: var(--surface-sunken); text-decoration: none; }
.ftabs a[aria-current="true"] { background: var(--accent-tint); color: var(--accent-strong); font-weight: 600; }
@media (prefers-color-scheme: dark) {
  .ftabs a[aria-current="true"] { color: var(--teal-300); }
}
.mform { display: flex; flex-direction: row; align-items: center; gap: 8px; }
.mform select { width: auto; min-width: 200px; }

/* Attempts table. */
.tw { overflow-x: auto; margin: 0 -8px; }
.tw table { width: 100%; border-collapse: collapse; font-size: 14px; min-width: 820px; }
.tw th {
  text-align: left;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-weight: 500;
  padding: 10px 8px;
  border-bottom: 1px solid var(--border-default);
}
.tw td { padding: 12px 8px; border-bottom: 1px solid var(--divider); vertical-align: middle; }
.tw tr.main { transition: background 0.12s; }
.tw tr.main:hover { background: var(--surface-sunken); }
.tw tr.main.fail td:first-child { box-shadow: inset 2px 0 0 var(--status-danger); }
.tw td .sub { display: block; font-size: 12px; color: var(--text-muted); margin-top: 2px; }
.tw td .sub.danger { color: var(--status-danger); }
.tw td .strong { color: var(--text-strong); }
.tw td .dc { display: flex; align-items: center; gap: 10px; }
.tw td .dc svg { flex: none; color: var(--text-muted); }

/* Expandable detail rows without JS: a focusable checkbox the chevron label
   toggles; :has() reveals the sibling detail row. */
.tw .row-toggle { position: absolute; opacity: 0; width: 1px; height: 1px; }
.tw .chev { display: inline-flex; color: var(--text-muted); cursor: pointer; border-radius: 4px; }
.tw .chev svg { transition: transform 0.15s; }
.tw .row-toggle:focus-visible + .chev { outline: 2px solid var(--accent); outline-offset: 2px; }
.tw tr.det { display: none; }
.tw tr.main:has(.row-toggle:checked) + tr.det { display: table-row; }
.tw tr.main:has(.row-toggle:checked) .chev svg { transform: rotate(90deg); }
.tw tr.det td { background: var(--surface-sunken); padding: 4px 16px 18px 42px; }
.tw .dl { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px 24px; margin: 0 0 14px; }
.tw .dl dt {
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 2px;
}
.tw .dl dd { margin: 0; font-size: 13px; color: var(--text-strong); word-break: break-word; }
.tw .no-rows { padding: 28px 8px; text-align: center; color: var(--text-muted); }
.tfoot {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 14px;
  font-size: 13px;
  color: var(--text-muted);
  gap: 12px;
  flex-wrap: wrap;
}

@media (max-width: 900px) {
  .dev { grid-template-columns: minmax(0, 1fr); }
  .dev .row:nth-child(2) { border-top: 1px solid var(--divider); padding-top: 12px; }
  .tw .dl { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .filters .mform { width: 100%; }
  .filters .mform select { flex: 1; }
}

/* Read-only field line (plan/07 §4 Step 7): the profile's email address —
   label + value + trailing action, keeping the field grid's rhythm. Scoped
   compound selectors only; :where() discipline for the hover state. */
.static-row { display: flex; flex-direction: column; gap: 8px; margin: 0; min-width: 0; }
.static-row label { font-size: 14px; font-weight: 500; color: var(--text-strong); }
.static-line { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.static-value { font-weight: 500; color: var(--text-strong); word-break: break-word; }
.static-line .btn-sm { margin-left: auto; }

/* Button that reads as a link (the verify step's "Use a different address"
   — cancel is POST-only, so it cannot be a plain anchor). */
.btn-link {
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-size: 14px;
  color: var(--text-link);
  cursor: pointer;
}
:where(.btn-link:hover) {
  color: var(--accent-strong);
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* --- Avatars (plan/08) ---------------------------------------------------------
   The endpoint <img> and the picker panel. Hue pairs mirror accounts/avatars.py
   HUES (§1.3 — keep the two in sync); hue-0 stays on the stock .avatar tokens,
   the dark token flip recolours it for free. */

img.avatar { object-fit: cover; }

.avatar.hue-1 { background: var(--teal-300); color: var(--teal-700); }
.avatar.hue-2 { background: var(--teal-500); color: #fff; }
.avatar.hue-3 { background: var(--basalt-800); color: var(--teal-100); }
@media (prefers-color-scheme: dark) {
  .avatar.hue-1 { color: var(--basalt-950); }
  .avatar.hue-3 { background: var(--basalt-950); color: var(--teal-300); }
}

.avatar-lg { width: 40px; height: 40px; font-size: 15px; }
.avatar-empty {
  background: var(--surface-sunken);
  border: 1px dashed var(--border-default);
}

/* Picker rows: house radio via accent-color (the .conn precedent); the upload
   box and the provider buttons appear only while Photo is selected — pure
   :has(), no runtime JS. */
.avatar-opts { display: flex; flex-direction: column; }
.avatar-opt { display: flex; align-items: center; }
.avatar-opt input[type="radio"] { accent-color: var(--accent); margin-right: 12px; flex: none; }
.avatar-opt label {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 8px;
  flex: 1;
  min-width: 0;
  cursor: pointer;
  border-radius: 8px;
}
:where(.avatar-opt label:hover) { background: var(--surface-sunken); }
.avatar-opt .t { display: flex; flex-direction: column; min-width: 0; }
.avatar-opt .t b { font-weight: 600; color: var(--text-strong); }
.avatar-opt .t span { font-size: 13px; color: var(--text-muted); }
.avatar-upload { display: none; padding: 4px 8px 0; }
.avatar-upload input[type="file"] { font: inherit; font-size: 14px; color: var(--text-body); }
.avatar-upload .hint { margin: 8px 0 0; }
.avatar-form:has(input[name="kind"][value="photo"]:checked) .avatar-upload { display: block; }

/* --- Notifications (plan/10, handoff "Notifications" mockup) --------------------
   The Mailbox/Preferences sub-nav, the channel matrix table and the two-pane
   mailbox. Same token vocabulary as everything above; link hovers scoped under
   their component classes (:where() discipline), em-dash = not offered. */

/* Sub-nav: underlined tabs under the page title (Mailbox | Preferences). */
.nsub {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--border-subtle);
}
.nsub a {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  font-size: 14px;
  color: var(--text-muted);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.nsub a:hover { color: var(--text-strong); text-decoration: none; }
.nsub a[aria-current="page"] {
  color: var(--text-strong);
  font-weight: 600;
  border-bottom-color: var(--accent);
}

/* Legend above the matrix. */
.legend { display: flex; gap: 20px; flex-wrap: wrap; font-size: 13px; color: var(--text-muted); margin-bottom: 8px; }
.legend > span { display: flex; align-items: center; gap: 6px; }
.legend b { font-weight: 600; color: var(--text-strong); }

/* Channel matrix. Column headers are the channels; em-dash = not offered. */
.mx { width: 100%; border-collapse: collapse; font-size: 14px; min-width: 720px; }
.mx th {
  text-align: center;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
  font-weight: 500;
  padding: 10px 8px;
  border-bottom: 1px solid var(--border-default);
  width: 112px;
}
.mx th:first-child { text-align: left; width: auto; }
.mx td { padding: 16px 8px; border-bottom: 1px solid var(--divider); vertical-align: top; }
.mx td.c { text-align: center; vertical-align: middle; }
.mx .nm { font-weight: 600; color: var(--text-strong); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.mx .ds { font-size: 13px; color: var(--text-muted); margin-top: 3px; max-width: 52ch; text-wrap: pretty; }
.mx .note { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-muted); margin-top: 6px; }
.mx .mx-check { justify-content: center; }
/* Mandatory row: sunken tint + one locked cell spanning the channel columns. */
.mx tr.req td { background: color-mix(in srgb, var(--surface-sunken) 45%, transparent); }
.locked {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text-muted);
  background: var(--surface-sunken);
  border-radius: 8px;
  padding: 8px 12px;
}
/* Group header rows ("Security", "News", …). */
.mx tr.grp td {
  padding: 20px 8px 8px;
  border-bottom: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-muted);
}
/* Save row: the footnote explains the em-dash; the button sits right. */
.mx-form .factions { justify-content: space-between; flex-wrap: wrap; margin-top: 20px; }
.mx-form .factions .muted { margin: 0; }
@media (max-width: 900px) {
  .mx { min-width: 0; }
  .mx th { width: auto; }
}

/* Two-pane mailbox: list left, reading pane right; both stack under 820px. */
.mb {
  display: grid;
  grid-template-columns: minmax(280px, 380px) minmax(0, 1fr);
  border: 1px solid var(--border-subtle);
  border-radius: 12px;
  background: var(--surface-card);
  overflow: hidden;
  min-height: 420px;
}
.ml { border-right: 1px solid var(--border-subtle); display: flex; flex-direction: column; min-width: 0; }
.ml-h {
  padding: 12px 16px;
  border-bottom: 1px solid var(--border-subtle);
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  color: var(--text-muted);
}
.ml ul { list-style: none; margin: 0; padding: 0; overflow-y: auto; max-height: 560px; }
.ml li a {
  display: grid;
  grid-template-columns: 32px minmax(0, 1fr);
  gap: 12px;
  padding: 14px 16px;
  border-bottom: 1px solid var(--divider);
  color: inherit;
  box-shadow: inset 3px 0 0 transparent;
  transition: background 0.12s;
}
:where(.ml li a):hover { background: var(--surface-sunken); text-decoration: none; color: inherit; }
.ml li a[aria-current="true"] { background: var(--accent-tint); box-shadow: inset 3px 0 0 var(--accent); }
@media (prefers-color-scheme: dark) {
  .ml li a[aria-current="true"] { color: var(--text-body); }
}
.ml .ic {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  background: var(--surface-sunken);
  display: grid;
  place-items: center;
  color: var(--text-body);
}
.ml li a[aria-current="true"] .ic { background: var(--surface-card); color: var(--accent-strong); }
@media (prefers-color-scheme: dark) {
  .ml li a[aria-current="true"] .ic { color: var(--teal-300); }
}
.ml-t { min-width: 0; }
.ml .top { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; }
.ml .ti { font-size: 14px; font-weight: 600; color: var(--text-strong); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ml time { font-size: 12px; color: var(--text-muted); white-space: nowrap; flex: none; }
.ml .ty { font-size: 12px; color: var(--text-muted); display: block; margin-top: 2px; }
.ml .ex {
  font-size: 13px;
  color: var(--text-body);
  margin-top: 4px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Reading pane. */
.rd { padding: 28px 32px; display: flex; flex-direction: column; gap: 20px; min-width: 0; }
.rd-h { display: flex; flex-direction: column; gap: 10px; padding-bottom: 20px; border-bottom: 1px solid var(--divider); }
.rd-h .meta { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; font-size: 13px; color: var(--text-muted); }
.rd-h .meta span { display: flex; align-items: center; gap: 6px; }
.rd h3 {
  font-family: var(--font-display);
  font-size: 22px;
  letter-spacing: -0.01em;
  color: var(--text-strong);
  margin: 0;
  text-wrap: pretty;
}
.rd .bd { font-size: 15px; line-height: 1.7; max-width: 64ch; overflow-wrap: anywhere; }
.rd .bd p { margin: 0 0 12px; text-wrap: pretty; }
.rd .bd p:last-child { margin-bottom: 0; }
.rd .ro { margin-top: auto; display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-muted); }
.rd .btn { align-self: flex-start; }
.rd-empty, .mb-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  font-size: 14px;
  padding: 32px;
  min-height: 200px;
  text-align: center;
  text-wrap: pretty;
}
.mb-empty { grid-column: 1 / -1; }

/* Mailbox pagination ("1–25 of 63" + numbered links). */
.pgn {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin: 0;
  padding: 10px 12px;
  border-top: 1px solid var(--border-subtle);
  font-size: 13px;
  color: var(--text-muted);
}
.pgn .p { display: flex; gap: 4px; align-items: center; flex-wrap: wrap; }
.pgn .p a, .pgn .p span.cur, .pgn .p .off {
  min-width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border-radius: 8px;
  padding: 0 8px;
  font-size: 13px;
}
.pgn .p a { color: var(--text-body); }
:where(.pgn .p a):hover { background: var(--surface-sunken); text-decoration: none; }
.pgn .p span.cur { background: var(--accent-tint); color: var(--accent-strong); font-weight: 600; }
@media (prefers-color-scheme: dark) {
  .pgn .p span.cur { color: var(--teal-300); }
}
.pgn .p .off { opacity: 0.45; }

@media (max-width: 820px) {
  .mb { grid-template-columns: minmax(0, 1fr); }
  .ml { border-right: 0; }
  .rd { padding: 20px; }
}

/* plan/34: the native email-management page (account_email) — since the
   2026-10-10 panel restyle the address rows are the generic .row list
   (radio + address, like the connected-accounts rows), so only the picker
   quirks remain, plus the link-styled submit used by the confirm-code
   page's "Use a different address" escape hatch. */
/* :first-of-type, not :first-child — {% csrf_token %} renders a hidden
   input ahead of the rows inside the form, so the first .row is never the
   form's first child and :first-child lets its divider double up against
   the chead rule (email page, 2026-10-13). */
.chead + form .row:first-of-type { border-top: 0; padding-top: 0; }
.emailpick { display: flex; align-items: center; gap: 10px; min-width: 0; cursor: pointer; }
.emailpick input { accent-color: var(--accent); }
.emailaddr { overflow-wrap: anywhere; }
.btnrow { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 16px; }
.linklike {
  background: none; border: 0; padding: 0; color: var(--accent-strong);
  font: inherit; cursor: pointer; text-decoration: underline;
}

/* Code-entry footer (confirm_login_code): the wrong-address hint reads in
   the same muted voice as the page intro and sits well clear of Verify;
   the action under it keeps one small consistent gap in both branches. */
.login-foot { margin-top: 1.5rem; }
.login-foot .muted { margin: 0; text-align: center; }
.login-foot .links, .login-foot form { margin-top: 8px; }

/* plan/35 (allauth.mfa): the management pages ride the dashboard shell —
   a readable column instead of the full 1200px stretch — plus the QR block
   and the recovery-code list. */
.narrow-panel { max-width: 640px; }
.narrow-panel .btnrow { justify-content: flex-start; }
/* Add-security-key page: the intro paragraph needs more air before the
   Name field's label than paragraphs give each other. */
.add-key > .muted { margin-bottom: 1.5rem; }
.qr { margin: 16px 0; }
.qr svg { width: 180px; height: 180px; display: block; background: #fff; border-radius: 8px; padding: 8px; }
.recovery-codes ol {
  margin: 12px 0 0; padding: 12px 12px 12px 36px; list-style: decimal;
  display: grid; grid-template-columns: 1fr 1fr; gap: 4px 16px;
  background: var(--basalt-900, #161b21); color: #e8edf2;
  border-radius: 8px; font-family: var(--font-mono); font-size: 14px;
}
@media (max-width: 480px) { .recovery-codes ol { grid-template-columns: 1fr; } }

/* mfa_index two-column body: 70/30 — the factor rows are the working half,
   the notes panel a narrow aside. Stacks under 900px (its own rule: it sits
   later in the file than the shared .grid collapse, so it would otherwise
   win the cascade and keep two columns on phones). Phones additionally
   reflow each factor row into icon | text with the action button dropping
   under the text — flex keeps it inline, and at 320px a "Deactivate"
   button would crush the description into a sliver. */
.mfa-grid { grid-template-columns: minmax(0, 7fr) minmax(0, 3fr); }
@media (max-width: 900px) {
  .mfa-grid { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 600px) {
  .mfa-grid .row { display: grid; grid-template-columns: 36px minmax(0, 1fr); gap: 8px 14px; align-items: start; }
  .mfa-grid .row .ico { grid-row: 1 / span 2; align-self: start; margin-top: 2px; }
  .mfa-grid .row .btn { grid-column: 2; justify-self: start; }
}
