/* idp.css — the identity provider's design system.
 *
 * One stylesheet serves both surfaces: the server-rendered sign-in pages
 * (src/api/session_routes.cpp) and the admin console (ui/). They are built by
 * different toolchains and rendered by different processes, which is exactly
 * the condition under which two stylesheets drift apart and the same control
 * ends up looking like two controls. There is one file, and it is the source
 * for both.
 *
 * The palette is white, gray, royal blue and black. Blue is reserved: it marks
 * the one thing on a page that the user is there to do, and the focus ring. A
 * page with two blue buttons has one too many.
 *
 * Controls are outlined rather than filled — a 2px border, a 4px radius, and a
 * fill that arrives on hover. That is a deliberate choice for an identity
 * provider: a sign-in form should read as plain and mechanical, not as
 * marketing.
 */

/* ── Tokens ───────────────────────────────────────────────────────────────
   Referenced by name everywhere below. A deployment that wants its own
   branding overrides these on :root and touches nothing else. */
:root {
  --paper: #ffffff;
  --paper-sunk: #f6f7f9;      /* page background, table stripes, disabled fills */
  --ink: #10131a;             /* body text; near-black rather than #000, which
                                 vibrates against white at small sizes */
  --ink-soft: #3d4451;        /* de-emphasized body text that is still text */

  --slate: #6b7280;           /* secondary text, icon strokes at rest */
  --slate-soft: #9ca3af;      /* placeholder text */
  --slate-faint: #e4e7ec;     /* hairlines, table rules, input borders */

  --royal: #2647c8;           /* the accent. Primary action, focus, active nav */
  --royal-deep: #1b348f;      /* hover/active of the above */
  --royal-soft: #eaeeff;      /* tinted fills behind selected rows and chips */
  --royal-ink: #ffffff;       /* what reads on a --royal fill: the filled
                                 button's label and its icon. Not --paper.
                                 They are the same color here and are not the
                                 same question, and a theme with a tinted card
                                 and a saturated accent needs to answer them
                                 separately. */

  /* Two tints over --paper-sunk, at the positions in the `body` rule below.
     Transparent by default, so the page is flat until a theme asks for them.
     Fully transparent black rather than the keyword `transparent`: the value
     is what a color input round-trips, and the keyword is not. */
  --gradient-start: #00000000;
  --gradient-end: #00000000;

  /* Secondaries, used only for status. Each is a hue the palette does not
     otherwise spend, so none of them can be mistaken for the accent. */
  --danger: #b42318;
  /* Derived from --danger rather than fixed, because --danger is the one a
     tenant can set. When it was a literal, branding moved the error notice's
     spine and its text to the tenant's red and left the fill behind them the
     shipped pink, so the one part of the bar with the most area on screen was
     the part that ignored the setting. Mixing against --paper keeps the tint
     following the surface as well, which matters on a dark theme where a fixed
     pale pink would be a bright block. */
  --danger-soft: color-mix(in srgb, var(--danger) 12%, var(--paper));
  --success: #17663c;
  --success-soft: #e7f4ed;
  --warning: #8a5a00;
  --warning-soft: #fdf3e2;

  --radius: 4px;
  --radius-lg: 10px;
  --border: 2px;              /* the outline weight that defines the button */

  --shadow-sm: 0 1px 2px rgba(16, 19, 26, 0.06);
  --shadow: 0 8px 24px rgba(16, 19, 26, 0.10);
  /* Mixed from --royal rather than the literal rgba() this was, which pinned
     the focus ring to the shipped blue: a tenant who set a green accent got a
     green button with a blue halo around it on focus. */
  --focus: 0 0 0 3px color-mix(in srgb, var(--royal) 35%, transparent);

  --font-ui: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* One height for every control on a row — button, input, select — so a form
     row reads as a single band rather than as things of several sizes. */
  --control-h: 40px;
  --control-h-sm: 32px;

  /* Not `light dark`. The palette is defined in light terms and a browser
     forcing dark form controls into it produces black-on-black inputs. A dark
     theme is a matter of overriding the tokens above, not of asking the UA to
     improvise one. */
  color-scheme: light;
}

