/* Design tokens live in tokens.css, which base.html loads before this file for
   every page in the app. The `:root` block that used to sit here has been
   removed rather than moved: three stylesheets each declaring their own copy of
   the palette is precisely how they drifted apart. Everything it defined is
   still available under the same names -- see the compatibility section at the
   bottom of tokens.css -- so no rule below had to change to keep working. */

/* =============================================================================
   auth.css — sign in, register, the three password-reset steps, and the
   maintenance page.

   ONE SHELL, SIX SCREENS
   ----------------------
   These six pages grew separately and looked it: three different heading sizes,
   two different card widths, a "back" link in three different places, and a
   maintenance page that borrowed landing.css and therefore rendered in a
   different visual family from the login screen one click away. Somebody moving
   login → forgot → code → new password crossed four seams in a single sitting.

   So there is now exactly one shell, and every screen renders it:

     .auth-page                 the centred viewport
       .auth-shell              the 420px column
         .auth-brand            the wordmark, same place every time
         .auth-card             the card — same width, padding, radius, shadow
           .auth-head           eyebrow + h1 + subhead, in that order
           …content…
         .auth-footer           legal links + a way back to the homepage

   Differences between the screens are *content*. Nothing below lets a page pick
   a different card, a different width or a different rhythm, because that is the
   only way six pages maintained by six separate edits stay looking like one
   product.

   MOBILE FIRST
   ------------
   Every rule here is written for a 320px phone and widened at a breakpoint, not
   the other way round. These are the screens most likely to be opened on a
   phone — a password reset happens where the mail arrives.

   FOCUS
   -----
   `:focus-visible` only, everywhere, and never `outline: none` without a
   replacement ring. This is the one part of the app where getting that wrong
   does not degrade the experience, it locks people out.
   ============================================================================= */

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: var(--mf-font-sans);
  background: var(--mf-bg);
  color: var(--mf-fg);
  font-size: var(--mf-text-base);
  line-height: var(--mf-leading-normal);
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}

/* Same ambient wash as the landing page, so arriving here from the marketing
   site does not feel like landing in a different product. */
.glow-backdrop {
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--mf-bg-sunken);
  background-image: var(--mf-gradient-glow);
}

/* Visually hidden, still announced. Used for the step states in the reset
   progress bar, where the visual cue is a filled dot and a filled dot says
   nothing out loud. */
.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;
}

/* ---------------- Flash messages ----------------

   base.html renders these into a fixed overlay at the top of the viewport, for
   every page in the app, and this file cannot change that. What it can do is
   make sure they never land on top of the thing they are talking about: see the
   top padding on .auth-page below. */

.flash-container {
  position: fixed;
  top: var(--mf-space-4);
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--mf-z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--mf-space-2);
  width: min(92vw, 420px);
}

.flash {
  padding: var(--mf-space-3) var(--mf-space-4);
  border-radius: var(--mf-radius-md);
  font-size: var(--mf-text-md);
  line-height: var(--mf-leading-snug);
  background: var(--mf-surface-raised);
  border: 1px solid var(--mf-border-strong);
  box-shadow: var(--mf-shadow-md);
}

/* A tinted 1px border is easy to miss on a dark surface; the left bar is what
   actually communicates severity at a glance. */
.flash-error { border-color: var(--mf-danger-wash); border-left: 3px solid var(--mf-danger-fg); color: var(--mf-danger-fg); }
.flash-success { border-color: var(--mf-ok-wash); border-left: 3px solid var(--mf-ok-fg); color: var(--mf-ok-fg); }
.flash-info { border-color: var(--mf-info-wash); border-left: 3px solid var(--mf-info-fg); color: var(--mf-info-fg); }
.flash-warning { border-color: var(--mf-warn-wash); border-left: 3px solid var(--mf-warn-fg); color: var(--mf-warn-fg); }

/* =============================================================================
   The shell
   ============================================================================= */

.auth-page {
  min-height: 100vh;
  /* dvh where it exists: on a phone, 100vh is the *largest* viewport, so a
     centred card sits partly under the browser's own chrome until the address
     bar collapses. The 100vh line above stays as the fallback. */
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  /* The generous top padding is not decoration. Flash messages are a fixed
     overlay pinned to the top of the viewport (base.html), and on a phone the
     register card is taller than the screen — so without this reservation the
     toast carrying "Passwords do not match." covers the wordmark and the
     heading of the very form it is complaining about. */
  padding: var(--mf-space-8) var(--mf-space-4) var(--mf-space-6);
}

