/* Tether "Analyze a file" page. Tokens only, from web/shared.css; both schemes come from
   there. State-coloured text sits on the opaque --tint-* fills, never the -dim washes
   (tests/test_web_contrast_tint_discipline.py). Motion is added only where it encodes a
   state -- see the [data-state] rules below and web/analyze/analyze.js -- and no
   transition here runs longer than 200ms (--dur); shared.css zeroes all of it under
   prefers-reduced-motion. */

.analyze {
  max-width: 56rem;
  margin: 0 auto;
  padding: var(--s-7) var(--gutter) var(--s-8);
  min-width: 0;
}

.analyze__header { margin-bottom: var(--s-6); }

.analyze__chips {
  display: flex; flex-wrap: wrap; gap: var(--s-2);
  margin: 0 0 var(--s-4);
}

.analyze h1 {
  font-size: var(--t-display);
  line-height: 1.08;
  letter-spacing: -0.03em;
  text-wrap: balance;
  max-width: 22ch;
  margin-bottom: var(--s-3);
}

.analyze__sub {
  text-wrap: pretty;
  font-size: var(--t-15);
  color: var(--text-2);
  line-height: 1.55;
  max-width: var(--measure);
}

/* ---- drop zone ------------------------------------------------------------------- */
.drop { margin: 0 0 var(--s-6); }

.drop__zone {
  position: relative;
  display: flex; flex-direction: column; align-items: center;
  gap: var(--s-2);
  padding: var(--s-7) var(--s-5);
  border-radius: var(--r-lg);
  border: 1.5px dashed var(--hairline-strong);
  background:
    radial-gradient(420px 160px at 50% 0%, var(--tint-info), transparent 78%),
    var(--surface-1);
  box-shadow: var(--e-1), inset 0 1px 0 var(--overlay-up);
  text-align: center;
  min-width: 0;
  /* transform/box-shadow/border-color only: compositor-cheap, and zeroed by shared.css
     under prefers-reduced-motion. */
  transition: transform var(--dur) var(--ease), box-shadow var(--dur) var(--ease),
              border-color var(--dur) var(--ease), background var(--dur) var(--ease);
}
/* Keyboard affordance: the Choose button is the control, but the whole zone lights up
   while it holds focus, so a keyboard user sees what Enter will act on. */
.drop__zone:has(.drop__choose:focus-visible) {
  border-style: solid;
  border-color: var(--info);
  box-shadow: var(--e-2), 0 0 0 3px var(--info-line);
}
.drop[data-state="dragging"] .drop__zone {
  transform: translateY(-4px) scale(1.012);
  border-style: solid;
  border-color: var(--info);
  background: var(--surface-2);
  box-shadow: var(--e-3), 0 0 0 4px var(--info-line);
}
.drop[data-state="analyzing"] .drop__zone { border-color: var(--info-line); }
.drop[data-state="done"] .drop__zone { border-color: var(--pass-line); }
.drop[data-state="refused"] .drop__zone { border-color: var(--fail-line); }

/* Analyzing: a slow sweep across the zone's top edge -- only while bytes are in flight. */
.drop__zone::after {
  content: ""; position: absolute; left: 12px; right: 12px; top: -1px; height: 2px;
  border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--info), transparent);
  background-size: 40% 100%; background-repeat: no-repeat;
  opacity: 0;
  transition: opacity var(--dur) var(--ease);
}
.drop[data-state="analyzing"] .drop__zone::after {
  opacity: 1;
  animation: analyze-sweep 1.1s var(--ease) infinite;
}
@keyframes analyze-sweep {
  from { background-position: -40% 0; }
  to { background-position: 140% 0; }
}

/* A small stack of sheets: depth that opens (fans out) when a drag arrives, so the
   "lift" reads as a physical object about to receive files, not a border change. */