*,
*::before,
*::after { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--font-ui);
  font-size: 15px;
  line-height: 1.5;
  color: var(--ink);
  /* The flat page color, with two optional tints laid over it. Their positions
     are fixed here rather than exposed, because a position is layout and this
     feature is values: a theme supplies two colors and gets the composition the
     stylesheet composes. Both are transparent by default, so this renders
     exactly as the single `background: var(--paper-sunk)` it replaces until one
     of them is set. background-color, not a third layer in the shorthand, so
     the page still has a color to paint an overscroll with. */
  background-color: var(--paper-sunk);
  background-image:
    radial-gradient(circle at 85% 10%, var(--gradient-start), transparent 50%),
    radial-gradient(circle at 15% 90%, var(--gradient-end), transparent 55%);
  background-attachment: fixed;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--royal); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:hover { color: var(--royal-deep); }

/* The icon sprite is inlined into the page so that <use> resolves without a
   second request. It defines symbols and draws nothing, but as an inline
   element it still generates a line box, which showed as a band of empty space
   above the page. It cannot hide itself with a style attribute, because the
   Content-Security-Policy has no 'unsafe-inline' in style-src. */
.idp-sprite,
#idp-sprite { display: none; }

/* One focus treatment for everything. :focus-visible rather than :focus, so a
   pointer user clicking a button does not get a ring they did not ask for. */
:focus-visible {
  outline: none;
  box-shadow: var(--focus);
  border-radius: var(--radius);
}

/* ── Icons ────────────────────────────────────────────────────────────────
   The sprite (assets/idp-icons.svg) is drawn in the double-stroke style: 1.3
   stroke on a 24 grid, butt caps, miter joins, a 2.7 unit center-to-center gap
   between parallel strokes. Consequences for the CSS: the stroke is set on the
   symbol and must not be overridden per-size, or the parallel lines thicken
   until they merge; and the icons are stroke-only, so they take their color
   from `currentColor` like text does.

   Below about 18px the twin strokes stop resolving as two lines, so there is no
   size smaller than that here. */
.icon {
  width: 24px;
  height: 24px;
  flex: none;
  display: inline-block;
  vertical-align: -0.28em;
  color: inherit;
}
/* A small button is 32px tall, and a 24px mark inside it leaves no room to
   breathe, so the mark steps back down rather than the button growing. */
.btn--sm .icon { width: 20px; height: 20px; }
.icon--lg { width: 28px; height: 28px; }
.icon--xl { width: 44px; height: 44px; }

/* The wordmark is not an icon: it has its own viewBox and its own aspect, so it
   gets its own class rather than a modifier on .icon. Both axes are set because
   a <use> of a symbol scales to the outer viewport and does not derive one
   dimension from the symbol's viewBox. The symbol is 82 by 32, and these are
   its own units doubled and halved rather than an arbitrary box: at 32 by 82
   every measurement in the drawing lands on a whole pixel, which is why the
   mark was moved up from 28 by 72. The 2.2-unit gap in the a that made it look
   soft at 28 is 2.2px here. */
.wordmark { display: block; height: 32px; width: 82px; flex: none; color: var(--royal); }
.wordmark--lg { height: 48px; width: 123px; }
/* A single letter on the wordmark's metrics but in its own 22-wide box, so the
   heights match a wordmark set beside it and only the width differs. */
.glyph { display: block; height: 32px; width: 22px; flex: none; color: var(--royal); }
.glyph--lg { height: 48px; width: 33px; }

/* ── Buttons ──────────────────────────────────────────────────────────────
   The outlined button is the default. It carries a 2px slate border at rest and
   fills with ink on hover — an inversion rather than a tint, so the hovered
   state is unmistakable at a glance and does not depend on distinguishing two
   similar grays. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-height: var(--control-h);
  padding: 0 0.9rem;
  border: var(--border) solid var(--slate);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
  font: 600 0.92rem/1 var(--font-ui);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.btn:hover {
  background: var(--ink);
  border-color: var(--ink);
  color: var(--paper);
}
/* A press moves the button 1px down. It is the cheapest possible confirmation
   that the click landed, and it costs no color. */
.btn:active { transform: translateY(1px); }

