/* The shared visual vocabulary.
 *
 * Both surfaces render the same objects - a score, a reliability label, a chip,
 * an agreement bar, a penalty ledger, a refusal - so they are defined once here
 * and consumed by the web interface and the HTML report alike. Before this file
 * existed, `.chip` was a 999px lowercase pill in one and a 2px uppercase tag in
 * the other, and a brand instruction like "our corners are sharp" was a two-file
 * edit no token layer could absorb.
 *
 * Rules that matter more than they look:
 *   - Colour is never the only carrier. The reliability level prints its word,
 *     the agreement bar prints its percentage, excluded rows are struck through.
 *     A palette that goes wrong then degrades to ugly rather than to wrong.
 *   - Excluded and discarded content uses --neutral, which is held to the body
 *     text contrast floor. Fading it would be a quieter way of hiding it.
 *   - Anything with a background that carries meaning also carries a border, so
 *     it survives printing with backgrounds off and Windows High Contrast.
 */

/* sections ----------------------------------------------------------------- */
.card {
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: var(--space-7) var(--space-7);
}

.card h2,
.section-title {
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--ink-soft);
  font-weight: var(--weight-semibold);
  margin: 0 0 var(--space-2);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.count {
  font-family: var(--font-mono);
  background: var(--surface-sunken);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 0 var(--space-2);
  font-size: var(--text-micro);
}

.lede {
  margin: var(--space-3) 0 var(--space-6);
  color: var(--ink-soft);
  font-size: var(--text-body);
  max-width: 68ch;
}

.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--ink);
}

.eyebrow--dim {
  color: var(--ink-soft);
}

.two-up {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
}

.scroll {
  overflow-x: auto;
}

.muted {
  color: var(--ink-soft);
}

.small {
  font-size: var(--text-small);
}

.stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
}

@media (max-width: 760px) {
  .two-up {
    grid-template-columns: 1fr;
  }
}

/* verdict band ------------------------------------------------------------- *
 * The score is never rendered alone. Score, reliability and spread share one
 * bordered unit, and the reliability word is set one step below the numeral -
 * not as a caption beside it. */
.verdict {
  display: grid;
  grid-template-columns: minmax(190px, 0.8fr) 1.4fr minmax(170px, 0.9fr);
  gap: var(--space-8);
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  padding: var(--space-7);
}

.verdict-label {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--ink-soft);
  display: block;
  margin-bottom: var(--space-3);
}

.score-line {
  display: flex;
  align-items: baseline;
  gap: var(--space-1);
}

.score {
  font-family: var(--font-mono);
  font-size: var(--text-score);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.score-of {
  font-family: var(--font-mono);
  font-size: var(--text-title);
  color: var(--ink-soft);
}

.grade {
  display: inline-block;
  margin-top: var(--space-3);
  font-size: var(--text-body);
  text-transform: capitalize;
  color: var(--ink);
  border-bottom: 2px solid var(--brand);
  padding-bottom: var(--space-1);
}

.rel-line {
  font-family: var(--font-mono);
  font-size: var(--text-reliability);
  text-transform: uppercase;
  letter-spacing: var(--tracking-slight);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-tight);
}

.rel-line--high {
  color: var(--good);
}

.rel-line--medium {
  color: var(--caution);
}

.rel-line--low {
  color: var(--critical);
}

.rel-why {
  margin: var(--space-4) 0 0;
  padding-left: var(--space-5);
  font-size: var(--text-body);
  color: var(--ink);
}

.rel-why li {
  margin: var(--space-1) 0;
}

.verdict-meta {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.metric {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  border-bottom: 1px solid var(--line-soft);
  padding-bottom: var(--space-2);
}

.metric-label {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--ink-soft);
}

.metric-value {
  font-family: var(--font-mono);
  font-size: var(--text-body);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

/* The relationship between the headline and the per-pass range, in words.
 * A run whose consensus filtering dropped eight observations printed CONDITION
 * 6.90 beside SCORE RANGE 6.5 - 6.7 and said nothing about why the headline sat
 * outside its own range - which reads as a bug, and hid the fact that the
 * filtering had moved the number in the flattering direction. */
.metric-note {
  margin: var(--space-2) 0 0;
  font-size: var(--text-caption);
  line-height: var(--leading-snug);
  color: var(--ink-soft);
}

/* Which penalty table produced the number. A 7.5 on the leather scale is not a
 * 7.5 on the sneaker scale - different weights over a different set of regions -
 * so the scale travels with the score rather than being inferred from context. */
.scale-stamp {
  display: block;
  margin-top: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--ink-soft);
}

