/* Lead Extractor - workspace.
   The look is the Google Stitch "Lead Extractor Workspace" screen: emerald
   primary, near-white surfaces, hairline borders, small radii, Inter.

   One token layer, two densities, light only. No raw colour below the token
   block, so a colour is changed in one place. */

/* ================================================================== tokens */

:root {
  color-scheme: light;

  /* surfaces, from furthest back to closest to the reader.
     The light values are the Stitch design system verbatim. */
  --bg:            #f8f9fa;
  --surface:       #ffffff;
  --surface-2:     #f8f9fa;
  --surface-3:     #f3f4f5;
  --surface-4:     #e7e8e9;
  --border:        #e1e3e4;
  --border-strong: #bbcabf;

  /* text */
  --text:       #191c1d;
  --text-muted: #3c4a42;
  --text-faint: #777584;

  /* brand. --primary carries the meaning, --primary-bright fills buttons. */
  --primary:        #006c49;
  --primary-bright: #10b981;
  --primary-hover:  #006c49;
  --primary-soft:   #e8f5ef;
  --on-primary:     #ffffff;

  --accent:       #006c4a;
  --accent-hover: #00543a;
  --on-accent:    #ffffff;

  /* status. Each pair clears 4.5:1 on its own surface. */
  --ok:            #047857;
  --ok-bg:         #d1fae5;
  --ok-border:     #a7f3d0;
  --warn:          #b45309;
  --warn-bg:       #fef3c7;
  --warn-border:   #fde68a;
  --danger:        #ba1a1a;
  --danger-bg:     #ffdad6;
  --danger-border: #ffb3af;
  --info:          #0369a1;

  /* the floating bar and anything else that inverts */
  --inverse:    #2e3132;
  --on-inverse: #f0f1f2;

  --ring: #006c49;

  /* Stitch radius scale: badges 2, cards and inputs 4, buttons 6,
     overlays 8, chips 12. */
  --radius-xs:   2px;
  --radius-sm:   4px;
  --radius-md:   6px;
  --radius-lg:   8px;
  --radius-pill: 12px;

  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;

  --shadow-1: 0 1px 2px rgb(25 28 29 / 0.06);
  --shadow-2: 0 10px 25px rgb(25 28 29 / 0.10);
  --shadow-3: 0 24px 60px rgb(25 28 29 / 0.18);

  /* One family for every word a person reads. There used to be a second one for
     menus and dropdowns, on the theory that what opens over the page should not
     read as part of the page underneath it. In practice it made the panel look
     like two products stitched together, and the difference was never the thing
     telling you a menu was open - the shadow and the border already do that. */
  --font-body: Inter, "Segoe UI", system-ui, -apple-system, sans-serif;
  /* The one exception, and it is not decoration: a run id, an endpoint, a log
     line and a column of numbers all have to line up character by character to
     be read at all. */
  --font-mono: "JetBrains Mono", ui-monospace, Consolas, monospace;

  /* type scale, straight from the Stitch config */
  --fs-body:     14px;
  --lh-body:     20px;
  --fs-body-sm:  13px;
  --lh-body-sm:  18px;
  --fs-label:    12px;
  --lh-label:    16px;
  --fs-label-sm: 11px;
  --lh-label-sm: 14px;
  --fs-title:    16px;
  --fs-headline: 20px;
  --fs-display:  24px;

  --z-sticky: 20;
  --z-float: 30;
  --z-appbar: 40;
  --z-overlay: 90;
  --z-toast: 100;

  --duration: 150ms;
  --duration-slow: 240ms;
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);

  --appbar-h: 56px;
  /* The rail's resting width. The grip on its edge overrides this one
     inline, between the two bounds below. */
  --filters-w: 280px;
  --filters-w-min: 220px;
  --filters-w-max: 560px;

  --row-y: 12px;
  --field-h: 38px;
  --group-gap: 8px;
}

/* ================================================================== base */

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

/* Class rules below set display, which would beat the browser default for
   [hidden]. Without this every hidden block would still paint. */
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  scrollbar-color: var(--border) transparent;
}

body {
  margin: 0;
  height: 100dvh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body);
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body-sm);
  overscroll-behavior-y: none;
}

h1, h2, h3, h4 { margin: 0; line-height: 1.3; font-weight: 600; }
h2 { font-size: var(--fs-title); }
h3 { font-size: var(--fs-body); }
p { margin: 0; }

a { color: var(--primary); text-underline-offset: 2px; }
a:hover { text-decoration: underline; }

::-webkit-scrollbar { width: 6px; height: 6px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }
::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }

:where(button, a, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.icon {
  width: 20px; height: 20px;
  flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.icon--sm { width: 16px; height: 16px; }
.icon--empty { width: 32px; height: 32px; stroke-width: 1.4; }

/* Only for a state that is genuinely still moving, so the motion means "this is
   working" and nothing else. It stops entirely when the machine asks it to. */
.icon--spin { animation: icon-spin 1.6s linear infinite; transform-origin: 50% 50%; }
@keyframes icon-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .icon--spin { animation: none; }
}

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

.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: var(--z-toast);
  background: var(--primary); color: var(--on-primary);
  padding: 8px 14px; border-radius: 0 0 var(--radius-md) 0;
  font-size: var(--fs-label); font-weight: 600;
}
.skip-link:focus { left: 0; }

kbd {
  font-family: var(--font-mono);
  font-size: 10px;
  line-height: 1;
  padding: 3px 5px;
  border: 1px solid var(--border);
  border-bottom-width: 2px;
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text-faint);
}

/* ================================================================== app bar */

.appbar {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  height: var(--appbar-h);
  padding: 0 var(--space-5);
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  z-index: var(--z-appbar);
}

.appbar__brand { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }

/* One control, in one place, for both states. It never moves and never
   changes size, so folding the rail away cannot shuffle the app bar. The
   icon is the only thing that changes: a chevron pointing into the panel
   when the rail is open, out of it when the rail is folded. */
.sidetoggle {
  position: relative;
  margin-right: var(--space-3);
}
.sidetoggle .icon { width: 20px; height: 20px; }
.sidetoggle .sidetoggle__collapse { display: none; }
.sidetoggle.is-open .sidetoggle__collapse { display: block; }
.sidetoggle.is-open .sidetoggle__expand { display: none; }
.sidetoggle.is-open { color: var(--primary); }

/* A hairline between the toggle and the wordmark, so the button reads as
   chrome rather than as part of the logo. Same separator the segmented
   control and the bulk bar already use. */
.sidetoggle::before {
  content: "";
  position: absolute;
  top: 50%;
  right: calc(var(--space-3) / -2);
  width: 1px;
  height: 20px;
  margin-top: -10px;
  background: var(--border);
}

/* The button reads at 30px like every other icon button, but the area a
   finger has to hit is the full 44px the touch guidance asks for. Painted
   as an overlay so widening it never moves the app bar. */
.sidetoggle::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 44px; height: 44px;
  transform: translate(-50%, -50%);
}

.brandmark { display: grid; place-items: center; color: var(--primary); }
.brandmark .icon { width: 22px; height: 22px; stroke-width: 1.9; }

.appbar__name {
  font-size: var(--fs-display);
  line-height: 32px;
  font-weight: 900;
  letter-spacing: -0.01em;
  color: var(--primary);
  white-space: nowrap;
}

.tag {
  margin-left: var(--space-2);
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--surface-4);
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
  font-weight: 500;
  letter-spacing: 0.03em;
  white-space: nowrap;
}

/* tabs ---------------------------------------------------------------- */

.tabs { display: flex; gap: var(--space-5); align-items: stretch; height: 100%; }

.tab {
  appearance: none;
  border: 0;
  background: none;
  padding: 0 2px;
  height: 100%;
  color: var(--text-muted);
  font: inherit;
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  transition: color var(--duration), border-color var(--duration);
}
.tab:hover { color: var(--primary); }
.tab.is-on { color: var(--primary); border-bottom-color: var(--primary); }

.appbar__actions { display: flex; align-items: center; gap: var(--space-2); }
/* Set apart from the status light by a rule, because it ends the session
   rather than reporting on it. */
.appbar__signout { margin-left: var(--space-2); position: relative; }
.appbar__signout::before {
  content: ""; position: absolute; left: calc(var(--space-2) * -1 - 1px); top: 6px; bottom: 6px;
  width: 1px; background: var(--border);
}
.appbar__signout[hidden] { display: none; }
@media (max-width: 560px) {
  /* The word goes on a phone; the icon, the title and the aria-label stay. */
  .appbar__signout-word { display: none; }
}

/* ================================================================== pills */

.pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-muted);
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.pill--button { cursor: pointer; font-family: inherit; }
.pill--button:hover { background: var(--surface-3); }
.pill .dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
.pill--ok   { color: var(--ok); background: var(--ok-bg); border-color: var(--ok-border); }
.pill--bad  { color: var(--danger); background: var(--danger-bg); border-color: var(--danger-border); }
.pill--warn { color: var(--warn); background: var(--warn-bg); border-color: var(--warn-border); }
.pill--busy { color: var(--primary); background: var(--primary-soft); border-color: var(--primary-soft); }
.pill--busy .dot { animation: blink 1.2s ease-in-out infinite; }
.pill--muted { color: var(--text-faint); }

@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

.statusdot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--text-faint); }
.statusdot--ok { background: var(--ok); }
.statusdot--bad { background: var(--danger); }
.statusdot--warn { background: var(--warn); }
.statusdot--busy { background: var(--primary); animation: blink 1.2s ease-in-out infinite; }

/* ================================================================== run strip */

.runstrip { flex: none; background: var(--surface-2); border-bottom: 1px solid var(--border); }

.runstrip__track {
  position: relative;
  height: 4px;
  background: var(--border);
  overflow: hidden;
}
.runstrip__track > span {
  position: absolute; inset: 0 auto 0 0;
  width: 0;
  background-image: linear-gradient(90deg, var(--primary) 0%, var(--primary-bright) 50%, var(--primary) 100%);
  background-size: 200% 100%;
  animation: strip-shift 2s linear infinite;
  transition: width var(--duration-slow) var(--ease-out);
}
.runstrip__track[data-state="indeterminate"] > span { width: 35% !important; animation: strip-sweep 1.4s ease-in-out infinite; }
.runstrip__track[data-state="done"] > span { animation: none; background: var(--primary); }
.runstrip__track[data-state="failed"] > span { animation: none; background: var(--danger); }

@keyframes strip-shift { 0% { background-position: 100% 0; } 100% { background-position: -100% 0; } }
@keyframes strip-sweep { 0% { left: -35%; } 100% { left: 100%; } }