/* The one action the page exists for. Filled, so it is found without reading. */
.btn--primary {
  background: var(--royal);
  border-color: var(--royal);
  color: var(--royal-ink);
}
.btn--primary:hover {
  background: var(--royal-deep);
  border-color: var(--royal-deep);
  color: var(--royal-ink);
}

/* Destructive. Outlined at rest so it is not mistaken for the primary action,
   and only fills red once the pointer is on it and the intent is deliberate. */
.btn--danger { border-color: var(--danger); color: var(--danger); }
.btn--danger:hover { background: var(--danger); border-color: var(--danger); color: var(--paper); }

/* Borderless, for actions that sit inside a row that already has an outline of
   its own — a second box inside a box reads as a control that is not one. */
.btn--quiet { border-color: transparent; background: transparent; color: var(--slate); }
.btn--quiet:hover { background: var(--paper-sunk); border-color: var(--slate-faint); color: var(--ink); }

.btn--sm { min-height: var(--control-h-sm); padding: 0 0.6rem; font-size: 0.85rem; }
/* Square, for an icon with no label. The width matches the height exactly, so a
   row of them is a row of squares. */
.btn--icon { width: var(--control-h); padding: 0; }
.btn--sm.btn--icon { width: var(--control-h-sm); }
.btn--block { width: 100%; }

/* Off, two ways. `disabled` is the plain case. aria-disabled is the case that
   has something to say — a real disabled button takes no pointer events, so its
   tooltip could never appear, and the explanation is usually the whole point. */
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
}
.btn:disabled:hover,
.btn[aria-disabled="true"]:hover {
  background: var(--paper);
  color: var(--ink);
  border-color: var(--slate);
  transform: none;
}
.btn--primary:disabled:hover,
.btn--primary[aria-disabled="true"]:hover {
  background: var(--royal);
  border-color: var(--royal);
  color: var(--royal-ink);
}

/* ── Tooltip ──────────────────────────────────────────────────────────────
   The explanation the aria-disabled rule above exists to make room for: why a
   control is off, why a field will not take what was typed. Not for anything a
   reader must have — a tooltip does not survive touch, print, or a narrow
   window, so what only a tooltip says is what a good half of readers never get.
   Text they need is a .field__hint, in the page, always visible.

   Pure CSS on an attribute rather than a component, because both sides of this
   codebase have to be able to raise one: the console builds its DOM in
   TypeScript, the sign-in and setup pages are strings of HTML from C++, and an
   attribute is the only thing they can both write without sharing code.

   Deep royal rather than the near-black --ink, so the surface is the palette's
   and not a second dark grey competing with body text; over it --royal-ink,
   the same color the filled button's label uses, which at #ffffff on #1b348f
   clears AA several times over at this size. */
