/* ProfMatch — product UI. Light + dark, no framework.
 * Tokens first: every colour, shadow and font size below comes from here (colours are checked by
 * tests/test_design_tokens.py). */

:root {
  --bg: #f5f7f9;
  --bg-tint: #eceff3;
  --surface: #ffffff;
  --surface-2: #f8fafb;
  --ink: #15191e;
  --ink-2: #434b55;
  --muted: #677079;
  --line: #e1e5ea;
  --line-strong: #cbd2d9;
  /* A11Y-02: field borders need ≥3:1 against --surface/--bg; --line-strong (1.53:1/1.6:1) is
   * too faint, so fields use this instead: 3.65:1 vs --surface, 3.4:1 vs --bg (computed). */
  --field-border: #7c8794;
  --accent: #0e7a5b;
  --accent-hover: #0b6a4f;
  --accent-press: #095a43;
  --accent-ink: #ffffff;
  --accent-soft: #e5f3ed;
  --accent-soft-2: #cfe9de;
  /* A11Y-02 (4G7): an olive gold, 5.51:1 on --accent-2-soft, for the "Saved" button and the
   * position badge; greener than --warn and --warn-soft, so the badge doesn't read as a warning. */
  --accent-2: #6b6100;
  --accent-2-soft: #f3f1d6;
  --good: #1e7a3c;
  --good-soft: #e3f3e7;
  --warn: #8a5a00;
  --warn-soft: #fcf1d8;
  --bad: #b3261e;
  --bad-soft: #fbe7e5;
  --bad-hover: #9a211a;
  --focus: #0e7a5b;
  --ring: color-mix(in srgb, var(--focus) 28%, transparent);
  --snackbar: #1f2328;
  --snackbar-ink: #f1f3f4;
  --snackbar-action: #7ee0bd;
  --snackbar-error: #ff8a80;
  --scrim: rgba(10, 12, 11, 0.42);
  --scrim-strong: rgba(9, 12, 15, 0.55);
  --star: #fbbc04;
  --star-edge: #a86f00;
  --shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.05);
  --shadow: 0 1px 2px rgba(16, 24, 40, 0.05), 0 1px 3px rgba(16, 24, 40, 0.07);
  --shadow-md: 0 6px 16px -4px rgba(16, 24, 40, 0.12), 0 2px 4px -2px rgba(16, 24, 40, 0.06);
  --shadow-lg: 0 24px 56px -16px rgba(16, 24, 40, 0.28), 0 4px 12px -4px rgba(16, 24, 40, 0.08);
  --radius: 12px;
  --radius-sm: 8px;
  /* Spacing: a 4px scale (UI-03). Margins, padding and gaps use these steps. */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-7: 28px;
  --space-8: 32px;
  --space-9: 36px;
  --space-10: 40px;
  --space-11: 44px;
  --space-12: 48px;
  --space-14: 56px;
  --space-15: 60px;
  --space-17: 68px;
  --space-18: 72px;
  --tap: 44px;
  /* Page layout (UI-05): the content column is the window less a 16-48 px margin on each side,
   * up to 1,600 px wide, and the sticky header's height. */
  --page-max: 1600px;
  --gutter: clamp(16px, 4vw, 48px);
  --topbar-h: 64px;
  /* The open profile's sticky bar below 1,280 px: its button, --space-3 above and below and a
   * 1 px rule (taller on a touch screen, see pointer: coarse). */
  --drawer-bar-h: 57px;
  /* The space left between the sticky top bar and whatever the browser scrolls to. */
  --scroll-gap: var(--space-4);
  /* A11Y-09: smallest allowed click target (not theme-dependent). */
  --target-min: 24px;
  --ease: cubic-bezier(0.2, 0, 0, 1);
  /* Type scale (UI-21): the only font sizes in this file. Body is --text-md. */
  --text-xs: 0.75rem; /* 12px: badges, overlines */
  --text-sm: 0.875rem; /* 14px: meta, hints, small buttons */
  --text-md: 1rem; /* 16px: body and every form field */
  --text-lg: 1.25rem; /* 20px: card titles, lede */
  --text-xl: 1.625rem; /* 26px: section headings, big numbers */
  --text-2xl: clamp(2rem, 4.5vw, 3rem); /* 32-48px: page titles */
  --sans: "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, BlinkMacSystemFont, "SF Pro Text",
    Roboto, "Helvetica Neue", Arial, sans-serif;
  --display: "Segoe UI Variable Display", "Segoe UI", system-ui, -apple-system, BlinkMacSystemFont,
    "SF Pro Display", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "Cascadia Mono", "SF Mono", Consolas, monospace;
  color-scheme: light;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0e1113;
    --bg-tint: #1a1e22;
    --surface: #15191c;
    --surface-2: #1a1e22;
    --ink: #e8eaed;
    --ink-2: #bec3c9;
    --muted: #979ea6;
    --line: #262b31;
    --line-strong: #353c43;
    /* A11Y-02: --field-border 3.31:1 vs --surface, 3.55:1 vs --bg (computed). */
    --field-border: #626c77;
    --accent: #4fd1a5;
    --accent-hover: #6ddcb6;
    --accent-press: #8ae5c6;
    --accent-ink: #04241a;
    --accent-soft: #11302a;
    --accent-soft-2: #174035;
    --accent-2: #e2bb6c;
    --accent-2-soft: #2e2615;
    --good: #7fd69a;
    --good-soft: #13291b;
    --warn: #ecc26f;
    --warn-soft: #2d2513;
    --bad: #f2a097;
    --bad-soft: #36191a;
    --bad-hover: #f5b3ac;
    --focus: #6ddcb6;
    --snackbar: #e8eaed;
    --snackbar-ink: #15191e;
    --snackbar-action: #0d7456;
    --snackbar-error: #b3261e;
    --scrim: rgba(0, 0, 0, 0.6);
    --scrim-strong: rgba(0, 0, 0, 0.7);
    --star: #fdd663;
    --star-edge: #fdd663;
    --shadow-sm: none;
    --shadow: 0 1px 0 rgba(255, 255, 255, 0.03), 0 1px 3px rgba(0, 0, 0, 0.5);
    --shadow-md: 0 8px 24px -6px rgba(0, 0, 0, 0.6);
    --shadow-lg: 0 30px 80px -20px rgba(0, 0, 0, 0.85);
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --bg: #0e1113;
  --bg-tint: #1a1e22;
  --surface: #15191c;
  --surface-2: #1a1e22;
  --ink: #e8eaed;
  --ink-2: #bec3c9;
  --muted: #979ea6;
  --line: #262b31;
  --line-strong: #353c43;
  /* A11Y-02: --field-border 3.31:1 vs --surface, 3.55:1 vs --bg (computed). */
  --field-border: #626c77;
  --accent: #4fd1a5;
  --accent-hover: #6ddcb6;
  --accent-press: #8ae5c6;
  --accent-ink: #04241a;
  --accent-soft: #11302a;
  --accent-soft-2: #174035;
  --accent-2: #e2bb6c;
  --accent-2-soft: #2e2615;
  --good: #7fd69a;
  --good-soft: #13291b;
  --warn: #ecc26f;
  --warn-soft: #2d2513;
  --bad: #f2a097;
  --bad-soft: #36191a;
  --bad-hover: #f5b3ac;
  --focus: #6ddcb6;
  --snackbar: #e8eaed;
  --snackbar-ink: #15191e;
  --snackbar-action: #0d7456;
  --snackbar-error: #b3261e;
  --scrim: rgba(0, 0, 0, 0.6);
  --scrim-strong: rgba(0, 0, 0, 0.7);
  --star: #fdd663;
  --star-edge: #fdd663;
  --shadow-sm: none;
  --shadow: 0 1px 0 rgba(255, 255, 255, 0.03), 0 1px 3px rgba(0, 0, 0, 0.5);
  --shadow-md: 0 8px 24px -6px rgba(0, 0, 0, 0.6);
  --shadow-lg: 0 30px 80px -20px rgba(0, 0, 0, 0.85);
  color-scheme: dark;
}

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

/* The hidden attribute always wins, even over a component's own display
   (an empty .notice would otherwise show as a coloured strip). */
[hidden] {
  display: none !important;
}

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* A11Y-01, WCAG 2.2 SC 2.4.11: a control the browser scrolls to (Tab, Shift+Tab, an anchor,
     scrollIntoView) stops below the sticky top bar, never under it. */
  scroll-padding-top: calc(var(--topbar-h) + var(--scroll-gap));
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--text-md);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img,
svg {
  display: block;
  max-width: 100%;
}