.runstrip__line {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-4);
  padding: 6px var(--space-5);
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
  letter-spacing: 0.03em;
  color: var(--text-muted);
}
.runstrip__left { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.runstrip__right { display: flex; align-items: center; gap: var(--space-4); min-width: 0; flex: none; }

/* What the run is doing, in words. Darker than the numbers beside it, because
   it is the one thing worth reading first. */
.runstrip__phase {
  color: var(--text); font-weight: 600; letter-spacing: 0;
  white-space: nowrap;
}
.runstrip__headline {
  color: var(--text-faint); letter-spacing: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Which run this is, and who asked for it. Beside the state rather than in the
   tab strip, so both names are read where the run itself is shown. */
.runstrip__who {
  display: inline-flex; align-items: baseline; gap: var(--space-2);
  min-width: 0; letter-spacing: 0;
}
.runstrip__runname {
  color: var(--text); font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 34ch;
}
.runstrip__runby { color: var(--text-faint); white-space: nowrap; }

/* The count reconciliation. Its own row rather than a slot on the strip,
   because it is a sentence and the strip truncates. Rare by design: it appears
   only when the table and the workflow's own count disagree. */
.runstrip__note {
  margin: var(--space-2) 0 0; padding: 0 var(--space-4) var(--space-2);
  color: var(--text-muted); font-size: var(--fs-label); line-height: 1.4;
}

/* One fact per slot: a quiet label above, the value under it. Tabular figures
   so a ticking clock or a rising count never shifts the row sideways. */
.runstat { display: flex; flex-direction: column; line-height: 1.15; min-width: 0; }
.runstat__label {
  font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-faint);
}
.runstat__value {
  font-size: var(--fs-label); font-weight: 600; color: var(--text);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* The percentage is the one number the eye goes to, so it is the only thing on
   the row allowed to carry the state colour. */
.runstrip__percent {
  font-family: var(--font-mono); font-size: var(--fs-title); font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--primary); letter-spacing: 0;
  min-width: 52px; text-align: right;
}
.runstrip__percent[data-state="done"] { color: var(--ok); }
.runstrip__percent[data-state="failed"] { color: var(--danger); }

/* Stop sits at the far end of the strip, away from every download and copy
   button on the page, because it is the one control here that changes a run
   rather than reading it. Quiet until hovered: it is not the usual next step. */
.runstrip__stop { flex: none; }
.runstrip__stop:hover:not(:disabled) { color: var(--danger); border-color: var(--danger); background: transparent; }
.runstrip__stop:disabled { opacity: 0.7; }

/* Stacked, the strip keeps the status and the phase on the first line and lets
   the numbers wrap under them rather than squeezing every slot. */
@media (max-width: 860px) {
  .runstrip__line { flex-wrap: wrap; gap: var(--space-2) var(--space-3); }
  .runstrip__right { flex-wrap: wrap; gap: var(--space-3); }
  .runstrip__headline { display: none; }
}

/* ================================================================== shell */

.shell { flex: 1; display: flex; min-height: 0; position: relative; }

/* Folded away, the rail keeps its place in the flow but takes no width. */
.shell:not(.filters-open) .filters {
  width: 0;
  overflow: hidden;
  border-right-width: 0;
  pointer-events: none;
}
/* Zero width still leaves the fields reachable by Tab and readable by a
   screen reader, so JS marks the rail `inert` while it is folded. */

/* ================================================================== filters */

.filters {
  flex: none;
  width: var(--filters-w);
  transition: width var(--duration-slow) var(--ease-out);
  display: flex;
  flex-direction: column;
  background: var(--surface-2);
  border-right: 1px solid var(--border);
  min-height: 0;
}

.filters__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-3) var(--space-4) 0;
}
.filters__title { flex: 1; font-size: var(--fs-title); font-weight: 700; }

.linkbtn {
  font: inherit;
  font-size: var(--fs-label-sm);
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--primary);
  background: none;
  border: 0;
  padding: 2px 4px;
  border-radius: var(--radius-sm);
  cursor: pointer;
  text-align: left;
}
.linkbtn:hover:not(:disabled) { background: var(--primary-soft); }
.linkbtn:disabled { opacity: 0.4; cursor: not-allowed; }

.filters__sub {
  padding: 2px var(--space-4) var(--space-3);
  color: var(--text-faint);
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
}

.filters__foot {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
}
.filters__note {
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
  color: var(--text-faint);
  text-align: center;
}

.filters__scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 0 var(--space-3) var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--group-gap);
}

.filters__more {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  width: 100%;
  margin-top: var(--space-2);
  padding: 9px var(--space-3);
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  font: inherit;
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: background var(--duration), color var(--duration), border-color var(--duration);
}
.filters__more:hover { background: var(--surface); color: var(--primary); border-color: var(--primary); }

.scrim { display: none; }

/* rail width ------------------------------------------------------------ */

/* How wide the rail should be is a preference, not a constant. A run with long
   company names in it wants the rail wide; a twelve-column table wants it out
   of the way. Both are right on the same screen ten seconds apart, so the grip
   settles it and the browser remembers the answer.

   It is a real flex item rather than something floating over the edge, so it
   can never sit on top of the rail's own scrollbar. It is 8px wide with a pixel
   of negative margin on each side, so it is comfortably wide enough to hit
   while costing the canvas the 6px the border area already used. */
.filters__grip {
  flex: none;
  width: 8px;
  margin: 0 -1px;
  position: relative;
  z-index: 1;
  cursor: col-resize;
  background: transparent;
  /* Otherwise a touch drag scrolls the page instead of moving the rail. */
  touch-action: none;
}

/* The handle, and it is always there. A control nobody can see is a control
   nobody uses: the first version of this only appeared on hover, which meant
   the only way to discover it was to already know it existed. So a short bar
   sits on the seam at all times, quiet enough to ignore and specific enough to
   read as something that moves. */
.filters__grip::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 2px;
  width: 4px;
  height: 36px;
  margin-top: -18px;
  border-radius: 2px;
  background: var(--border-strong);
  transition: background var(--duration) var(--ease-out),
              height var(--duration) var(--ease-out);
}

/* Under the pointer, the whole seam lights up so it is obvious what the drag
   is going to move. */
.filters__grip::after {
  content: '';
  position: absolute;
  top: 0; bottom: 0; left: 3px;
  width: 2px;
  background: var(--primary);
  opacity: 0;
  transition: opacity var(--duration) var(--ease-out);
}
.filters__grip:hover::after,
.filters__grip:focus-visible::after,
.shell.is-resizing .filters__grip::after { opacity: 1; }
.filters__grip:hover::before,
.filters__grip:focus-visible::before,
.shell.is-resizing .filters__grip::before {
  background: var(--primary);
  height: 56px;
  margin-top: -28px;
}
/* The lit seam is the focus ring. A second outline around an 8px strip is
   noise, and it would not follow the shape of what is focused anyway. */
.filters__grip:focus-visible { outline: none; }

/* Nothing to drag when there is no rail. */
.shell:not(.filters-open) .filters__grip { display: none; }

/* A drag has to follow the pointer exactly, so the fold animation is off while
   one is running, and the cursor stays a resize cursor even when the pointer
   has left the strip - which it does, within the first few pixels. */
.shell.is-resizing { cursor: col-resize; user-select: none; }
.shell.is-resizing .filters { transition: none; }

/* filter cards ---------------------------------------------------------- */

.fcards { display: flex; flex-direction: column; gap: var(--space-2); }

.fcard {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
}

.fcard > summary {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3);
  background: var(--surface-2);
  cursor: pointer;
  list-style: none;
  transition: background var(--duration);
}
.fcard > summary::-webkit-details-marker { display: none; }
.fcard > summary:hover { background: var(--surface-3); }
.fcard[open] > summary { border-bottom: 1px solid var(--border); }

.fcard__title {
  flex: 1; min-width: 0;
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text);
}
.fcard__note {
  font-size: var(--fs-label-sm);
  letter-spacing: 0.03em;
  color: var(--text-faint);
  max-width: 45%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.fcard[open] > summary .fcard__note { display: none; }
.fcard__chev { color: var(--text-faint); transition: transform var(--duration) var(--ease-out); }
.fcard[open] > summary .fcard__chev { transform: rotate(180deg); }

.fcard__body { padding: var(--space-3); display: flex; flex-direction: column; gap: var(--space-2); }

.ticks { display: flex; flex-direction: column; gap: var(--space-2); max-height: 220px; overflow-y: auto; }
.ticks.is-empty::after {
  content: "Nothing matches. Press Enter to add it.";
  font-size: var(--fs-label-sm);
  color: var(--text-faint);
}

.tick {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body-sm);
  color: var(--text-muted);
  cursor: pointer;
}
.tick > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tick:hover > span { color: var(--text); }
/* An industry the corpus holds too few companies for in the chosen country.
   The row says "too few" in words as well; the colour only repeats it. */
.tick--low > span, .tick--low:hover > span { color: var(--warn); }
.fgroup > .ticks__note { color: var(--warn); }

.ticks__add {
  height: 30px;
  font-size: var(--fs-label);
  border-style: dashed;
}

.fgroup { display: flex; flex-direction: column; gap: var(--space-2); }

/* One control, wrapped so it can be hidden on its own. Automatic mode works
   some settings out and leaves the rest to you, and those two live on the same
   card: the Lead Quality card keeps its minimum score and hides the two limits
   the planner decides. A wrapper with nothing of its own is what lets a card be
   half shown without the card knowing. */
.fctl { display: flex; flex-direction: column; gap: var(--space-2); }
.fctl[hidden] { display: none; }

/* What automatic picked. Read-only by design: these are worked out from the
   country and the industries, and the run is about to use them. */
.planbox { display: flex; flex-direction: column; gap: var(--space-2); }
.planrow { display: flex; gap: var(--space-2); align-items: baseline; justify-content: space-between; }
.planrow__label { color: var(--text-faint); font-size: var(--fs-label); }
.planrow__value {
  color: var(--text); font-size: var(--fs-label); font-weight: 600;
  font-variant-numeric: tabular-nums; text-align: right;
}
.planlist {
  list-style: none; margin: 0; padding: 0; text-align: right;
  display: flex; flex-direction: column; gap: 2px;
  color: var(--text); font-size: var(--fs-label);
}
.planbox__note { margin: 0; color: var(--text-faint); font-size: var(--fs-label); line-height: 1.45; }
.fgroup__title {
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--text-muted);
}

.ffield { display: flex; flex-direction: column; gap: 4px; }
/* The one required box in the rail. Marked with more than colour, so the
   asterisk still reads for anyone who cannot pick the red out. */
.ffield__req {
  margin-left: 3px;
  font-style: normal;
  font-weight: 700;
  color: var(--danger);
}
/* Written as a child rule on purpose. The ".ffield > span" block below styles
   every direct span as a bold label, and it outranks a plain ".ffield__hint",
   so the hints under these fields were all rendering as second labels. This
   has to be at least as specific to win. A tick list has no label element of
   its own, so its hint hangs off the group and takes the same rule. */
.ffield > .ffield__hint,
.fgroup > .ffield__hint,
.fpair > .ffield__hint {
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
  font-weight: 400;
  letter-spacing: 0;
  color: var(--text-faint);
}
.ffield > span,
.fpair > span {
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--text-muted);
}
.fpairs { display: flex; gap: var(--space-2); }
.fpair { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 4px; }

/* Two number boxes for the size range no band covers. The word between them
   is what makes it read as one setting rather than two. */
.range { display: flex; align-items: center; gap: var(--space-2); }
.range__box { flex: 1 1 0; min-width: 0; height: 30px; font-size: var(--fs-label); }
.range__to {
  flex: 0 0 auto;
  font-size: var(--fs-label-sm);
  color: var(--text-faint);
}

/* The Similar Companies card. Seeds are chips because a company address is
   long and a stack of full-width rows would push the rest of the card off
   the screen. */
.seeds { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.seeds.is-empty::after {
  content: "No companies yet. Paste one below.";
  font-size: var(--fs-label-sm);
  color: var(--text-faint);
}
.seed {
  display: inline-flex; align-items: center; gap: 4px;
  max-width: 100%;
  padding: 3px 4px 3px 8px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill, 999px);
  background: var(--surface-2, var(--surface));
  font-size: var(--fs-label-sm);
  color: var(--text-muted);
}
.seed__name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.seed__x {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  width: 16px; height: 16px; padding: 0;
  border: 0; border-radius: 50%;
  background: none; color: inherit; opacity: 0.6;
  cursor: pointer;
}
.seed__x:hover { opacity: 1; background: var(--border); }

.seeds__read { justify-content: center; }
.seeds__apply { width: 100%; justify-content: center; }
.seeds__out { display: flex; flex-direction: column; gap: var(--space-2); }

