/* =========================================================================
   Sign-in: the dialog, and the gate that stands in front of a signed-out tool.

   Split out of style.css because the three studio tools do not load it -- they
   carry their own chrome -- and they still have to be able to put up the
   same sign-in dialog rather than a second one that merely looks similar.
   Every custom property below therefore carries a literal fallback, so this
   file is correct on a page that defines none of the site's tokens.
   ========================================================================= */

/* --- the two themes -------------------------------------------------------

   The site is dark by default and light by choice (assets/js/theme.js puts
   data-theme on <html>; no attribute at all means dark). This file is loaded
   by pages that bring none of style.css's tokens, so it carries its own
   palette, named --auth-* so it cannot collide with a tool's --surface or
   --text: dark on the bare :root, the light values (the dialog and the gate
   exactly as they were) under [data-theme="light"], written with :where() so
   neither adds specificity. The values are the site's own (style.css):
   dialog = --card, fields = --field, outlines = --control-line, accent =
   --blue, text on it = --on-blue. Contrast, dark: ink 15.5 and ink-2 6.5 on
   the dialog; accent 5.6 on the dialog; #00224d 5.2 on the accent; field
   outline 3.9 against the field; error 5.9 and ok 6.1 on their tints. */

:root {
  --auth-ground: #000000; /* the gate */
  --auth-card: #1d1d1f; /* the dialog */
  --auth-field: #2c2c2e;
  --auth-hover: #2c2c2e; /* an outline button under the pointer */
  --auth-line: #86868b; /* field and outline-button edges */
  --auth-ghost-line: #86868b; /* the gate's "Back" pill */
  --auth-ghost-hover: rgba(255, 255, 255, 0.1);
  --auth-ink: #f5f5f7;
  --auth-ink-2: #a1a1a6;
  --auth-accent: #2997ff;
  --auth-accent-hover: #4aa7ff;
  --auth-on-accent: #00224d;
  --auth-err-bg: #41292a;
  --auth-err-ink: #ff8a84;
  --auth-ok-bg: #203a28;
  --auth-ok-ink: #30d158;
  --auth-close-hover: rgba(255, 255, 255, 0.1);
  --auth-scrim: rgba(0, 0, 0, 0.6);
  /* On black a shadow alone gives the dialog no edge: a faint rim does. */
  --auth-shadow: 0 0 0 1px rgba(255, 255, 255, 0.1), 0 24px 60px rgba(0, 0, 0, 0.7);
}

:root:where([data-theme='light']) {
  --auth-ground: #ffffff;
  --auth-card: #ffffff;
  --auth-field: #ffffff;
  --auth-hover: #f5f5f7;
  /* A field's edge is what says "field": 3.6:1 on white (#c7c7cc was 1.7:1,
     under the 3:1 a control's boundary needs). The site's --control-line. */
  --auth-line: #86868b;
  --auth-ghost-line: rgba(0, 0, 0, 0.18);
  --auth-ghost-hover: transparent;
  --auth-ink: #1d1d1f;
  --auth-ink-2: #6e6e73;
  --auth-accent: #0071e3;
  --auth-accent-hover: #0077ed;
  --auth-on-accent: #ffffff;
  --auth-err-bg: #fdecea;
  --auth-err-ink: #8a1c12; /* 7.6:1 on the tint above */
  --auth-ok-bg: #e8f5ec;
  --auth-ok-ink: #14532d;
  --auth-close-hover: rgba(0, 0, 0, 0.06);
  --auth-scrim: rgba(0, 0, 0, 0.45);
  --auth-shadow: 0 24px 60px rgba(0, 0, 0, 0.28);
}

/* Printed, the dialog and the gate are light, like the rest of the page. */
@media print {
  :root {
    --auth-ground: #ffffff;
    --auth-card: #ffffff;
    --auth-field: #ffffff;
    --auth-hover: #f5f5f7;
    --auth-line: #c7c7cc;
    --auth-ghost-line: rgba(0, 0, 0, 0.18);
    --auth-ghost-hover: transparent;
    --auth-ink: #1d1d1f;
    --auth-ink-2: #6e6e73;
    --auth-accent: #0071e3;
    --auth-accent-hover: #0077ed;
    --auth-on-accent: #ffffff;
    --auth-err-bg: #fdecea;
    --auth-err-ink: #8a1c12;
    --auth-ok-bg: #e8f5ec;
    --auth-ok-ink: #14532d;
    --auth-close-hover: rgba(0, 0, 0, 0.06);
    --auth-scrim: rgba(0, 0, 0, 0.45);
    --auth-shadow: 0 24px 60px rgba(0, 0, 0, 0.28);
  }
}

