/* ==========================================================================
   COMPONENT — Verification board: the sticky summary, claim cards, verdict
   chips, calibrated confidence, document integrity, per-claim failure + retry.

   State is NEVER carried by colour alone. Every verdict chip and every stance
   marker pairs a glyph with a text label; colour is the third channel.
   ========================================================================== */

.claims-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 0.9rem;
}
.claims-header h3 { margin: 0; color: var(--text-strong); font-size: var(--text-xl); }
.claims-header-info { display: flex; align-items: baseline; gap: 0.6rem; flex-wrap: wrap; }

.claims-count {
    font-size: var(--text-sm);
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   STICKY BOARD SUMMARY

   A long board answers three questions badly: how did this document do, which
   claims are the problems, and can I trust this one verdict. Scrolling used to
   destroy the first two — the count and the integrity meter were in a header
   that left the viewport on the first scroll and never came back.

   This bar does not leave. It carries the integrity score, a tally per verdict
   where every tally is a BUTTON that jumps to the first claim of that verdict,
   and a next-problem control that walks the disputed / unsupported /
   insufficient claims in board order. It is opaque, because it slides over
   content, and it parks below the site nav rather than under it.
   ========================================================================== */
.board-summary {
    /* -3px so the bar's top edge tucks UNDER the site nav rather than leaving
       a hairline gap for content to scroll through. The nav is z-50 (its own
       stacking context) and this is z-20, so the nav wins that overlap, which
       is the order we want. */
    position: sticky;
    top: calc(var(--nav-h) - 3px);
    z-index: var(--z-sticky);
    display: flex;
    align-items: center;
    gap: 0.9rem 1.1rem;
    flex-wrap: wrap;
    margin: 0 0 1rem;
    padding: 0.65rem 0.9rem;
    background: var(--surface-3);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
}
.board-summary[hidden] { display: none; }

/* ---- Document integrity ------------------------------------------------ */
.integrity-meter {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-shrink: 0;
}
.integrity-score {
    font-size: var(--text-2xl);
    font-weight: var(--fw-bold);
    line-height: 1;
    color: var(--text-strong);
    font-variant-numeric: tabular-nums;
    min-width: 2.6ch;
}
.integrity-copy { display: flex; flex-direction: column; gap: 0.2rem; }
.integrity-label {
    font-size: var(--text-3xs);
    font-weight: var(--fw-bold);
    text-transform: uppercase;
    letter-spacing: var(--ls-caps);
    color: var(--text-tertiary);
    white-space: nowrap;
}
.integrity-bar {
    width: 108px;
    height: 5px;
    background: var(--surface-input);
    border-radius: var(--radius-pill);
    overflow: hidden;
}
.integrity-fill {
    height: 100%;
    width: 0;
    background: var(--verdict-insufficient-solid);
    border-radius: var(--radius-pill);
    transition: width 0.8s var(--ease-out), background var(--duration-base) var(--ease-out);
}
.integrity-fill.v-supported   { background: var(--verdict-supported-solid); }
.integrity-fill.v-disputed    { background: var(--verdict-disputed-solid); }
.integrity-fill.v-unsupported { background: var(--verdict-unsupported-solid); }

.board-divider {
    width: 1px;
    align-self: stretch;
    background: var(--border-default);
}

/* ---- Verdict tally ----------------------------------------------------- */
.board-tally {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    flex-wrap: wrap;
    flex: 1;
    min-width: 0;
}

.tally-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    min-height: 30px;
    padding: 0.2rem 0.6rem;
    font-family: inherit;
    font-size: var(--text-2xs);
    font-weight: var(--fw-semibold);
    border-radius: var(--radius-pill);
    border: 1px solid transparent;
    background: transparent;
    cursor: pointer;
    transition: background var(--duration-fast) var(--ease-out),
                border-color var(--duration-fast) var(--ease-out);
}
.tally-chip .tally-n { font-variant-numeric: tabular-nums; font-weight: var(--fw-bold); }
.tally-chip .tally-label { margin-left: 0.3rem; }
.tally-chip:hover { border-color: currentColor; }