a {
  color: var(--accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

a:hover {
  text-decoration-thickness: 2px;
}

/* A11Y-01: --focus vs --bg/--surface, computed for this audit.
 * Light: 4.95:1 (bg), 5.31:1 (surface). Dark: 11.34:1 (bg), 10.58:1 (surface). All ≥ 3:1. */
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

::selection {
  background: var(--accent-soft-2);
}

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

.skip-link {
  position: absolute;
  left: 16px;
  top: -60px;
  background: var(--ink);
  color: var(--bg);
  padding: var(--space-2) var(--space-3);
  border-radius: 8px;
  z-index: 100;
}

.skip-link:focus {
  top: 12px;
}

h1,
h2,
h3,
h4 {
  font-family: var(--display);
  font-weight: 650;
  letter-spacing: -0.02em;
  line-height: 1.2;
  margin: 0;
}

p {
  margin: 0;
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
}

/* ---------- Layout ---------- */

/* The page follows the window (UI-05): 343 px at 375, 1,184 at 1,280, 1,270 at 1,366, and
   1,600 from about 1,700. Reading text and forms keep their own caps inside it (UI-06). */
.wrap {
  width: min(100% - 2 * var(--gutter), var(--page-max));
  margin-inline: auto;
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
  transition: box-shadow 0.2s var(--ease);
}

.topbar.is-scrolled {
  box-shadow: var(--shadow-md);
}

/* On the same width rule as .wrap: the brand starts where the page's content starts and the
   account actions end where it ends, at any width. */
.topbar-inner {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: min(100% - 2 * var(--gutter), var(--page-max));
  height: var(--topbar-h);
  margin-inline: auto;
}

/* Connection lost or back (app.js). Part of the sticky top bar, so it stays in view. Its colour
   runs edge to edge; its text lines up with the page's content. */
.offline-banner {
  padding: var(--space-2) max(var(--gutter), (100% - var(--page-max)) / 2);
  border-top: 1px solid var(--line);
  background: var(--warn-soft);
  color: var(--warn);
  font-size: var(--text-sm);
}

.offline-banner.is-back {
  background: var(--good-soft);
  color: var(--good);
}

.offline-banner:empty {
  display: none;
}

.topbar-actions {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--ink);
  text-decoration: none;
  font-family: var(--display);
  font-weight: 700;
  font-size: var(--text-lg);
  letter-spacing: -0.02em;
}

/* The mark is a drawn image (static/img/icon.svg: a stem and a ring that overlap), the same file
 * as the tab icon, so it carries its own colours and corner radius. */
.brand-mark {
  display: block;
  flex: none;
  width: 32px;
  height: 32px;
}

/* The second half of the name takes the brand colour, as the mark's square does. */
.brand-match {
  color: var(--accent);
}

.nav {
  margin-left: var(--space-5);
  display: flex;
  align-items: center;
  gap: 2px;
}

/* Desktop links are text only; phones show these icons instead. */
.nav-icon {
  display: none;
  flex: none;
}

/* One icon set (UI-50): icon() in dom.js and the inline SVGs in index.html. */
.icon {
  flex: none;
}

.icon-label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

.theme-icon {
  display: none;
}

#theme-toggle[data-theme-state="system"] .theme-icon[data-for="system"],
#theme-toggle[data-theme-state="light"] .theme-icon[data-for="light"],
#theme-toggle[data-theme-state="dark"] .theme-icon[data-for="dark"] {
  display: block;
}

.nav a,
.icon-btn {
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 36px;
  padding: 0 var(--space-3);
  border-radius: 999px;
  color: var(--ink-2);
  text-decoration: none;
  font-size: var(--text-sm);
  font-weight: 550;
  border: 0;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.15s var(--ease), color 0.15s var(--ease);
}

.icon-btn {
  width: 36px;
  padding: 0;
  justify-content: center;
}

.nav a:hover,
.icon-btn:hover {
  background: var(--bg-tint);
  color: var(--ink);
}

.nav a[aria-current="page"] {
  color: var(--accent);
  background: var(--accent-soft);
}

/* The phone menu button (UI-38); shown only at 480px and below.
   Kept at least 44x44px as a touch target, wider than the other icon
   buttons' 36px, on touch screens and with a mouse alike. */
.nav-toggle {
  display: none;
  min-width: var(--tap);
  min-height: var(--tap);
}

.nav-toggle[aria-expanded="true"] {
  background: var(--bg-tint);
  color: var(--ink);
}

.count-badge {
  min-width: 20px;
  height: 20px;
  padding: 0 var(--space-1); /* 4px keeps one digit a circle; 8px made it an oval */
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--text-xs);
  font-weight: 700;
  display: inline-grid;
  place-items: center;
}

.count-badge[hidden] {
  display: none;
}

main {
  min-height: calc(100vh - 62px - 120px);
}

footer.site-footer {
  border-top: 1px solid var(--line);
  margin-top: var(--space-18);
  padding: var(--space-8) 0 var(--space-11);
  color: var(--muted);
  font-size: var(--text-sm);
}

footer.site-footer .wrap {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-7);
  justify-content: space-between;
}

.footer-links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
}

footer.site-footer a {
  color: var(--ink-2);
}

/* ---------- Hero ---------- */

/* The first screen (UI-80): one column below 1,024 px, in the page's order: the headline, the
   form right under it, then the lede and facts, then an example of a result. From 1,024 px the
   form sits on the right, beside the headline, with the lede and the example under the headline.
   Text is left-aligned (UI-23). */
.home-top {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--space-6);
  align-items: start;
  padding-top: var(--space-12);
}

@media (min-width: 1024px) {
  .home-top {
    /* The headline's column keeps 24rem, so at 1,024 px the form gives up some of its 35rem. */
    grid-template-columns: minmax(24rem, 1fr) minmax(28rem, 35rem);
    /* The last row takes whatever height the form needs beyond the left column's. */
    grid-template-rows: auto auto 1fr;
    grid-template-areas:
      "hero form"
      "more form"
      "example form";
    column-gap: clamp(var(--space-8), 4vw, var(--space-14));
    padding-top: var(--space-6);
  }
  .home-top > .hero {
    grid-area: hero;
  }
  .home-top > .composer {
    grid-area: form;
  }
  .home-top > .hero-more {
    grid-area: more;
  }
  .home-top > .example-result {
    grid-area: example;
  }
}

/* UI-10: neutral, like the facts; the brand colour is for actions, links and focus. */
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  font-weight: 650;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-2);
  background: var(--surface);
  border: 1px solid var(--line);
  padding: var(--space-1) var(--space-3);
  border-radius: 999px;
}

.hero h1 {
  font-size: var(--text-2xl);
  margin: var(--space-4) 0 0;
  max-width: 17ch;
  font-weight: 700;
  letter-spacing: -0.035em;
  line-height: 1.08;
}

.hero h1 em {
  font-style: normal;
}

.hero-more .lede {
  max-width: 60ch;
  color: var(--ink-2);
  font-size: var(--text-lg);
}

.stat-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-7);
  margin-top: var(--space-4);
  min-height: 20px;
  color: var(--muted);
  font-size: var(--text-sm);
}

/* UI-30: hidden until the session is known, but keeps its layout space so
 * what follows does not jump once it is swapped for the summary. */
[data-pending] {
  visibility: hidden;
}

.stat-row strong {
  color: var(--ink);
  font-weight: 650;
}

.home-summary {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  margin-top: var(--space-4);
  min-height: 20px;
  color: var(--ink-2);
  font-size: var(--text-sm);
}

.home-summary p {
  margin: 0;
}

.home-summary strong {
  color: var(--ink);
  font-weight: 650;
}

/* The shape of a result, with placeholders (UI-81, UI-82): the caption and the dashed outline say
   it is an example, it holds no numbers, and nothing in it can be clicked. */
.example-result {
  max-width: 35rem;
  margin: 0;
}

/* Real results replace the whole first screen (4W7c), so it never sits above them. */

.example-result figcaption {
  margin-bottom: var(--space-2);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
}

.example-card {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius);
  background: var(--surface);
}

.example-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
}

.example-name {
  font-weight: 650;
}

.example-affil {
  margin-top: var(--space-1);
  color: var(--ink-2);
  font-size: var(--text-sm);
}

.example-fit {
  flex: none;
}

.example-why {
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: var(--text-sm);
}

.example-why-title {
  margin-bottom: var(--space-1);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---------- Cards & forms ---------- */

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

/* At most 35rem (560 px) wide at every width (UI-06); in the first screen's grid (see .home-top). */
.composer {
  width: 100%;
  max-width: 35rem;
  padding: var(--space-5);
  box-shadow: var(--shadow-md);
}

.composer-label {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}

.composer-label label {
  font-family: var(--display);
  font-size: var(--text-lg);
  font-weight: 650;
}

.char-count {
  font-size: var(--text-xs);
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  flex: none;
}

textarea,
input[type="text"],
input[type="search"],
input[type="date"],
input[type="email"],
input[type="password"],
select {
  width: 100%;
  min-height: 40px;
  background: var(--surface);
  /* A11Y-02: --field-border, not --line-strong, so an empty field stays visible (see token comment). */
  border: 1px solid var(--field-border);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease);
}

textarea:hover,
input[type="text"]:hover,
input[type="search"]:hover,
input[type="date"]:hover,
input[type="email"]:hover,
input[type="password"]:hover,
select:hover {
  border-color: color-mix(in srgb, var(--ink) 30%, var(--field-border));
}

textarea:focus,
input:focus,
select:focus {
  /* A11Y-01: 2px --focus outline (4.95-11.34:1, see global :focus-visible comment), ring is extra. */
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-color: var(--focus);
  box-shadow: 0 0 0 3px var(--ring);
}

[aria-invalid="true"] {
  border-color: var(--bad) !important;
}

[aria-invalid="true"]:focus {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--bad) 22%, transparent);
}

textarea {
  resize: vertical;
  min-height: 72px;
  line-height: 1.5;
}

/* Four rows tall (rows="4"), so the button below stays on the first screen. A block, so no
   line-box gap is left under it. */
.interests-input {
  display: block;
  font-size: var(--text-md);
  padding: var(--space-3) var(--space-4);
}

.hint {
  font-size: var(--text-sm);
  color: var(--muted);
  margin-top: var(--space-2);
}

/* The university picker's status line stays in the page, empty, so a screen reader is already
   watching it when text arrives (UI-40); empty, it takes no room. */
#inst-status:empty {
  margin: 0;
}

/* Under the label, above the box (UI-32): the hint, and the count at the end of its line. */
.interests-meta {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin: calc(-1 * var(--space-1)) 0 var(--space-2);
}

.interests-meta .interests-hint {
  margin: 0;
}

/* Out of sight while the box is empty, but still in the page, so a screen reader hears it as
   before. */
#search-form:has(#interests:placeholder-shown) #interests-count {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* Right under the box it is about (A11Y-04). */
#form-error {
  margin: var(--space-2) 0 0;
}

