/* DiffamAI portal — design system.
 *
 * This file is organised in four layers, and nothing may skip a layer:
 *
 *   1. TOKENS      the only place a raw colour, size or radius is written
 *   2. BASE        element defaults
 *   3. PRIMITIVES  surface, button, field, table, chip — reused everywhere
 *   4. COMPONENTS  the things built out of primitives
 *
 * The previous version had 31 font sizes, 10 radii, 9 button styles and four
 * levels of nested shadow. That is why it read as cheap: with everything
 * emphasised, nothing was. The scales below are deliberately short — if a new
 * value seems necessary, the answer is almost always an existing one.
 *
 * ELEVATION IS THE RULE THAT MATTERS MOST: exactly one shadow, spent only on
 * surfaces that sit directly on the page background. Anything nested inside a
 * surface separates with a hairline. Two stacked shadows read as clutter and
 * destroy the hierarchy they were meant to create.
 */

/* ═══ 1. TOKENS ═══════════════════════════════════════════════════════ */

/* Dark is the product default and the OS preference does not override it —
   only the toggle does. base.html stamps data-theme before first paint. */
:root[data-theme="dark"], :root:not([data-theme]) {
  /* Lumyra: Obsidian ground, Deep Blue surfaces, Aura accent, Light ink. */
  --ground:       #0b0b0f;   /* Obsidian */
  --surface:      #121420;
  --surface-2:    #1b1f38;   /* Deep Blue: hover / recessed wells */
  --line:         #232842;
  --line-strong:  #353b60;

  --ink:          #e7eaf3;   /* Light */
  --ink-soft:     #a9afc6;
  --ink-mute:     #767c98;

  --accent:       #7b8cff;   /* Aura */
  --accent-soft:  rgba(123, 140, 255, 0.15);
  --accent-ink:   #0b0b0f;
  --warm:         #d8c9b8;   /* Sand: the one warm note, used sparingly */

  --status-waiting: #e1cd66;
  --ok:           #5cc98a;
  --ok-soft:      rgba(92, 201, 138, 0.14);
  --warn:         #f0a94a;
  --warn-soft:    rgba(240, 169, 74, 0.14);
  --bad:          #f0665a;
  --bad-soft:     rgba(240, 102, 90, 0.14);

  --shadow: 0 1px 2px rgba(0, 0, 0, 0.5), 0 14px 34px -22px rgba(0, 0, 0, 0.95);
  --glow:   0 0 24px rgba(123, 140, 255, 0.35);
}

:root[data-theme="light"] {
  --ground:       #f4f5fa;
  --surface:      #ffffff;
  --surface-2:    #eceef7;
  --line:         #dde0ee;
  --line-strong:  #c3c8dc;

  --ink:          #0b0b0f;
  --ink-soft:     #3c4159;
  --ink-mute:     #6b7089;

  --accent:       #4a58d9;   /* Aura, darkened to hold contrast on white */
  --accent-soft:  #e8eafd;
  --accent-ink:   #ffffff;
  --warm:         #8a7457;

  --status-waiting: #776209;
  --ok:           #2f7a4e;
  --ok-soft:      #e6f3ec;
  --warn:         #8a5a06;
  --warn-soft:    #fbf0dc;
  --bad:          #b3362b;
  --bad-soft:     #fbeae8;

  --shadow: 0 1px 2px rgba(11, 11, 15, 0.06), 0 12px 32px -20px rgba(27, 31, 56, 0.35);
  --glow:   none;
}

:root {
  /* Sora carries the identity (headings, brand, controls, big figures);
     the body face stays narrower so the dense tables of steps 3-5 keep
     their density. */
  --font-display: "Sora", "Instrument Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  --font-serif:   "Instrument Serif", "Iowan Old Style", Georgia, serif;
  --font-body:    "Instrument Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  /* Type — seven sizes, and that is the whole scale. Anything that wants an
     eighth is asking for emphasis it should get from weight or colour. */
  --fs-fig:  2rem;      /* the one number a stat tile exists to show */
  --fs-xl:   1.5rem;    /* h1 */
  --fs-lg:   1.0625rem; /* h3, panel titles that are real headings */
  --fs-md:   1rem;      /* body */
  --fs-sm:   0.875rem;  /* secondary text, table cells */
  --fs-xs:   0.8rem;    /* captions, dense metadata */
  --fs-2xs:  0.72rem;   /* uppercase eyebrows, table headers */

  /* Space — a 4px ramp. */
  --s1: 4px;  --s2: 8px;  --s3: 12px; --s4: 16px;
  --s5: 24px; --s6: 32px; --s7: 48px;

  /* Radius — four values. */
  --r-sm: 6px; --r-md: 10px; --r-lg: 14px; --r-pill: 999px;

  --focus: 0 0 0 3px var(--accent-soft);
}

/* Charts — ordinal blue ramp: ONE hue, light→dark, because score bands are a
   magnitude and not five identities. Blue rather than the accent, so a bar is
   never mistaken for something clickable.
 *
 * This is a SEQUENTIAL ramp, so the checks that apply are lightness
 * monotonicity and contrast against the surface — not the categorical
 * lightness-band/chroma rules, which demand the opposite (every swatch in one
 * mid band) and would flatten the very gradient that carries the order.
 * Both sets below clear 3:1 on their own surface, verified with the validator.
 * Re-run it if you touch a step: the light ramp had to be darkened wholesale
 * because its top two steps sat at 2.1:1 and 3.0:1 on white. */
:root[data-theme="dark"], :root:not([data-theme]) {
  --band-1: #cde2fb; --band-2: #9ec5f4; --band-3: #6da7ec;
  --band-4: #3987e5; --band-5: #256abf;
  --viz-grid: #253029;
}
:root[data-theme="light"] {
  --band-1: #5e97d8; --band-2: #407dc6; --band-3: #2764a8;
  --band-4: #17497f; --band-5: #0b2f55;
  --viz-grid: #dde5e1;
}

/* ═══ 2. BASE ═════════════════════════════════════════════════════════ */

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  font-family: var(--font-body);
  font-size: 15px;
  line-height: 1.55;
  background: var(--ground);
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Numbers that sit in a column or change in place must not jitter. */
.grid td, .kpi span, .tile span, .score, .num, td.num, .chip.score,
.hbars__value, .chart__colvalue, .funnel__value, .money__value,
.hero__figure, .tabs__count { font-variant-numeric: tabular-nums; }

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

h1 { margin: 0 0 var(--s4); font-family: var(--font-display); font-size: var(--fs-xl); font-weight: 500; color: var(--ink); letter-spacing: -0.02em; line-height: 1.25; text-wrap: balance; }
h2 { margin: var(--s6) 0 var(--s3); font-family: var(--font-display); font-size: var(--fs-2xs); color: var(--ink-mute); text-transform: uppercase; letter-spacing: 0.09em; font-weight: 600; }
h3 { margin: var(--s5) 0 var(--s2); font-family: var(--font-display); font-size: var(--fs-lg); font-weight: 500; color: var(--ink); letter-spacing: -0.01em; }

p { margin: 0 0 var(--s3); }

code {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 0.9em;
  padding: 0.1em 0.38em;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
}

hr { border: none; border-top: 1px solid var(--line); margin: var(--s5) 0; }

/* Every interactive element gets a visible ring. Components that restyle
   themselves must not drop it — that is why this is stated once, broadly. */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* ═══ 3. PRIMITIVES ═══════════════════════════════════════════════════ */

/* ── surface ──────────────────────────────────────────────────────────
   One shadowed box. Everything that sits directly on the page ground is a
   surface; everything inside one is separated by a hairline instead. */
.panel, .grid, .hero, .stepper, .login-card,
.toolbar, .bulk-bar, .graph-box, .tabs__panel > .chart {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow);
}

/* ── buttons ──────────────────────────────────────────────────────────
   One geometry, four intents. The variants change only fill and ink; nothing
   redefines padding, radius or weight, so a row of mixed buttons lines up. */
button, .btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--s2);
  padding: 0.5rem 0.95rem;
  font: inherit;
  font-family: var(--font-display);   /* after the shorthand, which resets it */
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: 1.3;
  color: var(--ink);
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  cursor: pointer;
  transition: background 0.14s ease, border-color 0.14s ease, color 0.14s ease;
  white-space: nowrap;
}
button:hover, .btn:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }
button:disabled, .btn:disabled { opacity: 0.45; cursor: not-allowed; }
button:disabled:hover, .btn:disabled:hover { border-color: var(--line-strong); color: var(--ink); }

.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.primary:hover { filter: brightness(1.08); color: var(--accent-ink); border-color: var(--accent); }

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

.btn-ghost { background: transparent; border-color: transparent; color: var(--ink-soft); font-weight: 500; }
.btn-ghost:hover { background: var(--surface-2); color: var(--ink); border-color: transparent; }

.icon-btn {
  width: 38px; height: 38px;
  padding: 0;
  background: transparent;
  border-color: transparent;
  color: var(--ink-soft);
  border-radius: var(--r-md);
}
.icon-btn:hover { background: var(--surface-2); color: var(--accent); border-color: transparent; }
.icon-btn .icon { width: 19px; height: 19px; }

/* Sun in dark mode (you are switching TO light), moon in light mode. */
:root[data-theme="dark"] .icon-moon,
:root:not([data-theme]) .icon-moon { display: none; }
:root[data-theme="light"] .icon-sun { display: none; }

/* ── fields ───────────────────────────────────────────────────────────
   Inputs are recessed against the surface they sit on, never raised. A raised
   input competes with the buttons beside it for the same visual job. */
input[type="text"], input[type="password"], input[type="number"],
input[type="url"], input[type="date"], input[type="email"], input[type="search"],
select, textarea {
  width: 100%;
  padding: 0.5rem 0.7rem;
  font: inherit;
  font-size: var(--fs-sm);
  color: var(--ink);
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  transition: border-color 0.14s ease, box-shadow 0.14s ease;
}
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: var(--focus);
}
textarea { min-height: 7rem; resize: vertical; line-height: 1.5; }
/* Element display rules (label, .hint...) would otherwise override the attribute. */
[hidden] { display: none !important; }
label { display: block; font-size: var(--fs-xs); font-weight: 600; color: var(--ink-soft); margin: 0 0 var(--s1); }
fieldset { border: none; margin: 0; padding: 0; }

.field { margin: 0 0 var(--s4); }
.fields { display: grid; gap: var(--s4); grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }

/* Explanatory text under a control: quieter than body, never competing. */
.hint { color: var(--ink-mute); font-size: var(--fs-xs); line-height: 1.5; margin: var(--s1) 0 0; max-width: 72ch; }
/* A page's opening paragraph, as opposed to a note under a control: it needs
   to clear the form that follows it. */
.hint--lede { font-size: var(--fs-sm); margin-bottom: var(--s5); }

legend { padding: 0; font-size: var(--fs-xs); font-weight: 600; color: var(--ink-soft); margin: 0 0 var(--s2); }

/* ── choices ──────────────────────────────────────────────────────────
   Radio cards. A bare radio list makes the reader guess what each option
   costs; here each one states its own trade-off, which is the whole basis for
   choosing. The label is the hit target, so the text is clickable too. */