.drop__stage { width: 96px; height: 96px; margin-bottom: var(--s-1); }
.drop__icon {
  width: 96px; height: 96px; overflow: visible;
  fill: none; stroke: var(--text-2); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
.drop__sheet { transform-box: fill-box; transform-origin: 50% 90%;
  transition: transform var(--dur) var(--ease); }
.drop__sheet rect { fill: var(--surface-2); }
.drop__sheet--back rect { fill: var(--surface-3); }
.drop__sheet--back { transform: translate(6px, 4px); opacity: 0.7; }
.drop__sheet--mid { transform: translate(3px, 2px); opacity: 0.85; }
.drop[data-state="dragging"] .drop__sheet--back { transform: translate(14px, 1px) rotate(7deg); }
.drop[data-state="dragging"] .drop__sheet--mid { transform: translate(6px, 0) rotate(3deg); }
.drop[data-state="dragging"] .drop__sheet--front { transform: translateY(-3px); }
.drop__icon-sheet { fill: var(--surface-1); }
.drop__icon-arrow { stroke: var(--info); }

.drop__lead { font-size: var(--t-18); font-weight: 560; color: var(--text-1); }
.drop__or { font-size: var(--t-13); color: var(--text-3); }
.drop__caps { font-size: var(--t-12); color: var(--text-3); margin-top: var(--s-2); max-width: 36ch; text-wrap: balance; }

/* ---- results --------------------------------------------------------------------- */
.results__head {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--s-2) var(--s-4);
  margin-bottom: var(--s-3);
}
.results__head h2 { font-size: var(--t-24); }
.results__actions { display: flex; flex-wrap: wrap; gap: var(--s-2); margin: 0; }
.results__actions .btn[hidden] { display: none; }

/* One bar for the whole run. Determinate: its value is bytes read over bytes selected. */
.results__progress {
  margin-bottom: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-md);
  border: 1px solid var(--hairline);
  background: var(--surface-1);
  box-shadow: var(--depth-1);
}
.results__progress progress,
.file__bar {
  display: block; width: 100%; height: 6px;
  appearance: none; -webkit-appearance: none;
  border: 0; border-radius: var(--r-pill);
  background: var(--surface-3);
  overflow: hidden;
}
.results__progress progress::-webkit-progress-bar,
.file__bar::-webkit-progress-bar { background: var(--surface-3); border-radius: var(--r-pill); }
.results__progress progress::-webkit-progress-value,
.file__bar::-webkit-progress-value {
  background: var(--info); border-radius: var(--r-pill);
  transition: width var(--dur) var(--ease);
}
.results__progress progress::-moz-progress-bar,
.file__bar::-moz-progress-bar { background: var(--info); border-radius: var(--r-pill); }
.results__progress[data-complete="1"] progress::-webkit-progress-value { background: var(--pass); }
.results__progress[data-complete="1"] progress::-moz-progress-bar { background: var(--pass); }
.results__counts {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--s-1) var(--s-4);
  margin: var(--s-2) 0 0;
  font-size: var(--t-12); color: var(--text-2);
}
.file__bar { height: 4px; margin-top: var(--s-2); max-width: 18rem; }
.file__bar[hidden] { display: none; }

.results__summary {
  margin: 0 0 var(--s-3);
  font-size: var(--t-13); font-weight: 560; color: var(--text-1);
}
.results__summary[hidden] { display: none; }
.results__summary[data-attention="1"] { color: var(--text-1); }
.results__summary[data-attention="1"]::before {
  content: ""; display: inline-block; width: 8px; height: 8px; margin-right: var(--s-2);
  border-radius: 50%; background: var(--warn); vertical-align: 0.05em;
}

.file[data-state="cancelled"] { opacity: 0.6; border-style: dashed; }
#results-note { margin-bottom: var(--s-4); }

.results__provenance {
  font-size: var(--t-12); color: var(--text-3);
  margin-bottom: var(--s-4);
  max-width: var(--measure);
}

.results__list {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--s-3);
}

.file {
  border-radius: var(--r-md);
  border: 1px solid var(--hairline-strong);
  background: var(--surface-1);
  box-shadow: var(--depth-1);
  padding: var(--s-4);
  min-width: 0;
  /* border-color and shadow only. NOT opacity: a card's state flips to "done" the
     moment its facts render, but the note, palette and histogram are painted later,
     on the thumbnail's image load. An opacity transition out of the queued dim was
     still running when that text appeared, and axe blends text through every
     ancestor's opacity, so the note was measured mid-fade -- #5b6675 at 0.82 reads
     as #79828e on #f5f7f9, 3.62:1, below the 4.5:1 the 12px size needs. MEASURED
     2026-10-31: the fade takes ~200ms to reach opacity 1 and the note exists from
     ~56ms, so the two windows overlap on every run. The queued cue is a dashed
     border instead, which changes no text colour. */
  transition: box-shadow var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.file[data-state="queued"] { border-style: dashed; }
.file[data-state="done"] { box-shadow: var(--depth-2); }
.file[data-state="analyzing"] { border-color: var(--info-line); }
.file[data-state="analyzing"] .file__thumb {
  background: linear-gradient(100deg, var(--surface-2) 30%, var(--surface-3) 50%, var(--surface-2) 70%);
  background-size: 220% 100%;
  animation: analyze-shimmer 1s linear infinite;
}
@keyframes analyze-shimmer {
  from { background-position: 120% 0; }
  to { background-position: -120% 0; }
}
.file[data-state="refused"] { border-color: var(--fail-line); }
.file[data-verdict="mismatch"],
.file[data-flagged="1"] { border-color: var(--warn-line); }

.file__head {
  display: grid;
  grid-template-columns: 56px minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--s-3);
}