.tally-chip.v-supported    { color: var(--verdict-supported-fg);    background: var(--verdict-supported-bg);    border-color: var(--verdict-supported-border); }
.tally-chip.v-disputed     { color: var(--verdict-disputed-fg);     background: var(--verdict-disputed-bg);     border-color: var(--verdict-disputed-border); }
.tally-chip.v-unsupported  { color: var(--verdict-unsupported-fg);  background: var(--verdict-unsupported-bg);  border-color: var(--verdict-unsupported-border); }
.tally-chip.v-insufficient { color: var(--verdict-insufficient-fg); background: var(--verdict-insufficient-bg); border-color: var(--verdict-insufficient-border); }
.tally-chip.v-pending      { color: var(--verdict-pending-fg);      background: var(--verdict-pending-bg);      border-color: var(--verdict-pending-border); }

.board-jump {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    min-height: 30px;
    margin-left: auto;
    padding: 0.2rem 0.7rem;
    font-family: inherit;
    font-size: var(--text-2xs);
    font-weight: var(--fw-semibold);
    color: var(--text-primary);
    background: var(--surface-4);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-pill);
    cursor: pointer;
    white-space: nowrap;
}
.board-jump:hover { border-color: var(--border-interactive); }
.board-jump[hidden] { display: none; }

/* ---- Run progress ------------------------------------------------------ */
.run-progress {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    font-size: var(--text-2xs);
    color: var(--text-secondary);
    font-variant-numeric: tabular-nums;
    flex: 0 1 190px;
    min-width: 130px;
}
.run-progress-bar {
    flex: 1;
    height: 4px;
    background: var(--surface-input);
    border-radius: var(--radius-pill);
    overflow: hidden;
}
.run-progress-fill {
    height: 100%;
    width: 0;
    background: var(--accent-fg);
    transition: width var(--duration-base) var(--ease-out);
}
.run-progress[hidden] { display: none; }

/* ---- Disclaimer + calibration note ------------------------------------- */
.verify-disclaimer {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    font-size: var(--text-2xs);
    line-height: var(--lh-snug);
    color: var(--text-secondary);
    background: var(--accent-bg);
    border: 1px solid var(--accent-border);
    border-radius: var(--radius-md);
    padding: 0.55rem 0.8rem;
    margin-bottom: 1rem;
}
.verify-disclaimer i { color: var(--accent-fg); margin-top: 0.15em; }

/* ---- Claim card -------------------------------------------------------- */
/* The board is a real <ul> of <li> cards — it is a list, and screen readers
   should be told how many items are in it. Bootstrap's reboot leaves the
   marker and indent on, so both are cleared here. */
.claims-list {
    display: flex;
    flex-direction: column;
    gap: 0.85rem;
    list-style: none;
    margin: 0;
    padding: 0;
}

.claim-card {
    background: var(--surface-2);
    border: 1px solid var(--border-subtle);
    border-left: 4px solid var(--border-default);
    border-radius: var(--radius-md);
    padding: 1.05rem 1.15rem;
    scroll-margin-top: calc(var(--nav-h) + var(--board-h) + 0.75rem);
    transition: border-color var(--duration-base) var(--ease-out);
}

/* Verdict tint on the left rail. `:has()` keeps the state on the chip — the
   single place the verdict is written — instead of duplicating it on the card.
   The rail is a non-text indicator, so it uses the -solid (7.46–9.82:1
   against the card surface), not the 32% border tint. */
.claim-card:has(.v-supported)    { border-left-color: var(--verdict-supported-solid); }
.claim-card:has(.v-disputed)     { border-left-color: var(--verdict-disputed-solid); }
.claim-card:has(.v-unsupported)  { border-left-color: var(--verdict-unsupported-solid); }
.claim-card:has(.v-insufficient) { border-left-color: var(--verdict-insufficient-solid); }

.claim-card[aria-busy="true"] { border-left-color: var(--accent-fg); }