/* --- the dialog ---------------------------------------------------------- */

.auth-scrim {
  position: fixed;
  inset: 0;
  z-index: 9500;
  display: grid;
  place-items: center;
  padding: 20px;
  background: var(--auth-scrim);
}

.auth-scrim[hidden] { display: none; }

/* Locks the page behind the dialog so a scroll gesture does not move the
   thing the reader is not currently looking at. */
html.auth-open { overflow: hidden; }

.auth-dialog,
.auth-dialog *,
.gate,
.gate * {
  /* The studio tools do not load style.css and so bring no reset with them.
     Without this the dialog's padding is added to its width and it lands
     56px wider there than on the pages it was designed against. */
  box-sizing: border-box;
}

.auth-dialog {
  position: relative;
  /* Checkboxes in the theme's accent, not the browser's own blue. */
  accent-color: var(--auth-accent);
  width: min(420px, 100%);
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  padding: 28px;
  border-radius: var(--r-lg, 28px);
  background: var(--auth-card);
  color: var(--auth-ink);
  box-shadow: var(--auth-shadow);
}

.auth-dialog h2 {
  margin: 0 0 6px;
  font-size: 24px;
  font-weight: 600;
  letter-spacing: -0.022em;
}

.auth-sub {
  margin: 0 0 18px;
  font-size: 14px;
  color: var(--auth-ink-2);
}

.auth-dialog label {
  display: block;
  margin-bottom: 4px;
  font-size: 12px;
  font-weight: 600;
  color: var(--auth-ink-2);
}

.auth-dialog input {
  width: 100%;
  height: 44px;
  margin-bottom: 14px;
  padding-inline: 12px;
  border: 1px solid var(--auth-line);
  border-radius: 10px;
  background: var(--auth-field);
  color: var(--auth-ink);
  font: inherit;
  font-size: 16px; /* 16px stops iOS zooming the page on focus */
}

/* The dialog's primary action. It carried a `btn-pill` class that is not
   defined anywhere in this stylesheet, so the most important button on the
   site's only form rendered as a raw browser button -- grey, square, and
   indistinguishable from "Continue with Google" beneath it. It now uses the
   same blue pill as every other primary action on the site. */
.auth-submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 44px;
  margin-top: 4px;
  padding: 12px 23px;
  border-radius: var(--r-pill, 980px);
  background: var(--auth-accent);
  border: 1px solid var(--auth-accent);
  color: var(--auth-on-accent);
  font-size: 17px;
  line-height: 1.2;
  letter-spacing: -0.022em;
  cursor: pointer;
  transition: background 140ms var(--ease, cubic-bezier(.28,.11,.32,1)), border-color 140ms var(--ease, cubic-bezier(.28,.11,.32,1));
}

.auth-submit:hover:not(:disabled) {
  background: var(--auth-accent-hover);
  border-color: var(--auth-accent-hover);
}

/* Busy state: the request is in flight and pressing again would send a second
   one. Dimmed rather than restyled, so it is still recognisably the button. */
.auth-submit:disabled {
  opacity: 0.55;
  cursor: default;
}

.auth-alt,
.auth-link {
  display: block;
  width: 100%;
  min-height: 44px;
  margin-top: 10px;
  border: 1px solid var(--auth-line);
  border-radius: var(--r-pill, 980px);
  background: var(--auth-card);
  color: var(--auth-ink);
  font: inherit;
  font-size: 15px;
  cursor: pointer;
}

.auth-link {
  border: 0;
  color: var(--auth-accent);
  text-decoration: underline; /* a link is underlined, not blue alone */
}

.auth-alt:hover { background: var(--auth-hover); }