.examples {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.chip-btn {
  position: relative;
  overflow: hidden;
  height: 32px;
  padding: 0 var(--space-4);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  border-radius: 999px;
  font-size: var(--text-sm);
  font-weight: 550;
  color: var(--ink-2);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease), color 0.15s var(--ease),
    transform 0.1s var(--ease);
}

.chip-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
  background: var(--accent-soft);
}

.chip-btn:active {
  transform: scale(0.97);
}

details.more {
  margin-top: var(--space-4);
  border-top: 1px solid var(--line);
  padding-top: var(--space-3);
}

/* About you, then More options: one rule between them, not two blocks of space. */
details.more + details.more {
  margin-top: var(--space-3);
}

/* What is set, in the More options summary (app.js). */
details.more > summary .more-set {
  font-weight: 400;
  color: var(--ink-2);
}

details.more > summary .more-optional {
  font-size: var(--text-sm);
  font-weight: 400;
  color: var(--muted);
}

/* Its words take the rest of the row and wrap beside the icon, not under it. */
#more-options-label {
  flex: 1 1 0;
  min-width: 0;
}

/* Closed, About you reads "About you (optional)" on one line, so the search button stays on the
   first screen; open, its longer note says what the fields are for and where they are kept. */
#about-you:not([open]) > summary .hint,
#about-you[open] > summary .more-optional {
  display: none;
}

details.more > summary {
  cursor: pointer;
  list-style: none;
  font-weight: 600;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  color: var(--ink-2);
}

details.more > summary::-webkit-details-marker {
  display: none;
}

/* UI-50: a plus from the SVG set that loses its vertical stroke when open;
   no "+"/"–" text for screen readers to read out. */
details.more > summary .more-toggle {
  display: grid;
  place-items: center;
  flex: none;
  width: 22px;
  height: 22px;
  border-radius: 6px;
  background: var(--bg-tint);
  color: var(--accent);
}

details.more[open] > summary .more-toggle-v {
  display: none;
}

details.more > summary .hint {
  margin: 0;
  font-weight: 400;
}

.grid-2 {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-4) var(--space-4);
  margin-top: var(--space-4);
}

.field label,
.field .label {
  display: block;
  font-size: var(--text-sm);
  font-weight: 600;
  margin-bottom: var(--space-2);
  color: var(--ink-2);
}

.field-wide {
  grid-column: 1 / -1;
}

/* Inside More options, below its summary. */
.filters {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-4) var(--space-4);
  margin-top: var(--space-4);
}

.filters .field-2 {
  grid-column: span 2;
}

.segmented {
  display: flex;
  background: var(--bg-tint);
  border-radius: var(--radius-sm);
  padding: var(--space-1);
  gap: var(--space-1);
}

.segmented label {
  flex: 1;
  margin: 0;
  position: relative;
}

.segmented input {
  position: absolute;
  opacity: 0;
  inset: 0;
  cursor: pointer;
}

.segmented span {
  display: block;
  text-align: center;
  padding: var(--space-2) var(--space-2);
  border-radius: 7px;
  font-size: var(--text-sm);
  font-weight: 550;
  color: var(--ink-2); /* --muted was 4.36:1 on --bg-tint (A11Y-02) */
  cursor: pointer;
}

.segmented input:checked + span {
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-sm);
}

.segmented input:focus-visible + span {
  outline: 2px solid var(--focus);
}

.token-input {
  position: relative;
}

.tokens {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.tokens:empty {
  display: none;
}

.token {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 999px;
  padding: var(--space-1) var(--space-2) var(--space-1) var(--space-3);
  font-size: var(--text-sm);
  font-weight: 550;
}

.token button {
  border: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
  width: 20px;
  height: 20px;
  /* A11Y-09: min-width/min-height raise the circle itself to --target-min (24px). */
  min-width: var(--target-min);
  min-height: var(--target-min);
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: var(--text-md);
  line-height: 1;
}

.token button:hover {
  background: color-mix(in srgb, var(--accent) 18%, transparent);
}

.suggestions {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 20;
  background: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-lg);
  max-height: 280px;
  overflow: auto;
  padding: var(--space-1);
  margin: 0;
  list-style: none;
}

.suggestions[hidden] {
  display: none;
}

.suggestions li {
  padding: var(--space-2) var(--space-3);
  border-radius: 7px;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--text-sm);
}

/* --ink-2, not --muted: the hint also sits on --accent-soft when its row is highlighted (A11Y-02). */
.suggestions li small {
  color: var(--ink-2);
  white-space: nowrap;
}

.suggestions li[aria-selected="true"],
.suggestions li:hover {
  background: var(--accent-soft);
}

.composer-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-4);
  flex-wrap: wrap;
}

.privacy-note {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--muted);
  font-size: var(--text-sm);
}

.btn {
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  height: 40px;
  padding: 0 var(--space-4);
  border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong);
  background: var(--surface);
  color: var(--ink);
  font-weight: 600;
  font-size: var(--text-sm);
  line-height: 1;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  box-shadow: var(--shadow-sm);
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease), box-shadow 0.15s var(--ease),
    color 0.15s var(--ease), transform 0.1s var(--ease);
}

.btn:hover {
  background: var(--bg-tint);
  border-color: color-mix(in srgb, var(--ink) 25%, var(--line-strong));
  text-decoration: none;
}

/* Pressing a button visibly pushes it in; the ripple (dom.js) adds the wave. */
.btn:active {
  transform: scale(0.97);
}

.btn:focus-visible {
  /* A11Y-01: on .btn-primary the border already equals --focus in light mode, so the 2px
   * offset (not the border) is what makes the ring visible; --focus vs --bg/--surface ratios
   * are in the global :focus-visible comment above, box-shadow ring stays as extra emphasis. */
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-color: var(--focus);
  box-shadow: 0 0 0 3px var(--ring);
}

.btn[disabled],
.btn[aria-disabled="true"] {
  opacity: 0.55;
  cursor: not-allowed;
  transform: none;
}

/* Busy: a spinner joins the label and clicks are ignored until it's done. */
.btn[aria-busy="true"] {
  cursor: progress;
  pointer-events: none;
  opacity: 0.9;
}

.btn-primary {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-ink);
  box-shadow: var(--shadow-sm);
}

.btn-primary:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  box-shadow: var(--shadow-md);
}

.btn-primary:active {
  background: var(--accent-press);
  border-color: var(--accent-press);
}

.btn-danger {
  background: var(--bad);
  border-color: var(--bad);
  color: var(--surface);
}

.btn-danger:hover {
  background: var(--bad-hover);
  border-color: var(--bad-hover);
}

.btn-lg {
  height: 48px;
  padding: 0 var(--space-6);
  font-size: var(--text-md);
  border-radius: 10px;
}

.btn-sm {
  height: 32px;
  padding: 0 var(--space-3);
  font-size: var(--text-sm);
}

.btn-block {
  width: 100%;
}

.btn-ghost {
  border-color: transparent;
  background: transparent;
  box-shadow: none;
}

.btn-ghost:hover {
  background: var(--bg-tint);
  border-color: transparent;
}

.btn.is-saved {
  color: var(--accent-2);
  border-color: color-mix(in srgb, var(--accent-2) 45%, transparent);
  background: var(--accent-2-soft);
}

.btn.is-done {
  color: var(--good);
  border-color: color-mix(in srgb, var(--good) 45%, transparent);
  background: var(--good-soft);
}

.pop {
  animation: pop 0.2s var(--ease);
}

@keyframes pop {
  40% {
    transform: scale(1.06);
  }
}

.spinner {
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-right-color: transparent;
  animation: spin 0.7s linear infinite;
  flex: none;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* ---------- The results view (4W7c) ---------- */

/* The search page is either the form (the hero and its form, "#/") or the results ("#/results",
   app.js sets .is-results): after a search the results replace the hero and the form, under a
   one-line bar that says what was searched for. "Edit search" opens the form in place above the
   results (.is-editing), without the hero, so the results' title stays the one h1 (A11Y-10). */
#view-search:not(.is-results) .results,
#view-search:not(.is-results) .search-summary,
#view-search.is-results:not(.is-editing) .home-top,
#view-search.is-editing .hero,
#view-search.is-editing .hero-more,
#view-search.is-editing .example-result {
  display: none;
}

#view-search.is-editing .home-top {
  display: block;
  padding-top: var(--space-4);
}

.search-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin-top: var(--space-6);
}

/* One line, cut with an ellipsis; the whole description is still read out. */
.search-summary-text {
  flex: 1 1 0;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--ink-2);
}

#new-search {
  gap: var(--space-2);
}

/* Phones: the description has the line to itself, the two buttons sit under it. */
@media (max-width: 640px) {
  .search-summary-text {
    flex-basis: 100%;
  }
}

/* ---------- Results ---------- */

/* At most 35rem wide: the heading and its controls, the progress, empty and error cards, the
   notice and the list share one column, so each card keeps its name, fit and Save close together
   and its text under about 70 characters a line (UI-06; the text below has its own caps too). */
.results {
  max-width: 35rem;
  margin-top: var(--space-11);
  /* Clear of the sticky top bar when scrolled to: html's scroll-padding-top does it. */
}

.results-head {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}

/* The note after the count wraps to two lines for "best match" and stays on one for the other
   sorts; hold two lines so the sort controls and the list don't move when the sort changes
   (3.1em is two lines at the body's 1.55 line height). The results column is at most 35rem, so
   this holds at every width. Not for the empty count line of "No matches yet": that would leave
   a gap. */
.results-head p:not(:empty) {
  min-height: 3.1em;
}