.auth-shell {
  width: 100%;
  /* One width, all six screens. A wider card for the register form and a
     narrower one for the code entry is exactly how the flow started to feel
     like four unrelated pages. */
  max-width: 420px;
  display: flex;
  flex-direction: column;
  gap: var(--mf-space-5);
}

.auth-brand {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  gap: var(--mf-space-2);
  font-weight: var(--mf-weight-bold);
  font-size: var(--mf-text-lg);
  letter-spacing: var(--mf-tracking-tight);
  color: var(--mf-fg);
  text-decoration: none;
  border-radius: var(--mf-radius-xs);
}

.brand-mark {
  /* 30/26px rather than a spacing step: this is an icon box sized to the glyph
     inside it, not a gap in the layout rhythm. */
  width: 30px;
  height: 30px;
  border-radius: var(--mf-radius-sm);
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  filter: drop-shadow(0 2px 6px rgba(124, 92, 255, 0.35));
}

.brand-mark svg { width: 26px; height: 26px; }

.auth-card {
  background: var(--mf-surface);
  border: 1px solid var(--mf-border-strong);
  border-radius: var(--mf-radius-lg);
  padding: var(--mf-space-5);
  box-shadow: var(--mf-shadow-lg);
  /* Left-aligned, on every screen. Centred body copy was the other half of why
     these pages read as unrelated: a centred paragraph over a left-aligned form
     has two axes, and each screen picked a different balance between them. */
  text-align: left;
  animation: authCardIn var(--mf-dur-slow) var(--mf-ease-out);
}

/* Entrance only — a short fade-and-rise. The old version also scaled, which on
   a form makes the fields look like they are settling into place. */
@keyframes authCardIn {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ---------------- Card header ---------------- */

.auth-head {
  margin: 0 0 var(--mf-space-5);
}

/* The one line that says which screen this is — "Sign in", "Step 2 of 3".
   Small, uppercase and accented, so it is read before the heading rather than
   as part of it. */
.auth-eyebrow {
  margin: 0 0 var(--mf-space-2);
  font-size: var(--mf-text-2xs);
  font-weight: var(--mf-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--mf-tracking-wider);
  color: var(--mf-accent-text);
}

.auth-card h1 {
  font-size: var(--mf-text-xl);
  line-height: var(--mf-leading-tight);
  margin: 0;
  letter-spacing: var(--mf-tracking-tighter);
  font-weight: var(--mf-weight-bold);
}

.auth-subhead {
  color: var(--mf-fg-muted);
  font-size: var(--mf-text-md);
  line-height: var(--mf-leading-snug);
  margin: var(--mf-space-2) 0 0;
}

/* =============================================================================
   Progress — the three reset steps

   A password reset is three pages and, until now, nothing on any of them said
   so. Somebody who had just asked for a code had no way to know whether one
   more screen was coming or five, and no way to tell a page that was waiting
   for them from a page that had gone wrong.
   ============================================================================= */

.auth-steps {
  display: grid;
  /* Three equal tracks that can actually shrink. `minmax(0, 1fr)`, never a px
     floor: at 320px a floor would push the row wider than the card and take the
     whole page sideways with it. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--mf-space-2);
  list-style: none;
  margin: 0 0 var(--mf-space-5);
  padding: 0;
  counter-reset: auth-step;
}

.auth-step {
  display: flex;
  flex-direction: column;
  gap: var(--mf-space-2);
  font-size: var(--mf-text-2xs);
  line-height: var(--mf-leading-snug);
  color: var(--mf-fg-subtle);
  min-width: 0;
}

/* The bar, not a numbered circle. Three circles joined by connectors need
   roughly 260px to stop overlapping; three bars with a label under each fit a
   320px screen with room left over, and read as progress at a glance. */
.auth-step::before {
  content: "";
  display: block;
  height: 3px;
  border-radius: var(--mf-radius-full);
  background: var(--mf-track);
}

.auth-step.is-done::before { background: var(--mf-ok-fg); }
.auth-step.is-current::before { background: var(--mf-accent); }

.auth-step.is-done { color: var(--mf-fg-muted); }
.auth-step.is-current { color: var(--mf-fg); font-weight: var(--mf-weight-semibold); }

/* =============================================================================
   Status panels — the honest states

   Registration switched off, maintenance mode, GitHub sign-up switched off, no
   mail server. Each of those used to either omit a control silently or mention
   it in a sentence tacked onto something else, and a silently missing control
   is the worst of the options: somebody locked out cannot tell "this server
   does not do that" from "I am on the wrong page".

   One panel, four tones, and every one of them says what is true *and* what to
   do instead.
   ============================================================================= */

.auth-status {
  border: 1px solid transparent;
  border-left: 3px solid var(--mf-track-strong);
  background: var(--mf-surface-raised);
  color: var(--mf-fg-muted);
  border-radius: var(--mf-radius-sm);
  padding: var(--mf-space-3);
  font-size: var(--mf-text-sm);
  line-height: var(--mf-leading-snug);
  margin: 0 0 var(--mf-space-5);
}

.auth-status:last-child { margin-bottom: 0; }

.auth-status strong {
  display: block;
  color: var(--mf-fg);
  font-weight: var(--mf-weight-semibold);
  margin-bottom: var(--mf-space-1);
}

.auth-status a {
  color: var(--mf-accent-text);
  border-radius: var(--mf-radius-xs);
}

.auth-status-warn { background: var(--mf-warn-wash); border-left-color: var(--mf-warn-fg); }
.auth-status-info { background: var(--mf-info-wash); border-left-color: var(--mf-info-fg); }
.auth-status-danger { background: var(--mf-danger-wash); border-left-color: var(--mf-danger-fg); }

/* Kept under its old name as well: login.html's maintenance banner has been
   called this since before the rebuild, and other pages may still reference it.
   Same panel, so the two cannot drift. */
.maintenance-notice {
  background: var(--mf-warn-wash);
  border: 1px solid transparent;
  border-left: 3px solid var(--mf-warn-fg);
  color: var(--mf-warn-fg);
  border-radius: var(--mf-radius-sm);
  padding: var(--mf-space-3);
  font-size: var(--mf-text-sm);
  line-height: var(--mf-leading-snug);
  text-align: left;
  margin: 0 0 var(--mf-space-5);
}

/* A live badge for the maintenance page. Same shape as the landing page's
   hero badge, drawn here so the maintenance screen no longer has to load a
   2000-line marketing stylesheet to render one pill. */
.auth-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--mf-space-2);
  padding: var(--mf-space-1) var(--mf-space-3);
  border-radius: var(--mf-radius-full);
  border: 1px solid var(--mf-border-accent);
  background: var(--mf-accent-wash);
  color: var(--mf-accent-text);
  font-size: var(--mf-text-2xs);
  font-weight: var(--mf-weight-semibold);
  text-transform: uppercase;
  letter-spacing: var(--mf-tracking-wider);
  margin-bottom: var(--mf-space-4);
}