.file__thumb {
  width: 56px; height: 56px;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  border: 1px solid var(--hairline);
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.file__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.file__glyph {
  font-family: var(--mono); font-size: var(--t-11); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-3);
}

.file__titles { min-width: 0; }
.file__name {
  font-size: var(--t-15); font-weight: 560;
  overflow-wrap: anywhere;
}
.file__headline { font-size: var(--t-13); color: var(--text-2); margin-top: 2px; }
.file__verdict { font-size: var(--t-13); font-weight: 560; color: var(--text-1); margin-top: 2px; }
.file__verdict[data-attention="1"]::before {
  content: ""; display: inline-block; width: 8px; height: 8px; margin-right: var(--s-2);
  border-radius: 50%; background: var(--warn); vertical-align: 0.05em;
}
.file__headline[hidden], .file__verdict[hidden] { display: none; }
.file__note { font-size: var(--t-12); color: var(--info); margin-top: var(--s-1); }
.file__meta { font-size: var(--t-12); color: var(--text-3); margin-top: 2px; }

.file__body:empty { display: none; }
.file__body { margin-top: var(--s-4); display: grid; gap: var(--s-3); min-width: 0; }

/* Findings first: what a visitor must act on sits above the measurements. Tone is
   carried by a text label AND a dot, never colour alone. State-coloured text sits on
   the opaque --tint-* fills (tests/test_web_contrast_tint_discipline.py). */
.flags { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); }
.flag {
  display: flex; align-items: flex-start; gap: var(--s-2);
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-sm);
  border: 1px solid var(--hairline);
  background: var(--surface-2);
  font-size: var(--t-13); line-height: 1.45; color: var(--text-1);
}
.flag__dot {
  flex: none; width: 8px; height: 8px; margin-top: 0.42em; border-radius: 50%;
  background: var(--text-3);
}
.flag--warn { border-color: var(--warn-line); background: var(--tint-warn); }
.flag--warn .flag__dot { background: var(--warn); }
.flag--fail { border-color: var(--fail-line); background: var(--tint-fail); }
.flag--fail .flag__dot { background: var(--fail); }
.flag--pass { border-color: var(--pass-line); background: var(--tint-pass); }
.flag--pass .flag__dot { background: var(--pass); }
.flag--info { border-color: var(--info-line); background: var(--tint-info); }
.flag--info .flag__dot { background: var(--info); }

.facts {
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: var(--s-3) var(--s-4);
}
.fact { display: grid; align-content: start; gap: 2px; min-width: 0; }
.fact--wide { grid-column: 1 / -1; }
.fact__label { font-size: var(--t-11); color: var(--text-3); letter-spacing: 0.06em; text-transform: uppercase; }
.fact__value { margin: 0; font-size: var(--t-13); color: var(--text-1); min-width: 0; overflow-wrap: anywhere; }
.fact__kind { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2); }
.fact__aside { color: var(--text-2); font-size: var(--t-12); }
.fact[data-verdict="mismatch"] .fact__value { color: var(--warn); font-weight: 560; }
.fact[data-verdict="match"] .fact__value { color: var(--pass); }

.fact__sha {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3);
  min-width: 0;
}
.fingerprint { flex: none; width: 64px; height: 32px; fill: var(--info); }
.fact__digest {
  font-size: var(--t-12);
  color: var(--text-2);
  overflow-wrap: anywhere;
  min-width: 0;
  flex: 1 1 14rem;
}
/* Wrap between 16-character groups, never through one. */
.fact__digest-group { display: inline-block; margin-right: 0.5ch; }
.fact__digest-group:last-child { margin-right: 0; }
.file__copy { min-height: var(--tap); font-size: var(--t-12); }
.file__copy[data-copied] { color: var(--pass); border-color: var(--pass-line); }