.auth-dialog button[disabled],
.auth-dialog input[disabled],
.auth-dialog select[disabled] { opacity: 0.55; cursor: default; }

/* The role question and the professional half behind it. The select is styled
   to match the inputs rather than left native, because a native select next to
   four rounded fields is the one control that looks like it belongs to a
   different form. */
.auth-dialog select {
  width: 100%;
  height: 44px;
  margin-bottom: 14px;
  padding-inline: 10px;
  border: 1px solid var(--auth-line);
  border-radius: 10px;
  background: var(--auth-field);
  color: var(--auth-ink);
  font: inherit;
  font-size: 16px;   /* 16px stops iOS zooming the page on focus */
}

/* Asking to be verified is a checkbox, not a field: it sits on its own line
   with the box beside the words rather than above them. */
.auth-dialog .auth-check {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin: 2px 0 4px;
  font-weight: 500;
  cursor: pointer;
}

.auth-dialog .auth-check input {
  width: 18px;
  height: 18px;
  margin: 2px 0 0;
  flex: none;
}

.auth-dialog .auth-hint {
  margin: 0 0 14px;
  font-size: 13px;
  line-height: 1.4;
  color: var(--auth-ink-2);
}

/* Errors carry an icon-free but explicit prefix in the text itself, and are
   announced: the element is role="alert" in the markup. */
.auth-error {
  margin: 0 0 14px;
  padding: 10px 12px;
  border-radius: 10px;
  background: var(--auth-err-bg);
  color: var(--auth-err-ink);
  font-size: 14px;
}

.auth-error.is-ok {
  background: var(--auth-ok-bg);
  color: var(--auth-ok-ink);
}

.auth-close {
  position: absolute;
  top: 8px;
  right: 8px;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--auth-ink-2);
  cursor: pointer;
}

.auth-close:hover { background: var(--auth-close-hover); }
.auth-close svg { width: 18px; height: 18px; }

/* auth.js labels the close button with a .visually-hidden span. The studio
   tools do not load style.css, which defines that class, so the word "Close"
   showed above the X there: the same rule, scoped to the dialog. */
.auth-close .visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

@media (max-width: 1099px) {
  /* In the collapsed menu the bar's auto margin has nothing to push against,
     and the hairline would cut across a stacked list. */
  .globalnav-auth {
    margin-inline-start: 0;
    margin-top: 10px;
  }

  .globalnav-auth a,
  .globalnav-auth button {
    width: 100%;
    justify-content: center;
    height: 50px;
    font-size: 17px;
  }
}

/* `hidden` must actually hide. .auth-link and .auth-confirm both set `display`
   from a class, which outranks the user-agent's `[hidden] { display: none }` --
   so "Forgot password?" stayed on screen and in the tab order after switching
   to sign-up, offering to reset a password that does not exist yet. Scoping the
   rule to the dialog gives it the specificity to win. */
.auth-dialog [hidden] {
  display: none;
}

.auth-switch {
  margin: 4px 0 0;
  text-align: center;
}

.auth-switch .auth-link {
  /* A real 44px target: this is the control somebody uses when they have
     realised they are on the wrong side of the dialog entirely. */
  min-height: 44px;
  padding-inline: 8px;
}

/* A closed Google or Apple window is not an error: the person closed it.
   Said in the box's place, in the dialog's own neutral tones. */
.auth-error.is-note {
  background: var(--auth-hover);
  color: var(--auth-ink);
}

/* --- Google and Apple (auth-providers.js draws the inside) ----------------

   Each brand's own button, as its guidelines ask, and the same size as each
   other (Apple: "no smaller than other sign-in buttons"): 44 px high (the
   site's smallest target; both brands let the height change), full width,
   a pill (both allow one), logo and title centred together, the same type
   size.

   Google ("Sign in with Google" branding): the four-colour G at 20 px,
   never recoloured; light theme #fff with a #747775 edge and #1f1f1f text,
   dark theme #131314 with a #8e918f edge and #e3e3e3 text; Roboto Medium
   where the device has it (no web font is loaded: that would be a request
   to Google before any consent), the system font otherwise.

   Apple (Human Interface Guidelines, Sign in with Apple, web): black with
   white logo and title on a light page, white with black on a dark one,
   and no other colour; the system font. Hover only dims a little: neither
   brand defines a hover colour beyond a light state layer. */