.claim-card-head {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 1rem;
}
.claim-main { flex: 1; min-width: 0; }
.claim-index {
    font-size: var(--text-3xs);
    font-weight: var(--fw-bold);
    text-transform: uppercase;
    letter-spacing: var(--ls-caps);
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
    margin: 0 0 0.35rem;
}

/* The claim is the product. It gets the largest type on the board and a
   measure short enough to read as a sentence rather than scan as a row. */
.claim-text {
    margin: 0;
    font-size: var(--text-lg);
    font-weight: var(--fw-semibold);
    color: var(--text-primary);
    line-height: var(--lh-snug);
    letter-spacing: var(--ls-tight);
    max-width: var(--measure-claim);
}

/* Was #6B7280 at 3.28:1 — the critique called it near-invisible and it was.
   It is kept (it tells you where in your text the claim came from) and made
   readable: 5.90:1, with an explicit label so it cannot be mistaken for part
   of the claim. */
.claim-context {
    display: flex;
    gap: 0.4rem;
    margin: 0.4rem 0 0;
    font-size: var(--text-2xs);
    color: var(--text-muted);
    line-height: var(--lh-snug);
    max-width: var(--measure-read);
}
.claim-context i { margin-top: 0.2em; font-size: 0.85em; }

.claim-type-badge {
    flex-shrink: 0;
    font-size: var(--text-3xs);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    letter-spacing: var(--ls-wide);
    color: var(--text-tertiary);
    background: var(--surface-3);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-pill);
    padding: 0.15rem 0.55rem;
}

/* ---- Status row -------------------------------------------------------- */
.claim-status-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.5rem 0.65rem;
    margin-top: 0.9rem;
}

.verdict-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    font-size: var(--text-sm);
    font-weight: var(--fw-semibold);
    padding: 0.3rem 0.75rem;
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
}
.verdict-chip.v-pending {
    color: var(--verdict-pending-fg);
    background: var(--verdict-pending-bg);
    border-color: var(--verdict-pending-border);
}
.verdict-chip.v-supported {
    color: var(--verdict-supported-fg);
    background: var(--verdict-supported-bg);
    border-color: var(--verdict-supported-border);
}
.verdict-chip.v-disputed {
    color: var(--verdict-disputed-fg);
    background: var(--verdict-disputed-bg);
    border-color: var(--verdict-disputed-border);
}
.verdict-chip.v-unsupported {
    color: var(--verdict-unsupported-fg);
    background: var(--verdict-unsupported-bg);
    border-color: var(--verdict-unsupported-border);
}
.verdict-chip.v-insufficient,
.verdict-chip.v-failed {
    color: var(--verdict-insufficient-fg);
    background: var(--verdict-insufficient-bg);
    border-color: var(--verdict-insufficient-border);
}

.disagree-flag {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: var(--text-3xs);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    letter-spacing: var(--ls-wide);
    color: var(--verdict-disputed-fg);
    background: var(--verdict-disputed-bg);
    border: 1px solid var(--verdict-disputed-border);
    border-radius: var(--radius-pill);
    padding: 0.18rem 0.55rem;
}

/* ==========================================================================
   CALIBRATED CONFIDENCE

   This is the most technically interesting number the product produces — the
   server caps and floors the model's own figure against an evidence rubric
   (how many INDEPENDENT groups, of what kind, how directly on point) — and it
   used to be a 90x6px unlabelled bar with a bare "91%" beside it.

   It is now a readout: the number at instrument size, a named band (High /
   Moderate / Low / Very low) so the figure is never the only signal, and a
   five-segment meter that reads as a measurement rather than a progress bar.

   The meter is coloured by BAND, never by verdict. A green bar next to
   "Unsupported 91%" says the opposite of what the number means: 91% is how
   sure we are that the claim is false. Verdict colour lives on the chip and
   the rail; confidence colour is about strength of evidence only.
   ========================================================================== */