[data-tooltip] {
  position: relative;
}
[data-tooltip]::after {
  content: attr(data-tooltip);
  position: absolute;
  bottom: calc(100% + 0.4rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: 40;
  /* Wide enough for a sentence, narrow enough that it does not become a
     paragraph nobody reads standing up. */
  width: max-content;
  max-width: 18rem;
  padding: 0.35rem 0.55rem;
  border-radius: var(--radius);
  background: var(--royal-deep);
  color: var(--royal-ink);
  font-size: 0.78rem;
  font-weight: 400;
  line-height: 1.35;
  text-align: left;
  white-space: normal;
  /* Never the target of its own hover: a tooltip that can be pointed at can
     flicker on and off as the pointer crosses the seam between it and the
     control. */
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.12s ease-in;
}
/* :focus-visible and not :focus, so the tooltip does not flash on every mouse
   click of a control that happens to carry one. */
[data-tooltip]:hover::after,
[data-tooltip]:focus-visible::after {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  [data-tooltip]::after { transition: none; }
}

/* ── Fields ───────────────────────────────────────────────────────────────
   Inputs carry a 1px border, not the button's 2px: they are surfaces to be
   filled rather than things to be pressed, and matching the button weight made
   every form look like a stack of buttons. */
.field { display: flex; flex-direction: column; gap: 0.3rem; margin-bottom: 0.9rem; }
.field__label { font-size: 0.85rem; font-weight: 600; color: var(--ink); }
.field__hint { font-size: 0.78rem; color: var(--slate); }

/* The label, and opposite it whatever that field needs to offer — at present
   only "Forgot your password?". Baseline alignment rather than centre so the
   two read as one line of text; the link is lighter than the label because the
   label names the box and the link is a way out of it.

   margin-inline-start rather than a float or text-align, so the pair swap ends
   in a right-to-left locale along with everything else on the page. */
.field__row { display: flex; align-items: baseline; gap: 0.75rem; }
.field__aside {
  margin-inline-start: auto;
  font-size: 0.78rem;
  font-weight: 400;
  white-space: nowrap;
}

/* A checkbox reads to the left of its caption, so this row is horizontal and
   the whole thing is one <label> — the caption is part of the hit target,
   which on a phone is most of the target there is. */
.field--inline {
  flex-direction: row;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9rem;
  cursor: pointer;
}
.checkbox {
  width: 16px;
  height: 16px;
  flex: none;
  accent-color: var(--royal);
  cursor: pointer;
}

.input,
.select {
  width: 100%;
  min-height: var(--control-h);
  padding: 0 0.65rem;
  border: 1px solid var(--slate-faint);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
  font: 400 0.95rem var(--font-ui);
}
.input::placeholder { color: var(--slate-soft); }
.input:hover,
.select:hover { border-color: var(--slate); }
.input:focus-visible,
.select:focus-visible { border-color: var(--royal); box-shadow: var(--focus); }
.input:disabled,
.select:disabled { background: var(--paper-sunk); color: var(--slate); cursor: not-allowed; }

/* A select is a field, but it is also the only field whose value is chosen
   rather than typed, so it says so with a chevron. Drawn as a background image
   because a pseudo-element cannot be placed on a replaced element. */
.select {
  appearance: none;
  padding-right: 2.2rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='1.3' stroke-linecap='butt' stroke-linejoin='miter'%3E%3Cpath d='M6.95 8.05L12 13.10L17.05 8.05'/%3E%3Cpath d='M5.05 9.95L12 16.90L18.95 9.95'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.55rem center;
  background-size: 18px 18px;
}

/* A field the operator has fixed in configuration — the realm on a client
   sign-in page. Shown, because the user is entitled to know which realm they
   are authenticating against, but flat and unfocusable: it is a statement of
   fact, not an input, and rendering it as a disabled <input> invited people to
   click at it and wonder why nothing happened. */
.field__fixed {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  min-height: var(--control-h);
  padding: 0 0.65rem;
  border: 1px dashed var(--slate-faint);
  border-radius: var(--radius);
  background: var(--paper-sunk);
  color: var(--slate);
  font-size: 0.95rem;
}
.field__fixed .icon { color: var(--slate-soft); }

/* ── Passwords ────────────────────────────────────────────────────────────
   A password box and its Show/Hide control.

   The button sits inside the box, over its right edge, rather than beside it.
   Beside it, the control strip was as wide as a word of running text and the
   field it belonged to was visibly shorter than every other field on the form,
   which made the password look like a different kind of question.

   The cost is the padding reserved on the right, which the browser's own
   password manager also wants for its offer. It is reserved rather than
   negotiated: a manager that puts its key over ours is a cosmetic collision,
   where a manager that pushes our button out of the box is a control the user
   cannot reach. */
.password { position: relative; display: block; }
/* Room for the wider of the two words plus the gap. Applied to the box rather
   than to a wrapper so that the text scrolls out from under the button rather
   than behind it. */
.password .input { width: 100%; padding-right: 4.4rem; }
/* Quiet: no fill and no border, because a bordered button inside a bordered
   box reads as two controls. Sized to the taller word so the label swapping
   between Show and Hide does not move anything.

   Centred by insets rather than by `translateY(-50%)`, because `.btn:active`
   presses a button down a pixel with a transform of its own -- and a transform
   replaces rather than composes, so clicking dropped this button half its own
   height down the field. */
.reveal {
  position: absolute;
  top: 0.25rem;
  right: 0.3rem;
  bottom: 0.25rem;
  min-width: 3.7rem;
  min-height: 0;
  padding: 0 0.4rem;
  border: 0;
  background: none;
  box-shadow: none;
  color: var(--slate);
  font-weight: 600;
}
/* The one-pixel press is given up rather than reimplemented. The button's
   effect is immediate and unmistakable -- the field beside it changes from
   dots to letters -- so it needs no second confirmation that it was clicked. */
.reveal:active { transform: none; }
.reveal:hover { background: var(--paper-sunk); color: var(--ink); }
.reveal:focus-visible { box-shadow: var(--focus); }

/* The rules a password has to satisfy, ticked live as it is typed.
   Stated up front rather than after a rejected submission: a rule you are told
   about only once you have broken it is a rule you meet by guessing, and the
   guessing is done in a field whose contents you cannot see. */
.rules { margin: 0.1rem 0 0.9rem; padding: 0; list-style: none; display: grid; gap: 0.3rem; }
.rules__intro { margin: 0 0 0.45rem; font-size: 0.78rem; color: var(--slate); }
.rules li {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.85rem;
  color: var(--slate);
}
/* The same mark the password policy page uses for a boolean, at the size a
   line of text can carry. It is the same question in both places -- is this
   condition on or off -- and answering it with a tick box there and a
   typographic bullet here made them look like different kinds of fact.
   Both icons are in the markup and CSS shows one, so nothing scripts the
   swap: the state is already in the DOM as data-met. */
.rules__mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 1.65rem;
  height: 1.65rem;
  border: 1px solid var(--slate-faint);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--slate-soft);
  box-shadow: var(--shadow-sm);
}
.rules__mark .icon { width: 1rem; height: 1rem; }
/* A checkable rule shows a cross from the moment it is on screen, and swaps to
   a tick when it is satisfied. An empty mark was tried first, on the reasoning
   that an untested rule has not failed -- but a column of empty boxes reads as
   a rendering fault rather than as a state, and it gave no starting picture to
   change from, so the ticks arrived without anything visibly having moved. The
   cross is not an accusation here because the list is a statement of what the
   password will need, not a report on one that was submitted; the colour is
   what keeps it that way. */