/* "stable" and "UNSTABLE" used to render identically - same size, same ink -
 * in a band whose whole job is qualifying the number beside it. */
.metric-value--critical {
  color: var(--critical);
  font-weight: var(--weight-semibold);
}

/* The single-pass case. A run with one pass has the least evidence, so it must
 * not be the run that displays the least uncertainty. */
.no-consensus {
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  line-height: var(--leading-snug);
  color: var(--ink);
  background: var(--neutral-wash);
  border: 1px solid var(--neutral-edge);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
}

@media (max-width: 900px) {
  .verdict {
    grid-template-columns: 1fr;
    gap: var(--space-6);
  }
}

/* chips -------------------------------------------------------------------- */
.chip {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-sm);
  border: 1px solid var(--line);
  background: var(--surface-sunken);
  color: var(--ink-soft);
  white-space: nowrap;
}

.chip--good {
  color: var(--good);
  background: var(--good-wash);
  border-color: var(--good-edge);
}

.chip--caution {
  color: var(--caution);
  background: var(--caution-wash);
  border-color: var(--caution-edge);
}

.chip--critical {
  color: var(--critical);
  background: var(--critical-wash);
  border-color: var(--critical-edge);
}

.chip--neutral {
  color: var(--neutral);
  background: var(--neutral-wash);
  border-color: var(--neutral-edge);
}

/* callouts ----------------------------------------------------------------- */
.callout {
  border-radius: var(--radius-md);
  padding: var(--space-5) var(--space-6);
  font-size: var(--text-body);
  border: 1px solid var(--line);
  background: var(--surface);
  color: var(--ink);
}

.callout p {
  margin: var(--space-2) 0 0;
}

.callout strong {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
}

.callout--note {
  background: var(--brand-wash);
  border-color: var(--brand-edge);
}

.callout--good {
  background: var(--good-wash);
  border-color: var(--good-edge);
}

.callout--caution {
  background: var(--caution-wash);
  border-color: var(--caution-edge);
}

.callout--critical {
  background: var(--critical-wash);
  border-color: var(--critical-edge);
}

/* A refusal is the correct output of a working system, not a malfunction.
 * Both surfaces used to paint it amber, alongside pipeline warnings and photo
 * quality problems, which teaches a reader that the guard is a bug. It is a
 * statement instead: full ink, on paper, inside a brand rule. */
.callout--refusal {
  background: var(--refusal-wash);
  border-color: var(--refusal-edge);
  border-left-width: 3px;
  color: var(--refusal);
}

/* tables ------------------------------------------------------------------- */
table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-body);
  color: var(--ink);
}

table.data th {
  text-align: left;
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--ink-soft);
  font-weight: var(--weight-semibold);
  padding: 0 var(--space-4) var(--space-3) 0;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}

table.data td {
  padding: var(--space-4) var(--space-4) var(--space-4) 0;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: top;
}

table.data tbody tr:last-child td {
  border-bottom: none;
}

.num {
  text-align: right;
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* A deduction is arithmetic, not alarm. Red here would mean both "unreliable"
 * and "minus", in the one table whose job is telling those apart. */
.figure-neg {
  color: var(--figure-negative);
}

.total {
  font-size: var(--text-title);
  font-weight: var(--weight-semibold);
  border-top: 2px solid var(--line-strong);
}

tfoot td {
  padding-top: var(--space-4) !important;
}

.is-base td {
  color: var(--ink-soft);
}

.is-excluded {
  color: var(--neutral);
}

.is-excluded .ledger-name {
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}

.excluded-mark {
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--neutral);
}