.auth-badge .dot {
  width: 6px;
  height: 6px;
  border-radius: var(--mf-radius-full);
  background: var(--mf-warn-solid);
  flex: 0 0 auto;
}

/* =============================================================================
   Form
   ============================================================================= */

.auth-form {
  display: flex;
  flex-direction: column;
  text-align: left;
  gap: var(--mf-space-2);
}

.auth-form label {
  font-size: var(--mf-text-sm);
  font-weight: var(--mf-weight-medium);
  color: var(--mf-fg-muted);
  margin-top: var(--mf-space-4);
}

/* `:first-of-type`, not `:first-child`. Every one of these forms opens with the
   hidden CSRF input, so the old `:first-child` rule matched nothing and the first
   label on every auth page carried a 16px top margin it was written not to have.
   A selector that silently matches nothing is worse than a missing rule: it reads
   as handled. */
.auth-form > label:first-of-type {
  margin-top: 0;
}

/* The ancestor is load-bearing, not decoration. controls.css is linked *after*
   this file (base.html says why) and styles `input[type="password"]` at the
   same specificity as `.auth-form input` — so on a tie the later file wins and
   these declarations would silently lose. One more class in the selector settles
   it in the direction the page intends. */
.auth-page .auth-form input {
  background: var(--mf-bg-sunken);
  border: 1px solid var(--mf-border-strong);
  border-radius: var(--mf-radius-sm);
  height: var(--mf-control-height-lg);
  padding: 0 var(--mf-control-padding-x);
  color: var(--mf-fg);
  font-size: var(--mf-text-base);
  font-family: inherit;
  width: 100%;
  min-width: 0;
  transition: border-color var(--mf-dur-fast) var(--mf-ease),
              box-shadow var(--mf-dur-fast) var(--mf-ease),
              background-color var(--mf-dur-fast) var(--mf-ease);
}