.conf-readout {
    display: grid;
    grid-template-columns: auto auto;
    grid-template-areas:
        "label value"
        "meter meter";
    align-items: baseline;
    gap: 0.15rem 0.55rem;
    margin-left: auto;
    padding: 0.3rem 0.6rem 0.4rem;
    min-width: 148px;
    background: var(--surface-3);
    border: 1px solid var(--border-default);
    border-radius: var(--radius-sm);
}
.conf-label {
    grid-area: label;
    font-size: var(--text-3xs);
    font-weight: var(--fw-bold);
    text-transform: uppercase;
    letter-spacing: var(--ls-caps);
    color: var(--text-muted);
    white-space: nowrap;
}
.conf-value {
    grid-area: value;
    justify-self: end;
    display: inline-flex;
    align-items: baseline;
    gap: 0.35rem;
    font-size: var(--text-base);
    font-weight: var(--fw-bold);
    line-height: 1;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}
.conf-band {
    font-size: var(--text-3xs);
    font-weight: var(--fw-bold);
    text-transform: uppercase;
    letter-spacing: var(--ls-wide);
}
.conf-band.c-high     { color: var(--accent-fg); }
.conf-band.c-moderate { color: var(--info-fg); }
.conf-band.c-low      { color: var(--warning-fg); }
.conf-band.c-verylow  { color: var(--neutral-fg); }

.conf-track {
    grid-area: meter;
    position: relative;
    height: 7px;
    margin-top: 0.2rem;
    background: var(--surface-input);
    border-radius: 2px;
    overflow: hidden;
}
.conf-fill {
    height: 100%;
    width: 0;
    transition: width var(--duration-slow) var(--ease-out);
    background: var(--neutral-fg);
}
.conf-fill.c-high     { background: var(--accent-fg); }
.conf-fill.c-moderate { background: var(--info-fg); }
.conf-fill.c-low      { background: var(--warning-fg); }
.conf-fill.c-verylow  { background: var(--neutral-fg); }

/* Five notches cut in the parent's own colour. Deterministic segments — no
   gradient interpolation, so the meter reads as five discrete steps of 20
   points rather than as a smear. */
.conf-track::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: repeating-linear-gradient(
        90deg,
        var(--surface-3) 0 2px,
        transparent 2px calc(20% + 0px)
    );
    background-position: -2px 0;
}

.claim-share {
    background: transparent;
    border: 1px solid var(--border-default);
    border-radius: var(--radius-sm);
    color: var(--text-tertiary);
    cursor: pointer;
    min-width: 34px;
    min-height: 34px;
    display: inline-grid;
    place-items: center;
    transition: border-color var(--duration-fast) var(--ease-out),
                color var(--duration-fast) var(--ease-out);
}
.claim-share:hover { border-color: var(--accent-fg); color: var(--accent-fg); }

/* ---- Per-claim failure + retry ----------------------------------------
   One claim failing must never take the run down. The card keeps its slot,
   states plainly that it failed, and offers a retry that re-runs only itself. */