.ledger-name {
  font-weight: var(--weight-medium);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.ledger-where {
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: var(--tracking-slight);
  margin-top: var(--space-1);
}

.ledger-note {
  font-size: var(--text-small);
  color: var(--ink);
  margin-top: var(--space-2);
  max-width: 46ch;
}

.working {
  font-family: var(--font-mono);
  font-size: var(--text-small);
  color: var(--ink-soft);
}

.evidence {
  max-width: 44ch;
  color: var(--ink-soft);
}

.tags {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-top: var(--space-2);
}

.plates-cell {
  white-space: nowrap;
}

.imgref {
  display: inline-block;
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  font-weight: var(--weight-semibold);
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  padding: 0 var(--space-3);
  margin-right: var(--space-1);
  color: var(--brand-ink);
  text-decoration: none;
}

.imgref:hover,
.imgref:focus-visible {
  background: var(--brand);
  color: var(--brand-contrast);
  border-color: var(--brand);
}

/* agreement bar ------------------------------------------------------------ *
 * The percentage is printed beside the bar on purpose: the fill is the only
 * part that disappears in print with backgrounds off, in Windows High Contrast,
 * and in a mail client that strips CSS. */
.agree-cell {
  min-width: 130px;
}

.agree {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.agree-track {
  flex: 1;
  height: 8px;
  background: var(--surface-sunken);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
  min-width: 52px;
}

.agree-fill {
  height: 100%;
  border-right: 1px solid var(--ink);
  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
}

.agree-fill--good {
  background: var(--good);
}

.agree-fill--caution {
  background: var(--caution);
}

.agree-fill--critical {
  background: var(--critical);
}

.agree-num {
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
  min-width: 34px;
  text-align: right;
}

.agree-absent {
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  color: var(--neutral);
}

.struck {
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}

/* claims ------------------------------------------------------------------- */
.claims {
  display: flex;
  flex-direction: column;
}

.claim {
  display: grid;
  grid-template-columns: 130px 1fr;
  gap: var(--space-5);
  padding: var(--space-4) 0;
  border-top: 1px solid var(--line-soft);
}

.claim:first-child {
  border-top: none;
  padding-top: 0;
}

.claim-label {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--ink-soft);
  padding-top: var(--space-2);
}

.claim-head {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}

.claim-value {
  font-size: var(--text-title);
  font-weight: var(--weight-medium);
  color: var(--ink);
}

.claim-value--empty {
  color: var(--ink-soft);
  font-weight: var(--weight-regular);
  font-style: italic;
}

.claim-why {
  margin: var(--space-1) 0 0;
  font-size: var(--text-body);
  color: var(--ink-soft);
  max-width: 62ch;
}

/* How much of the session stands behind the value, on the row with the value.
 *
 * A style code one pass invented renders identically to one every pass read off
 * the tag, and the rationale printed directly beneath it is that same pass's own
 * words. Without this line the row shows an assertion, its author's justification
 * for it, and nothing else — which is how a fabricated style code came to sit on
 * the page looking exactly like a read one. */
.claim-support {
  margin-top: var(--space-1);
  font-size: var(--text-caption);
  color: var(--ink-soft);
}

/* Weak support is the case worth seeing, so it is not the same grey as the rest
 * of the caption furniture. */
.claim-support--weak {
  color: var(--caution);
  font-weight: var(--weight-medium);
}

/* What the other passes said. It sits beside the claim rather than in a note at
 * the foot of the document, because the assertion is what a reader believes and
 * a contradiction four screens below it does not reach them. */
.claim-variants {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  align-items: center;
  margin-top: var(--space-2);
  font-size: var(--text-caption);
  color: var(--ink-soft);
}

/* Both surfaces state the shoe's name at the largest size on the page. When the
 * passes disagreed about it, that headline is a majority vote presented as a
 * fact, so the vote is printed under it rather than only in the pipeline notes. */
.subject-caveat {
  display: block;
  margin-top: var(--space-3);
  font-size: var(--text-body);
  font-family: var(--font-body);
  text-transform: none;
  letter-spacing: var(--tracking-normal);
  color: var(--critical);
  max-width: 68ch;
}

.materials {
  display: flex;
  gap: var(--space-4);
  align-items: baseline;
  margin-top: var(--space-5);
  border-top: 1px solid var(--line);
  padding-top: var(--space-5);
}

@media (max-width: 620px) {
  .claim {
    grid-template-columns: 1fr;
    gap: var(--space-2);
  }
}

/* plates ------------------------------------------------------------------- */
.plates {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  gap: var(--space-5);
}

.plate {
  margin: 0;
}

.plate-frame {
  border: 1px solid var(--line);
  background: var(--surface-sunken);
  border-radius: var(--radius-sm);
  overflow: hidden;
  aspect-ratio: 1;
  scroll-margin-top: var(--space-6);
}

/* On the web page the frame is a <button> that opens the photograph full size -
   a control only a mouse can reach is not a control. In the standalone report
   (app/report.py) the same class is a plain <div> with nothing behind it, and
   this file is embedded in both. So the affordance is scoped to the element that
   actually does something: a zoom cursor over a dead div in an exported report
   promises an interaction that does not exist there. */
button.plate-frame {
  padding: 0;
  display: block;
  width: 100%;
  cursor: zoom-in;
}

button.plate-frame:hover,
button.plate-frame:focus-visible {
  border-color: var(--brand);
}

.plate img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.plate figcaption {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  flex-wrap: wrap;
}

/* Inverted badge. The border is what keeps the plate number visible once a
 * printer drops the background - and every observation in the report cites one
 * of these numbers, so losing them makes the document unauditable. */
.plate-idx {
  background: var(--line-strong);
  color: var(--surface);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-sm);
  padding: 0 var(--space-2);
  font-weight: var(--weight-semibold);
  print-color-adjust: exact;
  -webkit-print-color-adjust: exact;
}