.auth-page .auth-form input::placeholder {
  color: var(--mf-fg-subtle);
}

/* One rule, on :focus-visible only. It used to be two: a bare `:focus` that
   tinted the border plus a `:focus-visible` that added the ring. The split was
   deliberate — border for mouse users, ring for keyboard users — and it is still
   the wrong trade, because `outline: none` on a bare `:focus` is the exact
   declaration that leaves a keyboard user with nothing to see on any browser
   that has not implemented `:focus-visible`. tests/test_responsive.py holds this
   line for every stylesheet in the app, and this file was the last offender.
   A mouse user loses a border tint they never looked for; a keyboard user keeps
   the only indicator they have. */
.auth-page .auth-form input:focus-visible {
  outline: none;
  border-color: var(--mf-accent);
  background: var(--mf-surface-raised);
  box-shadow: var(--mf-focus-ring);
}

/* Browsers set aria-invalid on constraint-validation failures and the server
   sets it on rejected fields, so the danger state needs no extra markup. */
.auth-page .auth-form input:user-invalid,
.auth-page .auth-form input[aria-invalid="true"] {
  border-color: var(--mf-danger-fg);
  background: var(--mf-danger-wash);
}

/* Wide-spaced monospace digits: it shows how many are expected without a word
   of instruction, and a typo is obvious at a glance. Lived in a <style> block
   inside reset_code.html until now, which is how a rule that belongs to the
   design system ends up being the one nobody can find. The trailing letter-space
   is compensated with a text-indent so the digits stay optically centred. */
.auth-page .auth-form input.code-input {
  letter-spacing: 0.5em;
  text-indent: 0.5em;
  text-align: center;
  font-size: var(--mf-text-xl);
  font-family: var(--mf-font-mono);
  font-variant-numeric: tabular-nums;
}

/* `display: flex` is stated, not assumed. This rule already set `flex-direction`
   and `gap`, and a <label> is not a flex container by default — so both
   declarations were inert and the box sat against its text with whatever
   whitespace the markup happened to contain. */
.auth-checkbox-row {
  display: flex;
  flex-direction: row !important;
  align-items: center;
  gap: var(--mf-space-2) !important;
  margin-top: var(--mf-space-4) !important;
  cursor: pointer;
  color: var(--mf-fg-muted);
  font-size: var(--mf-text-sm);
}

/* Slightly smaller than the app-wide 18px: this row is a quiet "remember me"
   under the fields, not a setting anybody came here to change. The box itself is
   drawn by controls.css — `accent-color` used to live here and is gone, because
   `appearance: none` makes it a no-op and a declaration that does nothing is
   worse than no declaration: the next person reads it as the thing that sets the
   colour. */
.auth-checkbox-row input[type="checkbox"] {
  width: 16px;
  height: 16px;
  cursor: pointer;
}

/* The caption under a field. Separate from .auth-note (which sits under a
   button) because this one is the target of an aria-describedby and therefore
   has to hug the control it describes — a gap here reads to a sighted user as
   belonging to the *next* label. */
.auth-hint {
  margin: var(--mf-space-1) 0 0;
  font-size: var(--mf-text-xs);
  line-height: var(--mf-leading-normal);
  color: var(--mf-fg-subtle);
}

/* A hint used as a standalone statement rather than a field caption — "This server
   cannot send mail." — leads with a bold line so the fact is readable before the
   explanation. Same shape as .auth-status, one weight quieter, because these sit in
   the card's link footer rather than in the flow of the form. */
.auth-hint strong {
  display: block;
  color: var(--mf-fg-muted);
  font-weight: var(--mf-weight-semibold);
}

/* Inside the form and inside the link footer the gap already comes from the flex
   column, so the caption's own margin would double it — and a hint sitting 12px
   under its field reads as belonging to the label below it instead. */
.auth-form .auth-hint,
.auth-links .auth-hint {
  margin: 0;
}

.auth-hint a,
.auth-note a {
  color: var(--mf-fg-muted);
  text-decoration: underline;
  border-radius: var(--mf-radius-xs);
}

.auth-hint a:hover,
.auth-note a:hover {
  color: var(--mf-fg);
}

.auth-optional {
  color: var(--mf-fg-subtle);
  font-weight: var(--mf-weight-normal);
}

