/* ==========================================================================
   REPORT — the product (§6.5 state two)
   --------------------------------------------------------------------------
   VISUAL_DENSITY 7 lives here and only here. The marketing sections stay at 3.

   The semantic scale (§4.5) is declared in this file and nowhere else, because
   §4.2 confines it to audit output. This file carries no accent token: inside
   the report, blue would read as a status (§4.2, validator checks 3 and 4).

   Semantic color is applied two ways and no others: as text, and as the 2px
   leading marker on the row. Never a row background, never a filled bar,
   never a badge pill.
   ========================================================================== */

.report {
  grid-column: 1 / -1;
  background: var(--sheet);
  border: 1px solid var(--rule);
  font-family: var(--font-mono);
  font-feature-settings: "zero" 1;
  font-variant-numeric: tabular-nums;

  /* §4.5 semantic scale, light mode */
  --pass: #14654a;
  --attention: #8a5a00;
  --problem: #c0202c;
}

@media (prefers-color-scheme: dark) {
  .report {
    --pass: #3fbf8c;
    --attention: #d9a23a;
    --problem: #f0616b;
  }
}

/* The only elements inside the report set in the sans. None of them may carry
   a numeral (§5.2, validator check 10). */
.report .sans {
  font-family: var(--font-sans);
  font-feature-settings: normal;
}

/* --- Head ---------------------------------------------------------------- */

.report-head {
  padding: var(--s-5) var(--s-5) var(--s-5);
}

/* The signature. A stranger's own domain, printed back to them in the face
   records are set in, with the slashed zero on. */
.report-subject {
  margin-top: var(--s-2);
  font-size: var(--t-subject);
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--ink);
  overflow-wrap: anywhere;
}

.report-meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-6);
  margin-top: var(--s-5);
}

.report-meta div {
  display: flex;
  flex-direction: column;
  gap: var(--s-1);
}

.report-meta dd {
  margin: 0;
  font-size: var(--t-mono-body);
  font-weight: 600;
  color: var(--ink);
}

/* --- Clusters (§6.3: one hairline per cluster, not one per row) ----------- */

.cluster {
  border-top: 1px solid var(--rule);
  padding: var(--s-5);
}

.cluster-title {
  font-size: var(--t-h3);
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: -0.008em;
  color: var(--ink);
}

.cluster-rows {
  margin-top: var(--s-3);
}

/* --- Finding rows -------------------------------------------------------- */

.finding-row {
  display: grid;
  grid-template-columns: minmax(0, 14rem) 5.5rem minmax(0, 1fr);
  align-items: baseline;
  gap: var(--s-2) var(--s-4);
  padding: 0.4375rem var(--s-4);
  /* The only 2px rule on the page (§6.2). It means "this is a finding" and
     nothing else uses it. */
  border-left: 2px solid var(--state);
  font-size: var(--t-mono-body);
  line-height: 1.5;
}

.finding-row[data-state="pass"] {
  --state: var(--pass);
}

.finding-row[data-state="attention"] {
  --state: var(--attention);
}

.finding-row[data-state="problem"] {
  --state: var(--problem);
}

.finding-name {
  color: var(--ink-muted);
}

.finding-word {
  font-weight: 600;
  color: var(--state);
}

.finding-detail {
  color: var(--ink);
  overflow-wrap: anywhere;
}

/* §7.1 item 2: one fast fade plus a 2px rise, staggered about 40ms, once. */
.finding-row {
  animation: finding-enter 240ms ease-out backwards;
  animation-delay: calc(var(--row-index, 0) * 40ms);
}