/* One block per company that was read. Every line carries the sentence it came
   from as a tooltip, so a wrong guess can be traced rather than argued with. */
.seedcard {
  display: flex; flex-direction: column; gap: 2px;
  padding: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm, 6px);
  background: var(--surface-2, var(--surface));
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
  color: var(--text-muted);
}
.seedcard--warn { border-color: var(--warn, var(--border)); }
.seedcard__name { font-weight: 600; color: var(--text); }
.seedcard__line { overflow: hidden; text-overflow: ellipsis; }
.seedcard__bad { color: var(--danger); }

.presetbar { display: flex; gap: var(--space-1); }
.presetbar select { flex: 1; min-width: 0; }

/* settings groups, in the Find Leads panel ------------------------------ */

.groups {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
  gap: var(--space-3);
  align-items: start;
}

.group {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
}

.group > summary {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-3);
  background: var(--surface-2);
  cursor: pointer;
  list-style: none;
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text);
  transition: background var(--duration);
}
.group > summary::-webkit-details-marker { display: none; }
.group > summary:hover { background: var(--surface-3); }
.group[open] > summary { border-bottom: 1px solid var(--border); }

.group__chev { color: var(--text-faint); transition: transform var(--duration) var(--ease-out); }
.group[open] > summary .group__chev { transform: rotate(90deg); }

.group__title { flex: 1; min-width: 0; }

.group__chip {
  font-weight: 400;
  font-size: var(--fs-label-sm);
  letter-spacing: 0.02em;
  color: var(--text-faint);
  max-width: 55%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.group[open] > summary .group__chip { display: none; }

.group__body { padding: var(--space-3); display: flex; flex-wrap: wrap; gap: var(--space-3); }

/* ================================================================== fields */

.field { display: flex; flex-direction: column; gap: 5px; min-width: 0; flex: 1 1 100%; }
.field--half { flex: 1 1 calc(50% - var(--space-2)); }
.field--quarter { flex: 1 1 calc(25% - var(--space-3)); min-width: 110px; }
.field--full { flex: 1 1 100%; }

.field > label,
.fieldset__legend {
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--text-muted);
}

input[type="text"], input[type="number"], input[type="password"],
input[type="search"], select, textarea {
  width: 100%;
  min-width: 0;
  font: inherit;
  font-size: var(--fs-body-sm);
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 0 10px;
  height: var(--field-h);
  transition: border-color var(--duration), background var(--duration);
}
textarea { height: auto; padding: 8px 10px; resize: vertical; line-height: 1.5; }
textarea.list { font-family: var(--font-mono); font-size: var(--fs-label); }
select { appearance: none; padding-right: 26px;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 14px) 51%, calc(100% - 9px) 51%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
input:hover, select:hover, textarea:hover { border-color: var(--border-strong); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 2px var(--primary-soft);
  background: var(--surface);
}
input[aria-invalid="true"], textarea[aria-invalid="true"] { border-color: var(--danger); }
::placeholder { color: var(--text-faint); }

.field__help { font-size: var(--fs-label-sm); line-height: var(--lh-label-sm); color: var(--text-faint); }
.field__error {
  display: flex; align-items: center; gap: 5px;
  font-size: var(--fs-label-sm); line-height: var(--lh-label-sm); color: var(--danger);
}

.fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
.fieldset__legend { padding: 0 0 6px; }

.checks { display: flex; flex-direction: column; gap: var(--space-2); }
.check, .pickbox {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-body-sm);
  color: var(--text-muted);
  cursor: pointer;
}

input[type="checkbox"] {
  appearance: none;
  width: 16px; height: 16px;
  flex: none;
  margin: 0;
  border: 1px solid var(--text-faint);
  border-radius: var(--radius-xs);
  background: var(--surface);
  display: grid; place-content: center;
  cursor: pointer;
  transition: background var(--duration), border-color var(--duration);
}
input[type="checkbox"]::before {
  content: "";
  width: 10px; height: 10px;
  transform: scale(0);
  transition: transform 120ms var(--ease-out);
  box-shadow: inset 1em 1em var(--on-primary);
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}
input[type="checkbox"]:checked {
  background: var(--primary);
  border-color: var(--primary);
}
input[type="checkbox"]:checked::before { transform: scale(1); }
input[type="checkbox"]:indeterminate { background: var(--primary); border-color: var(--primary); }
input[type="checkbox"]:indeterminate::before {
  transform: scale(1);
  clip-path: polygon(12% 42%, 88% 42%, 88% 58%, 12% 58%);
}

/* switch --------------------------------------------------------------- */

.switch { display: inline-flex; align-items: center; gap: var(--space-2); cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch__track {
  width: 32px; height: 18px; flex: none;
  border-radius: 9px;
  background: var(--surface-4);
  border: 1px solid var(--border);
  position: relative;
  transition: background var(--duration), border-color var(--duration);
}
.switch__thumb {
  position: absolute; top: 2px; left: 2px;
  width: 12px; height: 12px;
  border-radius: 50%;
  background: var(--surface);
  box-shadow: var(--shadow-1);
  transition: transform var(--duration) var(--ease-out);
}
.switch input:checked + .switch__track { background: var(--primary); border-color: var(--primary); }
.switch input:checked + .switch__track .switch__thumb { transform: translateX(14px); }
.switch input:focus-visible + .switch__track { outline: 2px solid var(--ring); outline-offset: 2px; }
.switch__label {
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-muted);
}

/* ================================================================== buttons */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  font: inherit;
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 8px 14px;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  background: var(--surface);
  color: var(--text);
  cursor: pointer;
  white-space: nowrap;
  /* a link that is styled as a button should not also be underlined */
  text-decoration: none;
  transition: background var(--duration), border-color var(--duration), color var(--duration);
}
a.btn:hover { text-decoration: none; }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }

.btn--primary { background: var(--primary-bright); color: var(--on-primary); box-shadow: var(--shadow-1); }
.btn--primary:hover:not(:disabled) { background: var(--primary); }

.btn--accent { background: var(--surface); color: var(--primary); border-color: var(--border); }
.btn--accent:hover:not([aria-disabled="true"]) { background: var(--primary-soft); }

.btn--ghost { background: transparent; color: var(--text-muted); border-color: var(--border); }
.btn--ghost:hover:not(:disabled):not([aria-disabled="true"]) { background: var(--surface-3); color: var(--text); }

.btn--danger { background: var(--danger); color: #fff; }
.btn--danger:hover:not(:disabled) { filter: brightness(0.92); }

.btn--small { padding: 5px 10px; font-size: var(--fs-label-sm); }
.btn--block { width: 100%; padding: 11px 14px; font-size: var(--fs-body); }

.btn__kbd { margin-left: 2px; background: rgb(255 255 255 / 0.16); border-color: transparent; color: inherit; opacity: 0.85; }
.btn--primary .btn__kbd { border-bottom-width: 1px; }

.iconbtn {
  display: inline-grid; place-items: center;
  width: 30px; height: 30px;
  flex: none;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--duration), color var(--duration);
}
.iconbtn:hover:not(:disabled) { background: var(--surface-3); color: var(--text); }
.iconbtn:disabled { opacity: 0.4; cursor: not-allowed; }
.iconbtn.is-done { color: var(--ok); }

/* ================================================================== canvas */

.canvas {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  background: var(--surface-2);
  overflow: hidden;
}

/* hero query ------------------------------------------------------------ */

.hero {
  flex: none;
  padding: var(--space-5);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}
.hero__inner { max-width: 896px; margin: 0 auto; }

/* The blocking run error, above the search box. It is left-aligned even on the
   empty screen, where the heading above it is centred: a sentence that names a
   country and says what to do instead is read, not glanced at. */
.hero__alert {
  margin-bottom: var(--space-3);
  text-align: left;
}

/* The query box works like a chat composer: the border belongs to the frame
   rather than to the field, so the box can grow a line at a time with the
   button parked in the corner instead of floating over the text. */
.searchbar {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  transition: border-color var(--duration), background var(--duration);
}
.searchbar:hover { border-color: var(--border-strong); }
.searchbar:focus-within {
  border-color: var(--primary);
  box-shadow: 0 0 0 2px var(--primary-soft);
  background: var(--surface);
}
.searchbar__icon {
  position: absolute; left: 12px; top: 0;
  height: 44px;
  display: flex; align-items: center;
  color: var(--text-faint);
  pointer-events: none;
}
/* One line is 22px, and the padding above and below is 11px, so the first
   line sits where it always did. The height is set in script as the sentence
   grows; max-height is seven lines, and the scrollbar takes over from there. */
.searchbar textarea {
  height: 44px;
  min-height: 44px;
  max-height: 176px;
  padding: 11px 12px 11px 40px;
  font-size: var(--fs-body);
  line-height: 22px;
  background: none;
  border: 0;
  border-radius: var(--radius-sm);
  resize: none;
  overflow-y: hidden;
}
.searchbar textarea:hover, .searchbar textarea:focus {
  background: none;
  border: 0;
  box-shadow: none;
}
.searchbar__foot { display: flex; justify-content: flex-end; gap: 8px; padding: 10px 0 0; }
.searchbar__go { height: 34px; }
@media (prefers-reduced-motion: no-preference) {
  .panel.is-on { animation: panel-fade-in 140ms ease-out; }
  @keyframes panel-fade-in { from { opacity: 0.65; } to { opacity: 1; } }
}

/* The rotating example. It sits exactly where the placeholder would, so the
   text can fade between examples instead of jumping. */
.searchbar__ghost {
  position: absolute;
  top: 0; left: 40px; right: 12px;
  height: 44px;
  display: flex; align-items: center;
  pointer-events: none;
  color: var(--text-faint);
  font-size: var(--fs-body);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-out);
}
.searchbar__ghost.is-leaving { opacity: 0; transform: translateY(-7px); }

/* suggestions ----------------------------------------------------------- */

.suggest {
  margin-top: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  box-shadow: var(--shadow-2);
  overflow: hidden;
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity 160ms var(--ease-out), transform 160ms var(--ease-out);
}
.suggest.is-open { opacity: 1; transform: none; }

.suggest__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 7px var(--space-3);
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
  font-size: var(--fs-label-sm);
  letter-spacing: 0.03em;
  color: var(--text-faint);
}

.suggest__item {
  width: 100%;
  display: flex; align-items: center; gap: 10px;
  padding: 10px var(--space-3);
  border: 0;
  background: none;
  color: var(--text-muted);
  font: inherit;
  font-size: var(--fs-body-sm);
  text-align: left;
  cursor: pointer;
  transition: background var(--duration), color var(--duration);
}
.suggest__item:hover { background: var(--surface-3); color: var(--text); }
.suggest__item .icon { flex: none; color: var(--text-faint); }
.suggest__item:hover .icon { color: var(--primary); }

/* The rows arrive one after another rather than all at once. */
.suggest.is-open .suggest__item { animation: suggest-in 240ms var(--ease-out) backwards; }
.suggest.is-open .suggest__item:nth-child(2) { animation-delay: 30ms; }
.suggest.is-open .suggest__item:nth-child(3) { animation-delay: 60ms; }
.suggest.is-open .suggest__item:nth-child(4) { animation-delay: 90ms; }
.suggest.is-open .suggest__item:nth-child(5) { animation-delay: 120ms; }
.suggest.is-open .suggest__item:nth-child(6) { animation-delay: 150ms; }

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

/* filter chips ---------------------------------------------------------- */

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

.chip-filter {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 8px;
  border-radius: var(--radius-pill);
  background: var(--surface-4);
  border: 1px solid var(--border);
  color: var(--text-muted);
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
  font-weight: 500;
  letter-spacing: 0.03em;
  transition: background var(--duration);
}
.chip-filter:hover { background: var(--border); }