.claim-retry {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    min-height: 32px;
    background: var(--surface-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    color: var(--text-primary);
    font-size: var(--text-2xs);
    font-weight: var(--fw-semibold);
    font-family: inherit;
    padding: 0.25rem 0.7rem;
    cursor: pointer;
    transition: border-color var(--duration-fast) var(--ease-out);
}
.claim-retry:hover { border-color: var(--accent-fg); color: var(--accent-fg); }

.claim-error {
    margin-top: 0.6rem;
    font-size: var(--text-2xs);
    color: var(--text-muted);
    line-height: var(--lh-snug);
}

.claim-card.is-cancelled { opacity: 0.72; }

/* ---- Flash (annotated-text / tally jump target) ------------------------- */
.claim-card.flash { animation: sc-card-flash 1.4s var(--ease-out); }
@keyframes sc-card-flash {
    0%, 100% { box-shadow: none; }
    25%      { box-shadow: 0 0 0 2px var(--accent-fg); }
}

/* ==========================================================================
   PROVENANCE FLAGS — honesty signals, not chrome

   `escalated` means a low-confidence verdict was thrown away and re-judged by
   a stronger model. `cached` means the verdict was served from the shared
   cache rather than retrieved just now. Neither changes the verdict; both
   change how much weight it deserves, which is exactly why neither belongs in
   a `title` attribute or in an undifferentiated grey pill. Escalation reads as
   information (info tone, upward glyph, full words); cached reads as quiet
   bookkeeping, because that is what it is.
   ========================================================================== */
.run-flag {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    font-size: var(--text-3xs);
    font-weight: var(--fw-semibold);
    letter-spacing: var(--ls-wide);
    padding: 0.18rem 0.55rem;
    border-radius: var(--radius-pill);
    border: 1px solid var(--border-default);
    background: var(--surface-3);
    color: var(--text-muted);
}
.run-flag.is-escalated {
    color: var(--info-fg);
    background: var(--info-bg);
    border-color: var(--info-border);
}
.run-flag.is-cached { color: var(--text-muted); }

@media (max-width: 768px) {
    /* Two rows instead of one — see the board-summary block below. */
    :root { --board-h: 115px; }

    /* The confidence readout stops competing for the verdict chip's row and
       takes a full line of its own, so neither gets truncated. */
    .conf-readout { margin-left: 0; width: 100%; order: 3; }
    .claim-share { order: 2; margin-left: auto; min-width: var(--hit-min); min-height: var(--hit-min); }
    .claim-retry { min-height: var(--hit-min); }
    .claim-card { padding: 0.9rem; }
    .claim-card-head { flex-direction: column; gap: 0.5rem; }
    .claim-type-badge { align-self: flex-start; order: -1; }
    .claim-text { font-size: var(--text-base); }

    /* The sticky bar has to stay a bar.

       At 390px the full-label tally stacked one chip per row and ate ~330px of
       an 844px viewport; compacting the chips to glyph+count still left ~163px
       across three ragged rows. Persistent chrome taking a fifth of a phone
       screen defeats the point — the user came to read claims.

       Two deliberate rows, ~93px total:
         row 1  integrity — score, full label and bar, on one line
         row 2  the four counts, then the jump control as a square icon button
       Each chip is still a 44px target and still carries its full label to
       assistive tech through aria-label; the glyph is a different SHAPE per
       verdict, so dropping the word never leaves colour as the only signal. */
    .board-summary {
        gap: 0.4rem 0.45rem;
        padding: 0.45rem 0.55rem;
    }
    .board-divider { display: none; }

    /* Row 1: integrity + the jump control. Row 2: the tally. Letting the jump
       wrap onto a line of its own made this a three-row, ~133px bar at 768px
       for no gain — it is one control and it fits beside the score. */
    .integrity-meter { flex: 1 1 auto; order: 1; gap: 0.5rem; min-width: 0; }
    .integrity-score { font-size: var(--text-lg); min-width: 0; }
    .integrity-copy { flex-direction: row; align-items: center; gap: 0.5rem; flex: 1; min-width: 0; }
    .integrity-bar { width: auto; flex: 1; min-width: 40px; max-width: 130px; }

    .board-tally { order: 3; flex: 1 0 100%; gap: 0.3rem; }
    .tally-chip {
        min-height: var(--hit-min);
        min-width: var(--hit-min);
        justify-content: center;
        padding: 0.2rem 0.5rem;
    }
    .board-jump {
        order: 2;
        margin-left: auto;
        min-height: var(--hit-min);
    }

    .run-progress { flex: 1 0 100%; order: 4; }
}

/* ---- Phones only ------------------------------------------------------
   At 768px there is room for the tally's words and they are worth the space.
   At 390px there is not: four labelled chips plus the jump control overflow
   the row and the bar grows a third and fourth line. Below 560px the chips
   keep their glyph and count and hand the word to assistive tech, and the
   jump control becomes a square icon button with a title. The glyph differs
   in SHAPE per verdict, so nothing here reduces to colour alone. */
@media (max-width: 560px) {
    /* "Document integrity" -> "integrity"; the full phrase is still on the
       meter's aria-label, and row 1 has a jump button to seat beside it. */
    .integrity-label-long { display: none; }

    .tally-chip .tally-label,
    .board-jump #board-jump-label {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: -1px;
        overflow: hidden;
        clip: rect(0, 0, 0, 0);
        white-space: nowrap;
    }

    .board-jump {
        min-width: var(--hit-min);
        padding: 0;
        justify-content: center;
    }
}