.auth-or {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 18px 0 12px;
  font-size: 12px;
  color: var(--auth-ink-2);
}

.auth-or::before,
.auth-or::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--auth-line);
  opacity: 0.45;
}

.auth-providers {
  display: grid;
  gap: 10px;
}

.auth-provider {
  --gsi-fill: #ffffff;
  --gsi-edge: #747775;
  --gsi-ink: #1f1f1f;
  --gsi-layer: rgba(31, 31, 31, 0.08);
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  width: 100%;
  min-height: 44px;
  margin: 0;
  padding: 0 12px;
  border-radius: var(--r-pill, 980px);
  font-size: 16px;
  font-weight: 500;
  line-height: 20px;
  text-align: center;
  cursor: pointer;
}

.auth-provider[data-provider='google.com'] {
  border: 1px solid var(--gsi-edge);
  background: var(--gsi-fill);
  color: var(--gsi-ink);
  font-family: Roboto, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
  letter-spacing: 0.25px;
}

/* Hover only where there is a pointer that hovers: on a touch screen the
   state stays on the last button touched. */
@media (hover: hover) {
  .auth-provider[data-provider='google.com']:hover:not(:disabled) {
    background-image: linear-gradient(var(--gsi-layer), var(--gsi-layer));
  }
}

/* Dark: Google's dark button. The light one is the bare rule above, so a
   tool page with no data-theme at all still shows a correct Google button. */
:root:not([data-theme='light']) .auth-provider[data-provider='google.com'] {
  --gsi-fill: #131314;
  --gsi-edge: #8e918f;
  --gsi-ink: #e3e3e3;
  --gsi-layer: rgba(227, 227, 227, 0.08);
}

.auth-provider[data-provider='apple.com'] {
  border: 1px solid #000000;
  background: #000000;
  color: #ffffff;
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  letter-spacing: -0.01em;
}

:root:not([data-theme='light']) .auth-provider[data-provider='apple.com'] {
  border-color: #ffffff;
  background: #ffffff;
  color: #000000;
}

@media (hover: hover) {
  .auth-provider[data-provider='apple.com']:hover:not(:disabled) { opacity: 0.86; }
}

.auth-provider:focus-visible {
  outline: 3px solid var(--auth-accent);
  outline-offset: 2px;
}

.auth-provider-logo {
  display: block;
  flex: none;
}

/* The Apple logo sits a touch high, as in Apple's own button, so its leaf
   and the title's capitals line up. */
.auth-provider[data-provider='apple.com'] .auth-provider-logo { margin-top: -3px; }

/* Narrow phones (under 360 px): the brands' titles stay on one line, as both
   guidelines expect -- at 16 px the longer ones ("Jatka Google-tunnuksella",
   "Continuar com a Google", "Kontynuuj przez Google") wrapped inside the
   dialog's and the Account card's buttons at 320 px. Google's own size,
   14 px, with its 8 px icon gap and no extra tracking; both buttons alike,
   so they stay the same size as each other. The height stays 44 px. */
@media (max-width: 359.98px) {
  .auth-provider {
    gap: 8px;
    padding: 0 8px;
    font-size: 14px;
  }

  .auth-provider[data-provider='google.com'],
  .auth-provider[data-provider='apple.com'] { letter-spacing: 0; }
}

@media print {
  .auth-provider[data-provider='google.com'] {
    --gsi-fill: #ffffff;
    --gsi-edge: #747775;
    --gsi-ink: #1f1f1f;
  }
}


/* --- the gate ------------------------------------------------------------

   A page that is only for signed-in people has to be hidden from the FIRST
   PAINT, not after a module has loaded and Firebase has answered -- otherwise
   the tool appears, works for a second, and is then taken away, which is a
   worse experience than never showing it and looks like a fault.

   So the page ships with `class="gated"` already on <html> and the rule below
   hides the document body before any script runs. `visibility` rather than
   `display` because visibility is inherited AND can be turned back on by a
   descendant: the gate panel re-enables itself inside a hidden body, so one
   declaration hides everything except the one thing meant to be seen. When
   the reader turns out to be signed in, the class comes off and the page is
   simply itself again, with nothing to re-render.

   This is a gate, not a wall. Everything these pages do happens in the
   browser, so their markup and their code are fetchable by anyone who wants
   them regardless of what is drawn on top. It is here to ask people to sign
   in, not to keep anybody out.
   ------------------------------------------------------------------------ */