/* A chip read out of the query box carries a mark, so it is clear the app put
   it there rather than the person. */
.chip-filter__auto { display: none; width: 12px; height: 12px; margin-right: 2px; color: var(--primary); }
.chip-filter--auto { background: var(--primary-soft); border-color: var(--primary-soft); color: var(--primary); }
.chip-filter--auto:hover { background: var(--primary-soft); filter: brightness(0.97); }
.chip-filter--auto .chip-filter__auto { display: block; }
.chip-filter--auto .chip-filter__x { color: var(--primary); opacity: 0.7; }

.chip-filter.is-entering { animation: chip-in 240ms var(--ease-out); }
.chip-filter.is-leaving { animation: chip-out 180ms ease-in forwards; pointer-events: none; }
.chip-filter.is-bumped { animation: chip-bump 260ms var(--ease-out); }

@keyframes chip-in { from { opacity: 0; transform: scale(0.88) translateY(-5px); } }
@keyframes chip-out { to { opacity: 0; transform: scale(0.88); } }
@keyframes chip-bump { 45% { transform: scale(1.07); } }
.chip-filter__x {
  display: inline-grid; place-items: center;
  padding: 0; margin: 0 -2px 0 0;
  border: 0; background: none;
  color: var(--text-faint);
  cursor: pointer;
}
.chip-filter__x .icon { width: 13px; height: 13px; stroke-width: 2.2; }
.chip-filter:hover .chip-filter__x { color: var(--text); }

.chip-filter--add {
  background: var(--surface-2);
  color: var(--primary);
  border: 1px dashed var(--primary);
  cursor: pointer;
  font-family: inherit;
}
.chip-filter--add:hover { background: var(--primary-soft); }

/* Shown while the query box is still reading the sentence. It is not a filter,
   so it takes no pointer and no hover. */
.chip-filter--busy {
  background: var(--surface-2);
  border-style: dashed;
  color: var(--text-faint);
  cursor: default;
  pointer-events: none;
}
.chip-filter--busy:hover { background: var(--surface-2); }

/* A chip opens its own card in the rail, so it has to look pressable. */
.chip-filter[data-chip] { cursor: pointer; }
.chip-filter[data-chip]:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

/* ================================================================== menu */

/* A short list of actions that drops out of a button. Not picker.js, which sets
   a value: this one does something. Both wear the menu font, so anything that
   opens over the page reads as a layer above it rather than part of it. */

.menu { position: relative; display: inline-flex; }
/* The trigger wears the menu font as well, so a closed menu already reads as a
   menu rather than as another button on the page. */
.menu > button { font-family: var(--font-body); }
.menu__chev { transition: transform var(--duration) var(--ease-out); margin-left: -2px; }
.menu [aria-expanded="true"] .menu__chev { transform: rotate(180deg); }

/* An icon-only trigger has no room for a chevron, and the icon already says
   that something opens. */
.menu .iconbtn .menu__chev { display: none; }
.menu .iconbtn { width: 28px; height: 28px; }

.menu__pop {
  position: fixed;
  z-index: var(--z-toast);
  display: flex; flex-direction: column; gap: 2px;
  padding: 4px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--shadow-3);
  font-family: var(--font-body);

  /* Closed. The class is what opens it, so the animation has a state to run
     from in both directions. */
  opacity: 0;
  transform: translateY(-4px) scale(0.98);
  transform-origin: top center;
  transition:
    opacity 140ms var(--ease-out),
    transform 140ms var(--ease-out);
}
.menu__pop[data-dir="up"] { transform-origin: bottom center; transform: translateY(4px) scale(0.98); }
.menu__pop.is-open { opacity: 1; transform: translateY(0) scale(1); }

.menu__item {
  display: flex; align-items: center; gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text);
  font: inherit;
  font-size: var(--fs-body-sm);
  text-align: left;
  cursor: pointer;
  transition: background var(--duration);
}
.menu__item:hover, .menu__item:focus-visible { background: var(--surface-3); outline: none; }
.menu__item:focus-visible { box-shadow: inset 0 0 0 2px var(--ring); }
.menu__item .icon { color: var(--text-faint); }
.menu__item:hover .icon, .menu__item:focus-visible .icon { color: var(--primary); }
.menu__text { display: flex; flex-direction: column; min-width: 0; }
.menu__label { font-weight: 600; line-height: 1.25; }
.menu__hint { font-size: var(--fs-label-sm); color: var(--text-faint); line-height: 1.3; }

/* Each row arrives a beat after the one above it, so the menu reads as one
   thing unfolding rather than a block appearing. */
@media (prefers-reduced-motion: no-preference) {
  .menu__pop.is-open .menu__item { animation: menu-item-in 160ms var(--ease-out) both; }
  .menu__pop.is-open .menu__item:nth-child(2) { animation-delay: 35ms; }
  .menu__pop.is-open .menu__item:nth-child(3) { animation-delay: 70ms; }
  .menu__pop.is-open .menu__item:nth-child(4) { animation-delay: 105ms; }
  @keyframes menu-item-in { from { opacity: 0; transform: translateY(-3px); } to { opacity: 1; transform: none; } }
}

/* Nothing moves for anyone who asked for less motion. It still opens and
   closes, it just arrives already in place. */
@media (prefers-reduced-motion: reduce) {
  .menu__pop { transition: none; transform: none; }
  .menu__pop[data-dir="up"] { transform: none; }
  .menu__chev { transition: none; }
}

/* ================================================================== picker */

/* Every dropdown in the app. The long ones get a search box; the short ones
   look exactly the same without one. */
.picker { position: relative; }

.picker__button {
  width: 100%;
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  height: var(--field-h);
  padding: 0 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--text);
  font: inherit;
  font-size: var(--fs-body-sm);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--duration), background var(--duration);
}
/* The trigger wears the menu font too. It is the part that is on screen all the
   time, so putting the font only on the panel changed nothing anyone could see. */
.picker__button { font-family: var(--font-body); }
.picker__button:hover { border-color: var(--border-strong); }

/* A dropdown that sits in a toolbar next to a search box, rather than in the
   filters rail. It has to match the controls beside it: same 30px height, same
   text size, same white ground, and only as wide as its longest choice. */
.picker--sm { width: auto; flex: none; }
.picker--sm .picker__button {
  width: auto;
  height: 30px;
  padding: 0 8px 0 10px;
  gap: 6px;
  font-size: var(--fs-label);
  background: var(--surface);
}
.picker__button[aria-expanded="true"] {
  border-color: var(--primary);
  box-shadow: 0 0 0 2px var(--primary-soft);
  background: var(--surface);
}
.picker__value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker__value.is-placeholder { color: var(--text-faint); }
.picker__chev { color: var(--text-faint); flex: none; }
.picker__button[aria-expanded="true"] .picker__chev { transform: rotate(180deg); }

.picker__pop {
  position: fixed;
  z-index: var(--z-toast);
  display: flex; flex-direction: column;
  max-height: 280px;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--shadow-3);
  overflow: hidden;
  /* The menu font, so every panel that opens over the page shares one voice. */
  font-family: var(--font-body);

  /* Closed. The class is what opens it, so the animation has a state to run
     from in both directions. */
  opacity: 0;
  transform: translateY(-4px) scale(0.98);
  transform-origin: top center;
  transition:
    opacity 140ms var(--ease-out),
    transform 140ms var(--ease-out);
}
.picker__pop[data-dir="up"] { transform-origin: bottom center; transform: translateY(4px) scale(0.98); }
.picker__pop.is-open { opacity: 1; transform: translateY(0) scale(1); }

@media (prefers-reduced-motion: reduce) {
  .picker__pop, .picker__pop[data-dir="up"] { transition: none; transform: none; }
}

/* The search row. The field is inset in a padded strip rather than welded to
   the panel edges, so the panel reads as one card with a header instead of two
   boxes stacked on top of each other. */
.picker__searchrow {
  position: relative;
  display: flex; align-items: center;
  flex: none;
  padding: 6px;
  border-bottom: 1px solid var(--border);
  background: var(--surface);
}
.picker__searchicon {
  position: absolute;
  left: 16px;
  color: var(--text-faint);
  pointer-events: none;
  transition: color var(--duration);
}
.picker__searchrow:focus-within .picker__searchicon { color: var(--primary); }

input.picker__search {
  height: 32px;
  padding: 0 28px 0 32px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  font-size: var(--fs-body-sm);
}
/* The panel already opens on a click, so the global 2px offset ring drew a
   floating box around the field the moment it took focus. The field says it has
   focus with its own border and halo instead, which stays inside the panel. */
input.picker__search:focus, input.picker__search:focus-visible {
  outline: none;
  border-color: var(--primary);
  box-shadow: 0 0 0 2px var(--primary-soft);
  background: var(--surface);
}

.picker__wipe {
  position: absolute;
  right: 11px;
  display: flex; align-items: center; justify-content: center;
  width: 18px; height: 18px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-xs);
  background: none;
  color: var(--text-faint);
  cursor: pointer;
}
.picker__wipe:hover { background: var(--surface-4); color: var(--text); }
.picker__wipe .icon { width: 14px; height: 14px; }

.picker__list {
  overflow-y: auto;
  /* The list scroll stops at its own ends rather than handing the rest to the
     page, which would scroll the rail and close the panel mid-search. */
  overscroll-behavior: contain;
  padding: 4px;
  scroll-padding: 4px;
}
.picker__list.is-empty::after {
  content: "Nothing matches.";
  display: block;
  padding: var(--space-3) var(--space-2);
  font-size: var(--fs-label-sm);
  color: var(--text-faint);
  text-align: center;
}
/* A hairline thumb inside the panel, and no stepper arrows.
   html sets scrollbar-color, every element inherits it, and an inherited
   scrollbar-color puts Chrome on the standard scrollbar - which is 15px wide
   with an arrow at each end, and which ignores every ::-webkit rule in this
   file. Handing both properties back to `auto` here is what returns this one
   list to the styled 4px bar. */
.picker__list { scrollbar-width: auto; scrollbar-color: auto; }
.picker__list::-webkit-scrollbar { width: 4px; }
.picker__list::-webkit-scrollbar-button { display: none; height: 0; }
.picker__list::-webkit-scrollbar-track { background: transparent; }
.picker__list::-webkit-scrollbar-thumb { background: var(--surface-4); border-radius: 2px; }
.picker__list::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }

.picker__opt {
  width: 100%;
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-2);
  padding: 7px 8px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-muted);
  font: inherit;
  font-size: var(--fs-body-sm);
  text-align: left;
  cursor: pointer;
}
.picker__opt:hover { background: var(--surface-3); color: var(--text); }
.picker__opt > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.picker__tick { flex: none; opacity: 0; color: var(--primary); }
.picker__opt.is-on { color: var(--primary); font-weight: 600; }
.picker__opt.is-on .picker__tick { opacity: 1; }

/* ================================================================== scale */

.scale { display: flex; flex-direction: column; gap: 4px; }
.scale__head { display: flex; align-items: baseline; gap: 6px; }
.scale__value {
  font-size: var(--fs-headline);
  line-height: 1;
  font-weight: 700;
  color: var(--primary);
  font-variant-numeric: tabular-nums;
}
.scale__word { font-size: var(--fs-label-sm); letter-spacing: 0.03em; color: var(--text-faint); }