.rules__mark .icon { display: none; }
.rules li .rules__mark .icon:last-child { display: block; }
.rules li[data-met="yes"] .rules__mark .icon:last-child { display: none; }
.rules li[data-met="yes"] .rules__mark .icon:first-child { display: block; }
.rules li[data-met="yes"] {
  color: var(--ink);
}
.rules li[data-met="yes"] .rules__mark {
  border-color: var(--success);
  background: var(--success-soft);
  color: var(--success);
}
/* Unmet rules are never drawn in the danger color while typing. Everything is
   unmet at the first keystroke, and a form that opens covered in red has
   accused the user of a mistake they have not had the chance to make. Red is
   reserved for the notice shown after a submission is actually refused. */
.rules li:not([data-met="yes"]) .rules__mark { color: var(--slate); }

/* ── Notices ──────────────────────────────────────────────────────────────
   A line of consequence: why the last attempt failed, or that something
   worked. Tinted fill, a 3px spine in the status color, no border — it is a
   message, not a container. */
.notice {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  padding: 0.6rem 0.75rem;
  margin: 0 0 1rem;
  border-left: 3px solid var(--slate);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--paper-sunk);
  font-size: 0.88rem;
}
.notice .icon { margin-top: 0.05em; }
.notice--error { border-left-color: var(--danger); background: var(--danger-soft); color: var(--danger); }
.notice--success { border-left-color: var(--success); background: var(--success-soft); color: var(--success); }
.notice--warning { border-left-color: var(--warning); background: var(--warning-soft); color: var(--warning); }
/* A notice that has to enumerate rather than assert: which saved values are not
   the ones being enforced. Set at the notice's own size, because a list inside
   a message is still the message and not a table under it. */
.notice p { margin: 0; }
.notice__list { margin: 0.35rem 0 0; padding-left: 1.1rem; display: grid; gap: 0.2rem; }

/* ── Card ─────────────────────────────────────────────────────────────────
   The generic panel: paper, a hairline, a large radius. No hover behavior,
   because a card is not a link. */
.card {
  background: var(--paper);
  border: 1px solid var(--slate-faint);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: 1.25rem 1.4rem;
}