.choices { display: grid; gap: var(--s2); }
.choice {
  display: flex; align-items: flex-start; gap: var(--s3);
  padding: var(--s3) var(--s4); margin: 0;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-md); cursor: pointer;
  font-weight: 400;
  transition: border-color 0.14s ease, background 0.14s ease;
}
.choice:hover { border-color: var(--line-strong); }
.choice input { flex: none; margin: 3px 0 0; accent-color: var(--accent); width: auto; }
.choice:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
.choice:has(input:focus-visible) { box-shadow: var(--focus); }
.choice__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.choice__title { font-size: var(--fs-sm); font-weight: 600; color: var(--ink); }
.choice__note { font-size: var(--fs-xs); color: var(--ink-mute); line-height: 1.45; }
.choice__warn { font-size: var(--fs-xs); color: var(--warn); font-weight: 600; }
.choice.is-disabled { opacity: 0.55; cursor: not-allowed; }
.choice.is-disabled:hover { border-color: var(--line); }

/* ── chips ────────────────────────────────────────────────────────────
   State as a small labelled pill. Colour is never the only carrier — the chip
   always holds a word. */
.chip {
  display: inline-flex; align-items: center; gap: var(--s1);
  padding: 0.14em 0.6em;
  font-size: var(--fs-2xs); font-weight: 600;
  border-radius: var(--r-pill);
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--ink-soft);
  white-space: nowrap;
}
.chip.score { background: var(--accent-soft); border-color: transparent; color: var(--accent); }
.cat-diffamazione, .cat-minaccia { background: var(--bad-soft); border-color: transparent; color: var(--bad); }
.cat-insulto, .cat-altro { background: var(--warn-soft); border-color: transparent; color: var(--warn); }
.cat-nessuna { background: var(--surface-2); color: var(--ink-mute); }
.chip--profile { font-family: ui-monospace, Menlo, monospace; font-weight: 500; }
.chip__engine { color: var(--ink-mute); font-weight: 500; }
.chip__auto { color: var(--accent); }

.dot { width: 7px; height: 7px; border-radius: 50%; background: var(--ink-mute); display: inline-block; flex: none; }
.dot--new { background: var(--ink-mute); }
.dot--closed { background: var(--ok); }
.dot--rejected { background: var(--bad); }

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

/* ── tables ───────────────────────────────────────────────────────────
   Records in a list, not a grid of boxes: hairline rows, a quiet header, and
   no vertical rules at all. */
.grid, .table-scroll table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  overflow: hidden;
}
.table-scroll { overflow-x: auto; }
.table-scroll table { background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg); }

.grid th, .grid td, .table-scroll table th, .table-scroll table td {
  padding: 0.7rem 0.9rem;
  text-align: left;
  font-size: var(--fs-sm);
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}
.grid th, .table-scroll table th {
  background: var(--surface-2);
  font-weight: 600;
  font-size: var(--fs-2xs);
  color: var(--ink-mute);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  white-space: nowrap;
  vertical-align: middle;
}
.grid tr:last-child td, .table-scroll table tr:last-child td { border-bottom: none; }
.grid tbody tr { transition: background 0.12s ease; }
.grid tbody tr:hover td { background: var(--surface-2); }
.grid td.num, .grid th.num { text-align: right; }

/* Comment text is evidence: its line breaks are part of what was written, and
   collapsing them changes the record. */
/* pre-wrap belongs on the element that holds the text, never on a grid or
   flex container: preserved whitespace between child tags becomes real text
   nodes, and a grid turns those into anonymous items that take up rows. That
   is what put 40px of dead space above and below every quoted comment —
   the template's own indentation, rendered. */
td.text, .comments-cell__quote { white-space: pre-wrap; overflow-wrap: anywhere; }
td.reasoning { max-width: 32ch; color: var(--ink-mute); font-size: var(--fs-xs); }
/* The step-3 table must fit the page. Auto layout sized columns by content,
   so a long reasoning squeezed the comment to a few letters per line: fix the
   columns instead and let the comment take whatever is left. */
.grid.comments { table-layout: fixed; }
.grid.comments th { white-space: normal; }
.grid.comments td, .grid.comments th { overflow-wrap: anywhere; }
.grid.comments th:nth-child(1) { width: 5.5%; }   /* Punteggio */
.grid.comments th:nth-child(2) { width: 8%; }     /* Categoria */
.grid.comments th:nth-child(3) { width: 5%; }     /* Gravità */
.grid.comments th:nth-child(4) { width: 12%; }    /* Autore */
.grid.comments th:nth-child(6) { width: 7.5%; }   /* Acquisito il */
.grid.comments th:nth-child(7) { width: 14%; }    /* Motivazione */
.grid.comments th:nth-child(8) { width: 10.5rem; } /* Rilevante: the three-way control is ~8rem plus padding */
.grid.comments td.reasoning { max-width: none; padding-inline: 0.6rem; line-height: 1.35; }
.grid.comments .chip { max-width: 100%; white-space: normal; }
.nowrap { white-space: nowrap; }

/* The "show more" sits inside the table it extends, so it reads as the end of
   the list rather than as a control floating beneath it.
 *
 * `tr.more-row td` rather than `.more-row td`: the pool table lives inside
 * .table-scroll, so the cell is already claimed by `.table-scroll table td`
 * — one class and two elements — which outranks a bare class-plus-element and
 * was quietly keeping text-align at left. */
tr.more-row td { text-align: center; padding: var(--s4); background: var(--surface-2); }
tr.more-row button { min-width: 14rem; }
tr.more-row .spinner { margin-left: var(--s2); vertical-align: middle; }

/* A long comment is clamped until opened. Both halves have to exist: the clamp
   itself, and the affordance that says there is more to see. */
.clamp {
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
details[open] .clamp { -webkit-line-clamp: unset; display: block; }
summary { cursor: pointer; list-style: none; }
summary::-webkit-details-marker { display: none; }
.more { display: block; font-size: var(--fs-2xs); font-weight: 600; color: var(--accent); margin-top: var(--s1); }
.more::after { content: "mostra tutto"; }
details[open] .more::after { content: "riduci"; }

/* ═══ 4. LAYOUT ═══════════════════════════════════════════════════════ */

.topbar {
  display: flex; align-items: center; gap: var(--s5);
  padding: var(--s3) var(--s5);
  background: color-mix(in srgb, var(--surface) 88%, transparent);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 10;
  backdrop-filter: saturate(140%) blur(10px);
}
.topbar .brand { display: flex; align-items: center; gap: var(--s3); color: var(--ink); }
/* Wordmark as on the identity boards: light weight, wide tracking, the Y lit. */
.wordmark { font-family: var(--font-display); font-weight: 300; letter-spacing: 0.34em; text-transform: uppercase; margin-right: -0.34em; }
.wordmark b { font-weight: 300; color: var(--accent); text-shadow: var(--glow); }
.topbar .wordmark { font-size: 0.95rem; }
.topbar .brand:hover { text-decoration: none; }
/* The mark carries the accent; the wordmark stays in ink, so the two read as
   one lockup rather than as a coloured word. */
.topbar .brand .logo { color: var(--accent); flex: none; }
.login-mark { display: flex; justify-content: center; color: var(--accent); margin: 0 0 var(--s3); }
.topbar nav { display: flex; gap: var(--s1); }
.topbar nav a {
  color: var(--ink-soft); padding: 0.35rem 0.7rem;
  border-radius: var(--r-sm); font-size: var(--fs-sm); font-weight: 500;
}
.topbar nav a:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; }
.topbar .spacer { margin-left: auto; }
.topbar .logout { margin: 0; }

.container { max-width: 1180px; margin: 0 auto; padding: var(--s5) var(--s5) var(--s7); }

.crumbs { color: var(--ink-mute); margin: 0 0 var(--s3); font-size: var(--fs-xs); }
.crumbs a { color: var(--ink-soft); }
.crumbs strong { color: var(--ink); font-weight: 600; }

.page-header { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s4); margin: 0 0 var(--s5); flex-wrap: wrap; }
.page-header h1 { margin: 0; }
.page-header .actions { display: flex; gap: var(--s2); align-items: center; }

.toolbar, .bulk-bar {
  display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap;
  padding: var(--s3) var(--s4);
  margin: 0 0 var(--s3);
}
/* A bar that mutates data does not get to look like a bar that filters one. */
.toolbar--bulk { background: var(--surface-2); margin-bottom: var(--s5); }
.toolbar__group { display: flex; align-items: center; gap: var(--s2); margin: 0; }
.toolbar__group label { margin: 0; white-space: nowrap; font-weight: 500; color: var(--ink-mute); }
.toolbar__group .hint { margin: 0; }
.toolbar__legend { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-mute); font-weight: 600; }
.input--num { width: 4.5em; text-align: right; }
.bulk-bar__hint { font-size: var(--fs-xs); color: var(--ink-mute); margin: 0; }

.vstack { display: flex; flex-direction: column; gap: var(--s3); }
.row { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
.inline { display: inline; margin: 0; }
.grow { flex: 1 1 auto; min-width: 0; }
.muted { color: var(--ink-mute); }
.spacer { margin-left: auto; }

.ok, .error, .warn {
  padding: var(--s3) var(--s4); border-radius: var(--r-md);
  border: 1px solid; margin: 0 0 var(--s4); font-size: var(--fs-sm);
}
.ok { color: var(--ok); background: var(--ok-soft); border-color: var(--ok); }
.error, .err { color: var(--bad); background: var(--bad-soft); border-color: var(--bad); }
.warn { color: var(--warn); background: var(--warn-soft); border-color: var(--warn); }

/* ═══ 5. COMPONENTS ═══════════════════════════════════════════════════ */

/* ── hero ─────────────────────────────────────────────────────────────
   Flex, not a fixed two-column grid: the old grid reserved two thirds of the
   width for tiles that most pages never render, so every VIP page opened with
   a headline stranded beside a large empty column. */
.hero {
  display: flex; flex-wrap: wrap; gap: var(--s5);
  padding: var(--s5);
  margin: 0 0 var(--s5);
}
.hero__lead { flex: 1 1 240px; min-width: 0; }
.hero__tiles { flex: 2 1 420px; display: grid; gap: var(--s4); grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); }
/* These are <span>s in the template. Height, vertical margin and width on an
   inline box do nothing — which is how the hero's progress meter ended up as a
   pill floating over the panel below it. Blockify them explicitly. */
.hero__figure, .hero__label, .hero__sub { display: block; }
.hero__figure { font-size: var(--fs-fig); font-weight: 700; letter-spacing: -0.03em; line-height: 1.05; color: var(--ink); }
.hero__label { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-mute); font-weight: 600; margin-top: var(--s1); }
.hero__sub { margin-top: var(--s2); font-size: var(--fs-sm); color: var(--ink-soft); }
.hero__lead .meter { margin-top: var(--s3); max-width: 22rem; }

/* ── stat tiles ───────────────────────────────────────────────────────
   .kpi and .tile were two components doing one job, on adjacent steps of the
   same funnel. They are one look now; both names are kept so no template has
   to change. Nested inside a surface, so hairline only — no shadow. */
.kpis { display: grid; gap: var(--s4); grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); margin: 0 0 var(--s5); }
/* A fixed count beats auto-fit when the tiles are a fixed set: auto-fit reflows
   them to four-and-two at some widths, which reads as two unrelated groups. */