.scale__range {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 18px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.scale__range:hover, .scale__range:focus { border: 0; box-shadow: none; background: none; }
.scale__range::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(to right,
    var(--primary) var(--fill, 70%), var(--surface-4) var(--fill, 70%));
}
.scale__range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 16px; height: 16px;
  margin-top: -6px;
  border-radius: 50%;
  background: var(--surface);
  border: 2px solid var(--primary);
  box-shadow: var(--shadow-1);
}
.scale__range::-moz-range-track { height: 4px; border-radius: 2px; background: var(--surface-4); }
.scale__range::-moz-range-progress { height: 4px; border-radius: 2px; background: var(--primary); }
.scale__range::-moz-range-thumb {
  width: 14px; height: 14px;
  border: 2px solid var(--primary);
  border-radius: 50%;
  background: var(--surface);
}

/* ================================================================== panels */

.panel { flex: 1; display: none; flex-direction: column; min-height: 0; position: relative; }
.panel.is-on { display: flex; }

/* The first paint, before any script has decided anything.
   The markup opens on the Workspace, which is right for a first visit and wrong
   for somebody who was reading the Lists tab and pressed refresh: they would see
   the Workspace flash past. The mark on the document comes from the tiny script
   in the head, which reads the same remembered tab, so the correct panel is the
   only one ever drawn. main.js removes the mark as soon as it takes over. */
[data-boot-panel="lists"] #panel-workspace.is-on { display: none; }
[data-boot-panel="lists"] #panel-lists { display: flex; }

.results { flex: 1; display: flex; flex-direction: column; min-height: 0; }

/* Nothing has run yet, so the query box owns the middle of the screen and the
   empty results area is not drawn at all. */
.panel--work.is-empty .results { display: none; }
.panel--work.is-empty .hero {
  margin: auto 0;
  border-bottom: 0;
  background: transparent;
  padding-bottom: 12vh;
}
.panel--work.is-empty .hero__intro { display: block; }
.hero__intro { display: none; text-align: center; margin-bottom: var(--space-4); }
.hero__intro h3 { font-size: var(--fs-display); line-height: 32px; font-weight: 700; letter-spacing: -0.01em; }
.hero__intro p { margin-top: 4px; font-size: var(--fs-body); color: var(--text-faint); }

/* The word "Filters" in that sentence is the control that opens the rail, so it
   has to read as one. Underlined as well as coloured, because colour alone is
   not a signal everybody gets. It inherits the sentence's size rather than
   using .linkbtn, which is a label-sized button and looks wrong mid-sentence. */
.hero__link {
  font: inherit;
  color: var(--primary);
  background: none;
  border: 0;
  /* The negative margin cancels the padding exactly. Anything less left a gap
     between the word and the full stop after it. */
  padding: 2px 4px;
  margin: 0 -4px;
  border-radius: var(--radius-xs);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.hero__link:hover { background: var(--primary-soft); }
.panel__scroll { flex: 1; min-height: 0; overflow-y: auto; padding: var(--space-5); }
.panel__inner { max-width: 1152px; margin: 0 auto; display: flex; flex-direction: column; gap: var(--space-4); }
.panel__spacer { height: 72px; }

/* result bar ------------------------------------------------------------ */

.resultbar {
  flex: none;
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
}
.resultbar__left { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.resultbar__right { display: flex; align-items: center; gap: var(--space-2); }

.resultbar__count { font-size: var(--fs-title); font-weight: 600; color: var(--text); white-space: nowrap; }

.selected-pill {
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: var(--primary-soft);
  color: var(--primary);
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
  font-weight: 600;
  letter-spacing: 0.03em;
  white-space: nowrap;
}

.searchbox { position: relative; display: flex; align-items: center; }
.searchbox .icon { position: absolute; left: 9px; color: var(--text-faint); pointer-events: none; }
.searchbox input {
  width: 190px;
  height: 30px;
  padding-left: 30px;
  padding-right: 26px;
  font-size: var(--fs-label);
  background: var(--surface);
}
.searchbox input::-webkit-search-cancel-button { display: none; }
.searchbox__kbd { position: absolute; right: 6px; pointer-events: none; }
.searchbox input:focus + .searchbox__kbd { opacity: 0; }

.segmented, .viewtoggle {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
}
.segmented__btn, .viewtoggle__btn {
  font: inherit;
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: 0.02em;
  border: 0;
  border-right: 1px solid var(--border);
  background: var(--surface-3);
  color: var(--text-muted);
  padding: 0 10px;
  height: 28px;
  cursor: pointer;
  transition: background var(--duration), color var(--duration);
}
.viewtoggle__btn { display: grid; place-items: center; padding: 0 10px; }
.segmented__btn:last-child, .viewtoggle__btn:last-child { border-right: 0; }
.segmented__btn:hover, .viewtoggle__btn:hover { background: var(--surface-4); }
.segmented__btn.is-on, .viewtoggle__btn.is-on { background: var(--surface-2); color: var(--primary); }
/* The rail's first choice: two full-width rows, since neither label fits half
   a 280px rail. Same tokens as the inline group. */
.segmented--stack { display: flex; flex-direction: column; width: 100%; }
.segmented--stack .segmented__btn {
  height: auto; min-height: 36px; padding: 8px 10px; text-align: left;
  border-right: 0; border-bottom: 1px solid var(--border);
}
.segmented--stack .segmented__btn:last-child { border-bottom: 0; }
.segmented--stack .segmented__btn.is-on { box-shadow: inset 3px 0 0 var(--primary); }
/* The file box is hidden behind its label, so the label shows its focus. */
input[type="file"].visually-hidden:focus-visible + label { outline: 2px solid var(--ring); outline-offset: 2px; }

.resultbar .filters__open { display: none; }

/* ================================================================== table */

.tablewrap {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  overflow: hidden;
}

table.leads { width: 100%; border-collapse: collapse; table-layout: fixed; }

.leads thead th {
  text-align: left;
  padding: var(--space-2) var(--space-4);
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text-muted);
  position: sticky;
  top: 0;
  z-index: var(--z-sticky);
}
.leads .col-pick    { width: 44px; }
.leads .col-name    { width: 26%; }
.leads .col-company { width: 22%; }
/* Name, company, email, phone; the rest waits for "More columns". */
.leads:not(.is-more) .col-more { display: none; }
.leads .col-status  { width: auto; }
.leads .col-score   { width: 78px; text-align: right; }

.sortbtn {
  font: inherit;
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.sortbtn:hover { color: var(--primary); }
.sortbtn::after { content: ""; width: 0; height: 0; opacity: 0; }
.sortbtn[data-dir]::after {
  opacity: 1;
  border-left: 3.5px solid transparent;
  border-right: 3.5px solid transparent;
}
.sortbtn[data-dir="desc"]::after { border-top: 4px solid currentColor; }
.sortbtn[data-dir="asc"]::after { border-bottom: 4px solid currentColor; }
.col-score .sortbtn { justify-content: flex-end; width: 100%; }

.leads tbody tr {
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  transition: background var(--duration);
}
.leads tbody tr:last-child { border-bottom: 0; }
.leads tbody tr:hover { background: var(--surface-3); }
.leads tbody tr:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
.leads tbody tr.is-new { animation: row-in var(--duration-slow) var(--ease-out); }

@keyframes row-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }

.leads td {
  padding: var(--row-y) var(--space-4);
  vertical-align: middle;
  font-size: var(--fs-body-sm);
  color: var(--text-muted);
  overflow: hidden;
}
.leads td.num { text-align: right; }

/* name cell ------------------------------------------------------------- */

.who { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }

.avatar {
  width: 32px; height: 32px;
  flex: none;
  border-radius: 50%;
  display: grid; place-items: center;
  background: var(--primary-soft);
  color: var(--primary);
  border: 1px solid var(--border);
  font-size: var(--fs-label);
  font-weight: 600;
  letter-spacing: 0.02em;
}

.who__text { display: flex; flex-direction: column; min-width: 0; }
.who__name {
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.who__where {
  display: flex; align-items: center; gap: 3px;
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
  color: var(--text-faint);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.who__where .icon { width: 12px; height: 12px; }
.who__title {
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
  color: var(--text-faint);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* company cell ---------------------------------------------------------- */

.org { display: flex; align-items: center; gap: var(--space-2); min-width: 0; }
.org__logo {
  width: 20px; height: 20px;
  flex: none;
  display: grid; place-items: center;
  border-radius: var(--radius-xs);
  background: var(--surface-4);
  color: var(--text-muted);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
}
.org__text { display: flex; flex-direction: column; min-width: 0; }
.org__name { color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.reachstack { display: flex; flex-direction: column; min-width: 0; }
.reachstack > * { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cell-strong { color: var(--text); font-weight: 500; }
.cell-sub { font-size: var(--fs-label-sm); line-height: var(--lh-label-sm); color: var(--text-faint); }
.cell-empty { color: var(--text-faint); }
.cell-link { color: var(--primary); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.cell-link:hover { text-decoration: underline; }

/* status cell ----------------------------------------------------------- */

.status { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }

.badge {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 2px 6px;
  border-radius: var(--radius-xs);
  border: 1px solid var(--border);
  background: var(--surface-4);
  color: var(--text-muted);
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
  font-weight: 500;
  letter-spacing: 0.03em;
  white-space: nowrap;
}
.badge .icon { width: 12px; height: 12px; }
.badge--ok   { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-border); }
.badge--warn { background: var(--warn-bg); color: var(--warn); border-color: var(--warn-border); }
.badge--bad  { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-border); }

.sources { display: inline-flex; align-items: center; gap: 2px; color: var(--text-faint);
  font-size: var(--fs-label-sm); line-height: var(--lh-label-sm); }
.sources .icon { width: 14px; height: 14px; }

.reach { display: inline-flex; align-items: center; gap: 2px; margin-left: auto; }
.reach .iconbtn { width: 24px; height: 24px; }
.reach .iconbtn .icon { width: 14px; height: 14px; }

/* score cell ------------------------------------------------------------ */

.score-circle {
  display: inline-grid; place-items: center;
  width: 32px; height: 32px;
  border-radius: 50%;
  border: 2px solid var(--primary);
  background: var(--primary-soft);
  color: var(--primary);
  font-size: var(--fs-label-sm);
  font-weight: 700;
  letter-spacing: 0.02em;
}
.score-circle--mid { border-color: var(--border-strong); color: var(--text-muted); background: var(--surface-2); }
.score-circle--low { border-width: 1px; border-color: var(--border); color: var(--text-faint); background: var(--surface-2); }

/* card layout for the grid view and for narrow screens ------------------ */

.tablewrap.is-grid { border: 0; background: none; overflow: visible; }
.tablewrap.is-grid table,
.tablewrap.is-grid tbody { display: block; }
.tablewrap.is-grid thead { display: none; }
.tablewrap.is-grid tbody { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: var(--space-3); }
.tablewrap.is-grid tbody tr {
  display: flex; flex-direction: column; gap: 6px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  padding: var(--space-3);
  box-shadow: var(--shadow-1);
}
.tablewrap.is-grid td { display: flex; align-items: center; gap: var(--space-2); padding: 0; width: auto !important; }
.tablewrap.is-grid td.num { justify-content: flex-start; }
.tablewrap.is-grid td::before {
  content: attr(data-label);
  flex: none;
  width: 68px;
  font-size: var(--fs-label-sm);
  letter-spacing: 0.03em;
  color: var(--text-faint);
}
.tablewrap.is-grid td.col-pick::before { display: none; }
.tablewrap.is-grid td.col-pick { position: absolute; top: var(--space-3); right: var(--space-3); }
.tablewrap.is-grid tbody tr { position: relative; }

/* ================================================================== bulk bar */

.bulkbar {
  position: absolute;
  bottom: var(--space-4);
  left: 50%;
  transform: translateX(-50%);
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-lg);
  border: 1px solid var(--border);
  background: var(--inverse);
  color: var(--on-inverse);
  box-shadow: var(--shadow-2);
  z-index: var(--z-float);
  animation: bar-in var(--duration-slow) var(--ease-out);
}

@keyframes bar-in { from { opacity: 0; transform: translate(-50%, 12px); } to { opacity: 1; transform: translate(-50%, 0); } }

.bulkbar__count {
  display: flex; align-items: center; gap: var(--space-2);
  padding-right: var(--space-4);
  border-right: 1px solid var(--border-strong);
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  font-weight: 600;
  letter-spacing: 0.02em;
  white-space: nowrap;
}
.bulkbar__badge {
  display: grid; place-items: center;
  min-width: 24px; height: 24px;
  padding: 0 6px;
  border-radius: var(--radius-pill);
  background: var(--primary-bright);
  color: var(--on-primary);
  font-size: var(--fs-label-sm);
  font-weight: 600;
}
.bulkbar__actions { display: flex; align-items: center; gap: var(--space-2); }

.bulkbar__btn {
  display: inline-flex; align-items: center; gap: 6px;
  font: inherit;
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 6px 12px;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: inherit;
  cursor: pointer;
  transition: background var(--duration);
}
.bulkbar__btn:hover { background: rgb(255 255 255 / 0.12); }
.bulkbar__btn--go { background: var(--primary-bright); color: var(--on-primary); }
.bulkbar__btn--go:hover { background: var(--primary); }
.bulkbar__btn--quiet { padding: 6px; color: inherit; opacity: 0.7; }
.bulkbar__btn--quiet:hover { opacity: 1; }
.bulkbar__btn.is-done { color: var(--primary-bright); }

/* ================================================================== cards */

.card {
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.card__head { display: flex; flex-direction: column; gap: 2px; }
.card__head--row { flex-direction: row; align-items: center; justify-content: space-between; gap: var(--space-3); }
.card__sub { font-size: var(--fs-label-sm); line-height: var(--lh-label-sm); color: var(--text-faint); }
.card__tools { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.card__foot {
  display: flex; align-items: center; gap: var(--space-4); flex-wrap: wrap;
  padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}

/* stages ---------------------------------------------------------------- */

.stages { display: flex; gap: var(--space-2); list-style: none; margin: 0; padding: 0; flex-wrap: wrap; }
.stage {
  display: flex; align-items: center; gap: 6px;
  padding: 5px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface-2);
  font-size: var(--fs-label-sm);
  letter-spacing: 0.03em;
  color: var(--text-faint);
}
.stage__dot { width: 7px; height: 7px; border-radius: 50%; background: var(--border-strong); flex: none; }
.stage.is-done { color: var(--ok); border-color: var(--ok-border); background: var(--ok-bg); }
.stage.is-done .stage__dot { background: var(--ok); }
.stage.is-now { color: var(--primary); border-color: var(--primary); background: var(--primary-soft); }
.stage.is-now .stage__dot { background: var(--primary); animation: blink 1.2s ease-in-out infinite; }
.stage.is-bad { color: var(--danger); border-color: var(--danger-border); background: var(--danger-bg); }
.stage.is-bad .stage__dot { background: var(--danger); }

.endpoint { font-size: var(--fs-label-sm); color: var(--text-faint); }
.endpoint > summary { cursor: pointer; color: var(--text-muted); font-weight: 600; letter-spacing: 0.02em; }
.endpoint__body { padding-top: 6px; font-family: var(--font-mono); font-size: var(--fs-label-sm); word-break: break-all; }

/* ================================================================== stats */

.stats { display: flex; gap: var(--space-2); flex-wrap: wrap; align-items: stretch; }
.stat {
  flex: 1 1 92px;
  max-width: 200px;
  padding: 8px var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.stat__value { font-size: var(--fs-headline); line-height: 26px; font-weight: 700; font-variant-numeric: tabular-nums; }
.stat__label { font-size: var(--fs-label-sm); letter-spacing: 0.03em; color: var(--text-faint); }
.stat--good .stat__value { color: var(--primary); }
.stat--bad .stat__value { color: var(--danger); }
.stat--accent .stat__value { color: var(--warn); }


/* ================================================================== alerts */

.alert {
  display: flex; gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  font-size: var(--fs-body-sm);
  color: var(--text-muted);
}
.alert .icon { color: var(--info); }
.alert__body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.alert__body ul { margin: 0; padding-left: 18px; }
.alert--warn { background: var(--warn-bg); border-color: var(--warn-border); color: var(--warn); }
.alert--warn .icon { color: var(--warn); }
.alert--danger { background: var(--danger-bg); border-color: var(--danger-border); color: var(--danger); }
.alert--danger .icon { color: var(--danger); }
.alert__link {
  font: inherit; color: inherit; background: none; border: 0; padding: 0;
  text-decoration: underline; cursor: pointer; text-align: left;
}

/* ================================================================== chips */

/* the small state chips used inside the lead sheet and the email column */
.chip {
  display: inline-flex; align-items: center;
  padding: 2px 7px;
  border-radius: var(--radius-xs);
  border: 1px solid var(--border);
  background: var(--surface-4);
  color: var(--text-muted);
  font-size: var(--fs-label-sm);
  line-height: var(--lh-label-sm);
  font-weight: 500;
  letter-spacing: 0.03em;
  white-space: nowrap;
}
.chip--xs { padding: 1px 5px; }
.chip--high { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-border); }
.chip--mid { background: var(--primary-soft); color: var(--primary); border-color: var(--primary-soft); }
.chip--warn { background: var(--warn-bg); color: var(--warn); border-color: var(--warn-border); }
.chip--bad { background: var(--danger-bg); color: var(--danger); border-color: var(--danger-border); }

/* ================================================================== empty */

.empty {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: var(--space-6) var(--space-4);
  border: 1px dashed var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface);
  text-align: center;
  color: var(--text-faint);
}
.empty .icon--empty { color: var(--border-strong); }
.empty__title { font-size: var(--fs-body); font-weight: 600; color: var(--text); }
.empty__body { max-width: 44ch; font-size: var(--fs-body-sm); }
.empty__action { margin-top: var(--space-2); }

.skeleton { border-radius: var(--radius-sm); background: var(--surface-3); animation: pulse 1.4s ease-in-out infinite; }
.skeleton--group { height: 44px; }
.skeleton--row { height: 52px; margin-bottom: var(--space-2); }
.skeleton-rows { display: block; }

@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }

/* ================================================================== log */

.logbox > summary {
  display: flex; align-items: center; gap: var(--space-2);
  cursor: pointer; list-style: none;
  font-size: var(--fs-label); font-weight: 600; letter-spacing: 0.02em; color: var(--text);
}
.logbox > summary::-webkit-details-marker { display: none; }
.logbox[open] > summary .group__chev { transform: rotate(90deg); }
.logbox__count { margin-left: auto; font-weight: 400; color: var(--text-faint); font-size: var(--fs-label-sm); }
.logbox__body { display: flex; flex-direction: column; gap: var(--space-2); padding-top: var(--space-3); }
.log {
  margin: 0; padding: var(--space-2);
  list-style: none;
  max-height: 260px; overflow-y: auto;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  background: var(--surface-2);
  font-family: var(--font-mono); font-size: var(--fs-label-sm); line-height: 1.7;
  color: var(--text-muted);
}
.log li { display: flex; gap: var(--space-2); }
.log time { color: var(--text-faint); flex: none; }

/* ================================================================== ledger note */

.history__note { font-size: var(--fs-body-sm); color: var(--text-faint); padding: var(--space-2) 0; }

/* ================================================================== dialogs */

dialog { border: 0; padding: 0; color: var(--text); background: var(--surface); }
/* .sheet and .modal set display, which would beat the browser default that
   keeps a closed dialog hidden. */
dialog:not([open]) { display: none !important; }
dialog::backdrop { background: rgb(25 28 29 / 0.35); backdrop-filter: blur(2px); }

.sheet {
  width: min(680px, calc(100vw - 32px));
  max-height: min(86dvh, 860px);
  margin: auto;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-3);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.sheet--wide { width: min(1100px, calc(100vw - 32px)); max-height: min(90dvh, 900px); }
.sheet--wide .sheet__head { flex-wrap: wrap; }
.sheet[open] { animation: pop var(--duration-slow) var(--ease-out); }

@keyframes pop { from { opacity: 0; transform: translateY(8px) scale(0.99); } to { opacity: 1; transform: none; } }

.sheet__head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-4);
  border-bottom: 1px solid var(--border);
}
.sheet__titles { min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.sheet__sub { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: var(--fs-label-sm); color: var(--text-faint); }
.sheet__body { flex: 1; overflow-y: auto; padding: var(--space-4); display: flex; flex-direction: column; gap: var(--space-4); }
.sheet__foot {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--border);
  background: var(--surface-2);
}
.sheet__nav { display: flex; align-items: center; gap: var(--space-2); }
.sheet__pos { font-size: var(--fs-label-sm); color: var(--text-faint); font-variant-numeric: tabular-nums; }

.sheetsec { display: flex; flex-direction: column; gap: 6px; }
.sheetsec__title {
  font-size: var(--fs-label-sm); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-faint);
}
.sheetsec dl { display: grid; grid-template-columns: minmax(120px, 34%) 1fr; gap: 4px var(--space-3); margin: 0; }
.sheetsec dt { font-size: var(--fs-label-sm); color: var(--text-faint); }
.sheetsec dd { margin: 0; font-size: var(--fs-body-sm); word-break: break-word; }