.file__refused { display: grid; gap: var(--s-2); }
.file__refusal { margin: 0; font-size: var(--t-13); color: var(--text-1); }
.file__refusal-next { margin: 0; font-size: var(--t-13); color: var(--text-2); }
.file__retry { justify-self: start; }
.file__refusal-detail { margin: 0; font-size: var(--t-12); color: var(--text-3); overflow-wrap: anywhere; }

.findings { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); }
.findings__row {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-2) var(--s-3);
  font-size: var(--t-12); color: var(--text-2);
}
.findings__where { color: var(--text-1); }
.findings__more { font-size: var(--t-12); color: var(--text-3); }

.file__warnings {
  margin: 0; padding: var(--s-2) var(--s-3) var(--s-2) var(--s-6);
  border-radius: var(--r-sm);
  border: 1px solid var(--warn-line);
  background: var(--tint-warn);
  font-size: var(--t-12); color: var(--text-2);
}

.file__source {
  font-size: var(--t-11); color: var(--text-3);
  letter-spacing: 0.02em;
}

/* ---- sample button ---------------------------------------------------------------- */
.drop__sample { min-height: var(--tap); margin-top: var(--s-1); }

/* ---- measured pixels: palette + histogram --------------------------------------------
   Opaque tint fill, text on --text-*, never a -dim wash (contrast discipline). The swatch
   colour itself is set from script through the CSSOM, which `style-src 'self'` permits. */
.visuals {
  display: grid; gap: var(--s-3);
  padding: var(--s-3) var(--s-4);
  border-radius: var(--r-sm);
  border: 1px solid var(--hairline);
  background: var(--surface-2);
  min-width: 0;
}
.visuals__title, .preview__title {
  margin: 0; font-size: var(--t-11); font-weight: 560; color: var(--text-3);
  letter-spacing: 0.06em; text-transform: uppercase;
}
.visuals__status { margin: 0; font-size: var(--t-13); color: var(--text-2); }
.visuals__status[hidden] { display: none; }
.visuals[data-state="loading"] .visuals__status {
  background: linear-gradient(100deg, var(--surface-2) 30%, var(--surface-3) 50%, var(--surface-2) 70%);
  background-size: 220% 100%;
  animation: analyze-shimmer 1s linear infinite;
  border-radius: var(--r-sm);
}
.visuals[data-state="error"] { border-color: var(--warn-line); background: var(--tint-warn); }
.visuals[data-state="error"] .visuals__status { color: var(--text-1); }

.palette {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr)); gap: var(--s-2);
}
.palette__item {
  display: grid; grid-template-columns: 28px auto 1fr; align-items: center; gap: var(--s-2);
  font-size: var(--t-12); color: var(--text-2); min-width: 0;
}
.palette__chip {
  width: 28px; height: 28px; border-radius: var(--r-sm);
  border: 1px solid var(--hairline-strong);
  /* A checker behind the swatch is not needed: swatches are opaque by construction. */
}
.palette__hex { color: var(--text-1); }
.palette__share { justify-self: end; }

.histogram { display: block; width: 100%; height: 56px; fill: var(--info); }
.visuals__summary, .visuals__note { margin: 0; font-size: var(--t-12); color: var(--text-2); }
.visuals__note { color: var(--text-3); }

/* ---- text preview --------------------------------------------------------------------- */
.preview { display: grid; gap: var(--s-2); min-width: 0; }
.preview__text {
  margin: 0; padding: var(--s-3);
  border-radius: var(--r-sm);
  border: 1px solid var(--hairline);
  background: var(--surface-2);
  font-size: var(--t-12); line-height: 1.5; color: var(--text-1);
  white-space: pre-wrap; overflow-wrap: anywhere; max-height: 14rem; overflow: auto;
}
.preview__text:focus-visible { outline: 2px solid var(--info); outline-offset: 2px; }
.preview__status, .preview__more { margin: 0; font-size: var(--t-12); color: var(--text-2); }
.preview[data-state="withheld"] .preview__status { color: var(--text-1); }