.kpis--three { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 640px) { .kpis--three { grid-template-columns: 1fr; } }
.kpi, .tile {
  padding: var(--s4);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  min-width: 0;
}
.hero__tiles .kpi, .hero__tiles .tile { background: var(--surface-2); }
.kpi span, .tile span { display: block; font-family: var(--font-display); font-size: var(--fs-fig); font-weight: 500; letter-spacing: -0.03em; line-height: 1.1; color: var(--ink); }
/* The dossier's tiles use <label> where the rest use <small>; both are the
   tile's caption and must look identical. */
.kpi small, .tile small, .kpi > label, .tile > label {
  display: flex; align-items: center; gap: var(--s1);
  font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--ink-mute); font-weight: 600; margin: var(--s1) 0 0;
}
.kpi.accent span, .tile.accent span { color: var(--accent); }

/* ── post card (step 1) ───────────────────────────────────────────────
   Cover beside the facts. The cover is square and clipped by the card's own
   radius, with a neutral fill behind it so a slow or failed load is a quiet
   tile rather than a broken-image glyph. */
.postcard {
  display: flex; gap: var(--s5); align-items: stretch;
  padding: var(--s4);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); box-shadow: var(--shadow);
  margin: 0 0 var(--s5);
}
/* The placeholder sits in the frame and the image covers it. Removing a failed
   <img> therefore reveals the tile underneath instead of collapsing the
   layout — no class toggling, no second code path. */
.postcard__frame {
  position: relative; flex: none;
  width: 160px; height: 160px;
  border-radius: var(--r-md); overflow: hidden;
  background: var(--surface-2);
}
.postcard__none {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--s2); padding: var(--s3); text-align: center;
  color: var(--ink-mute);
  border: 1px dashed var(--line-strong); border-radius: var(--r-md);
}
.postcard__none svg { width: 26px; height: 26px; opacity: 0.7; }
.postcard__none em {
  font-style: normal; font-size: var(--fs-2xs);
  text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600;
}
.postcard__cover {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block;
}
.postcard__facts {
  flex: 1 1 auto; min-width: 0; margin: 0;
  display: grid; grid-template-columns: max-content 1fr;
  gap: var(--s2) var(--s5); align-content: center;
  font-size: var(--fs-sm);
}
.postcard__facts dt { color: var(--ink-mute); font-size: var(--fs-xs); }
.postcard__facts dd { margin: 0; color: var(--ink); min-width: 0; overflow-wrap: anywhere; }

/* ── split bar ────────────────────────────────────────────────────────
   Three parts of one whole, so one bar rather than three counters: the
   question is how much of the review is still open, and separate numbers make
   the reader do that subtraction. Segments are flex-grown by their own value,
   with a surface gap between them so neighbouring fills stay distinct. */
.split { display: flex; height: 26px; border-radius: var(--r-sm); overflow: hidden; gap: 2px; background: var(--surface-2); }
.split__seg { display: block; min-width: 3px; cursor: default; }
.split__seg--yes { background: var(--ok); }
.split__seg--no { background: var(--bad); }
/* Distinctly lighter than the track, or the "not yet reviewed" share — usually
   the biggest one — reads as an empty bar. It stays a neutral, though: unset is
   the absence of a decision, not a third verdict. */
.split__seg--unset { background: color-mix(in srgb, var(--ink-mute) 65%, transparent); }
.split__key { list-style: none; margin: var(--s3) 0 0; padding: 0; display: flex; gap: var(--s5); flex-wrap: wrap; font-size: var(--fs-xs); color: var(--ink-soft); }
.split__key li { display: flex; align-items: center; gap: var(--s2); }
.split__swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }

/* ── panels ───────────────────────────────────────────────────────────── */
.panels { display: grid; gap: var(--s5); margin: 0 0 var(--s5); grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
/* A panel standing on its own carries its own bottom margin — .panels only
   spaced its children from each other, so a lone panel above a .panels grid
   sat flush against it. Inside the grid the gap already does the work. */
.panel { padding: var(--s4) var(--s5) var(--s5); min-width: 0; margin: 0 0 var(--s5); }
.panels > .panel { margin: 0; }
.panel--funnel { grid-column: 1 / -1; }
.panel__title { margin: 0 0 var(--s4); font-family: var(--font-display); font-size: var(--fs-2xs); color: var(--ink-mute); text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; }

/* ── charts ───────────────────────────────────────────────────────────
   Marks are thin, the grid is recessive, and labels take ink tokens rather
   than the series colour — a coloured mark beside a label already carries the
   identity, so colouring the text too just lowers its contrast. */
.chart { min-width: 0; }
.chart__note { margin: var(--s3) 0 0; font-size: var(--fs-2xs); color: var(--ink-mute); line-height: 1.45; }

/* `align-items: stretch` is load-bearing: with flex-end the columns shrank to
   their content, so .chart__track had no resolved height and the bars' own
   percentage heights computed to zero — every bar rendered as its 2px minimum.
   The columns must fill the plot height for a % height to mean anything. */
.chart__bars { display: flex; align-items: stretch; gap: var(--s2); height: 150px; }
.chart__col { flex: 1 1 0; display: flex; flex-direction: column; min-width: 0; cursor: default; border-radius: var(--r-sm); }
.chart__colvalue { font-size: var(--fs-2xs); font-weight: 600; color: var(--ink-soft); text-align: center; line-height: 1.3; flex: none; }
/* Column flex, bottom-aligned: the value is the item directly above the bar,
   so it rides at the bar's own height instead of at the top of the plot. */
.chart__track { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; justify-content: flex-end; border-bottom: 1px solid var(--viz-grid); }
.chart__bar {
  width: 100%; flex: none;
  /* Rounded only at the data end; the baseline end stays square so the bar
     reads as anchored to the axis rather than floating above it. */
  border-radius: var(--r-sm) var(--r-sm) 0 0;
  background: var(--band-3);
  min-height: 2px;
  transition: filter 0.14s ease;
}
.chart__col:hover .chart__bar, .chart__col:focus-visible .chart__bar { filter: brightness(1.15); }
.chart__collabel { font-size: var(--fs-2xs); color: var(--ink-mute); text-align: center; margin-top: var(--s2); white-space: nowrap; }
.band-1 { background: var(--band-1); } .band-2 { background: var(--band-2); }
.band-3 { background: var(--band-3); } .band-4 { background: var(--band-4); }
.band-5 { background: var(--band-5); }

.hbars { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s2); }
.hbars__row { display: grid; grid-template-columns: 8.5rem 1fr 2.5rem; align-items: center; gap: var(--s3); cursor: default; border-radius: var(--r-sm); }
.hbars__label { font-size: var(--fs-xs); color: var(--ink-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hbars__track { height: 9px; background: var(--surface-2); border-radius: var(--r-pill); overflow: hidden; }
.hbars__bar { display: block; height: 100%; background: var(--band-3); border-radius: var(--r-pill); min-width: 2px; }
.hbars__value { font-size: var(--fs-xs); font-weight: 600; color: var(--ink); text-align: right; }
.hbars__row:hover .hbars__bar { filter: brightness(1.15); }

/* ── funnel ───────────────────────────────────────────────────────────
   Stage names render as the stored identifier in monospace with a plain gloss
   underneath. Half-translated labels read worse than the English terms they
   came from, and the identifier is what the operator sees in the database. */
.funnel { display: flex; flex-direction: column; gap: var(--s3); }
/* Four columns, because the row has four children: label, track, count, rate.
   With three, the rate wrapped into an implicit second row and rendered under
   the label, looking like a stray number belonging to nothing. */
.funnel__row { display: grid; grid-template-columns: 11rem 1fr 4.5rem 3.5rem; align-items: center; gap: var(--s3); }
.funnel__label { font-family: ui-monospace, Menlo, monospace; font-size: var(--fs-xs); color: var(--ink-soft); overflow: hidden; text-overflow: ellipsis; }
.funnel__track { height: 22px; background: var(--surface-2); border-radius: var(--r-sm); overflow: hidden; }
.funnel__bar { display: block; height: 100%; background: var(--band-3); border-radius: 0 var(--r-sm) var(--r-sm) 0; min-width: 2px; }
.funnel__value { font-size: var(--fs-sm); font-weight: 700; color: var(--ink); text-align: right; }
.funnel__rate { font-size: var(--fs-2xs); color: var(--ink-mute); text-align: right; }

/* ── stepper ──────────────────────────────────────────────────────────
   The whole step is the hit target. The stretched-link ::before must anchor to
   the .step — when it anchored to the <a> (which had position: relative) only
   the text itself was clickable, 2 of 7 sample points across the row. */
.stepper { padding: var(--s4) var(--s5); margin: 0 0 var(--s5); }
.stepper__list { list-style: none; margin: 0; padding: 0; display: flex; }
.stepper__open { margin: var(--s3) 0 0; text-align: right; font-size: 0.85rem; }
.stepper__step { flex: 1 1 0; position: relative; min-width: 0; display: flex; }
.stepper__rail {
  position: absolute; top: 17px; left: 50%; right: -50%; height: 2px;
  background: var(--line); z-index: 0;
}
.stepper__step.is-done .stepper__rail { background: var(--accent); }

.stepper__hit {
  position: static;
  display: flex; flex-direction: column; align-items: center; gap: var(--s2);
  width: 100%; padding: 0 var(--s2);
  color: inherit; text-align: center; z-index: 1;
}
.stepper__hit:hover { text-decoration: none; }
/* Stretched link: anchored to the .step, not to the <a>. */
.stepper__step.is-clickable .stepper__hit::before {
  content: ""; position: absolute; inset: 0; border-radius: var(--r-md); z-index: 2;
}
.stepper__step.is-clickable:hover { cursor: pointer; }
.stepper__step.is-clickable:hover .stepper__node { border-color: var(--accent); color: var(--accent); }
.stepper__step.is-clickable:hover .stepper__title { color: var(--ink); }
.stepper__step.is-locked .stepper__hit { cursor: not-allowed; }

.stepper__node {
  position: relative; z-index: 1;
  display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; flex: none;
  border-radius: 50%;
  background: var(--surface);
  border: 2px solid var(--line-strong);
  color: var(--ink-mute);
  font-size: var(--fs-sm); font-weight: 700;
  transition: background 0.14s ease, border-color 0.14s ease, color 0.14s ease;
}
.stepper__node svg { width: 16px; height: 16px; }
.stepper__step.is-done .stepper__node { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.stepper__step.is-active .stepper__node { border-color: var(--accent); color: var(--accent); }
.stepper__step.is-current .stepper__node { box-shadow: var(--focus); }
.stepper__step.is-locked .stepper__node { background: var(--surface-2); border-color: var(--line); color: var(--ink-mute); }

.stepper__text { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.stepper__num { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-mute); font-weight: 600; }
.stepper__title { font-size: var(--fs-xs); font-weight: 600; color: var(--ink-soft); line-height: 1.3; }
.stepper__step.is-current .stepper__title { color: var(--ink); }
.stepper__step.is-locked .stepper__title { color: var(--ink-mute); }

/* ── tabs ─────────────────────────────────────────────────────────────── */
.tabs { display: flex; gap: var(--s1); border-bottom: 1px solid var(--line); margin: 0 0 var(--s5); }
.tabs__tab {
  background: transparent; border: none; border-bottom: 2px solid transparent;
  border-radius: 0;
  padding: var(--s3) var(--s4);
  color: var(--ink-mute); font-weight: 600; font-size: var(--fs-sm);
  margin-bottom: -1px;
}
.tabs__tab:hover { color: var(--ink); border-color: transparent; border-bottom-color: var(--line-strong); background: transparent; }
.tabs__tab.is-active { color: var(--accent); border-bottom-color: var(--accent); }
.tabs__count { font-size: var(--fs-2xs); color: var(--ink-mute); font-weight: 600; margin-left: var(--s1); }
.tabs__tab.is-active .tabs__count { color: var(--accent); }
.tabs__panel[hidden] { display: none; }

/* ── tri-state ────────────────────────────────────────────────────────
   One control for the three-way decision the product asks in three places.
   There used to be two: an icon version for relevance and a word version on
   the dossier, at different sizes with different padding. They are the same
   question, so they are the same control — see templates/_tristate.html. */
.relevance { display: inline-flex; border: 1px solid var(--line-strong); border-radius: var(--r-md); overflow: hidden; background: var(--surface-2); }
.relevance__btn {
  border: none; border-radius: 0; background: transparent;
  padding: 0.35rem 0.7rem; min-width: 38px;
  color: var(--ink-mute);
}
.relevance__btn svg { width: 15px; height: 15px; }
/* The separator lives on the button in both modes, never on the wrapping
   form: on the form it sits outside the button's border-box and the control
   comes out 2px wider than the same control rendered the other way. */
.relevance__btn { border-left: 1px solid var(--line); }
.relevance > :first-child .relevance__btn,
.relevance > .relevance__btn:first-child { border-left: none; }
.relevance__btn:hover { background: var(--surface); color: var(--ink); border-color: transparent; }
.relevance__btn:focus-visible { outline-offset: -2px; }
.relevance__btn--yes.is-active { background: var(--ok); color: #fff; }
.relevance__btn--no.is-active { background: var(--bad); color: #fff; }
.relevance__btn--unset.is-active { background: var(--line-strong); color: var(--ink); }
/* Each button sits in its own form in that mode; the form must not introduce
   a box of its own between the container and the button. */
.relevance form { display: flex; margin: 0; }

/* ── people: identity block ───────────────────────────────────────────
   The same author information appears on step 2 and step 3. It used to be
   styled on one page only — .author-cell had no rules at all — so the same
   person looked like two different components. Both names, one look. */
/* These five classes sit on <td> elements. A table cell must not be given
   `display: flex` — it stops being a cell, the row loses its column, and the
   browser wraps the rest in an anonymous cell, which showed up as a stray
   bordered box under some authors. Block children stack on their own. */
.who, .author-cell { min-width: 0; }
.who > * + *, .author-cell > * + * { margin-top: 2px; }
.who__handle, .author-cell__handle { font-family: ui-monospace, Menlo, monospace; font-size: var(--fs-sm); font-weight: 600; color: var(--ink); display: flex; align-items: center; gap: var(--s1); }
.who__handle svg, .author-cell__handle svg { width: 14px; height: 14px; flex: none; }
.who__name, .author-cell__name { font-size: var(--fs-xs); color: var(--ink-soft); }
.who__stats, .author-cell__stat { font-size: var(--fs-2xs); color: var(--ink-mute); }
.who__stats { display: flex; gap: var(--s2); flex-wrap: wrap; }
.who__bio { font-size: var(--fs-2xs); color: var(--ink-mute); display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; max-width: 34ch; }
.who__url { font-size: var(--fs-2xs); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 30ch; }
.who__refresh { font-size: var(--fs-2xs); }

.comments-cell > * + * { margin-top: var(--s1); }
.comments-cell__top { font-size: var(--fs-2xs); color: var(--ink-mute); font-weight: 600; }
.comments-cell__quote { font-size: var(--fs-xs); color: var(--ink-soft); border-left: 2px solid var(--line-strong); padding-left: var(--s2); }

.linkedin-cell > * + *, .relevance-cell > * + * { margin-top: var(--s1); }
.li-action { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; }
/* Destructive, but it sits on every worked row: quiet until reached for. */
.clear-linkedin-btn { color: var(--ink-mute); }
.clear-linkedin-btn:hover { background: var(--bad-soft); color: var(--bad); }
.li-meta, .li-feedback, .li-action { font-size: var(--fs-2xs); color: var(--ink-mute); }
/* A pause is a state, not a failure: no red box, just when it resumes. */
.li-feedback.wait { display: flex; align-items: center; gap: var(--s1); color: var(--ink-soft); }
.li-feedback.wait::before { content: ""; flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--warn); }
.li-result { font-size: var(--fs-xs); }
.ig-link { font-family: ui-monospace, Menlo, monospace; font-size: var(--fs-xs); }

.web-evidence { margin-top: var(--s2); }
.web-evidence__title { font-size: var(--fs-2xs); font-weight: 600; color: var(--ink-mute); }
.web-evidence__group { margin: var(--s2) 0 0; }
.web-evidence__group-label { font-size: var(--fs-2xs); font-weight: 600; color: var(--ink-soft); }
.web-evidence__list { list-style: none; margin: var(--s1) 0 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.web-evidence__domain { font-size: var(--fs-2xs); color: var(--ink-mute); }

/* ── dossier (step 5) ────────────────────────────────────────────────
   A table, like every other step. It used to be a stack of cards, one per
   subject, which meant comparing two people on the same attribute took a
   scroll; in columns "who still has no amount" is one glance down a column.
   The card-era rules (head, actions, group, money, person, who) are gone with
   it — .who and .who__* are shared with step 4 and still apply. */
.dossier__ident { font-size: var(--fs-2xs); color: var(--ink-mute); display: flex; gap: var(--s1); align-items: center; flex-wrap: wrap; margin-top: var(--s1); }
.dossier__template { margin: 0; font-size: var(--fs-sm); color: var(--ink-soft); }
.dossier__totals { margin: 0 0 var(--s3); }

/* One quote: score beside the text, never above it. */
.dossier__quotes { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s3); }
.dossier__quote {
  display: grid; grid-template-columns: 2.5rem 1fr; gap: var(--s3);
  align-items: start;
  border-left: 2px solid var(--line-strong); padding-left: var(--s3);
  font-size: var(--fs-sm); color: var(--ink-soft);
}
.dossier__score { font-size: var(--fs-sm); font-weight: 700; color: var(--accent); line-height: 1.5; text-align: right; }
.dossier__body { min-width: 0; }
/* pre-wrap belongs here, on the element holding the text — never on the grid
   above it, where the template's own indentation becomes anonymous grid items
   and opens 40px of dead space around every comment. */
.dossier__text { margin: 0; font-size: var(--fs-sm); color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; }
.dossier__meta { margin: var(--s1) 0 0; font-size: var(--fs-2xs); color: var(--ink-mute); display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; }

/* Cells. The quotes column carries the long text and so is the one that gets
   to be wide; the three action columns stay as narrow as their controls. */
.dossier__quotes-cell { min-width: 22rem; }
/* No white-space: nowrap on these cells. It was redundant — .money-form is
   flex and buttons set their own nowrap — and actively harmful once the
   <dialog> moved into .dossier__doc: the property inherited into the modal
   and stopped its inputs wrapping below their labels, pushing them 77px past
   the modal's own edge. */
.dossier__amount .money-form { margin: 0; align-items: center; }
.dossier__amount input { width: 7.5rem; }
.dossier__doc .money-form__warn { display: block; white-space: normal; max-width: 15rem; margin-top: var(--s2); }

/* ── money ────────────────────────────────────────────────────────────
   Two figures in the same unit, one a part of the other: a meter, because the
   only question is how much of the request arrived. */
.money { display: flex; flex-direction: column; gap: var(--s3); }
.money__figures { display: flex; gap: var(--s5); flex-wrap: wrap; }
.money__fig { display: flex; flex-direction: column; }
.money__value { font-size: var(--fs-xl); font-weight: 700; letter-spacing: -0.02em; color: var(--ink-soft); line-height: 1.15; }
.money__value--paid { color: var(--accent); }
.money__label { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-mute); font-weight: 600; }
.money__track, .meter { display: block; height: 10px; background: var(--surface-2); border-radius: var(--r-pill); overflow: hidden; }
.money__fill, .meter__fill { display: block; height: 100%; background: var(--accent); border-radius: var(--r-pill); }
.money__pct { margin: 0; font-size: var(--fs-2xs); color: var(--ink-mute); }
/* Sits inside a table cell now, so it separates with spacing rather than a
   box of its own. */
.money-form { display: flex; align-items: center; gap: var(--s2); margin: 0; }
.money-form input { width: 8rem; }
.money-form__warn { font-size: var(--fs-2xs); color: var(--warn); }
.money-form__when { font-size: var(--fs-2xs); color: var(--ink-mute); }
/* Rows the status redirect lands on (#person-…) would otherwise sit under the
   sticky top bar. */
tr[id^="person-"] { scroll-margin-top: 5rem; }

/* ── modal ────────────────────────────────────────────────────────────── */
.modal {
  /* The dialog is rendered wherever its trigger lives — currently a table
     cell — so it resets the inherited text properties it cares about rather
     than trusting its surroundings. */
  white-space: normal;
  border: 1px solid var(--line); border-radius: var(--r-lg);
  background: var(--surface); color: var(--ink);
  padding: var(--s5); max-width: 32rem; width: calc(100% - 2rem);
  box-shadow: var(--shadow);
}
.modal::backdrop { background: rgba(0, 0, 0, 0.55); backdrop-filter: blur(2px); }
.modal__title { margin: 0 0 var(--s3); font-size: var(--fs-lg); font-weight: 600; }
.modal__x { position: absolute; top: var(--s3); right: var(--s3); }
.modal__form { display: flex; flex-direction: column; gap: var(--s3); }
/* Caption above field, stated rather than left to inline wrapping. */
.modal__form label { display: flex; flex-direction: column; gap: var(--s1); margin: 0; }
.modal__form .row { gap: var(--s3); align-items: flex-end; }
.modal__actions { display: flex; gap: var(--s2); justify-content: flex-end; flex-wrap: wrap; }
.modal__warn { font-size: var(--fs-2xs); color: var(--ink-mute); }
/* The intro sits above the form, so it needs to clear it — .hint only carries
   a top margin, which is right under a control and wrong above one. */
.modal > .hint { margin-bottom: var(--s4); }
/* Evidence modal: lists of candidates and web hits want room, and a long
   candidate list scrolls inside the dialog rather than growing past it. */
.modal--wide { max-width: 46rem; max-height: 85vh; overflow: auto; }
.idm__section + .idm__section { margin-top: var(--s4); }
.idm__section h4 { margin: 0 0 var(--s2); font-size: var(--fs-2xs); font-weight: 600; color: var(--ink-mute); text-transform: uppercase; letter-spacing: 0.04em; }
.idm__section .web-evidence__group:first-of-type { margin-top: 0; }

/* ── VIP index ────────────────────────────────────────────────────────── */
.vips__name { font-weight: 600; color: var(--ink); }
.vips__note, .vips__when { font-size: var(--fs-2xs); color: var(--ink-mute); }
.vips__profiles { display: flex; gap: var(--s1); flex-wrap: wrap; }

/* ── Graph authorisation panel ────────────────────────────────────────
   This had no rules at all: the panel that carries the VIP's authorisation URL
   — the single most important thing on the account form — rendered as bare
   text on the page background. */
.graph-box { padding: var(--s4); margin: var(--s4) 0 0; display: flex; flex-direction: column; gap: var(--s2); }
.graph-box input { font-family: ui-monospace, Menlo, monospace; font-size: var(--fs-2xs); }

/* ── login ────────────────────────────────────────────────────────────── */
/* The one screen that carries the brand at full size: the horizon glow from
   the identity boards, behind a plain form. */
.login-body {
  display: grid; place-items: center; min-height: 100vh; padding: var(--s5);
  background:
    radial-gradient(ellipse 70% 45% at 50% 108%, color-mix(in srgb, var(--accent) 22%, transparent), transparent 70%),
    var(--ground);
}
.login-card { width: min(24rem, 100%); padding: var(--s6); }
.login-brand { display: flex; flex-direction: column; align-items: center; gap: var(--s3); margin-bottom: var(--s6); }
.login-brand .logo { color: var(--accent); filter: drop-shadow(0 0 14px color-mix(in srgb, var(--accent) 55%, transparent)); }
.login-brand .wordmark { font-size: 1.5rem; color: var(--ink); }
.login-brand__tag { font-family: var(--font-display); font-size: var(--fs-2xs); letter-spacing: 0.32em; text-transform: uppercase; color: var(--ink-mute); margin: 0; }

/* ── chart tooltip ────────────────────────────────────────────────────
   `display: flex` on the component beat the `hidden` attribute's UA default,
   so once shown the readout never went away. The [hidden] rule must live on
   the component itself to win that fight. */
.viz-tip {
  position: fixed; top: 0; left: 0; z-index: 50;
  display: flex; flex-direction: column; gap: 1px;
  padding: var(--s2) var(--s3);
  background: var(--surface); border: 1px solid var(--line-strong);
  border-radius: var(--r-md); box-shadow: var(--shadow);
  pointer-events: none; white-space: nowrap;
}
.viz-tip[hidden] { display: none; }
.viz-tip__value { font-size: var(--fs-sm); font-weight: 700; color: var(--ink); }
.viz-tip__label { font-size: var(--fs-2xs); color: var(--ink-mute); }

/* ── misc ─────────────────────────────────────────────────────────────── */
.spinner {
  width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid var(--line-strong); border-top-color: var(--accent);
  animation: spin 0.7s linear infinite; display: inline-block;
}
@keyframes spin { to { transform: rotate(360deg); } }
.htmx-indicator { display: none; }
.htmx-request .htmx-indicator, .htmx-request.htmx-indicator { display: inline-block; }

.kv { display: grid; grid-template-columns: max-content 1fr; gap: var(--s2) var(--s4); font-size: var(--fs-sm); margin: 0; }
.kv dt { color: var(--ink-mute); font-size: var(--fs-xs); }
.kv dd { margin: 0; color: var(--ink); min-width: 0; overflow-wrap: anywhere; }

.caption { font-size: var(--fs-xs); color: var(--ink-soft); }
.danger-form { display: inline; margin: 0; }

/* The "move the funnel forward" block that closes each step page. It is the
   page's conclusion, so it gets a rule above it and room to breathe. */
.advance {
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  margin: var(--s6) 0 0; padding: var(--s5) 0 0;
  border-top: 1px solid var(--line);
}
.advance--top { margin: 0 0 var(--s5); padding: 0 0 var(--s4); border-top: 0; border-bottom: 1px solid var(--line); }
.advance form { margin: 0; }
.advance .actions { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }
.advance .hint { flex-basis: 100%; }

@media (max-width: 720px) {
  .container { padding: var(--s4) var(--s4) var(--s6); }
  .stepper__list { flex-direction: column; gap: var(--s3); }
  .stepper__step { flex-direction: row; }
  .stepper__rail { display: none; }
  .stepper__hit { flex-direction: row; justify-content: flex-start; text-align: left; }
  .funnel__row { grid-template-columns: 8rem 1fr 3.5rem; }
  .hbars__row { grid-template-columns: 6rem 1fr 2.2rem; }
}

/* ── re-classify ──────────────────────────────────────────────────────
   A disclosure rather than a button in the row above: filling in missing
   verdicts and overwriting existing ones are different acts, and the second
   should take a deliberate click to even see. */
.classify__redo { margin-top: var(--s4); border-top: 1px solid var(--line); padding-top: var(--s3); }
.classify__redo summary { font-size: var(--fs-xs); font-weight: 600; color: var(--ink-mute); }
.classify__redo summary:hover { color: var(--ink); }
.classify__redo form { display: flex; align-items: flex-end; gap: var(--s2); flex-wrap: wrap; margin-top: var(--s3); }
.classify__redo label { margin: 0 0 var(--s1); }
.classify__redo input { width: 14rem; }

/* ── post cards (VIP index) ───────────────────────────────────────────
   The cover and the caption are what let an operator recognise a post; the
   shortcode only identifies it to the API. Whole card is one link, so the
   footer's chips sit outside it rather than nesting interactive elements. */
.postgrid {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--s4);
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
}
.postcard-sm {
  display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--r-lg); overflow: hidden;
  box-shadow: var(--shadow);
  transition: border-color 0.14s ease;
}
.postcard-sm:hover { border-color: var(--accent); }
.postcard-sm__link { display: block; color: inherit; }
.postcard-sm__link:hover { text-decoration: none; }
/* Square frame with the placeholder behind the image: a cover that 404s
   between render and load reveals the tile instead of collapsing the card. */
.postcard-sm__frame { position: relative; display: block; aspect-ratio: 1; background: var(--surface-2); }
.postcard-sm__none {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--ink-mute);
}
.postcard-sm__none svg { width: 28px; height: 28px; opacity: 0.55; }
.postcard-sm__cover { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.postcard-sm__body { display: block; padding: var(--s3) var(--s4) var(--s2); }
.postcard-sm__title {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: var(--fs-sm); font-weight: 600; color: var(--ink); line-height: 1.35;
}
.postcard-sm__meta { display: block; margin-top: var(--s1); font-size: var(--fs-2xs); color: var(--ink-mute); }
/* The footer is pinned to the bottom so cards in a row end together whatever
   their captions do, and stacked so nothing has to wrap. */
.postcard-sm__link { flex: 1 1 auto; }
.postcard-sm__foot {
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--s2);
  padding: var(--s2) var(--s4) var(--s4);
  font-size: var(--fs-2xs); margin-top: auto;
}
.postcard-sm__stat { color: var(--ink-mute); font-variant-numeric: tabular-nums; }
.postcard-sm__stat b { color: var(--accent); font-weight: 700; }