/* The toolbar sits beside the heading only when the longest count line fits as well, and
   otherwise on its own row. The heading's width is a fixed 37rem (the longest count line is
   about 35rem), not the width of whatever its text is, so the answer is the same for every
   sort and filter: the toolbar and the list below don't move when the count line changes
   length (UI-90, UI-05). */
.results-head > div:first-child {
  flex: 1 1 37rem;
}

/* The results view's h1 (4W7c), at the size the section heading had. */
.results-head h1 {
  font-size: var(--text-xl);
}

.results-head p {
  color: var(--muted);
  margin-top: var(--space-1);
}

.toolbar {
  display: flex;
  gap: var(--space-2);
  align-items: center;
  flex-wrap: wrap;
}

.sort-control {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

.sort-label {
  font-size: var(--text-sm);
  color: var(--muted);
  white-space: nowrap;
}

.toolbar select {
  width: auto;
  padding: var(--space-2) var(--space-3);
}

.toggle-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
  cursor: pointer;
  background: var(--surface);
  user-select: none;
}

.toggle-chip input {
  accent-color: var(--accent);
  margin: 0;
}

.notice {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
  background: var(--warn-soft);
  color: var(--warn);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  margin-bottom: var(--space-4);
}

.notice.info {
  background: var(--accent-soft);
  color: var(--accent);
}

.notice.error {
  background: var(--bad-soft);
  color: var(--bad);
}

/* Saved: the shortlist is syncing or didn't sync; Retry sits at the end. */
.sync-status .notice {
  align-items: center;
}

.sync-status .notice .btn {
  margin-left: auto;
  flex: none;
}

.result-list {
  display: grid;
  gap: var(--space-4);
}

.result {
  position: relative;
  padding: var(--space-5) var(--space-6);
  display: grid;
  gap: var(--space-3);
  transition: border-color 0.2s var(--ease), box-shadow 0.2s var(--ease);
  animation: rise 0.2s var(--ease) both;
}

/* The fade-in is for the results arriving. Sorting or filtering moves and shows the same cards
   again, which would replay it on every one (UI-90); results.js drops `is-fresh` once the
   arrival is over. */
.result-list:not(.is-fresh) > .result {
  animation: none;
}

/* Cards below the screen aren't laid out or painted until they scroll near it, which is most
   of the 25: with it, re-sorting redraws only what is on screen (UI-90; without it a sort
   took 300 to 700 ms on a throttled phone, with it about 100). The size is a guess for a card
   height for a card that hasn't been drawn yet, so the scrollbar is roughly right; `auto` swaps
   in the real height once it has been seen. The guess is for a typical real card: a name, two
   or three papers with full titles and up to five topics. Drawn that way the 35rem card of 4W7b
   measured 30 to 39rem at 1366px wide and 42 to 48rem at 375px (the browser tests' two-line
   cards are shorter). Only the height: a width here would make the cards (and the page) that
   wide on a phone. Tab, find-in-page and screen readers still reach every card; printing shows
   them all (see @media print). */
.result-list > .result {
  content-visibility: auto;
  contain-intrinsic-height: auto 32rem;
}

.result:hover {
  border-color: var(--line-strong);
  box-shadow: var(--shadow-md);
}

@keyframes rise {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
}

.result-top {
  display: flex;
  gap: var(--space-4);
  align-items: flex-start;
  justify-content: space-between;
}

.result-name {
  font-size: var(--text-lg);
}

.result-name a {
  color: var(--ink);
  text-decoration: none;
}

@media (hover: hover) {
  .result-name a:hover {
    color: var(--accent);
  }
}

/* UI-31: the name link stretches over the whole result card, so a click
   anywhere on it opens the profile; Save, the card's one button, sits above it.
   Scoped to .result: Saved cards reuse .result-name but hold notes and controls
   that must stay clickable. */
.result .result-name a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--radius);
}

/* Keyboard focus rings the whole card, the thing a click opens, not only the name (A11Y-01). */
.result .result-name a:focus-visible {
  outline: none;
}

.result .result-name a:focus-visible::after {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

/* While the list is sorted by citations or by recent papers, each card shows that figure in its
   bottom row, before the chevron, since the card no longer shows the totals (results.js toggles
   the class on the list; no card is built again, and no card changes height). */
.result-figure {
  display: none;
  margin-inline-start: auto;
  color: var(--muted);
  font-size: var(--text-sm);
}

.result-list.by-cited .figure-cited,
.result-list.by-active .figure-active {
  display: inline;
}

.result .btn {
  position: relative;
  z-index: 1;
}

/* Save on the left; the chevron at the end is the cue that the card opens the profile. */
.result-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  margin-inline-start: calc(-1 * var(--space-3));
}

/* Clicks pass through it to the name link below (its hover nudge would otherwise lift it above). */
.result-chevron {
  display: inline-flex;
  color: var(--muted);
  pointer-events: none;
  transition: color 0.15s var(--ease), transform 0.15s var(--ease);
}

@media (hover: hover) {
  .result:hover .result-chevron {
    color: var(--ink);
    transform: translateX(2px);
  }
}

/* Text in a card stays under about 70 characters a line at every width (UI-06): each piece gets
   a measure in its own font size, 56ch being about 70 of these characters. A row of labels has
   padding round each one, so a little less. */
.result .affil,
.result .paper-title,
.result .paper-meta {
  max-width: 56ch;
}

.result .badges,
.result .topics {
  max-width: 48ch;
}

.affil {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-2);
  color: var(--ink-2);
  margin-top: var(--space-1);
  font-size: var(--text-md);
}

/* Institution · Country: the dot trails the institution, so a wrapped line never starts with it.
   The empty alternative text keeps a screen reader from reading it as part of the institution's name;
   a browser that doesn't know it keeps the first line. */
.affil > span:not(:last-child)::after {
  content: "\00b7";
  content: "\00b7" / "";
  margin-inline-start: var(--space-2);
}

.badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  border-radius: 999px;
  padding: var(--space-1) var(--space-2);
  font-size: var(--text-xs);
  font-weight: 600;
  background: var(--bg-tint);
  color: var(--ink-2);
  white-space: nowrap;
}

.badge.accent {
  background: var(--accent-soft);
  color: var(--accent);
}

.badge.gold {
  background: var(--accent-2-soft);
  color: var(--accent-2);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}

.badge.good {
  background: var(--good-soft);
  color: var(--good);
}

.badge.warn {
  background: var(--warn-soft);
  color: var(--warn);
}

.badge.bad {
  background: var(--bad-soft);
  color: var(--bad);
}

.fit {
  flex: none;
  text-align: right;
}

/* The words and the meter show how strong a match is; the pill is neutral at every level, so the
   brand colour is kept for actions, links and focus (UI-10). */
.fit-label {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: 650;
  font-size: var(--text-sm);
  padding: var(--space-1) var(--space-3);
  border-radius: 999px;
  background: var(--bg-tint);
  color: var(--ink-2);
}

.fit-meter {
  width: 110px;
  height: 5px;
  margin: var(--space-2) 0 0 auto;
  display: block;
}

.fit-meter-track {
  fill: var(--bg-tint);
}

/* Neutral like the pill (UI-10); --ink-2 on the --bg-tint track is well over 3:1. */
.fit-meter-value {
  fill: var(--ink-2);
}

.why {
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
}

.why-title {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--space-2);
}

.paper-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: grid;
  gap: var(--space-2);
}

.paper-list li {
  display: grid;
  gap: 2px;
}

.paper-title {
  color: var(--ink);
  font-weight: 550;
  text-decoration: none;
  line-height: 1.35;
}

a.paper-title:hover {
  color: var(--accent);
  text-decoration: underline;
}

.paper-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-2);
  font-size: var(--text-xs);
  color: var(--muted);
  align-items: center;
}

.role {
  font-weight: 650;
  color: var(--ink-2);
}

.topics {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.topic {
  font-size: var(--text-xs);
  padding: var(--space-1) var(--space-2);
  border-radius: 6px;
  border: 1px solid var(--line);
  color: var(--ink-2);
}

.result-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
}

.empty {
  text-align: center;
  padding: var(--space-12) var(--space-6);
  color: var(--muted);
}

.empty h1,
.empty h2,
.empty h3 {
  color: var(--ink);
  margin-bottom: var(--space-2);
  font-size: var(--text-lg);
}

.empty p {
  max-width: 60ch;
  margin-inline: auto;
}

.empty .btn {
  margin-top: var(--space-4);
}

/* skeletons */

.offscreen {
  position: fixed;
  opacity: 0;
  pointer-events: none;
  left: -9999px;
}

.spaced {
  margin-top: var(--space-4);
}

.skeleton-card {
  display: grid;
  gap: var(--space-2);
}

.skeleton-title {
  height: 22px;
  width: 45%;
}

.skeleton-line {
  height: 14px;
  width: 100%;
}

.skeleton-line.short {
  width: 55%;
}

.skeleton-block {
  height: 64px;
  width: 100%;
}

/* Drawer placeholders stay still: no looping shimmer (UI-41, UI-70). */
.drawer .skeleton::after {
  content: none;
}

.skeleton {
  position: relative;
  overflow: hidden;
  /* --line, not --bg-tint: without the shimmer the blocks must show on a dark card. */
  background: var(--line);
  border-radius: 8px;
}

.progress-step {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--muted);
  font-size: var(--text-sm);
}

.progress-step.is-active {
  color: var(--ink);
  font-weight: 550;
}

/* ---------- Profile pane (4W7a) ---------- */

/* A section of the search page, not a dialog (UI-35, A11Y-11): no backdrop, no focus trap, no
   scroll lock. Below 1,280 px it shows only while a profile is open, in the list's place; from
   1,280 px it sits beside the results (see the grid below), empty until a card is chosen. */