/* =============================================================================
   Buttons

   Exactly one filled button per screen, and it is always the primary path.
   Which path that *is* changes with the server's configuration — see the
   GitHub section below — so the templates swap the class, never the shape.
   ============================================================================= */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--mf-space-2);
  border-radius: var(--mf-radius-sm);
  font-weight: var(--mf-weight-semibold);
  font-size: var(--mf-text-md);
  letter-spacing: var(--mf-tracking-tight);
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  transition: transform var(--mf-dur-fast) var(--mf-ease),
              background-color var(--mf-dur-normal) var(--mf-ease),
              border-color var(--mf-dur-normal) var(--mf-ease),
              box-shadow var(--mf-dur-normal) var(--mf-ease),
              filter var(--mf-dur-normal) var(--mf-ease);
}

.btn-primary {
  background: var(--mf-gradient-brand);
  color: var(--mf-fg-on-accent);
  box-shadow: var(--mf-shadow-brand);
}

.btn-primary:hover {
  transform: translateY(-1px);
  box-shadow: var(--mf-shadow-brand), var(--mf-shadow-md);
}

/* THE FOCUS RING ON A BUTTON HAS TO BE RESTATED HERE, and it is not a style
   preference. tokens.css draws the app-wide ring with
   `:focus-visible { box-shadow: var(--mf-focus-ring) }` — specificity (0,1,0).
   `.btn-primary` is also (0,1,0), and this file loads *after* tokens.css, so on a
   focused primary button the tie went to `box-shadow: var(--mf-shadow-brand)` and
   the ring simply never painted. The submit button on the login form — the single
   most important focus target in the application — had no visible focus state at
   all, and it looked fine, because a button under the mouse is never focus-visible.

   Each rule below keeps its own elevation *and* the ring, ring first so it is not
   the shadow that gets clipped. */
.btn:focus-visible {
  outline: none;
  box-shadow: var(--mf-focus-ring);
}

.btn-primary:focus-visible {
  box-shadow: var(--mf-focus-ring), var(--mf-shadow-brand);
}

/* The secondary path's submit. Outlined rather than filled, which is the whole
   point: on a screen where GitHub is offered, the password form is the fallback
   and has to look like one, or the page has two primary actions and answers
   nobody's question about which to use. */
.btn-outline {
  background: transparent;
  border-color: var(--mf-border-strong);
  color: var(--mf-fg);
}

.btn-outline:hover {
  background: var(--mf-surface-raised);
  border-color: var(--mf-border-accent);
}

.btn-outline:active {
  background: var(--mf-surface-hover);
}

.auth-submit {
  margin-top: var(--mf-space-5);
  width: 100%;
  height: var(--mf-control-height-lg);
  padding: 0 var(--mf-space-5);
}

/* =============================================================================
   The card footer, and the shell footer

   .auth-links is the row of onward journeys inside the card ("Forgot your
   password?", "Already have an account?"). It is separated by a rule, because
   those used to be loose links hanging under the form with an inline
   `style="margin-top:12px"` on each — three of them, with three different
   margins, on three different pages.
   ============================================================================= */

.auth-links {
  display: flex;
  flex-direction: column;
  gap: var(--mf-space-3);
  margin-top: var(--mf-space-5);
  padding-top: var(--mf-space-4);
  border-top: 1px solid var(--mf-border);
  font-size: var(--mf-text-sm);
}

.auth-links a {
  color: var(--mf-fg-muted);
  text-decoration: none;
  border-radius: var(--mf-radius-xs);
}

.auth-links a:hover {
  color: var(--mf-fg);
  text-decoration: underline;
}

/* The shell footer. Outside the card on purpose: it belongs to the site, not to
   whichever step of the flow the card is showing. */
.auth-footer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--mf-space-2) var(--mf-space-4);
  font-size: var(--mf-text-xs);
  color: var(--mf-fg-subtle);
}

.auth-footer-legal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--mf-space-2) var(--mf-space-4);
}

/* Not muted into invisibility. § 5 DDG asks for the imprint to be "leicht
   erkennbar und unmittelbar erreichbar", and these six pages had no footer at
   all — the only route to it was the public navigation, which the auth screens
   deliberately do not render. The quietest thing down here should be the "back
   to homepage" link, not the legal one. */
.auth-footer-legal a {
  color: var(--mf-fg-muted);
  font-weight: var(--mf-weight-medium);
  text-decoration: none;
  border-radius: var(--mf-radius-xs);
}

.auth-footer-legal a:hover {
  color: var(--mf-fg);
  text-decoration: underline;
}

.auth-footer-home {
  color: var(--mf-fg-subtle);
  text-decoration: none;
  border-radius: var(--mf-radius-xs);
  margin-left: auto;
}