.scorebars { display: flex; flex-direction: column; gap: 6px; }
.scorebar { display: grid; grid-template-columns: minmax(110px, 30%) 34px 1fr; align-items: center; gap: var(--space-2); }
.scorebar__name { font-size: var(--fs-label-sm); color: var(--text-faint); }
.scorebar__value { font-size: var(--fs-label); font-weight: 600; font-variant-numeric: tabular-nums; text-align: right; }
.scorebar__track { height: 6px; border-radius: 3px; background: var(--surface-4); overflow: hidden; }
.scorebar__fill { display: block; height: 100%; background: var(--primary); }

.modal {
  width: min(440px, calc(100vw - 32px));
  margin: auto;
  padding: var(--space-4);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.modal[open] { animation: pop var(--duration-slow) var(--ease-out); }
.modal p { font-size: var(--fs-body-sm); color: var(--text-muted); }
.modal__help { font-size: var(--fs-label-sm); color: var(--text-faint); }
.modal__actions { display: flex; justify-content: flex-end; gap: var(--space-2); }

/* The preview box. Shows the change itself before it happens, so it needs room
   for a two column list and a scroll of its own on a short window. */
.modal--details { width: min(560px, calc(100vw - 32px)); }
.detailkv {
  display: grid;
  grid-template-columns: minmax(110px, auto) minmax(0, 1fr);
  gap: 6px var(--space-3);
  margin: 0;
  max-height: min(50vh, 420px);
  overflow-y: auto;
  padding: var(--space-3);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.detailkv dt { font-size: var(--fs-label); color: var(--text-faint); padding-top: 1px; }
.detailkv dd { margin: 0; font-size: var(--fs-body-sm); color: var(--text); overflow-wrap: anywhere; }

/* The column chooser. Forty tick boxes need more width than a confirm box and
   a list that scrolls on its own, so the buttons underneath never leave the
   screen on a short window. */
.modal--columns { width: min(620px, calc(100vw - 32px)); }
.columns__tools { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2); }
.columns__count { margin-left: auto; font-size: var(--fs-label-sm); color: var(--text-faint); }
.columns__list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 2px var(--space-3);
  max-height: min(46vh, 380px);
  overflow-y: auto;
  padding: var(--space-2);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
}
.columns__item {
  display: flex; align-items: center; gap: 8px;
  padding: 4px 6px;
  border-radius: var(--radius-sm);
  font-size: var(--fs-body-sm);
  cursor: pointer;
}
.columns__item:hover { background: var(--surface-3); }
.columns__item input { flex: none; }
.columns__item span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.shortcuts { display: grid; grid-template-columns: auto 1fr; gap: 8px var(--space-4); margin: 0; align-items: center; }
.shortcuts dt { display: flex; gap: 4px; }
.shortcuts dd { margin: 0; font-size: var(--fs-body-sm); color: var(--text-muted); }