/* ==========================================================================
   EXTRACTION PHASE
   Shown only while extract_claims is in flight. Before this the run began by
   hiding the worked example and rendering nothing, so the column was blank for
   the whole call and the only sign of life was the button turning into Cancel.
   The ghost cards matter more than the spinner: they announce the SHAPE of what
   is coming, which is what actually shortens the perceived wait.
   ========================================================================== */

.extract-phase {
    padding: var(--space-5);
    margin-bottom: var(--space-5);
}

.extract-phase-head {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
}

.extract-phase-title {
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold);
    color: var(--text-primary);
    margin: 0;
}

.extract-phase-sub {
    margin: 0.15rem 0 0;
    font-size: var(--fs-sm);
    color: var(--text-secondary);
}

/* Tabular figures so a ticking counter doesn't shuffle the layout each second. */
.extract-phase-elapsed {
    margin-left: auto;
    flex: none;
    min-width: 3.25rem;
    text-align: right;
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-sm);
    color: var(--text-muted);
}

.extract-phase-spinner {
    flex: none;
    width: 1.25rem;
    height: 1.25rem;
    margin-top: 0.15rem;
    border-radius: 50%;
    border: 2px solid var(--border-default);
    border-top-color: var(--accent-fg);
    animation: sc-spin 0.8s linear infinite;
}

/* Indeterminate by design. A percentage here would have to be invented, and
   this tool does not assert things it cannot support. */
.extract-phase-bar {
    position: relative;
    height: 3px;
    margin: var(--space-4) 0 var(--space-5);
    border-radius: var(--radius-pill);
    background: var(--surface-3);
    overflow: hidden;
}
.extract-phase-bar > span {
    position: absolute;
    inset-block: 0;
    width: 40%;
    border-radius: inherit;
    background: linear-gradient(90deg, var(--accent-bg), var(--accent-fg), var(--accent-bg));
    animation: sc-sweep 1.4s var(--ease-in-out) infinite;
}

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

.claim-skeleton {
    padding: var(--space-4);
    border: 1px solid var(--border-subtle);
    border-radius: var(--radius-lg);
    background: var(--surface-2);
    display: grid;
    gap: var(--space-2);
}
.claim-skeleton:nth-child(2) { opacity: 0.66; }
.claim-skeleton:nth-child(3) { opacity: 0.36; }

.sk {
    display: block;
    height: 0.7rem;
    border-radius: var(--radius-xs);
    background: linear-gradient(90deg, var(--surface-3), var(--surface-4), var(--surface-3));
    background-size: 200% 100%;
    animation: sc-shimmer 1.6s linear infinite;
}
.sk-eyebrow { width: 5.5rem; height: 0.55rem; }
.sk-line    { height: 0.95rem; }
.sk-short   { width: 62%; }
.sk-chip    { width: 7rem; height: 1.4rem; border-radius: var(--radius-pill); }

/* Queued vs searching. Only VERIFY_CONCURRENCY claims are ever in flight, so
   labelling every card "Searching evidence" overstates what is happening and
   leaves the board looking frozen between completions. */
.verdict-chip.v-queued {
    color: var(--neutral-fg);
    background: var(--neutral-bg);
    border-color: var(--neutral-border);
}

@keyframes sc-spin    { to { transform: rotate(360deg); } }
@keyframes sc-sweep   { 0% { left: -40%; } 100% { left: 100%; } }
@keyframes sc-shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }

/* The panel still conveys "working" through the live elapsed count and the
   staged copy, so nothing is lost by holding everything still. */
@media (prefers-reduced-motion: reduce) {
    .extract-phase-spinner,
    .extract-phase-bar > span,
    .sk { animation: none; }
    .extract-phase-bar > span { left: 0; width: 100%; opacity: 0.5; }
}