.plate-view {
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--ink);
}

.plate-issue {
  color: var(--caution);
}

.plate:target .plate-frame {
  border-color: var(--brand);
  box-shadow: 0 0 0 2px var(--brand-wash);
}

/* coverage ----------------------------------------------------------------- */
.coverage {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--space-4) var(--space-7);
  margin-top: var(--space-6);
  border-top: 1px solid var(--line);
  padding-top: var(--space-5);
}

.coverage div {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.k {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--ink-soft);
}

.v {
  font-family: var(--font-mono);
  font-size: var(--text-small);
  color: var(--ink);
}

.v--caution {
  color: var(--caution);
}

/* pricing ------------------------------------------------------------------ */
.price {
  display: flex;
  align-items: baseline;
  gap: var(--space-6);
  flex-wrap: wrap;
  margin-bottom: var(--space-2);
}

.price-main {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.price-cur {
  font-family: var(--font-mono);
  font-size: var(--text-lead);
  color: var(--ink-soft);
}

.price-val {
  font-family: var(--font-mono);
  font-size: var(--text-score);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.price-range {
  font-family: var(--font-mono);
  font-size: var(--text-lead);
  color: var(--ink-soft);
}

.chain {
  margin: var(--space-5) 0 0;
  padding-left: var(--space-6);
  font-size: var(--text-body);
  color: var(--ink-soft);
}

.chain li {
  margin: var(--space-2) 0;
  padding-left: var(--space-2);
}

/* lists + disclosure ------------------------------------------------------- */
.notes,
.plain {
  margin: var(--space-3) 0 0;
  padding-left: var(--space-6);
  font-size: var(--text-body);
}

.notes li,
.plain li {
  margin: var(--space-2) 0;
}

.notes {
  color: var(--ink);
}

.plain li strong {
  display: block;
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
}

details summary {
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--brand-ink);
}

details pre {
  margin-top: var(--space-4);
}

/* --------------------------------------------------------------------------
 * The spec panel - the client's deck layout
 *
 * `.on-dark` supplies the palette; nothing here names a colour. That is what
 * lets one panel be navy while the page around it stays light, and what keeps
 * this block honest when the brand file changes again.
 * ----------------------------------------------------------------------- */

.spec-panel {
  background: var(--ground);
  border-color: var(--line);
}

.spec-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.spec-head h2 {
  font-family: var(--font-display);
  font-size: var(--text-title);
  color: var(--ink);
  margin: 0;
}

.spec-subject {
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  color: var(--ink-soft);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
}

.spec-rows {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

/* Icon, label, value. The value box is what the deck shows, so it keeps its own
 * surface and border rather than sitting loose on the panel. */
.spec-row {
  display: grid;
  grid-template-columns: auto minmax(6rem, 10rem) minmax(0, 1fr);
  align-items: center;
  gap: var(--space-3);
}

.spec-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.9rem;
  height: 1.9rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  color: var(--ink-soft);
  font-size: var(--text-small);
}

.spec-label {
  font-family: var(--font-mono);
  font-size: var(--text-caption);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--ink-soft);
}

.spec-value {
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--line);
  border-radius: var(--radius-md);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--text-small);
  font-variant-numeric: tabular-nums;
}

/* A number the system produced, given the weight of one. */
.spec-value--figure {
  color: var(--good);
  font-weight: var(--weight-medium);
}

/* Something we looked for and could not determine. Muted, not alarming: an
 * unread size tag is a fact about the photographs, not a fault. */
.spec-value--absent {
  color: var(--ink-muted);
  font-style: italic;
}

/* A row with no rule behind it yet. Marked in caution rather than left blank,
 * because a blank box reads as a value that failed to load. */
.spec-value--pending {
  color: var(--caution);
  border-style: dashed;
}

.spec-foot {
  margin: var(--space-4) 0 0;
  font-size: var(--text-caption);
  color: var(--ink-muted);
  max-width: 62ch;
}