/* ================================================================== toasts */

.toasts {
  position: fixed;
  right: var(--space-4); bottom: var(--space-4);
  z-index: var(--z-toast);
  display: flex; flex-direction: column; gap: var(--space-2);
  max-width: min(400px, calc(100vw - 32px));
  pointer-events: none;
}
.toast {
  display: flex; align-items: flex-start; gap: var(--space-2);
  padding: 10px var(--space-3);
  border-radius: var(--radius-md);
  border: 1px solid var(--border);
  background: var(--surface);
  box-shadow: var(--shadow-2);
  font-size: var(--fs-body-sm);
  color: var(--text);
  pointer-events: auto;
  animation: toast-in var(--duration-slow) var(--ease-out);
}
.toast.is-leaving { animation: toast-out 180ms ease-in forwards; }
.toast__body { flex: 1; min-width: 0; }
.toast__close {
  border: 0; background: none; padding: 0; cursor: pointer; color: var(--text-faint);
  display: grid; place-items: center;
}
.toast__close:hover { color: var(--text); }
.toast--ok .icon { color: var(--ok); }
.toast--bad { border-color: var(--danger-border); }
.toast--bad .icon { color: var(--danger); }

@keyframes toast-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes toast-out { to { opacity: 0; transform: translateY(6px); } }

/* ================================================================== responsive */

@media (max-width: 1180px) {
  .searchbox input { width: 150px; }
}

@media (max-width: 1024px) {
  :root { --filters-w: 250px; }
  .appbar__name { font-size: var(--fs-headline); }
  .searchbox { display: none; }
}

/* Below this the sidebar becomes a drawer and the table becomes cards. */
@media (max-width: 860px) {
  .appbar { height: auto; flex-wrap: wrap; padding: var(--space-2) var(--space-3); gap: var(--space-2); }
  .tabs { gap: var(--space-3); order: 3; width: 100%; height: auto; }
  .tab { height: auto; padding-bottom: 4px; }
  .tag { display: none; }
  #healthText { display: none; }

  .filters,
  .shell:not(.filters-open) .filters {
    position: absolute;
    inset: 0 auto 0 0;
    z-index: var(--z-overlay);
    width: min(330px, 88vw);
    border-right-width: 1px;
    overflow: visible;
    pointer-events: auto;
    box-shadow: var(--shadow-3);
    transform: translateX(-102%);
    transition: transform var(--duration-slow) var(--ease-out);
  }
  .shell.filters-open .filters { transform: none; }
  /* A drawer sits over the page at a fixed width. There is no border
     between two panes here, so there is nothing to drag. */
  .filters__grip { display: none; }
  .panel--work.is-empty .hero { padding-bottom: var(--space-5); }

  /* A card heading and its tools sit side by side on a wide screen. At this
     width the tools cannot shrink any further, so the heading was squeezed into
     a column one word wide and the sort control was clipped at the edge of the
     card. They stack instead: the heading takes the width it needs, the tools
     take the line under it. */
  .card__head--row { flex-direction: column; align-items: stretch; }
  .card__tools { width: 100%; }
  .card__tools .searchbox { flex: 1; min-width: 0; }
  .card__tools .searchbox input { width: 100%; }
  .card__tools .select--sm,
  .card__tools .picker__button { flex: none; }

  .scrim {
    display: block;
    position: absolute; inset: 0;
    z-index: calc(var(--z-overlay) - 1);
    background: rgb(25 28 29 / 0.35);
  }

  .hero { padding: var(--space-3); }
  .searchbar textarea {
    height: 40px; min-height: 40px; max-height: 128px;
    padding: 9px 12px 9px 36px;
    font-size: var(--fs-body-sm);
  }
  .searchbar__icon { height: 40px; left: 10px; }
  .searchbar__ghost { left: 36px; right: 12px; height: 40px; font-size: var(--fs-body-sm); }
  .searchbar__go { width: 100%; height: 38px; }
  .btn__kbd { display: none; }

  .resultbar { padding: var(--space-3); }
  .panel__scroll { padding: var(--space-3); }

  /* the table turns into the same cards the grid view uses */
  .tablewrap { border: 0; background: none; overflow: visible; }
  .tablewrap table, .tablewrap tbody { display: block; }
  .tablewrap thead { display: none; }
  .tablewrap tbody { display: flex; flex-direction: column; gap: var(--space-2); }
  .tablewrap tbody tr {
    position: relative;
    display: flex; flex-direction: column; gap: 6px;
    border: 1px solid var(--border);
    border-radius: var(--radius-sm);
    background: var(--surface);
    padding: var(--space-3);
    box-shadow: var(--shadow-1);
  }
  .tablewrap td { display: flex; align-items: center; gap: var(--space-2); padding: 0; width: auto !important; }
  .tablewrap td.num { justify-content: flex-start; }
  .tablewrap td::before {
    content: attr(data-label);
    flex: none; width: 68px;
    font-size: var(--fs-label-sm); letter-spacing: 0.03em; color: var(--text-faint);
  }
  .tablewrap td.col-pick { position: absolute; top: var(--space-3); right: var(--space-3); }
  .tablewrap td.col-pick::before { display: none; }

  .bulkbar { left: var(--space-3); right: var(--space-3); transform: none; justify-content: space-between; }
  .bulkbar[hidden] { display: none !important; }
  @keyframes bar-in { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
}

@media (max-width: 560px) {
  .segmented { display: none; }
  .bulkbar__count { padding-right: var(--space-2); }
  .bulkbar__btn span { display: none; }
}

/* ================================================================== motion */

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

/* ================================================================== print */

@media print {
  .appbar, .filters, .hero, .resultbar, .bulkbar, .toasts, .runstrip { display: none !important; }
  body { height: auto; overflow: visible; }
  .panel__scroll { overflow: visible; padding: 0; }
  .tablewrap { border: 0; }
  .leads tbody tr { break-inside: avoid; }
}

/* ================================================================== lists screen

   One row per run, every action that run has on the row itself, and a preview
   dialog that walks the whole file.

   One column. The width that matters is the canvas, not the window: opening the
   filters rail takes 280px away from it. So the action column measures itself
   against the panel, and gives its label back whichever side the width came
   from. */

.panel__inner--wide {
  max-width: 1400px;
  container-type: inline-size;
  container-name: listspanel;
}

.lists { display: grid; gap: var(--space-4); }
.lists__main { min-width: 0; }
.lists__foot { justify-content: flex-start; gap: var(--space-2); }
.lists__foot-gap { flex: 1; }

.mono { font-family: var(--font-mono); }
.tag--warn { color: var(--warn); background: var(--warn-bg); border-color: var(--warn-border); }

/* The app already has one look for a select: surface, hairline border, small
   radius, the two-triangle caret. This only makes it short enough to sit beside
   the compact search box; everything else is inherited, so it matches the
   selects in the filters rail and the settings form. */
.select--sm {
  width: auto;
  height: 30px;
  font-size: var(--fs-label);
  cursor: pointer;
}

/* ---------------------------------------------------------------- status tabs */

/* one row, always: the search and the sort are small and belong together */
.lists__main .card__head--row { align-items: flex-start; }
.lists__main .card__tools { flex-wrap: nowrap; align-items: center; gap: var(--space-2); }
.lists__main .card__tools .searchbox input { width: 210px; }
.lists__main .card__tools .select--sm { width: 148px; flex: none; }

.statustabs { display: flex; flex-wrap: wrap; gap: 4px; }
.statustab {
  display: inline-flex; align-items: center; gap: 6px;
  height: 28px; padding: 0 10px;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: transparent;
  font: inherit; font-size: var(--fs-label);
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--duration) var(--ease-out), color var(--duration) var(--ease-out);
}
.statustab:hover { background: var(--surface-3); color: var(--text); }
.statustab.is-on {
  background: var(--primary-soft);
  border-color: var(--ok-border);
  color: var(--primary);
  font-weight: 500;
}
.statustab__count { font-family: var(--font-mono); font-size: var(--fs-label-sm); opacity: 0.75; }

/* The runs pager. Same pieces as the lead preview's, so the two screens are
   read the same way, and it wraps to its own line on a phone rather than
   squeezing the page number out of sight. */
.runs__foot {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: var(--space-2) var(--space-3);
}

/* Two ticks side by side. The pair is one question - which kinds of contact are
   worth having - so they read as a pair rather than as a list of two. */
.ticks--inline { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); }
.ticks--inline .tick { flex: none; }

/* The company-only tick is a different question from the two above it, so it
   sits under a line rather than as a third item in the same list. */
.fgroup__extra {
  display: flex; flex-direction: column; gap: 4px;
  margin-top: var(--space-3); padding-top: var(--space-3);
  border-top: 1px solid var(--border);
}
/* A tick list clips its labels to keep a long list of industries in line. This
   one is a sentence, and a sentence cut off at "Also include companies where..."
   says nothing, so it wraps instead. */
.fgroup__extra .tick { align-items: flex-start; }
.fgroup__extra .tick > span { overflow: visible; white-space: normal; }
.fgroup__extra .tick input { margin-top: 2px; }

/* ---------------------------------------------------------------- runs table */

/* Fixed layout, so a long company name can never push the action buttons off
   the row. Every cell that can overrun is clipped with an ellipsis instead. */
table.runs { width: 100%; border-collapse: collapse; table-layout: fixed; }

.runs thead th {
  /* sticks to the top of the panel's own scroll, so a long list keeps its
     headings without a second scrollbar anywhere */
  position: sticky; top: 0; z-index: 5;
  text-align: left;
  padding: 6px 10px 8px;
  background: var(--surface);
  font-size: var(--fs-label-sm); line-height: var(--lh-label-sm);
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-faint); font-weight: 600;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}
.runs thead th.num { text-align: right; }
/* Wide enough for a first name and a surname before the ellipsis takes over. */
.runs .col-by { width: 132px; }
.runs .col-state { width: 104px; }
.runs .col-leads { width: 64px; }
.runs .col-when { width: 116px; }
/* Wide enough for the whole action group: one labelled button and four icons.
   The width the rail used to take is what pays for it. */
.runs .col-do { width: 244px; }

.runrow { border-bottom: 1px solid var(--border); cursor: pointer; }
.runrow > td { padding: 10px; vertical-align: middle; }
.runrow > td.num { text-align: right; }
.runrow:last-child { border-bottom: 0; }
.runrow:hover { background: var(--surface-2); }
.runrow:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }

/* Only the row being read is marked. The status pill already says what
   happened, and a stripe on every row turned a screen of failed runs into a
   wall of red. */
.runrow.is-picked { background: var(--primary-soft); box-shadow: inset 3px 0 0 var(--primary); }

/* A sample is readable, never dressed up as work the app did. */
.runs__samplenote td {
  display: flex; align-items: flex-start; gap: var(--space-2);
  padding: var(--space-3) 10px var(--space-2);
  font-size: var(--fs-label); line-height: var(--lh-label);
  color: var(--text-faint);
  border-top: 1px solid var(--border);
}
.runs__samplenote .icon { flex: none; margin-top: 1px; }
.runrow.is-sample { background: var(--surface-2); }
.runrow.is-sample:hover { background: var(--surface-3); }
.runrow.is-sample.is-picked { background: var(--primary-soft); }
.runrow.is-sample .avatar { opacity: 0.8; }