.auth-footer-home:hover {
  color: var(--mf-fg);
}

/* Kept for anything still reaching for the old name. */
.auth-back {
  display: inline-block;
  font-size: var(--mf-text-sm);
  color: var(--mf-fg-subtle);
  text-decoration: none;
  border-radius: var(--mf-radius-xs);
  transition: color var(--mf-dur-fast) var(--mf-ease);
}

.auth-back:hover {
  color: var(--mf-fg);
}

/* =============================================================================
   GitHub

   The hierarchy, decided once and applied to both screens that offer it:
   **GitHub is the primary path, the password form is the fallback.**

   Not a fashion. A module developer now needs a linked GitHub account before
   they can publish anything at all (routes/store.py, store_ownership.py), so an
   account created with a password and no linked identity is an account that
   cannot do the thing people come here to do. Leading with GitHub puts the
   step they need first instead of after a form they will have to supplement
   anyway. The password path stays fully present, because sign-up through
   GitHub is gated (account age, verified address, allowlists) and can
   legitimately refuse somebody the form would accept.

   Two consequences the templates implement:

     * the GitHub button comes first in the DOM, so it is also first in the
       focus order — a keyboard user meets the primary action first;
     * the password submit switches to .btn-outline when GitHub is offered and
       back to .btn-primary when it is not, so there is never a screen with two
       filled buttons and never one with none.

   The divider between the two is a labelled separator ("or with a password"),
   not a bare "or" — that word after a button reads as an afterthought, which is
   exactly what the GitHub block was on the register page.
   ============================================================================= */

.auth-divider {
  display: flex;
  align-items: center;
  gap: var(--mf-space-3);
  margin: var(--mf-space-5) 0 var(--mf-space-4);
  color: var(--mf-fg-subtle);
  font-size: var(--mf-text-2xs);
  text-transform: uppercase;
  letter-spacing: var(--mf-tracking-wider);
  font-weight: var(--mf-weight-semibold);
}

/* Rules drawn with pseudo-elements rather than a border on the label, so the
   line stays centred on the text at any width. */
.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--mf-border);
}

/* Filled, and filled in GitHub's own near-black rather than the brand gradient.
   Filled because it is the primary action; GitHub-black because a purple
   "Continue with GitHub" costs the one thing this button has going for it,
   which is that people recognise it without reading. */
.btn-github {
  background: var(--mf-github-bg);
  border: 1px solid var(--mf-border-strong);
  color: var(--mf-github-fg);
  box-shadow: var(--mf-shadow-md);
}

.btn-github:hover {
  background: var(--mf-github-bg-hover);
  border-color: var(--mf-border-accent);
  transform: translateY(-1px);
  box-shadow: var(--mf-shadow-md), var(--mf-shadow-sm);
}

.btn-github:active {
  transform: none;
  background: var(--mf-github-bg-active);
}

/* Same reasoning as .btn-primary:focus-visible above — this one carries an
   elevation shadow too, so it would swallow the ring the same way. */
.btn-github:focus-visible {
  box-shadow: var(--mf-focus-ring), var(--mf-shadow-md);
}

.btn-github.auth-submit {
  /* It is the first thing in the card body, so it does not need the top margin
     that separates the form's submit from the last field above it. */
  margin-top: 0;
}

.btn-github-mark {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
}

/* The explanatory line under an optional field or a secondary button. Both
   classes were already in the markup with no rule behind them, so the text
   rendered at body size and read as an instruction rather than as a footnote —
   which on the register page put a caveat about email recovery at the same
   weight as the labels. Sized down and dimmed instead, and the line height is
   kept loose because these are the only multi-line sentences on the card and at
   320px they wrap to three lines. */
.auth-note {
  margin: var(--mf-space-3) 0 0;
  font-size: var(--mf-text-sm);
  line-height: var(--mf-leading-normal);
  color: var(--mf-fg-subtle);
}

/* =============================================================================
   Wider viewports

   The only breakpoint in the file, and it does one thing: give the card more
   air and the heading one step more size once there is room for both.
   ============================================================================= */

@media (min-width: 480px) {
  .auth-page {
    padding: var(--mf-space-9) var(--mf-space-5) var(--mf-space-7);
  }

  .auth-card {
    padding: var(--mf-space-6);
  }

  .auth-card h1 {
    font-size: var(--mf-text-2xl);
  }

  .auth-step {
    font-size: var(--mf-text-xs);
  }
}