.drawer {
  display: none;
  flex-direction: column;
  min-width: 0;
  margin-top: var(--space-11);
  /* Right under the top bar when scrolled to: html's scroll-padding-top leaves the bar's height
     and --scroll-gap; this takes the --scroll-gap back. */
  scroll-margin-top: calc(-1 * var(--scroll-gap));
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.drawer.is-open {
  display: flex;
}

/* While a profile is open the home page's sections below give way, and with no search in this
   tab (opened from a link) the profile has the column to itself. */
#view-search:has(.drawer.is-open) .home-more,
#view-search:not(.is-results):has(.drawer.is-open) .home-top {
  display: none;
}

/* Below 1,280 px the open profile takes the list's place: the list is hidden, not removed, and
   profile.js brings the page back to where it was. */
@media (max-width: 1279.98px) {
  #view-search:has(.drawer.is-open) .results {
    display: none;
  }
  /* At least a screen tall, so the page can bring it to the top while the profile loads. */
  .drawer.is-open {
    min-height: calc(100dvh - var(--topbar-h));
  }
  /* Back to results stays in reach while the profile scrolls with the page. */
  .drawer-bar {
    position: sticky;
    top: var(--topbar-h);
    z-index: 2;
    border-radius: var(--radius) var(--radius) 0 0;
  }
  /* A11Y-01, WCAG 2.2 SC 2.4.11: html's scroll-padding-top clears the top bar, this clears the
     profile's own bar under it. */
  .drawer.is-open .drawer-body :is(a[href], button, input, select, textarea, summary, [tabindex]) {
    scroll-margin-top: var(--drawer-bar-h);
  }
}

/* From 1,280 px: list and pane side by side, the pane under the header, scrolling on its own. */
@media (min-width: 1280px) {
  #view-search > .wrap {
    display: grid;
    grid-template-columns: minmax(20rem, 35rem) minmax(0, 1fr);
    column-gap: var(--space-8);
    align-items: start;
  }
  #view-search > .wrap > * {
    grid-column: 1 / -1;
  }
  #view-search > .wrap > .results {
    grid-column: 1;
  }
  #view-search > .wrap > .drawer {
    grid-column: 2;
    position: sticky;
    top: calc(var(--topbar-h) + var(--space-4));
    max-height: calc(100dvh - var(--topbar-h) - 2 * var(--space-4));
  }
  /* The empty pane waits only where there is somebody to choose: not beside the search's progress,
     a refusal or an error, or a search that found nobody (UI-42). */
  #view-search.is-results:has(.result-list:not([hidden])) .drawer {
    display: flex;
  }
  #view-search:not(.is-results) > .wrap > .drawer {
    grid-column: 1 / -1;
  }
  .drawer-body {
    overflow-y: auto;
    overscroll-behavior: contain;
  }
}

/* "Back to results" where the pane replaces the list; "Close" where the list stays beside it. */
#drawer-close .pane-back,
#drawer-close .pane-close {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

#drawer-close .pane-close {
  display: none;
}

@media (min-width: 1280px) {
  #view-search.is-results #drawer-close .pane-back {
    display: none;
  }
  #view-search.is-results #drawer-close .pane-close {
    display: inline-flex;
  }
  /* A profile opened from the shortlist goes back to it, even beside the results: the button says so. */
  #view-search.is-results #drawer[data-back-elsewhere] #drawer-close .pane-back {
    display: inline-flex;
  }
  #view-search.is-results #drawer[data-back-elsewhere] #drawer-close .pane-close {
    display: none;
  }
}

.drawer:not(.is-open) .drawer-bar {
  display: none;
}

.drawer-bar {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--line);
  background: var(--surface);
}

.drawer-bar .grow {
  flex: 1;
}

.drawer-body {
  min-height: 0;
  padding: var(--space-6) var(--space-7) var(--space-15);
  flex: 1;
}

/* Reading text in the pane stays under about 70 characters a line (UI-06). */
.drawer-body > * {
  max-width: 70ch;
}

.drawer-empty {
  color: var(--muted);
}

.drawer:not(.is-open) .drawer-body {
  padding-bottom: var(--space-6);
}

/* The open card: chosen, in a look that differs from the focus ring (a 2px outline outside the
   card). In dark mode --accent and --focus look alike, so the shape tells them apart. */
.result:has(.result-name a[aria-current="true"]) {
  background: var(--accent-soft);
  border-color: var(--accent);
}

.profile-head h2 {
  font-size: var(--text-xl);
}

.profile-head .affil {
  font-size: var(--text-md);
  margin-top: var(--space-2);
}

.profile-head .badges {
  margin-top: var(--space-3);
}

.stat-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-2);
  margin-top: var(--space-5);
}

.stat {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
}

.stat b {
  display: block;
  font-family: var(--display);
  font-size: var(--text-xl);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.stat span {
  font-size: var(--text-xs);
  color: var(--muted);
}

.tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--line);
  margin: var(--space-6) 0 var(--space-5);
  overflow-x: auto;
  scrollbar-width: none;
}

.tabs button {
  position: relative;
  overflow: hidden;
  border: 0;
  background: none;
  height: 44px;
  padding: 0 var(--space-4);
  border-radius: 8px 8px 0 0;
  font-weight: 600;
  font-size: var(--text-sm);
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
  -webkit-tap-highlight-color: transparent;
  transition: color 0.15s var(--ease), background 0.15s var(--ease);
}

.tabs button:hover {
  color: var(--ink);
  background: var(--bg-tint);
}

/* A11Y-01: .tabs scrolls sideways, which clips anything drawn outside it, so the 2px ring goes
   just inside each tab's edge instead of 2px outside it. */
.tabs button:focus-visible {
  outline-offset: -2px;
}

.tabs button[aria-selected="true"] {
  color: var(--accent);
}

.tabs button[aria-selected="true"]::after {
  content: "";
  position: absolute;
  left: 12px;
  right: 12px;
  bottom: 0;
  height: 3px;
  border-radius: 3px 3px 0 0;
  background: var(--accent);
  animation: grow-x 0.2s var(--ease);
}

@keyframes grow-x {
  from {
    transform: scaleX(0.3);
    opacity: 0;
  }
}

.section {
  margin-top: var(--space-6);
}

.section:first-child {
  margin-top: 0;
}

.section h3 {
  font-size: var(--text-md);
  margin-bottom: var(--space-2);
}

.section .sub {
  color: var(--muted);
  font-size: var(--text-sm);
  margin: calc(var(--space-1) * -1) 0 var(--space-3);
}

.bio {
  color: var(--ink-2);
  white-space: pre-line;
}

.positions {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--space-2);
}

.positions li {
  display: grid;
  grid-template-columns: 96px 1fr;
  gap: var(--space-3);
  font-size: var(--text-sm);
}

.positions .years {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.positions b {
  font-weight: 600;
}

.work {
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--line);
}

.work:last-child {
  border-bottom: 0;
}

.abstract {
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  color: var(--ink-2);
}

.abstract summary {
  cursor: pointer;
  color: var(--accent);
  font-size: var(--text-sm);
  font-weight: 600;
  width: fit-content;
}

.abstract p {
  margin-top: var(--space-2);
}

.links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.link-pill {
  position: relative;
  overflow: hidden;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  border: 1px solid var(--line-strong);
  border-radius: 999px;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
  color: var(--ink-2);
  text-decoration: none;
  background: var(--surface);
}

.link-pill:hover {
  color: var(--accent);
  border-color: var(--accent);
}

.sparkline {
  padding-top: var(--space-2);
}

.spark-svg {
  width: 100%;
  height: 96px;
  overflow: visible;
}

.spark-bar {
  fill: var(--accent);
  opacity: 0.85;
}

.spark-label {
  fill: var(--muted);
  /* SVG user units inside the chart's viewBox, not page type. */
  font-size: 4.2px;
  font-family: var(--sans);
}

.collab-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.collab-list button {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--line);
  background: var(--surface);
  border-radius: 999px;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
  cursor: pointer;
  color: var(--ink-2);
}

.collab-list button:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.collab-list small {
  color: var(--muted);
  margin-left: var(--space-1);
}

/* ---------- Contact ---------- */

.source-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--space-2);
  margin: var(--space-4) 0;
}

.source {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--muted);
  padding: var(--space-2) var(--space-2);
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--surface);
}

.source .ico {
  width: 18px;
  height: 18px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: var(--text-xs);
  font-weight: 800;
  flex: none;
  background: var(--bg-tint);
}

.source.found {
  color: var(--good);
  border-color: color-mix(in srgb, var(--good) 35%, transparent);
}

.source.found .ico {
  background: var(--good);
  color: var(--surface);
}

.source.checked .ico {
  color: var(--muted);
}

.email-card {
  padding: var(--space-4) var(--space-4);
  margin-bottom: var(--space-2);
}

.email-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.email-addr {
  font-family: var(--mono);
  font-size: var(--text-md);
  font-weight: 600;
  word-break: break-all;
}

.email-row .grow {
  flex: 1;
}

.reasons {
  margin: var(--space-2) 0 0;
  padding-left: var(--space-4);
  color: var(--ink-2);
  font-size: var(--text-sm);
  display: grid;
  gap: 2px;
}

.email-sources {
  margin-top: var(--space-2);
  font-size: var(--text-sm);
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
}

.callout {
  border-radius: var(--radius-sm);
  padding: var(--space-4) var(--space-4);
  background: var(--surface-2);
  border: 1px solid var(--line);
  font-size: var(--text-sm);
  color: var(--ink-2);
}

.callout strong {
  color: var(--ink);
}

/* ---------- Draft ---------- */

.draft-grid {
  display: grid;
  gap: var(--space-3);
  max-width: 640px;
}

.draft-body {
  min-height: 300px;
  font-size: var(--text-md);
  line-height: 1.6;
}

.draft-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
}