/* ── funnel stage chip ────────────────────────────────────────────────
   A post's position in the pipeline, coloured as a progression: the fill
   carries more of the accent the further along the work is. One hue, six
   steps — the same discipline as the chart ramps, because this is the same
   kind of quantity: ordered, not categorical.
 *
 * Mixing against --surface-2 rather than naming six literals means both themes
 * get a ramp that sits correctly on their own ground, and the accent stays the
 * single source of the hue. Ink flips to --accent-ink at the two strongest
 * steps, where the fill is too saturated to read dark text on.
 *
 * `new` is deliberately outside the ramp: nothing has happened yet, and giving
 * it a tint would suggest otherwise. */
.chip.stage { border-color: transparent; font-weight: 600; }
.chip.stage-new           { background: var(--surface-2); color: var(--ink-soft); border-color: var(--line); }
.chip.stage-comments_done { background: color-mix(in srgb, var(--accent) 14%, var(--surface-2)); color: var(--ink-soft); }
.chip.stage-persons_done  { background: color-mix(in srgb, var(--accent) 28%, var(--surface-2)); color: var(--ink); }
.chip.stage-lawyer_ready  { background: color-mix(in srgb, var(--accent) 48%, var(--surface-2)); color: var(--ink); }
.chip.stage-payments_done { background: color-mix(in srgb, var(--accent) 86%, var(--surface-2)); color: var(--accent-ink); }
.chip.stage-closed        { background: var(--accent); color: var(--accent-ink); }

/* Chips that mean an outcome, not a position. These used to borrow stage
   classes, which broke the moment the stages became a progression: "gestito"
   is a success, not an early step. */
.chip--ok    { background: var(--ok-soft); color: var(--ok); border-color: transparent; }
.chip--muted { background: var(--surface-2); color: var(--ink-mute); }

/* ── post filter ──────────────────────────────────────────────────────
   One toolbar: search, then two segmented controls (channel, stage) that
   combine, then the crawl action pushed to the far end. Segments are plain
   words with a count; the stage colours live on the cards, not here, so the
   bar reads as one quiet control rather than a row of badges. */
.postfilter { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; margin: 0 0 var(--s4); }
.postfilter > .muted { margin: 0; }
.postfilter__search { position: relative; display: flex; align-items: center; margin: 0; flex: 0 1 17rem; min-width: 12rem; }
.postfilter__search svg { position: absolute; left: 0.7rem; width: 15px; height: 15px; color: var(--ink-mute); pointer-events: none; }
.postfilter__search input { padding-left: 2.1rem; height: 2.1rem; border-radius: 999px; font-size: var(--fs-xs);
                            background: var(--surface); border-color: var(--line); }
.postfilter__search input::placeholder { color: var(--ink-mute); }
.postfilter__search input::-webkit-search-cancel-button { filter: grayscale(1) opacity(.5); }
.postfilter__group { display: inline-flex; align-items: center; gap: 2px; padding: 3px; border-radius: 999px;
                     background: var(--surface); border: 1px solid var(--line); }
.postfilter__btn {
  height: 1.75rem; padding: 0 0.8rem; border: 0; border-radius: 999px; background: transparent;
  font-size: var(--fs-xs); font-weight: 500; color: var(--ink-mute); gap: 0.35rem; white-space: nowrap;
}
.postfilter__btn:hover { background: transparent; color: var(--ink); }
.postfilter__btn.is-active { background: var(--surface-2); color: var(--ink); box-shadow: inset 0 0 0 1px var(--line-strong); }
.postfilter__n { font-size: var(--fs-2xs); color: var(--ink-mute); font-variant-numeric: tabular-nums; }
.postfilter__btn.is-active .postfilter__n { color: var(--ink-soft); }
.postfilter__crawl { margin-left: auto; gap: var(--s2); }

/* A card removed by the filter must actually leave the grid, or its track
   stays reserved and the layout keeps a hole where it was. */
.postcard-sm[hidden] { display: none; }

/* The mandate line sits above the hero as a quiet statement of fact; its
   absence is a .warn instead, because that is a condition to act on. */
.mandate-line { margin: 0 0 var(--s4); }

/* ── identification evidence ─────────────────────────────────────────
   The chosen match carries its reasons; the candidate list under it shows
   what was weighed and how. A namesake looks like a namesake here, and a
   face or model verdict is a labelled chip, not a hidden weight. */