/* ── Sign-in ──────────────────────────────────────────────────────────────
   The page a user meets before anything else, so it is the whole viewport with
   one card centered in it and nothing else competing.

   `min-height: 100svh` rather than `100vh`: on a phone the dynamic browser
   chrome makes vh taller than what is actually on screen, which pushed the
   Sign in button under the address bar on exactly the page where the button
   matters most. */
.auth {
  min-height: 100svh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1.5rem;
}
.auth__card {
  width: 100%;
  max-width: 24rem;
  background: var(--paper);
  border: 1px solid var(--slate-faint);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  padding: 1.75rem 1.6rem 1.5rem;
}
.auth__brand {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 1.25rem;
  color: var(--royal);
}
.auth__brand-name {
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--ink);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.auth__title { margin: 0 0 0.2rem; font-size: 1.3rem; font-weight: 700; letter-spacing: -0.02em; }
.auth__lede { margin: 0 0 1.25rem; font-size: 0.9rem; color: var(--slate); }
.auth__submit { margin-top: 1.15rem; }

/* The proof-of-work sentence, between the last field and the submit button.
   Its own margins are dropped: a bare <p> carries the browser's 1em top and
   bottom, which is a paragraph's worth of air on either side of one quiet
   line of hint text.

   The solver removes the paragraph once it has a solution, and marks the form.
   Without that mark the field above and the button below keep their own
   margins, which then meet and add up to twice the usual gap -- a hole exactly
   where a sentence used to be, which reads as something having gone missing
   rather than something having finished. */
.auth__work { margin: 0; }
.auth__form--solved .auth__submit { margin-top: 0.25rem; }

/* The "New here? Create an account" line. Quiet on purpose: it sits directly
   under the submit button, and anything that competes with the button would be
   offering a detour to people who do have an account and meant to press it. */
.auth__aside {
  margin: 0.85rem 0 0;
  text-align: center;
  font-size: 0.875rem;
}
/* Beneath the card: what the sign-in is for. The application the user is being
   returned to, and under it the hostname serving the form. The application
   leads because it is the name the user recognises — arriving here by redirect,
   the identity provider's hostname is often one they have never seen. The host
   still follows, in mono, as the technical fact rather than the answer. */
.auth__foot {
  margin: 1rem 0 0;
  text-align: center;
  font-size: 0.78rem;
  color: var(--slate);
}
.auth__host { font-family: var(--font-mono); color: var(--ink); }
/* A tenant's uploaded or linked mark, in place of the default shield. Bounded
   in both directions: the file is supplied by the tenant and an intrinsic size
   of 2000px would otherwise decide the layout of the sign-in page. */
.auth__logo {
  display: block;
  max-height: 40px;
  max-width: 180px;
  width: auto;
  height: auto;
}
/* A badge or group mark in the footer. Smaller than the logo and centred in the
   paragraph that carries it, and bounded for the same reason: the file is the
   tenant's, and its intrinsic size is not a layout decision. */
.auth__footer-image {
  display: inline-block;
  max-height: 28px;
  max-width: 140px;
  width: auto;
  height: auto;
}
/* The tenant's copyright or legal line, below the origin. Quieter than the
   origin, because it is the least important thing on the page. */
.auth__legal {
  margin: 0.35rem 0 0;
  text-align: center;
  font-size: 0.72rem;
  color: var(--slate-soft);
}
.auth__link { color: var(--slate); text-decoration: underline; }
.auth__link:hover { color: var(--royal); }

@media (max-width: 480px) {
  .auth { padding: 0; align-items: stretch; }
  .auth__card {
    max-width: none;
    min-height: 100svh;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
}

/* Touch targets. A 40px control is comfortable under a mouse and a coin-toss
   under a thumb, and these are the controls a user meets first. */
@media (max-width: 640px) {
  :root { --control-h: 46px; --control-h-sm: 38px; }
  /* The checkbox grows too. It does not scale with --control-h because a
     46px checkbox looks broken, but 16px under a thumb is a miss. */
  .checkbox { width: 20px; height: 20px; }
  .field--inline { min-height: 40px; }
}

@media (prefers-reduced-motion: reduce) {
  .btn { transition: none; }
  .btn:active { transform: none; }
}