.word-count {
  margin-left: auto;
  font-size: var(--text-sm);
  color: var(--muted);
}

.tips {
  margin: 0;
  padding-left: var(--space-4);
  color: var(--ink-2);
  font-size: var(--text-sm);
  display: grid;
  gap: var(--space-2);
}

/* ---------- Saved ---------- */

.page-head {
  padding: var(--space-12) 0 var(--space-5);
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.page-head h1 {
  font-size: var(--text-2xl);
}

.page-head p {
  color: var(--muted);
  margin-top: var(--space-2);
}

.pipeline {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.pipeline div {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
}

.pipeline b {
  font-family: var(--display);
  font-size: var(--text-lg);
  display: block;
}

.pipeline span {
  font-size: var(--text-xs);
  color: var(--muted);
}

.saved-item {
  padding: var(--space-4) var(--space-5);
  display: grid;
  grid-template-columns: minmax(0, 1fr) 220px;
  gap: var(--space-4);
}

.saved-item + .saved-item {
  margin-top: var(--space-2);
}

.saved-side {
  display: grid;
  gap: var(--space-2);
  align-content: start;
}

.saved-notes {
  margin-top: var(--space-3);
}

.saved-item textarea {
  min-height: 60px;
  font-size: var(--text-md);
}

/* ---------- About ---------- */

/* Reading text: about 70 characters per line at most. */
.prose,
.page-head p,
.step p,
.bio,
.abstract p {
  max-width: 72ch;
}

.prose h2 {
  font-size: var(--text-xl);
  margin: var(--space-10) 0 var(--space-3);
}

.prose p,
.prose li {
  color: var(--ink-2);
}

.prose p + p {
  margin-top: var(--space-3);
}

.prose ul {
  padding-left: var(--space-5);
  display: grid;
  gap: var(--space-2);
}

.steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-6);
}

.step {
  padding: var(--space-5);
}

.step .num {
  font-family: var(--display);
  font-size: var(--text-xl);
  color: var(--accent);
  font-style: italic;
}

.step h2 {
  font-size: var(--text-md);
  margin: var(--space-2) 0;
}

/* Smaller text, so a shorter cap than the 72ch above keeps it under about 70 characters a line
   when the cards are wide (UI-06). */
.step p {
  max-width: 56ch;
  color: var(--ink-2);
  font-size: var(--text-sm);
}

/* ---------- Home: below the search form (UI-81) ---------- */

.home-more {
  display: grid;
  gap: var(--space-10);
  margin-top: var(--space-14);
}

/* The results replace this block; it returns with the form (4W7c). */
#view-search.is-results .home-more {
  display: none;
}

.home-more-title {
  font-size: var(--text-xl);
}

.home-more .steps {
  margin-top: var(--space-4);
}

.home-more-link {
  margin-top: var(--space-3);
  font-size: var(--text-sm);
}

.home-limits {
  padding: var(--space-6);
}

.home-limits-cols {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-6);
  margin-top: var(--space-4);
}

.home-limits h3 {
  font-size: var(--text-md);
  margin-bottom: var(--space-2);
}

/* Each column's lines stay under about 70 characters on a wide window (UI-06). */
.home-limits ul {
  display: grid;
  gap: var(--space-2);
  max-width: 56ch;
  margin: 0;
  padding-left: var(--space-5);
  color: var(--ink-2);
  font-size: var(--text-sm);
}

.home-cta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  color: var(--ink-2);
  text-align: center;
}

/* ---------- Accounts ---------- */

.account-slot {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-left: var(--space-1);
}

.account-slot .btn {
  height: 36px;
}

/* The sign-up page has its own "Create account" submit button (UI-35); don't
   show the header's as well while that page is showing. */
body.route-signup .account-slot .btn-create {
  display: none;
}

.avatar-btn {
  position: relative;
  overflow: hidden;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.15s var(--ease);
}

.avatar-btn:hover,
.avatar-btn[aria-expanded="true"] {
  background: var(--bg-tint);
}

.avatar {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-ink);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: 0.02em;
}

.avatar.lg {
  width: 64px;
  height: 64px;
  font-size: var(--text-xl);
}

.account-menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  z-index: 60;
  width: 300px;
  padding: var(--space-2);
  border: 1px solid var(--line);
  border-radius: 16px;
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  transform-origin: top right;
  animation: menu-in 0.16s var(--ease);
}

.account-menu[hidden] {
  display: none;
}

@keyframes menu-in {
  from {
    opacity: 0;
    transform: translateY(-4px) scale(0.98);
  }
}

.account-who {
  display: grid;
  justify-items: center;
  gap: 2px;
  padding: var(--space-4) var(--space-3);
  margin-bottom: var(--space-1);
  border-bottom: 1px solid var(--line);
  text-align: center;
  overflow-wrap: anywhere;
}

.account-who strong {
  margin-top: var(--space-2);
  font-size: var(--text-md);
}

.account-email {
  color: var(--ink-2);
  font-size: var(--text-sm);
}

.account-who small {
  margin-top: var(--space-1);
  color: var(--muted);
  font-size: var(--text-xs);
}

.account-menu [role="menuitem"] {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  height: 40px;
  padding: 0 var(--space-3);
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--ink);
  font: inherit;
  font-size: var(--text-sm);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
}

.account-menu [role="menuitem"]:hover,
.account-menu [role="menuitem"]:focus-visible {
  background: var(--bg-tint);
}

.account-menu [role="menuitem"]:focus-visible {
  /* A11Y-01: 2px --focus outline, offset 2px (ratios in the global :focus-visible comment). */
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.menu-ico {
  display: grid;
  place-items: center;
  width: 20px;
  color: var(--muted);
}

.account-menu .danger,
.account-menu .danger .menu-ico {
  color: var(--bad);
}

.menu-sep {
  height: 1px;
  margin: var(--space-1);
  background: var(--line);
}

/* The sign-in / create-account page (several views), and confirm dialogs. */

.auth-page {
  padding: var(--space-10) 0 var(--space-14);
}

.auth-page .auth-inner {
  max-width: 448px;
  margin-inline: auto;
  border-radius: 18px;
}

.auth-dialog {
  width: min(448px, calc(100% - 24px));
  max-height: calc(100dvh - 24px);
  padding: 0;
  border: 1px solid var(--line);
  border-radius: 18px;
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--shadow-lg);
  overflow: auto;
}

.auth-dialog::backdrop {
  background: var(--scrim-strong);
}

.auth-dialog[open] {
  animation: dialog-in 0.2s var(--ease);
}

@keyframes dialog-in {
  from {
    opacity: 0;
    transform: translateY(10px) scale(0.98);
  }
}

.auth-inner {
  position: relative;
  padding: var(--space-8) var(--space-9) var(--space-7);
}

.auth-view {
  animation: view-in 0.2s var(--ease);
}

@keyframes view-in {
  from {
    opacity: 0;
    transform: translateX(10px);
  }
}

.auth-view h1 {
  font-size: var(--text-xl);
}

.auth-sub {
  margin-top: var(--space-1);
  color: var(--ink-2);
  font-size: var(--text-md);
  /* An address the provider gave can be long and has no place to break (the age step shows it). */
  overflow-wrap: anywhere;
}

/* Buttons side by side, wrapping on a narrow screen: the age step's "Try again" and "Start again". */
.auth-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.auth-banner {
  display: flex;
  gap: var(--space-2);
  align-items: flex-start;
  margin-top: var(--space-4);
  padding: var(--space-3);
  border: 1px solid var(--accent-soft-2);
  border-radius: 10px;
  background: var(--accent-soft);
  font-size: var(--text-sm);
}

.auth-banner .ico {
  display: inline-flex;
  color: var(--accent);
}

.auth-alert {
  margin-top: var(--space-4);
  /* The page scrolls it into view (A11Y-07), clear of the sticky top bar (html's scroll-padding-top). */
  padding: var(--space-2) var(--space-3);
  border-radius: 8px;
  background: var(--bad-soft);
  color: var(--bad);
  font-size: var(--text-sm);
}

.auth-alert.info {
  background: var(--accent-soft);
  color: var(--ink);
}

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

.auth-alert .link-btn {
  margin-left: var(--space-1);
  /* The link's own green is 4.46:1 on the pale red of an alert, under the 4.5:1 text needs; the
     alert's colour passes, and the underline still marks it as a link. */
  color: inherit;
}

.auth-alert .link-btn:hover {
  color: inherit;
  text-decoration-thickness: 2px;
}

/* "There is a problem": every field error at the top of the form, each a link to its field. */
.error-summary {
  padding: var(--space-3);
  border: 2px solid var(--bad);
  border-radius: var(--radius-sm);
  background: var(--bad-soft);
  color: var(--ink);
  font-size: var(--text-sm);
}

.error-summary[hidden] {
  display: none;
}

.error-summary:focus {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.error-summary-title {
  margin: 0 0 var(--space-1);
  font-size: var(--text-sm);
  color: var(--bad);
}

.error-summary ul {
  margin: 0;
  padding-left: var(--space-4);
}

.error-summary a {
  color: var(--bad);
  font-weight: 600;
}

/* WCAG 2.2 SC 2.5.8: one link per line, each at least 24px tall, so their targets don't touch. */
.error-summary li a {
  display: inline-block;
  min-height: var(--target-min);
}

.auth-form {
  display: grid;
  gap: var(--space-3);
  margin-top: var(--space-5);
}

.auth-field {
  display: grid;
  gap: var(--space-1);
}

.auth-field label {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--ink-2);
}

/* "I am 13 or older and agree to the Terms…": the box beside its sentence. */
.auth-agree label {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  font-weight: 400;
  line-height: 1.45;
}

.auth-agree input {
  flex: none;
  width: 18px;
  height: 18px;
  margin: 1px 0 0;
  accent-color: var(--accent);
}