@media (max-width: 33rem) {
  .spec-row {
    grid-template-columns: auto minmax(0, 1fr);
  }
  .spec-icon { grid-row: span 2; }
}

/* buttons ------------------------------------------------------------------
 * Shared, so they live here rather than in app.css: the admin console loads
 * components.css and not app.css, and a second copy of these rules is a second
 * place a brand change has to reach. Every value is a token, so they render
 * correctly inside .on-dark without a dark-specific rule. */
.btn--primary {
  background: var(--brand);
  color: var(--brand-contrast);
  border-color: var(--brand);
  font-weight: var(--weight-semibold);
  padding: var(--space-3) var(--space-7);
}

.btn--primary:hover:not(:disabled) {
  background: var(--brand-hover);
  border-color: var(--brand-hover);
}

.btn--primary:active:not(:disabled) {
  background: var(--brand-active);
  border-color: var(--brand-active);
}

.btn--ghost {
  background: var(--surface);
  color: var(--ink);
}

/* A qualifier on the table directly above it - smaller and quieter than a lede,
 * because it is read after the numbers rather than before them. Used where a
 * column would otherwise be added up by a reader and shouldn't be. */
.note-small {
  margin: var(--space-4) 0 0;
  font-size: var(--text-small);
  line-height: var(--leading-snug);
  color: var(--ink-muted);
  max-width: 70ch;
}

/* form fields --------------------------------------------------------------
 * Shared for the same reason the buttons above are: the login page loads
 * components.css and not app.css. `.field label` is styled alongside
 * `.field-label` so a plain <label> gets the same treatment without every
 * form having to remember the class. */
.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
}

.field-label {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--ink-soft);
}

.field textarea {
  min-height: 68px;
}

.field select {
  max-width: 320px;
}

.field label {
  font-family: var(--font-mono);
  font-size: var(--text-micro);
  text-transform: uppercase;
  letter-spacing: var(--tracking-label);
  color: var(--ink-soft);
}

.field input,
.field textarea,
.field select {
  width: 100%;
}


/* Here rather than in app.css, which only the appraisal page loads.

   It lived there while index.html was the only page using it, and the first
   console page to write `class="visually-hidden"` would have rendered the label
   in plain sight - a heading that says "Actions" where an empty one was meant,
   with no error anywhere. components.css is loaded by every page, so the class
   now works wherever it is written. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --- the evidence lightbox ------------------------------------------------ */

/* A plate is about 180px wide and the question being asked of it - crease or
   crack, scuff or shadow - cannot be answered at that size. */
.lightbox {
  border: 0;
  padding: 0;
  max-width: 100vw;
  max-height: 100vh;
  width: 100%;
  height: 100%;
  /* The ground goes on the dialog, which already fills the viewport, rather
     than on ::backdrop. Two reasons and the second is the real one: ::backdrop's
     inheritance of custom properties only settled recently, and a translucent
     wash is the kind of thing that gets written as a literal - which is exactly
     what the theme guard caught on the first attempt here. An opaque ground
     costs nothing on a photo viewer and stays inside the palette. */
  background: var(--figure-negative);
  color: var(--brand-contrast);
  overflow: hidden;
}

.lightbox-figure {
  margin: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  padding: var(--space-5);
}

.lightbox-img {
  /* Fits the viewport with room for the caption, and never upscales past its
     own pixels - a 200px rendition blown up to 1400 would look like detail that
     is not there, on the one surface built for looking at detail. */
  max-width: 100%;
  max-height: calc(100vh - var(--space-9) * 3);
  object-fit: contain;
  border-radius: var(--radius-sm);
}

.lightbox-caption {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--space-3);
  font-size: var(--text-small);
  color: var(--brand-contrast);
}

.lightbox-count {
  font-variant-numeric: tabular-nums;
  color: var(--brand-edge);
}

.lightbox-controls {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  display: flex;
  gap: var(--space-2);
}

.lightbox-nav,
.lightbox-close {
  font: inherit;
  font-size: var(--text-small);
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--brand-edge);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
}

.lightbox-nav:hover,
.lightbox-close:hover {
  border-color: var(--brand);
  background: var(--brand-wash);
}


/* A placeholder reference value. Every price this build produces starts from one,
   and the figure used to render at --text-score with the disclosure in 12px
   underneath - which is how a made-up $130 got read as a market price. */
.price--placeholder .price-val,
.price--placeholder .price-cur {
  color: var(--ink-muted);
}

.price-flag {
  margin-top: var(--space-2);
  font-size: var(--text-micro);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--caution);
}