.ev-why { list-style: none; margin: var(--s1) 0 0; padding: 0; font-size: var(--fs-2xs); color: var(--ink-mute); }
.ev-why li { padding-left: 0.9em; text-indent: -0.9em; }
.ev-why li::before { content: "· "; color: var(--ink-mute); }
.ev-cands { margin-top: var(--s2); }
.ev-cands summary { font-size: var(--fs-2xs); font-weight: 600; color: var(--ink-mute); }
.ev-cands summary:hover { color: var(--ink); }
.ev-list { list-style: none; margin: var(--s2) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--s2); }
.ev-item { padding: var(--s2) var(--s3); border-left: 2px solid var(--line); }
.ev-item.is-chosen { border-left-color: var(--accent); background: var(--accent-soft); border-radius: 0 var(--r-sm) var(--r-sm) 0; }
.ev-item__head { display: flex; align-items: center; gap: var(--s1); flex-wrap: wrap; font-size: var(--fs-2xs); }
.ev-src { font-family: ui-monospace, Menlo, monospace; font-weight: 500; }
.ev-src--self-declared { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
.ev-face--same, .ev-verdict--si { background: var(--ok-soft); color: var(--ok); border-color: transparent; }
.ev-face--near { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.ev-face--different, .ev-verdict--no { background: var(--bad-soft); color: var(--bad); border-color: transparent; }
.ev-face--unclear, .ev-verdict--incerto { color: var(--ink-mute); }


/* ═══ 6. LUMYRA REFINEMENT ════════════════════════════════════════════
   The identity boards are quiet and expensive: near-black ground, hairlines
   instead of boxes, light weights, wide-tracked capitals, one point of light.
   This layer moves the working UI toward that without touching layout: it
   only re-dresses the primitives above, so it can be read (or reverted) in
   one place. Later in the cascade, same specificity: these rules win. */

/* Ground: the horizon light, faint, from the top of the page. */
body {
  background:
    radial-gradient(ellipse 70% 38% at 50% -12%, color-mix(in srgb, var(--accent) 9%, transparent), transparent 72%),
    var(--ground);
  background-attachment: fixed;
}

/* Titles in a serif, as the billboard copy on the boards; sections in
   wide-tracked capitals. */
h1 { font-family: var(--font-serif); font-weight: 400; font-size: 2.1rem; letter-spacing: -0.005em; line-height: 1.15; }
h2, .panel__title, .kpi small, .tile small, .kpi > label, .tile > label,
.grid th, .table-scroll table th, .stepper__num, .login-brand__tag {
  font-family: var(--font-display); font-weight: 400; letter-spacing: 0.22em; font-size: 0.66rem;
}

/* Surfaces: a hairline and a breath of light at the top edge, not a box. */
.panel, .grid, .hero, .stepper, .login-card,
.toolbar, .bulk-bar, .graph-box, .tabs__panel > .chart {
  background: linear-gradient(180deg, color-mix(in srgb, var(--surface) 100%, var(--ink) 2.5%), var(--surface) 38%);
  border: 1px solid color-mix(in srgb, var(--ink) 8%, transparent);
  border-radius: 16px;
  box-shadow: 0 24px 60px -40px rgba(0, 0, 0, 0.9);
}
:root[data-theme="light"] .panel, :root[data-theme="light"] .grid, :root[data-theme="light"] .hero,
:root[data-theme="light"] .stepper, :root[data-theme="light"] .login-card, :root[data-theme="light"] .bulk-bar {
  box-shadow: 0 18px 44px -34px rgba(27, 31, 56, 0.45);
}

/* Figures: light and large; the tile itself dissolves into hairline columns. */
.kpi, .tile, .hero__tiles .kpi, .hero__tiles .tile {
  background: transparent; border: none; border-left: 1px solid color-mix(in srgb, var(--ink) 9%, transparent);
  border-radius: 0; padding: var(--s2) var(--s5);
}
.kpi span, .tile span { font-weight: 300; font-size: 2.4rem; letter-spacing: -0.02em; }

/* Buttons: pills. Secondary ones are outlines; the primary carries the light. */
button, .btn {
  border-radius: var(--r-pill);
  font-weight: 400; letter-spacing: 0.02em;
  padding: 0.5rem 1.15rem;
  background: transparent;
  border-color: color-mix(in srgb, var(--ink) 16%, transparent);
}
button:hover, .btn:hover { background: var(--accent-soft); border-color: color-mix(in srgb, var(--accent) 60%, transparent); color: var(--ink); }
.primary {
  background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 82%, #ffffff), var(--accent));
  border-color: transparent; color: var(--accent-ink); font-weight: 500;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 40%, transparent), 0 8px 28px -10px color-mix(in srgb, var(--accent) 70%, transparent);
}
.primary:hover { filter: brightness(1.06); color: var(--accent-ink); background: linear-gradient(180deg, color-mix(in srgb, var(--accent) 82%, #ffffff), var(--accent)); }
.btn-ghost, .icon-btn { border-color: transparent; }
.btn-ghost:hover, .icon-btn:hover { background: transparent; color: var(--accent); }

/* Chips: outlined, never filled. */
.chip { background: transparent; border: 1px solid color-mix(in srgb, currentColor 35%, transparent); font-weight: 500; letter-spacing: 0.02em; }
.chip.score, .cat-diffamazione, .cat-minaccia, .cat-insulto, .cat-altro, .cat-nessuna { background: transparent; border-color: color-mix(in srgb, currentColor 35%, transparent); }

/* Stepper: thin rings on a hairline; a finished step is a lit dot. */
.stepper__rail { height: 1px; top: 14px; }
.stepper__node { width: 28px; height: 28px; border-width: 1px; font-weight: 400; background: var(--surface); }
.stepper__node svg { width: 13px; height: 13px; }
.stepper__step.is-done .stepper__node {
  background: var(--surface); color: var(--accent); border-color: var(--accent);
  box-shadow: 0 0 14px color-mix(in srgb, var(--accent) 45%, transparent);
}
.stepper__title { font-family: var(--font-display); font-weight: 400; letter-spacing: 0.01em; }

/* Topbar: more air, navigation as tracked capitals. */
.topbar { padding: var(--s4) var(--s6); background: color-mix(in srgb, var(--ground) 82%, transparent); border-bottom-color: color-mix(in srgb, var(--ink) 7%, transparent); }
.topbar nav a { font-family: var(--font-display); font-size: 0.7rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink-soft); padding: var(--s2) var(--s3); }
.topbar nav a:hover { color: var(--accent); text-decoration: none; }

/* Funnel: labels in the display face, bars as thin light traces. */
.funnel__label { font-family: var(--font-display); font-weight: 300; font-size: var(--fs-sm); color: var(--ink-soft); }
.funnel__track, .hbars__track { height: 6px; background: color-mix(in srgb, var(--ink) 6%, transparent); border-radius: var(--r-pill); }
.funnel__bar, .hbars__bar { border-radius: var(--r-pill); background: linear-gradient(90deg, color-mix(in srgb, var(--accent) 45%, transparent), var(--accent)); }
.funnel__value, .hbars__value { font-weight: 400; }

/* Tables: hairline rows, header in tracked capitals, quiet hover. */
.grid th, .table-scroll table th { color: var(--ink-mute); background: transparent; }
.grid td, .table-scroll table td { border-color: color-mix(in srgb, var(--ink) 6%, transparent); }
.grid tbody tr:hover td { background: color-mix(in srgb, var(--accent) 4%, transparent); }

/* Tabs: tracked capitals, a lit underline. */
.tabs__tab { font-family: var(--font-display); font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase; font-weight: 400; }

/* Big figures elsewhere: the same light weight as the tiles. */
.hero__figure { font-family: var(--font-display); font-weight: 300; font-size: 2.4rem; letter-spacing: -0.02em; }
.hero__label, .money__label { font-family: var(--font-display); font-weight: 400; letter-spacing: 0.22em; font-size: 0.66rem; }
.money__value { font-family: var(--font-display); font-weight: 300; }
.money__track, .meter { height: 6px; background: color-mix(in srgb, var(--ink) 6%, transparent); }
.money__fill, .meter__fill { background: linear-gradient(90deg, color-mix(in srgb, var(--accent) 45%, transparent), var(--accent)); }

/* Tri-state: a tinted, ringed choice instead of a solid block of colour. */
.relevance { border-color: color-mix(in srgb, var(--ink) 12%, transparent); border-radius: var(--r-pill); overflow: hidden; background: transparent; }
.relevance__btn { border-left-color: color-mix(in srgb, var(--ink) 9%, transparent); border-radius: 0; padding-inline: 0.8rem; }
.relevance__btn:hover { background: color-mix(in srgb, var(--ink) 5%, transparent); }
.relevance__btn--yes.is-active   { background: color-mix(in srgb, var(--ok) 18%, transparent);  color: var(--ok);  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ok) 55%, transparent); }
.relevance__btn--no.is-active    { background: color-mix(in srgb, var(--bad) 18%, transparent); color: var(--bad); box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--bad) 55%, transparent); }
.relevance__btn--unset.is-active { background: color-mix(in srgb, var(--ink) 10%, transparent); color: var(--ink); }

/* Work remaining: red → orange → amber → green. Text retains the status. */
.chip.stage-new, .work-status--todo { --status-color: var(--bad); }
.chip.stage-comments_done, .chip.stage-persons_done,
.work-status--progress { --status-color: var(--warn); }
.chip.stage-lawyer_ready, .work-status--waiting { --status-color: var(--status-waiting); }
.chip.stage-payments_done, .chip.stage-closed,
.work-status--done { --status-color: var(--ok); }
.chip.stage, .chip.work-status {
  color: var(--status-color);
  background: color-mix(in srgb, var(--status-color) 12%, var(--surface));
  border-color: color-mix(in srgb, var(--status-color) 55%, transparent);
  box-shadow: none;
}

/* The glow marks the one action of a page. Repeated on every row of a table
   it stops meaning anything: in a row the primary is a quiet outline. */
.grid .primary, .table-scroll .primary {
  background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent) 55%, transparent);
}
.grid .primary:hover, .table-scroll .primary:hover { background: color-mix(in srgb, var(--accent) 24%, transparent); color: var(--ink); filter: none; }


/* ── home: the practice at a glance ─────────────────────────────────────
   The lead figure is money, set in the serif of the page titles: it is the
   one number the rest of the work exists to produce. */
.home-hero__money { font-family: var(--font-serif); font-weight: 400; font-size: 3.4rem; letter-spacing: -0.01em; color: var(--ink); }
.home-hero__tiles { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) { .home-hero__tiles { grid-template-columns: repeat(2, 1fr); } }
.tile span em { font-style: normal; font-size: 0.5em; color: var(--ink-mute); letter-spacing: 0; }

/* Pipeline: one bar, a segment per stage, lit more as the stage nears the
   payment — the same progression as the stage chips it sits above. */