.field-below {
  display: flex;
  justify-content: flex-end;
  font-size: var(--text-sm);
}

.field-error {
  font-size: var(--text-sm);
  color: var(--bad);
}

.field-error:empty {
  display: none;
}

.field-hint {
  font-size: var(--text-xs);
  color: var(--muted);
}

.field-hint:empty {
  display: none;
}

.password-wrap {
  position: relative;
}

.password-wrap input {
  padding-right: var(--space-17);
}

.reveal-btn {
  position: absolute;
  top: 50%;
  right: 6px;
  height: 30px;
  padding: 0 var(--space-2);
  transform: translateY(-50%);
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--accent);
  font-size: var(--text-sm);
  font-weight: 650;
  cursor: pointer;
}

.reveal-btn:hover {
  background: var(--accent-soft);
}

.strength {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-1);
}

.strength i {
  height: 4px;
  border-radius: 2px;
  background: var(--line);
  transition: background 0.2s var(--ease);
}

.strength[data-score="1"] i:nth-child(-n + 1) {
  background: var(--bad);
}

.strength[data-score="2"] i:nth-child(-n + 2) {
  background: var(--warn);
}

.strength[data-score="3"] i:nth-child(-n + 3) {
  background: var(--accent);
}

.strength[data-score="4"] i {
  background: var(--good);
}

/* `input.`: `input[type="text"]`'s padding shorthand is more specific than a bare class. */
input.code-input {
  height: 56px;
  text-align: center;
  font-family: var(--mono);
  font-size: var(--text-xl);
  font-weight: 600;
  letter-spacing: 0.3em;
  padding-left: calc(var(--space-3) + 0.3em);
}

.auth-divider {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-5) 0 0;
  color: var(--muted);
  font-size: var(--text-sm);
}

.auth-divider::before,
.auth-divider::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}

.provider-buttons {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-5);
}

.provider-btn {
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  height: 44px;
  padding: 0 var(--space-4);
  border: 1px solid var(--line-strong);
  border-radius: 10px;
  background: var(--surface);
  color: var(--ink);
  font-weight: 600;
  font-size: var(--text-sm);
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease), transform 0.1s var(--ease);
}

.provider-btn:hover {
  background: var(--bg-tint);
  border-color: color-mix(in srgb, var(--ink) 30%, var(--line-strong));
  text-decoration: none;
}

.provider-btn:active {
  transform: scale(0.98);
}

.provider-logo {
  width: 20px;
  height: 20px;
  flex: none;
}

.auth-foot {
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: 1px solid var(--line);
  font-size: var(--text-sm);
  color: var(--ink-2);
}

.fine-print {
  margin: var(--space-3) 0 0;
  color: var(--muted);
  font-size: var(--text-xs);
}

.fine-print a {
  color: var(--ink-2);
}

.dev-note {
  margin-top: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px dashed color-mix(in srgb, var(--warn) 60%, transparent);
  border-radius: 8px;
  background: var(--warn-soft);
  color: var(--warn);
  font-size: var(--text-sm);
}

.mail-icon {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  margin: 0 auto var(--space-3);
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: var(--text-xl);
}

.auth-dialog.confirm-dialog .auth-inner {
  padding: var(--space-7);
}

.confirm-dialog h2 {
  text-align: left;
  font-size: var(--text-lg);
}

.confirm-dialog p {
  margin-top: var(--space-2);
  color: var(--ink-2);
}

.confirm-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-6);
}

.submit-group {
  display: grid;
  justify-items: end;
  gap: var(--space-2);
  /* The footer wraps, and the group is as wide as the note under the button: without this the group
     starts at the line's left edge and the button moves sideways whenever the note changes length. */
  margin-inline-start: auto;
}

/* On the form, the way back to the list this tab still holds (UI-39): one quiet line above the
   question, the sentence and its button side by side, the button under it where they don't fit. */
.held-results {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-3);
  margin: 0 0 var(--space-4);
  color: var(--ink-2);
  font-size: var(--text-sm);
}

.trial-note {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-start;
  gap: var(--space-1) var(--space-2);
  margin: 0;
  color: var(--muted);
  font-size: var(--text-sm);
  text-align: start; /* the paused sentence wraps to several lines: they start at the left (UI-23) */
}

.trial-note[hidden] {
  display: none;
}

.link-btn {
  padding: 0;
  border: 0;
  background: none;
  color: var(--accent);
  font: inherit;
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}

.link-btn:hover {
  color: var(--accent-hover);
}

/* Under the search button: tall enough to tap, and clear of the button (WCAG 2.5.8, 24px). */
.trial-note .link-btn {
  min-height: var(--target-min);
}

.link-btn[disabled] {
  color: var(--muted);
  text-decoration: none;
  cursor: default;
}

.signin-card .btn {
  margin-top: var(--space-1);
}

/* ---------- Snackbars, progress bar, ripple ---------- */

.toast-root {
  position: fixed;
  left: 24px;
  bottom: 24px;
  z-index: 90;
  display: grid;
  gap: var(--space-2);
  width: min(440px, calc(100% - 32px));
  pointer-events: none;
}

.toast {
  pointer-events: auto;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 48px;
  padding: var(--space-2) var(--space-2) var(--space-2) var(--space-4);
  border-radius: 8px;
  background: var(--snackbar);
  color: var(--snackbar-ink);
  box-shadow: var(--shadow-lg);
  font-size: var(--text-sm);
  animation: toast-in 0.2s var(--ease);
}

.toast.is-leaving {
  animation: toast-out 0.2s var(--ease) forwards;
}

.toast-icon {
  flex: none;
  font-weight: 700;
}

.toast.success .toast-icon {
  color: var(--snackbar-action);
}

.toast.error .toast-icon {
  color: var(--snackbar-error);
}

.toast-msg {
  flex: 1;
  padding: var(--space-1) 0;
}

.toast-action,
.toast-close {
  position: relative;
  overflow: hidden;
  flex: none;
  height: 34px;
  padding: 0 var(--space-3);
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--snackbar-action);
  font: inherit;
  font-weight: 700;
  cursor: pointer;
}

.toast-close {
  width: 34px;
  padding: 0;
  color: inherit;
  opacity: 0.7;
  font-size: var(--text-md);
}

.toast-action:hover,
.toast-close:hover {
  background: color-mix(in srgb, var(--snackbar-ink) 12%, transparent);
  opacity: 1;
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(12px) scale(0.98);
  }
}

@keyframes toast-out {
  to {
    opacity: 0;
    transform: translateY(8px);
  }
}

/* A thin bar across the top while anything is loading (api.js). It fades in
   and stays still: no looping animation (UI-70). */
.progress-bar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  height: 3px;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s var(--ease);
}

.progress-bar::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--accent);
}

.progress-bar.is-active {
  opacity: 1;
}

/* Click feedback: dom.js adds a .ripple where the pointer went down. */
.ripple {
  position: absolute;
  border-radius: 50%;
  background: currentColor;
  opacity: 0.2;
  pointer-events: none;
  transform: scale(0);
  animation: ripple 0.2s var(--ease) forwards;
}

@keyframes ripple {
  to {
    transform: scale(1);
    opacity: 0;
  }
}

/* ---------- Search progress (results.js) ---------- */

.search-progress {
  padding: var(--space-5) var(--space-6) var(--space-4);
  margin-bottom: var(--space-3);
}

.search-progress-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-4);
}

.search-progress-title {
  margin: 0;
  font-size: var(--text-md);
}

.search-progress-hint {
  margin: 2px 0 0;
  color: var(--muted);
  font-size: var(--text-sm);
}

.search-progress-hint.is-slow {
  color: var(--warn);
}

.search-progress-percent {
  flex: none;
  font-family: var(--display);
  font-size: var(--text-xl);
  font-weight: 650;
  line-height: 1.1;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}

.search-progress-track {
  position: relative;
  height: 8px;
  margin: var(--space-4) 0;
  overflow: hidden;
  border-radius: 999px;
  background: var(--bg-tint);
}

/* Width comes from --p (0..1), set each frame through CSSOM: an inline
   style attribute would be blocked by the Content-Security-Policy. */
.search-progress-fill {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  background: var(--accent);
  transform-origin: left center;
  transform: scaleX(var(--p, 0));
}

.search-steps {
  display: grid;
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.search-step {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--muted);
  font-size: var(--text-sm);
  transition: color 0.2s var(--ease);
}

.search-step-icon {
  position: relative;
  flex: none;
  width: 18px;
  height: 18px;
  border: 2px solid var(--line-strong);
  border-radius: 50%;
}

.search-step.is-active {
  color: var(--ink);
  font-weight: 550;
}

.search-step.is-active .search-step-icon {
  border-color: var(--accent-soft-2);
  border-top-color: var(--accent);
  animation: spin 0.8s linear infinite;
}

.search-step.is-done {
  color: var(--ink-2);
}

.search-step.is-done .search-step-icon {
  border-color: var(--accent);
  background: var(--accent);
  animation: pop 0.2s var(--ease);
}

.search-step.is-done .search-step-icon::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 1px;
  width: 5px;
  height: 9px;
  border: solid var(--accent-ink);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.search-progress-foot {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--space-1);
}

/* ---------- Star ratings (feedback.js) ---------- */

.stars {
  display: inline-flex;
  gap: 2px;
}

.star {
  position: relative;
  display: inline-grid;
  place-items: center;
  width: 42px;
  height: 42px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: none;
  color: var(--muted);
  cursor: pointer;
  transition: transform 0.15s var(--ease), background-color 0.15s var(--ease);
}

.star svg {
  width: 30px;
  height: 30px;
  fill: transparent;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linejoin: round;
  transition: fill 0.12s var(--ease), stroke 0.12s var(--ease);
}