html.gated body { visibility: hidden; }
html.gated .gate { visibility: visible; }
/* The sign-in dialog the gate's own button opens lives in the same hidden
   body, so it has to re-enable itself too -- without this it opened
   invisibly over the gate, and Sign in did nothing anybody could see. */
html.gated .auth-scrim { visibility: visible; }
html.gated, html.gated body { overflow: hidden; }

.gate {
  position: fixed;
  inset: 0;
  z-index: 9400;
  display: grid;
  place-items: center;
  padding: 24px;
  background: var(--auth-ground);
  color: var(--auth-ink);
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text',
    'Helvetica Neue', 'Atkinson Hyperlegible', Helvetica, Arial, sans-serif;
  text-align: center;
}

.gate-card { width: min(420px, 100%); }

.gate-mark {
  width: 40px;
  height: 40px;
  margin: 0 auto 18px;
  color: var(--auth-ink);
}

.gate h1 {
  margin: 0 0 8px;
  font-size: clamp(24px, 3.2vw, 32px);
  font-weight: 600;
  letter-spacing: -0.022em;
  line-height: 1.12;
}

.gate p {
  margin: 0 0 22px;
  font-size: 16px;
  line-height: 1.5;
  color: var(--auth-ink-2);
}

.gate-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
}

.gate-actions .gate-in,
.gate-actions .gate-out {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 12px 23px;
  border-radius: var(--r-pill, 980px);
  font: inherit;
  font-size: 16px;
  cursor: pointer;
  text-decoration: none;
}

.gate-in {
  border: 1px solid var(--auth-accent);
  background: var(--auth-accent);
  color: var(--auth-on-accent);
}

.gate-in:hover { background: var(--auth-accent-hover); }

.gate-out {
  border: 1px solid var(--auth-ghost-line);
  background: transparent;
  color: var(--auth-ink);
}

.gate-out:hover { background: var(--auth-ghost-hover); }

/* The gate covers the page's own theme switch, so it has one of its own, top
   right where the bar's sits: a round 44px target, the ink at the nav's 0.68,
   full tone with a tray on hover. Styled here in full because the tools do
   not load style.css. */
.gate-theme {
  position: absolute;
  top: 12px;
  right: 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--auth-ink);
  opacity: 0.68;
  cursor: pointer;
}

.gate-theme:hover,
.gate-theme:focus-visible {
  opacity: 1;
  /* The dialog's round close button has the same tray, in both themes. */
  background: var(--auth-close-hover);
}

.gate-theme:focus-visible {
  outline: 3px solid var(--auth-accent);
  outline-offset: 2px;
}

.gate-theme svg {
  display: block;
  width: 20px;
  height: 20px;
}