.pipeline__bar { display: flex; gap: 3px; height: 8px; border-radius: var(--r-pill); overflow: hidden; margin: var(--s2) 0 var(--s4); }
.pipeline__seg { min-width: 6px; flex-basis: 0; }
.stage-seg-new           { background: color-mix(in srgb, var(--ink) 16%, transparent); }
.stage-seg-comments_done { background: color-mix(in srgb, var(--accent) 30%, transparent); }
.stage-seg-persons_done  { background: color-mix(in srgb, var(--accent) 50%, transparent); }
.stage-seg-lawyer_ready  { background: color-mix(in srgb, var(--accent) 72%, transparent); }
.stage-seg-payments_done { background: var(--accent); }
.stage-seg-closed        { background: var(--accent); box-shadow: 0 0 12px color-mix(in srgb, var(--accent) 60%, transparent); }
.pipeline__key { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: var(--s3) var(--s5); }
.pipeline__key li { display: flex; align-items: center; gap: var(--s2); }
.pipeline__key b { font-family: var(--font-display); font-weight: 300; font-size: var(--fs-lg); color: var(--ink); }
.pipeline__key li.is-empty { opacity: 0.45; }

/* ── report tab ─────────────────────────────────────────────────────── */
.report-form__row { display: flex; align-items: flex-end; gap: var(--s4); flex-wrap: wrap; margin: var(--s4) 0 var(--s2); }
.report-form__row label { display: flex; flex-direction: column; gap: var(--s1); font-size: var(--fs-2xs); letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-mute); font-family: var(--font-display); }
.report-form__row input[type="date"] { min-width: 11rem; color-scheme: dark; }
:root[data-theme="light"] .report-form__row input[type="date"] { color-scheme: light; }

/* Batch crawl of an artist's new posts (VIP page, Post tab). */
.crawlbatch { margin-bottom: var(--s4); padding: var(--s3) var(--s4); border: 1px solid var(--line); border-radius: 12px; background: var(--surface); }
.crawlbatch__head h3 { margin: 0 0 var(--s1); font-size: var(--fs-sm); }
.crawlbatch__head p { margin: 0 0 var(--s2); font-size: var(--fs-xs); max-width: 70ch; }
.crawlbatch__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--s1); }
.crawlbatch__list li { display: flex; align-items: baseline; gap: var(--s2); font-size: var(--fs-xs); }
.crawlbatch__list label { display: flex; flex-direction: column; gap: 2px; cursor: pointer; }
.crawlbatch__list .is-known { opacity: .55; }
.crawlbatch__list .is-error { color: var(--bad); }
.crawlbatch__check { width: 1.2em; flex: none; text-align: center; font-variant-numeric: tabular-nums; }
.crawlbatch__actions { display: flex; gap: var(--s2); margin-top: var(--s3); }

/* Channel tag beside a handle: where a comment or a person comes from. */
.chan { display: inline-block; margin-right: 4px; padding: 0 4px; border-radius: 4px; font-size: 10px; font-weight: 700;
        letter-spacing: .04em; line-height: 1.5; vertical-align: 1px; border: 1px solid currentColor; opacity: .85; }
.chan--ig { color: var(--ink-mute); }
.chan--tt { color: var(--accent, #7b8cff); }


/* Classification progress on a post card: thin track, the share done, and
   whether the worker is on it now or the post waits its turn. */
.classifybar { margin-top: calc(-1 * var(--s2)); padding: 0 var(--s4) var(--s4); display: flex; flex-direction: column; gap: 6px; }
.classifybar__track { height: 4px; border-radius: 999px; background: var(--surface-2); overflow: hidden; }
.classifybar__fill { display: block; height: 100%; border-radius: inherit; background: var(--accent); transition: width .6s ease; }
.classifybar.is-queued .classifybar__fill { background: var(--ink-mute); }
.classifybar__label { display: flex; justify-content: space-between; font-size: var(--fs-2xs); color: var(--ink-mute); font-variant-numeric: tabular-nums; }
.classifybar__label b { color: var(--ink-soft); font-weight: 600; }
.classifybar.is-running .classifybar__state { color: var(--accent); }
@media (prefers-reduced-motion: reduce) { .classifybar__fill { transition: none; } }

/* Post picker: every post the channels list, the ones already downloaded
   greyed, the new ones selectable. */
.pickposts__head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s3); margin-bottom: var(--s3); }
.pickposts__head h3 { margin: 0 0 2px; font-size: var(--fs-sm); }
.pickposts__head p { margin: 0; font-size: var(--fs-xs); }
.pickposts__head b { color: var(--accent); }
.pickposts__scroll { overflow-x: auto; }
.pickposts { width: 100%; border-collapse: collapse; font-size: var(--fs-xs); }
.pickposts th { text-align: left; font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .06em; color: var(--ink-mute);
                font-weight: 600; padding: 0 var(--s2) var(--s2); border-bottom: 1px solid var(--line); white-space: nowrap; }
.pickposts td { padding: var(--s2); border-bottom: 1px solid var(--line); vertical-align: middle; white-space: nowrap; }
.pickposts .num { text-align: right; font-variant-numeric: tabular-nums; }
.pickposts__sel { width: 1.5rem; }
.pickposts__sel input { width: auto; margin: 0; accent-color: var(--accent); }
.pickposts__thumb { width: 104px; padding-right: 0; }
.pickposts__frame { position: relative; display: block; width: 96px; height: 96px; border-radius: 8px; overflow: hidden; background: var(--surface-2); }
.pickposts__ph { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; padding: 6px; text-align: center; color: var(--ink-mute); }
.pickposts__ph svg { width: 26px; height: 26px; opacity: .7; }
.pickposts__ph em { font-style: normal; font-size: var(--fs-2xs); line-height: 1.2; }
.pickposts__thumb img { position: absolute; inset: 0; display: block; width: 96px; height: 96px; object-fit: cover; border-radius: 8px; }
.pickposts__cap { white-space: normal; min-width: 16rem; color: var(--ink); }
.pickposts__cap a { color: inherit; }
.pickposts tr.is-known td { color: var(--ink-mute); }
.pickposts tr.is-known .pickposts__cap { color: var(--ink-soft); }
.pickposts__state { font-size: var(--fs-2xs); font-weight: 600; padding: 2px 8px; border-radius: 999px; }
.pickposts__state.is-new { color: var(--accent); background: color-mix(in srgb, var(--accent) 14%, transparent); }
.pickposts__state.is-done { color: var(--ink-mute); background: var(--surface-2); }
.pickposts__note { margin: var(--s3) 0 0; font-size: var(--fs-2xs); max-width: 80ch; line-height: 1.55; }

/* Operator verdict on an identification (identification modal). */
.idm__verdict { display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap; margin-top: var(--s2); font-size: var(--fs-xs); }
.btn-xs { padding: 2px 10px; font-size: var(--fs-2xs); }

.id-score { font-size: var(--fs-2xs); color: var(--ink-mute); font-variant-numeric: tabular-nums; }

/* Commenter photo: the portal's own copy, initials underneath as fallback. */
.avatar { position: relative; display: inline-grid; place-items: center; flex: none; width: var(--av); height: var(--av);
          border-radius: 50%; overflow: hidden; background: var(--surface-2); vertical-align: middle; margin-right: 8px; }
.avatar__initials { font-size: calc(var(--av) * .38); font-weight: 600; color: var(--ink-mute); letter-spacing: .02em; }
.avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.who__handle, .author-cell__handle { display: flex; align-items: center; flex-wrap: wrap; }

/* Restricted post (age / sensitivity limits): its own state, not "no image". */
.postcard-sm__none--restricted, .postcard__none--restricted { flex-direction: column; gap: 8px;
  background: repeating-linear-gradient(135deg, var(--surface-2) 0 10px, color-mix(in srgb, var(--surface-2) 70%, transparent) 10px 20px); }
.postcard-sm__none--restricted svg, .postcard__none--restricted svg { opacity: .8; color: var(--warn); }
.postcard-sm__none--restricted em, .postcard__none--restricted em { font-style: normal; font-size: var(--fs-2xs); font-weight: 600;
  color: var(--ink-soft); text-transform: uppercase; letter-spacing: .06em; text-align: center; padding: 0 8px; }

/* The app icon as the brand mark: its own rounded tile, not themed. */
.logo--icon { display: inline-block; flex: none; border-radius: 22%; object-fit: cover; vertical-align: middle; }

.pickposts__others { margin-top: var(--s4); padding-top: var(--s3); border-top: 1px dashed var(--line); font-size: var(--fs-xs); color: var(--ink-soft); }
.pickposts__others h4 { margin: 0 0 4px; font-size: var(--fs-xs); }
.pickposts__others p { margin: 0 0 6px; font-size: var(--fs-2xs); }
.pickposts__others ul { margin: 0; padding-left: 1rem; display: flex; flex-direction: column; gap: 3px; }

.li-namesakes { list-style: none; margin: var(--s1) 0 var(--s2); padding: 0; display: flex; flex-direction: column; gap: var(--s1); font-size: var(--fs-2xs); }
.li-namesakes li { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; }
.li-namesakes__what { flex-basis: 100%; }

/* ── Identity review ─────────────────────────────────────────────── */
.idr { display: grid; grid-template-columns: minmax(220px, 280px) minmax(0, 1fr); gap: var(--s5); align-items: start; }
@media (max-width: 900px) { .idr { grid-template-columns: 1fr; } }
.idr-reviewer { display: flex; align-items: center; gap: var(--s2); font-size: var(--fs-xs); color: var(--ink-mute); }
.idr-reviewer input { width: 12rem; }
.idr-reviewer input.is-missing { border-color: var(--bad); box-shadow: 0 0 0 2px var(--bad-soft); }
.idr-summary { margin: 0 0 var(--s4); }
.idr-keys { margin-left: var(--s3); font-size: var(--fs-2xs); }
.idr-keys kbd { font-family: ui-monospace, Menlo, monospace; border: 1px solid var(--line-strong); border-radius: 4px; padding: 0 4px; }

.idq { padding: var(--s2); position: sticky; top: var(--s4); max-height: calc(100vh - 2 * var(--s4)); overflow-y: auto; margin: 0; }
.idq__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.idq__item a { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: var(--s2);
  padding: 6px var(--s2); border-radius: var(--r-sm); color: var(--ink); text-decoration: none; }
.idq__item a:hover { background: var(--surface-2); }
.idq__item.is-current a { background: var(--accent-soft); }
.idq__who { display: flex; flex-direction: column; min-width: 0; font-size: var(--fs-xs); }
.idq__who b, .idq__who span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.idq__who b { font-weight: 600; }
.idq__who span { color: var(--ink-mute); font-size: var(--fs-2xs); }
.idq__state { font-size: var(--fs-xs); width: 1.2em; text-align: center; color: var(--accent); }
.idq__item--confirmed .idq__state { color: var(--ok); }
.idq__item--rejected .idq__state { color: var(--bad); }
.idq__item--excluded { opacity: .55; }
.idq__item--excluded .idq__state { color: var(--ink-mute); }

.idr-main { display: flex; flex-direction: column; gap: var(--s4); min-width: 0; }
.idr-main > .panel { margin: 0; }
.idr-person__head { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: var(--s4); align-items: start; }
.idr-person__id h2 { margin: 0 0 4px; font-size: var(--fs-lg, 1.25rem); }
.idr-person__id > div { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; }
.idr-person__stats { font-size: var(--fs-xs); margin-top: 4px; }
.idr-person__bio { margin: var(--s2) 0 0; font-size: var(--fs-sm); white-space: pre-line; max-width: 60ch; }
.idr-person__outcome { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; font-size: var(--fs-xs); }
.idr-person__comments { margin-top: var(--s4); border-top: 1px solid var(--line); padding-top: var(--s3); }
.idr-person__comments h4, .idr-cands__title { margin: 0 0 var(--s2); font-size: var(--fs-2xs); font-weight: 600; color: var(--ink-mute); text-transform: uppercase; letter-spacing: 0.04em; }
.idr-person__comments blockquote { margin: 0 0 var(--s2); padding-left: var(--s3); border-left: 2px solid var(--accent); font-size: var(--fs-sm); }
.idr-person__more { margin-top: var(--s3); font-size: var(--fs-xs); }
.idr-person__more summary { cursor: pointer; color: var(--ink-mute); }
.idr-person__more ul { margin: var(--s2) 0 0; padding-left: 1rem; }