@keyframes finding-enter {
  from {
    opacity: 0;
    transform: translateY(2px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* --- Phase two (§7.2) ---------------------------------------------------- */

.phase-two {
  border-top: 1px solid var(--rule);
  padding: var(--s-5);
  /* The built-in ease-out is too weak to read as a number landing. */
  --wait-ease: cubic-bezier(0.23, 1, 0.32, 1);
}

.wait-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-3);
}

/* No spinner. A single thin rule advances left to right. It is indeterminate
   on purpose: it never fills to a percentage and parks (§7.2 lock). */
.wait-track {
  position: relative;
  height: 1px;
  margin-top: var(--s-4);
  background: var(--rule);
  overflow: hidden;
}

.wait-track::after {
  content: "";
  position: absolute;
  inset-block: 0;
  left: 0;
  width: 22%;
  background: var(--ink-muted);
  opacity: 1;
  /* linear on purpose: a scanning bar moves at a constant rate. */
  animation: wait-sweep 1500ms linear infinite;
  transition: opacity 200ms ease;
}

@keyframes wait-sweep {
  from {
    transform: translateX(-100%);
  }
  to {
    transform: translateX(455%);
  }
}

.wait-track[data-done] {
  background: var(--rule);
}

.wait-track[data-done]::after {
  opacity: 0;
  animation-play-state: paused;
}

.wait-status {
  margin-top: var(--s-3);
  min-height: 1.5em;
  color: var(--ink-muted);
  font-size: var(--t-mono-body);
  line-height: 1.5;
}

.wait-status:empty {
  display: none;
}

.wait-status[data-tick="a"],
.wait-status[data-tick="b"] {
  animation: wait-status-in 160ms ease;
}

@keyframes wait-status-in {
  from {
    opacity: 0.25;
  }
  to {
    opacity: 1;
  }
}

/* Reserved, labeled, empty frames from the first paint. Nothing reflows when
   the numbers land (§7.2, and it is the CLS fix). */
.scores {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin-top: var(--s-5);
  border-top: 1px solid var(--rule);
}

.score {
  padding: var(--s-4) var(--s-4) var(--s-3);
}

.score + .score {
  border-left: 1px solid var(--rule);
}

/* §4.2 lock: the score is set in ink, not in a status color. The color
   belongs to the specific checks that failed. */
.wait-score-value {
  display: block;
  margin-top: var(--s-2);
  min-height: calc(var(--t-score) * 1.05);
  font-size: var(--t-score);
  font-weight: 600;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--ink);
}

.wait-score-value[data-landed] {
  animation: wait-settle 240ms var(--wait-ease);
}

@keyframes wait-settle {
  from {
    opacity: 0;
    transform: translateY(2px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.score-scale {
  margin-top: var(--s-2);
  color: var(--ink-muted);
}

/* Failure is a designed state (§7.2). Phase one stays standing, this frame
   says plainly what happened, and nothing blames the visitor's site. */
.phase-two-failed .wait-status {
  color: var(--ink);
}

.phase-two-failed .scores {
  opacity: 0.55;
}

/* --- Foot ---------------------------------------------------------------- */

.report-foot {
  border-top: 1px solid var(--rule);
  padding: var(--s-4) var(--s-5);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-5);
  color: var(--ink-muted);
  font-size: var(--t-mono-body);
  line-height: 1.5;
}

/* --- Compact variant: the cached self-audit under the field (§6.7) -------- */

.report-compact .report-head {
  padding: var(--s-4) var(--s-5);
}

.report-compact .cluster,
.report-compact .phase-two {
  padding: var(--s-4) var(--s-5);
}

.report-compact .report-subject {
  font-size: var(--t-h3);
}

.report-compact .wait-score-value {
  font-size: 1.75rem;
  min-height: 0;
}

/* --- Below 768px: single column, no horizontal scroll (§6.3) ------------- */

@media (max-width: 767px) {
  /* The check and its verdict stay on one line. Only the detail drops. */
  .finding-row {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--s-1) var(--s-3);
    padding-block: var(--s-3);
  }

  .finding-detail {
    grid-column: 1 / -1;
  }

  .scores {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .score:nth-child(odd) {
    border-left: 0;
  }

  .score:nth-child(n + 3) {
    border-top: 1px solid var(--rule);
  }

  .report-head,
  .cluster,
  .phase-two {
    padding: var(--s-4);
  }

  .report-foot {
    padding: var(--s-4);
  }
}

/* --- Reduced motion (§7.2) ------------------------------------------------
   Status lines still update as text, because they are information. The rule
   jumps between positions instead of sweeping. Numbers appear without the
   settle. The staggered entry collapses to a single instant render. */

@media (prefers-reduced-motion: reduce) {
  .finding-row {
    animation: none;
  }

  .wait-score-value[data-landed] {
    animation: none;
  }

  .wait-status[data-tick="a"],
  .wait-status[data-tick="b"] {
    animation: none;
  }

  .wait-track::after {
    animation: wait-step 1600ms steps(4, jump-none) infinite;
  }

  @keyframes wait-step {
    from {
      transform: translateX(0);
    }
    to {
      transform: translateX(355%);
    }
  }
}

/* §14.2: the issuing line. It sits with the method note rather than apart
   from it, because a signature is part of the document and not a credit. */
.report-issuer {
  color: var(--ink-muted);
}