.gate-theme-label {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* A dark page. gate.js sets data-dark when the page it stands in front of is
   dark -- the site's dark theme, or a tool whose own --bg is dark -- and the
   gate borrows that page's --bg, --text and --muted where it has them, so it
   lands in the tool's own palette rather than in a near miss of it. Without
   them it is the site's dark ground. */
.gate[data-dark] {
  background: var(--bg, #000);
  color: var(--text, #f5f5f7);
}

.gate[data-dark] p { color: var(--muted, #a1a1a6); }
.gate[data-dark] .gate-mark { color: var(--text, #f5f5f7); }
.gate[data-dark] .gate-out {
  border-color: #86868b; /* 3:1 against black, as a control's edge must be */
  color: var(--text, #f5f5f7);
}
.gate[data-dark] .gate-out:hover { background: rgba(255, 255, 255, 0.1); }

/* While the gate is deciding.

   Hiding the body leaves the page's own background showing and nothing else,
   which on a dark tool is an unexplained black rectangle. This paints the
   ground properly and, after a beat, says something -- the delay is so that a
   normal load, where the answer arrives in a few hundred milliseconds, never
   flashes a message nobody needed to read. */
html.gated::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 9300;
  background: var(--bg, var(--paper, var(--auth-ground)));
}

html.gated::after {
  content: attr(data-gate-wait);
  position: fixed;
  inset: auto 0 0;
  top: 50%;
  z-index: 9301;
  text-align: center;
  font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-size: 15px;
  color: var(--muted, var(--ink-2, var(--auth-ink-2)));
  opacity: 0;
  animation: gate-wait 0.2s ease-out 0.7s forwards;
}

html.gated.gate-ready::after { content: none; animation: none; }

@keyframes gate-wait { to { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  /* The FADE goes; the delay before it stays. The site's rule is that
     animation is removed rather than shortened, and this removes it -- the
     0.7s wait is not motion, it is the thing that stops a message flashing up
     on every fast load, and taking it away would make the page busier for the
     reader who asked for it to be calmer. */
  html.gated::after { animation-duration: 0.001s; }
}

/* --- early birds (early-bird.js) --------------------------------------------
   The sign-in dialog's look, so the two read as one family. What is its own:
   a long title kept clear of the close button, two lines of terms, a field
   marked when it is the one the message is about, and the thank-you state. */
.eb-dialog h2 { padding-right: 36px; line-height: 1.2; }
.eb-dialog .auth-sub p { margin: 0 0 6px; }
.eb-dialog .auth-sub p:last-child { margin-bottom: 0; }
.eb-dialog .auth-submit { margin-top: 4px; }
.eb-dialog .auth-hint { margin: 12px 0 0; }
.eb-dialog input[aria-invalid='true'],
.eb-dialog select[aria-invalid='true'] { border-color: var(--auth-err-ink); }
.eb-done-title {
  margin: 4px 0 6px;
  font-size: 19px;
  font-weight: 600;
  color: var(--auth-ink);
}
.eb-done-title:focus,
.eb-dialog [data-eb-status]:focus,
.eb-dialog [data-eb-error]:focus { outline: none; }
.eb-dialog .eb-status { margin: 0 0 14px; }

/* --- Open Beta requests (open-beta.js) ---------------------------------------
   The early-bird dialog (.eb-dialog above), a little wider for its ten
   fields: the two names side by side, the devices as a row of ticks, a
   message box drawn like the fields, and the consent tick with its link. */
.ob-dialog { width: min(520px, 100%); }

.ob-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 12px;
}

.ob-dialog textarea {
  display: block;
  width: 100%;
  min-height: 88px;
  margin-bottom: 14px;
  padding: 10px 12px;
  border: 1px solid var(--auth-line);
  border-radius: 10px;
  background: var(--auth-field);
  color: var(--auth-ink);
  font: inherit;
  font-size: 16px; /* 16px stops iOS zooming the page on focus */
  line-height: 1.4;
  resize: vertical;
}

.ob-devices {
  margin: 0 0 14px;
  padding: 0;
  border: 0;
  min-width: 0;
}

.ob-devices legend {
  margin-bottom: 4px;
  padding: 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--auth-ink-2);
}

.ob-device-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0 18px;
}

/* Each tick's whole line is its target, 44 px high. */
.auth-dialog .ob-check,
.auth-dialog .ob-consent {
  align-items: center;
  min-height: 44px;
  margin: 0;
  color: var(--auth-ink);
  font-size: 15px;
}

.auth-dialog .ob-consent {
  align-items: flex-start;
  margin: 2px 0 12px;
  padding-top: 10px;
  font-size: 14px;
  line-height: 1.45;
  font-weight: 400;
}

.auth-dialog .ob-consent input { margin-top: 1px; }

.ob-consent a {
  color: var(--auth-accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.ob-dialog textarea[aria-invalid='true'],
.ob-dialog input[type='checkbox'][aria-invalid='true'] {
  border-color: var(--auth-err-ink);
  outline: 2px solid var(--auth-err-ink);
  outline-offset: 2px;
}

.ob-dialog textarea:disabled { opacity: 0.55; }

@media (max-width: 479px) {
  .ob-pair { grid-template-columns: 1fr; }
}