.idr-cands { display: flex; flex-direction: column; gap: var(--s3); }
.idc { display: grid; grid-template-columns: 72px minmax(0, 1fr); gap: var(--s4); margin: 0; border-left: 3px solid transparent; }
.idc--same { border-left-color: var(--ok); }
.idc--different { border-left-color: var(--bad); opacity: .72; }
.idc--unsure { border-left-color: var(--warn); }
.idc__photo { position: relative; width: 72px; height: 72px; border-radius: var(--r-md); background: var(--surface-2); overflow: hidden;
  display: flex; align-items: center; justify-content: center; }
.idc__photo img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.idc__nophoto { font-family: ui-monospace, Menlo, monospace; font-size: var(--fs-2xs); color: var(--ink-mute); }
.idc__head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); font-size: var(--fs-xs); }
.idc__head a { font-weight: 500; overflow-wrap: anywhere; }
.idc__what { margin: var(--s2) 0 0; font-size: var(--fs-sm); color: var(--ink-soft); }
.li-verdict { display: flex; gap: var(--s2); flex-wrap: wrap; margin-top: var(--s2); }
.idr-person__why { margin: var(--s2) 0 0; }
.idr-person__sub { margin: var(--s3) 0 var(--s1); font-size: var(--fs-xs); color: var(--ink-soft); }
details.idc { display: block; }
details.idc > summary.idc__head { cursor: pointer; list-style: none; display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); }
details.idc > summary.idc__head::-webkit-details-marker { display: none; }
details.idc > summary.idc__head::before { content: "▸"; color: var(--ink-mute); transition: transform .15s; }
details.idc[open] > summary.idc__head::before { transform: rotate(90deg); }
details.idc:not([open]) { padding-top: var(--s2); padding-bottom: var(--s2); }
.idc__inner { display: flex; gap: var(--s4); margin-top: var(--s3); }
.idc__sumwhat { font-size: var(--fs-2xs); flex-basis: 100%; padding-left: 1.2em; }
details.idc[open] .idc__sumwhat { display: none; }
.idc__clues { list-style: none; margin: var(--s2) 0 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px; }
.idc__clue { font-size: var(--fs-2xs); border: 1px solid var(--line); border-radius: var(--r-pill); padding: 2px 10px; color: var(--ink-soft); }
.idc__clue em { font-style: normal; color: var(--ink-mute); margin-left: 4px; }
.idc__clue--yes { border-color: transparent; background: var(--ok-soft); color: var(--ok); }
.idc__clue--no { color: var(--ink-mute); }
.idc__clue--no span { color: var(--bad); }
.idc__why { margin-top: var(--s2); font-size: var(--fs-2xs); }
.idc__why summary { cursor: pointer; color: var(--ink-mute); }
.idc__verdict { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); margin-top: var(--s3); font-size: var(--fs-2xs); }
.idc__note { flex: 1 1 14rem; min-width: 10rem; font-size: var(--fs-2xs); padding: 4px 8px; }
.idc__given { font-weight: 600; padding: 2px 8px; border-radius: var(--r-pill); }
.idc__given--same { background: var(--ok-soft); color: var(--ok); }
.idc__given--different { background: var(--bad-soft); color: var(--bad); }
.idc__given--unsure { background: var(--warn-soft); color: var(--warn); }

.idr-tools { display: flex; flex-direction: column; gap: var(--s3); }
.idr-tools form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--s2); }
.idr-tools label { display: flex; flex-direction: column; gap: 4px; flex: 1 1 20rem; font-size: var(--fs-2xs); color: var(--ink-mute); }
.idr-tools .htmx-indicator { display: none; font-size: var(--fs-2xs); }
.idr-tools .htmx-request .htmx-indicator, .idr-tools .htmx-request.htmx-indicator { display: inline; }
.idr-history summary { cursor: pointer; font-size: var(--fs-xs); color: var(--ink-mute); }
.idr-history ol { margin: var(--s2) 0 0; padding-left: 1.2rem; font-size: var(--fs-xs); display: flex; flex-direction: column; gap: 4px; }
.idr-hyps { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); font-size: var(--fs-2xs); color: var(--ink-mute); }
.idr-hyps form { display: inline; }
.idr-lens { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s2); margin-top: var(--s2); font-size: var(--fs-2xs); }
.lens-link { font-size: var(--fs-2xs); text-decoration: none; }

.idr-dialog { width: min(1440px, 96vw); height: 92vh; max-height: 92vh; padding: 0; border: 1px solid var(--line-strong);
  border-radius: var(--r-lg); background: var(--ground); color: var(--ink); overflow: hidden; }
.idr-dialog[open] { display: flex; flex-direction: column; }
.idr-dialog::backdrop { background: rgba(5, 6, 12, 0.66); }
.idr-dialog__head { display: flex; align-items: center; justify-content: space-between; gap: var(--s3);
  padding: var(--s3) var(--s5); border-bottom: 1px solid var(--line); }
.idr-dialog__head h2 { margin: 0; font-size: var(--fs-lg); }
.idr-dialog__body { flex: 1; overflow-y: auto; padding: var(--s4) var(--s5) var(--s6); }
.idr-dialog .idq { top: 0; max-height: calc(92vh - 140px); }
.crawl-wait { display: none; align-items: center; gap: var(--s2); font-size: var(--fs-xs); margin: 0 0 var(--s3); }
.crawl-wait.htmx-request { display: flex; }
.bulk-bar__why { font-size: var(--fs-xs); }
.idr-person__comments blockquote { white-space: pre-wrap; overflow-wrap: anywhere; }
.money-form input.is-proposal { border-style: dashed; color: var(--ink-soft); }
.money-form__proposal { margin-top: var(--s1); font-size: var(--fs-2xs); color: var(--ink-mute); max-width: 26rem; }
.money-form__proposal ul { margin: 2px 0 0; padding-left: 1rem; }

/* Contract draft form (artist page, «Contratto» tab) */
.contract-form { display: grid; gap: var(--s4); max-width: 46rem; }
.contract-form fieldset { border: 1px solid var(--line); border-radius: var(--r-md, 10px); padding: var(--s3) var(--s4); display: grid; gap: var(--s3); }
.contract-form legend { padding: 0 var(--s2); color: var(--ink-soft); font-size: var(--fs-xs); }
.contract-form label { display: grid; gap: 4px; font-size: var(--fs-xs); color: var(--ink-soft); }
.contract-form__check { display: flex !important; align-items: center; gap: var(--s2) !important; }

/* Editor's identification notes */
.idr-notes form { display: grid; gap: var(--s2); }
.idr-notes label { display: grid; gap: 2px; }
.idr-notes textarea { width: 100%; font: inherit; }
.idr-notes button { justify-self: start; }
.li-notes { margin: var(--s1) 0 0; font-size: var(--fs-2xs); color: var(--ink-soft); white-space: pre-line; }

/* Keep the sorting select inside its own grid cell at every viewport width. */
.comment-sort { display: grid; grid-template-columns: auto minmax(0, 18rem) auto; align-items: center; gap: var(--s2); margin: 0; max-width: 100%; }
.comment-sort label { margin: 0; white-space: nowrap; }
.comment-sort select { min-width: 0; width: 100%; box-sizing: border-box; }
.comment-sort button { white-space: nowrap; }
@media (max-width: 600px) {
  .comment-sort { width: 100%; grid-template-columns: minmax(0, 1fr) auto; }
  .comment-sort label { grid-column: 1 / -1; }
  .postfilter__group { max-width: 100%; flex-wrap: wrap; border-radius: var(--r-md); }
}

.post-refresh-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 20px; margin: 16px 0; }
.post-refresh-actions .btn, .comment-sort button { box-sizing: border-box; min-height: 42px; padding: 10px 20px; flex-shrink: 0; }
.post-refresh-actions .hint { margin: 0; flex: 1 1 22rem; }
.comment-sort select { min-height: 42px; }

.post-monitor { margin: 16px 0 24px; padding: 14px 18px; border: 1px solid var(--line); border-radius: var(--r-md); }
.post-monitor__summary { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; }
.post-monitor__review { margin-left: auto; padding: 8px 16px; }
.post-monitor__details { margin-top: 10px; font-size: var(--fs-xs); color: var(--ink-mute); }
.post-monitor__details summary { cursor: pointer; width: fit-content; }
.post-monitor__details > div { padding-top: 8px; }
.post-monitor__details p { margin: 0 0 8px; }
.post-monitor > .warn { margin: 10px 0 0; }
@media (max-width: 600px) { .post-monitor__review { margin-left: 0; } }

.review-task { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 16px 24px; padding: 20px; margin: 16px 0; border: 1px solid var(--line-strong); border-radius: var(--r-md); }
.review-task h2 { margin: 0 0 8px; }
.review-task p { margin: 6px 0 0; }
.review-task .btn { padding: 10px 20px; min-height: 42px; box-sizing: border-box; }
.review-filters { margin: 12px 0 20px; }
.review-filters summary { cursor: pointer; color: var(--ink-mute); }
.post-monitor__summary form { margin: 0; }

.review-finish-actions { display:flex; flex-wrap:wrap; gap:12px; margin:20px 0; }
.review-finish-actions button { padding:10px 20px; min-height:44px; white-space:normal; }

/* Keep the existing dossier actions separate from later comment reviews. */
.persons-next-actions { width: 100%; display: grid; gap: 12px; }
.persons-next-actions h2, .persons-next-actions p { margin: 0; }
.persons-workflow-actions { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }
.persons-review-notice { padding: 12px 16px; border-left: 2px solid var(--line); color: var(--ink-mute); }
.persons-next-actions > .btn, .persons-next-actions > form { justify-self: start; }

.comment-source-link { margin-top: 4px; font-size: .78rem; line-height: 1.3; white-space: normal; }
.comment-source-link a { display: inline-flex; align-items: center; white-space: nowrap; overflow-wrap: normal; }

/* Step 3: what a reply answers. Not pre-wrap like the comment it sits under:
   it is context, not evidence. */
td.text .reply-to { white-space: normal; margin-top: var(--s1); padding-left: var(--s2);
  border-left: 2px solid var(--line-strong); font-size: var(--fs-xs); color: var(--ink-mute); }
td.text .reply-to--artist { border-left-color: var(--accent); }
td.text .reply-to b { color: var(--ink-soft); font-weight: 600; }

/* First-access instructions stay readable on narrow login cards. */
.login-setup { margin-bottom: var(--s4); padding: var(--s3); border: 1px solid var(--line-strong); border-radius: var(--r-md); font-size: var(--fs-sm); }
.login-setup p { margin: var(--s2) 0; }
.login-setup code { display: block; white-space: normal; overflow-wrap: anywhere; }