.runrow .who { gap: var(--space-2); }
.runrow .avatar { width: 28px; height: 28px; font-size: var(--fs-label-sm); }
.runrow .who__name { font-weight: 500; }

/* The chevron that opens a run's detail. Turns to point down when open, the
   same way the log box and the filter cards say they are open. */
.rowopen { flex: none; width: 28px; height: 28px; color: var(--text-faint); }
.rowopen .icon { transition: transform var(--duration) var(--ease-out); }
.rowopen.is-open .icon { transform: rotate(90deg); }
.rowopen.is-open { color: var(--primary); }

/* A run's detail, directly under its row. Built from the parts the Workspace
   used for the same facts - the stat tiles, the warning box, the log box and
   the strip's progress bar - so nothing here is a new look. */
.rundetail > td { padding: 0 10px var(--space-3); border-bottom: 1px solid var(--border); background: var(--surface-2); }
.rundetail__body {
  display: flex; flex-direction: column; gap: var(--space-3);
  padding-top: var(--space-3);
  min-width: 0;
}
.rundetail__head { display: flex; align-items: flex-start; gap: var(--space-2); flex-wrap: wrap; }
.rundetail__reason { margin: 0; flex: 1 1 240px; font-size: var(--fs-body-sm); color: var(--text); }
.rundetail__who { margin: 0; display: flex; flex-wrap: wrap; gap: var(--space-2); font-size: var(--fs-body-sm); color: var(--text-muted); }
.rundetail__headline { margin: 0; font-size: var(--fs-body-sm); color: var(--text-muted); }
.rundetail__facts { max-height: none; overflow: visible; background: var(--surface); }
.rundetail__progress { display: flex; align-items: center; gap: var(--space-2); font-variant-numeric: tabular-nums; }
.rundetail__track { flex: 1 1 auto; max-width: 240px; border-radius: 2px; }
.rundetail__icp { display: block; white-space: pre-wrap; }
.rundetail__icp.is-clamped {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; line-clamp: 3; overflow: hidden;
}
.rundetail__more { margin-top: 4px; padding-left: 0; }
.rundetail__figures .stat { background: var(--surface); }
.rundetail__refused { display: flex; flex-direction: column; gap: var(--space-2); }
.rundetail__refused > p { margin: 0; font-size: var(--fs-body-sm); }
.rundetail__refused .detailkv { max-height: none; overflow: visible; background: var(--surface); font-variant-numeric: tabular-nums; }
.rundetail__log { padding: var(--space-3); border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface); }
.rundetail__loading { margin: 0; display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-body-sm); color: var(--text-faint); }
.rundetail__record { margin: 0; display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-label-sm); color: var(--text-muted); }
.rundetail__actions { display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center; }

/* Clipped rather than wrapped, so one long name cannot make its row twice the
   height of the rows around it. The whole name is on the title either way. */
.runrow .col-by > span {
  display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.runrow .col-do { text-align: right; }
.runrow .col-do .btn { white-space: nowrap; }

/* Everything a run can do, on the run's own row. Right aligned so the buttons
   line up down the table and the eye reads one column, not a ragged edge. */
.runacts { display: flex; align-items: center; justify-content: flex-end; gap: 4px; }
.runacts .iconbtn { width: 28px; height: 28px; text-decoration: none; }
/* Delete is the one that cannot be undone, so it turns red before it is pressed
   rather than looking like the buttons next to it. */
.runacts__danger:hover:not(:disabled) { color: var(--danger); background: var(--danger-bg); }

/* A row with a request in flight.

   Deleting a run is a round trip, and on a slow connection that is long enough
   to click again. The row used to sit there looking untouched, so the honest
   reading was that the click had missed. Now the buttons are replaced by what is
   happening, the row stops responding, and the whole thing is dimmed - three
   signals, so it reads the same without colour and without the animation. */
/* 0.7, not lower: below that the label text falls under the 4.5:1 contrast
   floor against the surface, and a busy row still has to be readable. Busy is
   also not the same as disabled and must not be dimmed as hard. */
.runrow.is-busy { cursor: default; opacity: 0.7; }
.runrow.is-busy:hover { background: transparent; }
.runacts__busy {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: var(--fs-label); color: var(--text-muted);
  /* Holds the width the buttons had, so the table does not jump when a row
     starts working and jump back when it stops. */
  min-height: 28px; padding: 0 4px;
}

/* A narrow canvas cannot hold a word next to five icons without eating the run
   name, which is the one thing a row is read for. The word goes; the title and
   the aria-label stay, so nothing is lost but the pixels. */
@container listspanel (max-width: 900px) {
  .runacts__label { display: none; }
  /* Except on a busy row, where the word is the only thing there is to read.
     A lone spinner does not say which of five actions is running. */
  .runacts__busy .runacts__label { display: inline; }
  .runs .col-do { width: 200px; }
}

/* ---------------------------------------------------------------- lead preview */

/* the preview is a spreadsheet, so it takes the width it can get */
.sheet--preview { width: min(1460px, calc(100vw - 32px)); }
.sheet--preview .sheet__head { flex-wrap: nowrap; gap: var(--space-4); }
.sheet--preview .sheet__titles { flex: 1; min-width: 0; }
.sheet--preview .sheet__titles h2 { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.preview__tools { display: flex; align-items: center; gap: var(--space-2); flex: none; flex-wrap: nowrap; }
.preview__tools .searchbox input { width: 230px; }
.preview__bar {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  padding: var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--border);
  background: var(--surface-2);
}
/* the narrow-screen rule that hides segmented controls must not hide the one
   control that decides how much of the file is on screen */
.preview__bar .segmented { display: inline-flex; }
.preview__chips { display: flex; gap: 6px; flex-wrap: wrap; }

.chip-toggle {
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--surface);
  font: inherit; font-size: var(--fs-label);
  color: var(--text-muted);
  cursor: pointer;
}
.chip-toggle:hover { background: var(--surface-3); }
.chip-toggle.is-on {
  background: var(--primary-soft); border-color: var(--ok-border); color: var(--primary); font-weight: 500;
}

.preview__body { padding: 0; }
/* while a page is being fetched the old rows stay put and fade, so the table
   never blinks empty between pages */
.preview.is-loading tbody { opacity: 0.4; pointer-events: none; }
.preview tbody { transition: opacity var(--duration) var(--ease-out); }
.tablewrap--preview { border: 0; border-radius: 0; overflow: auto; }

table.preview { width: 100%; border-collapse: collapse; }
.preview thead th {
  position: sticky; top: 0; z-index: 2;
  text-align: left;
  padding: 9px 10px;
  background: var(--surface-2);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-label-sm); letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-faint); font-weight: 600;
  white-space: nowrap;
}
.preview thead th.num, .preview td.num { text-align: right; }
.preview td {
  padding: 8px 10px;
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-body-sm);
  vertical-align: middle;
  max-width: 260px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.preview td.is-wide, .preview th.is-wide { max-width: 340px; }
.preview tbody tr { cursor: pointer; }
.preview tbody tr:hover td { background: var(--surface-2); }
.preview tbody tr:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }

/* the first column stays put while the rest scrolls sideways */
.preview thead th:first-child, .preview tbody td:first-child {
  position: sticky; left: 0; z-index: 1;
  background: var(--surface);
  box-shadow: 1px 0 0 var(--border);
}
.preview thead th:first-child { z-index: 3; background: var(--surface-2); }
.preview tbody tr:hover td:first-child { background: var(--surface-2); }

.preview__foot { flex-wrap: wrap; gap: var(--space-3); }
.preview__count { font-size: var(--fs-body-sm); color: var(--text-muted); }
.preview__pager { display: flex; align-items: center; gap: var(--space-2); }
.preview__page { font-size: var(--fs-body-sm); color: var(--text-muted); font-variant-numeric: tabular-nums; }
.preview__downloads { display: flex; gap: var(--space-2); margin-left: auto; }

/* ---------------------------------------------------------------- narrow */

/* the app hides every search box under 1024px. A long list of runs and a file
   of a few hundred leads are exactly where searching matters most, so these two
   stay. */
@media (max-width: 1024px) {
  .lists__main .searchbox, .preview__tools .searchbox { display: flex; }
  .lists__main .searchbox input, .preview__tools .searchbox input { width: 100%; min-width: 130px; }
  .preview__tools { flex-wrap: wrap; }
  .sheet--preview .sheet__head { flex-wrap: wrap; }
}

@media (max-width: 860px) {
  /* the shared rule above turns every table into cards */
  .runrow > td { padding: 0; }
  .runrow.is-picked { box-shadow: none; border-left: 3px solid var(--primary); }
  .setline { white-space: normal; }
  .preview__downloads { margin-left: 0; }
  /* stacked as a card, the row reads left to right like everything above it */
  .runacts { flex-wrap: wrap; justify-content: flex-start; }
  /* The detail is its own card straight under its run's card. The shared card
     rule labels every cell and lays it out as a row, and the detail is neither:
     it is one cell holding its own blocks. */
  .tablewrap tbody tr.rundetail { margin-top: calc(var(--space-2) * -1); border-top-left-radius: 0; border-top-right-radius: 0; background: var(--surface-2); }
  .tablewrap tr.rundetail > td { display: block; padding: 0; border: 0; background: none; }
  .tablewrap tr.rundetail > td::before { display: none; }
  .rundetail__body { padding-top: 0; }
  .rundetail__facts { grid-template-columns: minmax(84px, auto) minmax(0, 1fr); }
}

/* ------------------------------------------------------------ sign-in page
   Drawn by app/lib/session.js on both panels. The body rule above fixes the
   app shell to the viewport with no scroll; the sign-in page is a single card
   that may need to scroll on a short phone held sideways, so it lets go. */
body.login-body { height: auto; min-height: 100dvh; overflow: auto; }

.login {
  flex: 1;
  display: grid;
  place-items: center;
  padding: var(--space-6) var(--space-4);
}

.login__card {
  width: 100%;
  max-width: 380px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
  padding: var(--space-6) var(--space-5);
}

.login__brand { display: flex; align-items: center; gap: var(--space-2); color: var(--primary); }
.login__mark { width: 24px; height: 24px; flex: none; }
.login__name { font-size: var(--fs-title); font-weight: 700; color: var(--text); }

.login__title { margin-top: var(--space-5); font-size: var(--fs-display); font-weight: 700; }
.login__lead { margin-top: var(--space-1); color: var(--text-muted); font-size: var(--fs-body); }

.login__form { margin-top: var(--space-5); display: flex; flex-direction: column; gap: var(--space-4); }
.login__field { display: flex; flex-direction: column; gap: 6px; }
.login__label { font-size: var(--fs-body-sm); font-weight: 600; color: var(--text); }

/* 16px text, so a phone does not zoom in on focus; 44px tall, a thumb's width. */
.login__input {
  min-height: 44px;
  padding: 10px 12px;
  font: inherit;
  font-size: 16px;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
}
.login__input:hover { border-color: var(--text-faint); }
.login__input[aria-invalid="true"] { border-color: var(--danger); }

.login__error {
  color: var(--danger);
  background: var(--danger-bg);
  border: 1px solid var(--danger-border);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
  font-size: var(--fs-body-sm);
  line-height: 1.45;
}

/* --primary, not --primary-bright: white on the bright green is under 3:1,
   on --primary it clears 4.5:1. The one button on this page must be legible. */
.login__submit { min-height: 44px; margin-top: var(--space-1); background: var(--primary); cursor: pointer; }
.login__submit:hover:not(:disabled) { background: var(--accent-hover); }

.login__note { margin-top: var(--space-4); color: var(--text-muted); font-size: var(--fs-label); line-height: 1.5; }