.star:hover {
  background: var(--bg-tint);
}

.star:active {
  transform: scale(0.86);
}

.star:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 1px;
}

.star.is-on svg {
  fill: var(--star);
  stroke: var(--star-edge);
}

.star.pop svg {
  animation: star-pop 0.2s var(--ease);
}

@keyframes star-pop {
  45% {
    transform: scale(1.25) rotate(-8deg);
  }
}

.stars.is-locked .star {
  cursor: default;
}

.stars.is-locked .star:hover {
  background: none;
}

.stars.sm .star {
  width: 34px;
  height: 34px;
}

.stars.sm .star svg {
  width: 23px;
  height: 23px;
}

.rate-label {
  min-height: 1.4em;
  margin: 0;
  color: var(--muted);
  font-size: var(--text-sm);
}

.rate-label.is-set {
  color: var(--ink);
  font-weight: 600;
}

.rate-panel {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 35;
  width: min(372px, calc(100% - 32px));
  padding: var(--space-4) var(--space-3) var(--space-4) var(--space-5);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: var(--shadow-lg);
  animation: rate-in 0.2s var(--ease);
}

.rate-panel.is-leaving {
  animation: rate-out 0.2s var(--ease) forwards;
}

@keyframes rate-in {
  from {
    opacity: 0;
    transform: translateY(16px) scale(0.97);
  }
}

@keyframes rate-out {
  to {
    opacity: 0;
    transform: translateY(10px);
  }
}

.rate-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-2);
}

.rate-title {
  margin: 2px 0 0;
  font-size: var(--text-md);
}

.rate-sub {
  margin: 2px 0 0;
  color: var(--muted);
  font-size: var(--text-sm);
}

.rate-close {
  flex: none;
  margin: calc(-1 * var(--space-1)) -2px 0 0;
  font-size: var(--text-lg);
}

.rate-form .stars {
  /* -6px: the 42px star box has a 6px inset around its 30px glyph, so the first
     star lines up with the text above. */
  margin: var(--space-2) 0 2px calc(-1 * (var(--space-1) + 2px));
}

.rate-more {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-2);
  padding-right: var(--space-1);
}

.rate-more.is-entering {
  animation: view-in 0.2s var(--ease);
}

.rate-comment {
  min-height: 76px;
  font-size: var(--text-md); /* 16px: iOS zooms into smaller fields */
}

.rate-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.rate-note {
  margin: 0;
  color: var(--muted);
  font-size: var(--text-xs);
  line-height: 1.35;
}

.rate-error {
  margin: 0;
  color: var(--bad);
  font-size: var(--text-sm);
}

.rate-thanks {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-1) 2px 0;
}

.rate-thanks p {
  margin: 0;
  color: var(--muted);
  font-size: var(--text-sm);
}

.rate-thanks-icon {
  display: grid;
  flex: none;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--good-soft);
  color: var(--good);
  font-weight: 700;
  animation: pop 0.2s var(--ease);
}

.draft-rating {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-1) var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px solid var(--line);
}

.draft-rating .stars {
  margin-left: calc(-1 * var(--space-1));
}

/* ---------- Responsive ---------- */

@media (max-width: 900px) {
  .filters {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .steps {
    grid-template-columns: 1fr;
  }
  .pipeline {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
  /* Tablets: the links move beside the account actions and keep their words (4G12: words,
     not icons alone, from 481 px; the menu button takes over at 480 px and below). */
  .nav {
    margin-left: auto;
  }
  .nav a {
    padding: 0 var(--space-2);
    white-space: nowrap;
  }
  .topbar-actions {
    margin-left: 0;
  }
}

@media (max-width: 640px) {
  /* Phones: the search button stays on the first screen (UI-80). */
  .home-top {
    gap: var(--space-4);
    padding-top: var(--space-4);
  }
  .hero h1 {
    margin-top: var(--space-3);
  }
  .hero-more .lede {
    font-size: var(--text-md);
  }
  .composer {
    padding: var(--space-4);
  }
  /* The examples stay on one row, which scrolls sideways when they don't all fit, so the search
     button stays on the first screen. The padding keeps each chip's focus ring inside the row. */
  .examples {
    flex-wrap: nowrap;
    overflow-x: auto;
    margin-inline: calc(-1 * var(--space-1));
    padding: var(--space-1);
    scrollbar-width: thin;
  }
  .examples .chip-btn {
    flex: none;
  }
  .home-limits {
    padding: var(--space-4);
  }
  .home-limits-cols {
    grid-template-columns: 1fr;
  }
  .grid-2,
  .filters {
    grid-template-columns: 1fr;
  }
  .filters .field-2 {
    grid-column: auto;
  }
  .result {
    padding: var(--space-4);
  }
  .result-list > .result {
    contain-intrinsic-height: auto 44rem;
  }
  .result-top {
    flex-direction: column;
  }
  .fit {
    text-align: left;
  }
  .fit-meter {
    margin-left: 0;
  }
  .stat-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .drawer-body {
    padding: var(--space-5) var(--space-4) var(--space-15);
  }
  .saved-item {
    grid-template-columns: 1fr;
  }
  .pipeline {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .positions li {
    grid-template-columns: 1fr;
    gap: 0;
  }
  .composer-footer .btn-lg {
    width: 100%;
  }
  .submit-group {
    width: 100%;
    justify-items: stretch;
  }
  .auth-inner {
    padding: var(--space-7) var(--space-5) var(--space-6);
  }
  .account-slot .btn-create {
    display: none;
  }
  .toast-root {
    left: 16px;
    right: 16px;
    bottom: 16px;
    width: auto;
  }
  .rate-panel {
    right: 12px;
    bottom: 12px;
    left: 12px;
    width: auto;
  }
  .search-progress {
    padding: var(--space-4) var(--space-4) var(--space-4);
  }
  .search-progress-percent {
    font-size: var(--text-lg);
  }
}

/* Phones: the header holds the brand, the menu button, the theme button and
   Sign in. The links drop down under the bar while the menu is open (UI-38). */
@media (max-width: 480px) {
  .topbar-inner {
    gap: var(--space-1);
  }
  .nav-toggle {
    display: inline-flex;
    margin-left: auto;
  }
  .nav {
    display: none;
    position: absolute;
    top: calc(100% + 1px);
    left: 0;
    right: 0;
    margin: 0;
    padding: var(--space-2) var(--space-4) var(--space-3);
    flex-direction: column;
    align-items: stretch;
    background: var(--surface);
    border-bottom: 1px solid var(--line);
    box-shadow: var(--shadow-md);
  }
  .nav-toggle[aria-expanded="true"] + .nav {
    display: flex;
  }
  /* In the open menu each link has its icon beside its words. */
  .nav-icon {
    display: block;
  }
  .nav a {
    height: 44px;
    padding: 0 var(--space-3);
    gap: var(--space-3);
    border-radius: var(--radius-sm);
    font-size: var(--text-md);
  }
  .brand {
    gap: var(--space-2);
    font-size: var(--text-md);
  }
  .brand-mark {
    width: 28px;
    height: 28px;
  }
  .icon-btn {
    padding: 0 var(--space-2);
  }
  .account-slot {
    margin-left: 2px;
  }
  .account-slot .btn {
    padding: 0 var(--space-2);
  }
}

/* With the links' words in the header (4G12), "Create account" waits for room; "Sign in" stays
   and leads to it. */
@media (max-width: 680px) {
  .account-slot .btn-create {
    display: none;
  }
}

/* Just above the menu button's widths the three links' words need the room: the brand mark
   alone; the link keeps its accessible name. */
@media (min-width: 481px) and (max-width: 560px) {
  .brand-name {
    display: none;
  }
}

/* Narrow phones (under the 375px of an iPhone SE): the brand mark alone;
   the link keeps its accessible name. */
@media (max-width: 374px) {
  .brand-name {
    display: none;
  }
}

/* Touch screens: tap targets are at least 44px tall (UI-05). Icon-sized
   controls, including the header's brand link and icon buttons, also get
   44px of width; with the links behind the menu button (UI-38) the phone
   header has room for them down to 320px. */
@media (pointer: coarse) {
  :root {
    --drawer-bar-h: 69px;
  }
  .btn,
  .chip-btn,
  .nav a,
  .icon-btn,
  .brand,
  .avatar-btn,
  .link-pill,
  .collab-list button,
  .account-menu [role="menuitem"],
  .toast-action,
  .toast-close,
  .reveal-btn,
  .error-summary li a,
  .auth-agree label,
  .toggle-chip,
  .segmented label,
  details.more > summary,
  input[type="text"],
  input[type="search"],
  input[type="date"],
  input[type="email"],
  input[type="password"],
  select {
    min-height: var(--tap);
  }
  .avatar-btn,
  .dialog-close,
  .rate-close,
  .toast-close,
  .icon-btn,
  .brand {
    min-width: var(--tap);
  }
  .footer-links a {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap);
  }
  /* Country/institution chip "x": the visual circle stays 20px, but a
     transparent ::after gives it a 44px hit area centred on the same spot. */
  .token button {
    position: relative;
  }
  .token button::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: var(--tap);
    height: var(--tap);
    transform: translate(-50%, -50%);
  }
  /* About you and More options: the whole 44px row is the toggle, and it takes the space of
     the padding above it. */
  details.more {
    padding-top: 0;
  }
}

/* Keep the saved count on the star's corner now the link is taller. */
@media (pointer: coarse) and (max-width: 480px) {
  .nav .count-badge {
    top: 6px;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

@media print {
  .topbar,
  .site-footer,
  .result-actions,
  .result-foot,
  .drawer-bar {
    display: none;
  }
  /* Cards the browser skipped as off screen can print blank in some browsers. */
  .result-list > .result {
    content-visibility: visible;
  }
}