@media (max-width: 520px) {
  .drop__zone { padding: var(--s-6) var(--s-4); }
  .drop__choose { width: 100%; }
  .file__head { grid-template-columns: 48px minmax(0, 1fr); }
  .file__thumb { width: 48px; height: 48px; }
  .file__state { grid-column: 1 / -1; justify-self: start; }
  .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .results__actions .btn { flex: 1 1 auto; }
}

/* ---- result polish: severity word, image preview, actions, method ---------------------- */
.flag__tone {
  flex: none; min-width: 4.2em;
  font-size: var(--t-11); font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text-2); padding-top: 0.2em;
}

.imgprev { margin: 0; display: grid; gap: var(--s-2); min-width: 0; }
.imgprev__frame {
  display: flex; align-items: center; justify-content: center;
  min-height: 7rem; max-height: 18rem; overflow: hidden;
  border-radius: var(--r-sm); border: 1px solid var(--hairline);
  /* A checker behind the picture so transparency reads as transparency, not as the page. */
  background:
    conic-gradient(var(--surface-3) 25%, var(--surface-2) 0 50%, var(--surface-3) 0 75%, var(--surface-2) 0) 0 0 / 16px 16px;
}
.imgprev[data-state="loading"] .imgprev__frame {
  background: linear-gradient(100deg, var(--surface-2) 30%, var(--surface-3) 50%, var(--surface-2) 70%);
  background-size: 220% 100%;
  animation: analyze-shimmer 1s linear infinite;
}
.imgprev[data-state="failed"] .imgprev__frame { display: none; }
.imgprev__img { display: block; max-width: 100%; max-height: 18rem; width: auto; height: auto; object-fit: contain; }
.imgprev__caption { font-size: var(--t-12); color: var(--text-2); }
.imgprev[data-state="failed"] .imgprev__caption { color: var(--text-1); }

.file__actions { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.file__action { min-height: var(--tap); font-size: var(--t-12); }

.method {
  font-size: var(--t-12); color: var(--text-2);
  border-top: 1px solid var(--hairline); padding-top: var(--s-2);
}
.method__title { cursor: pointer; color: var(--text-2); min-height: var(--tap); display: flex; align-items: center; }
.method__title:focus-visible { outline: 2px solid var(--info); outline-offset: 2px; }
.method__line { margin: var(--s-1) 0 0; max-width: var(--measure); }

@media (max-width: 520px) {
  .file__actions .btn { flex: 1 1 auto; }
}
.file[data-partial="1"] .file__verdict { color: var(--text-1); }
.file[data-partial="1"]:not([data-flagged="1"]) { border-color: var(--warn-line); }

/* ---- Read text in image (opt-in, local) -------------------------------------------- */
.reader {
  display: grid; gap: var(--s-2); min-width: 0;
  padding: var(--s-3) var(--s-4);
  border: 1px solid var(--hairline); border-radius: var(--r-md); background: var(--surface-2);
}
.reader__title { margin: 0; font-size: var(--t-15); }
.reader__limit, .reader__privacy, .reader__meta, .reader__empty { margin: 0; max-width: var(--measure); font-size: var(--t-13); color: var(--text-2); line-height: 1.5; }
.reader__actions { display: flex; flex-wrap: wrap; gap: var(--s-2); }
.reader__go, .reader__cancel { min-height: var(--tap); }
.reader__go:focus-visible, .reader__cancel:focus-visible, .reader__out:focus-visible { outline: 2px solid var(--info); outline-offset: 2px; }
.reader__bar { width: 100%; max-width: 24rem; height: 6px; }
.reader__bar[hidden], .reader__status[hidden], .reader__out[hidden], .reader__cancel[hidden], .reader__go[hidden] { display: none; }
.reader__status { margin: 0; font-size: var(--t-13); color: var(--text-1); }
.reader[data-reader-state="error"] { border-color: var(--fail-line); }
.reader__out { display: grid; gap: var(--s-2); justify-items: start; min-width: 0; }
.reader__text {
  margin: 0; padding: var(--s-3); max-width: 100%; box-sizing: border-box; overflow: auto;
  white-space: pre-wrap; overflow-wrap: anywhere; user-select: text;
  background: var(--surface-1); border: 1px solid var(--hairline); border-radius: var(--r-sm);
  font: inherit; font-size: var(--t-15); line-height: 1.6; color: var(--text-1);
}
