/*
 * harness.css - the whole UI, one stylesheet, no preprocessor.
 *
 * Constraints that shaped this file:
 *
 *  - 16px base font size, everywhere, non-negotiable. iOS Safari and WKWebView zoom
 *    the viewport when a focused input has a computed font-size below 16px, and that
 *    zoom shifts the layout mid-test. A harness that triggers the bug it is measuring
 *    cannot measure it.
 *
 *  - 44x44px minimum for anything tappable. Below that, a failed tap could be the
 *    harness's fault rather than the WebView's.
 *
 *  - Every fixed edge is inset with env(safe-area-inset-*) AND a plain fallback, so
 *    the harness's own chrome stays reachable on a notched device even while the
 *    safe-area suite is reporting that env() returns nothing.
 *
 *  - A sentinel custom property the harness reads back, so "the stylesheet did not
 *    load" is detected rather than silently rendering an unstyled but functional page
 *    that a tester might mistake for a broken WebView.
 */

:root {
  /* Read back by the harness suite to confirm this file actually applied. */
  --wv-stylesheet: "loaded";

  --bg: #0f1419;
  --bg-raised: #161c24;
  --bg-sunken: #0b0f14;
  --card: #1a2027;
  --card-hover: #202833;
  --border: #2a333f;
  --border-strong: #3a4654;

  --text: #e8eaed;
  --text-dim: #9aa4b2;
  --text-faint: #6b7684;

  --pass: #4ade80;
  --pass-bg: #0e2a1c;
  --fail: #f87171;
  --fail-bg: #2d1416;
  --warn: #fbbf24;
  --warn-bg: #2b2007;
  --blocked: #fb923c;
  --blocked-bg: #2c1a0b;
  --unsupported: #a78bfa;
  --unsupported-bg: #211a33;
  --skipped: #64748b;
  --skipped-bg: #1a1f26;
  --error: #f0abfc;
  --error-bg: #2a1030;
  --pending: #38bdf8;
  --pending-bg: #082736;

  /*
   * #00843f, not the brighter #00a859 this started as. White text on the brighter
   * green measured 3.11:1, below the 4.5:1 WCAG AA threshold for normal text; this
   * one measures about 4.8:1. Caught by this harness's own contrast check, which
   * correctly reported it as "the harness stylesheet being thin, NOT a WebView fault".
   * A harness that fails its own accessibility suite is not one anyone should trust.
   */
  --brand: #00843f;
  --brand-accent: #00a859;
  --brand-dim: #047a42;

  --radius: 10px;
  --radius-sm: 6px;

  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
  --safe-l: env(safe-area-inset-left, 0px);
  --safe-r: env(safe-area-inset-right, 0px);

  --header-h: 92px;
  --actionbar-h: 68px;
}

/* The harness is dark by design, but a WebView with algorithmic darkening forced on
 * will try to invert it anyway. Declaring the scheme tells a well-behaved engine not
 * to, and the rendering suite checks whether it listened. */
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) {
    --bg: #f6f7f9;
    --bg-raised: #ffffff;
    --bg-sunken: #eceef1;
    --card: #ffffff;
    --card-hover: #f2f4f7;
    --border: #d8dde4;
    --border-strong: #b9c2cd;
    --text: #141a21;
    --text-dim: #4d5967;
    --text-faint: #7b8795;
    --pass-bg: #e6f8ee;
    --fail-bg: #fdeaea;
    --warn-bg: #fdf4e3;
    --blocked-bg: #fdefe2;
    --unsupported-bg: #f0ecfd;
    --skipped-bg: #eef1f4;
    --error-bg: #fbeefd;
    --pending-bg: #e5f4fd;
  }
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

body {
  margin: 0;
  /* 16px floor. See the note at the top of this file. */
  font: 16px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;
  color: var(--text);
  background: var(--bg);
  /* The harness must not itself introduce horizontal overflow; the layout suite
   * measures document scrollWidth and would otherwise report its own chrome. */
  overflow-x: hidden;

  /*
   * Every surface in this UI displays MACHINE-GENERATED text: user agents, URLs,
   * data: URIs, cookie values, stack traces. None of it contains spaces to break at.
   *
   * Without this, one long unbroken token stretches the document to its own width,
   * the page becomes infinitely zoomable, and the layout suite then measures the
   * harness's own chrome as horizontal overflow. That shipped once - a failed
   * base64 data: URI in an error banner made the page several thousand pixels wide.
   *
   * `anywhere` rather than `break-word` on purpose: only `anywhere` reduces the
   * min-content width, which is what actually stops a flex or grid child from
   * pushing its container open.
   */
  overflow-wrap: anywhere;
  word-break: break-word;
}

/* Belt and braces: nothing in the layout may establish a width larger than the
 * viewport, whatever it contains. */
.wv-app, .wv-main, .wv-header, .wv-banner, .wv-test, .wv-phase, .wv-verdict {
  max-width: 100%;
  min-width: 0;
}

h1, h2, h3 { margin: 0 0 8px; line-height: 1.3; }
h1 { font-size: 20px; }
h2 { font-size: 17px; }
h3 { font-size: 15px; }
p { margin: 0 0 10px; }
a { color: var(--pending); }

code, .mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
}
code { background: var(--bg-sunken); padding: 1px 5px; border-radius: 4px; word-break: break-word; }

pre {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px;
  line-height: 1.45;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 10px;
  margin: 8px 0;
  white-space: pre-wrap;
  word-break: break-word;
  overflow-x: auto;
  color: var(--text-dim);
}

/* ====================================================================== *
 * Boot / failure states
 * ====================================================================== */

.wv-boot {
  padding: calc(24px + var(--safe-t)) calc(20px + var(--safe-r)) calc(24px + var(--safe-b)) calc(20px + var(--safe-l));
  max-width: 760px;
  margin: 0 auto;
}
.wv-boot-status { color: var(--text-dim); }

.wv-bootfail {
  border: 1px solid var(--fail);
  background: var(--fail-bg);
  border-radius: var(--radius);
  padding: 16px;
  margin: 14px 0;
}
.wv-bootfail h1, .wv-bootfail h2 { color: var(--fail); }
.wv-bootfail-reason { font-weight: 600; }
.wv-bootfail ul, .wv-bootfail ol { margin: 8px 0; padding-left: 22px; }
.wv-bootfail li { margin: 4px 0; }
.wv-bootfail-ua { font-size: 11.5px; }

#wv-stage {
  position: absolute;
  left: -10000px;
  top: 0;
  width: 360px;
  height: 640px;
  overflow: hidden;
  pointer-events: none;
}

/* ====================================================================== *
 * App shell
 * ====================================================================== */

.wv-app { padding-bottom: calc(var(--actionbar-h) + var(--safe-b) + 12px); }

.wv-header {
  position: -webkit-sticky;
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--bg-raised);
  border-bottom: 1px solid var(--border);
  padding: calc(10px + var(--safe-t)) calc(14px + var(--safe-r)) 10px calc(14px + var(--safe-l));
}

.wv-header-top {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
}
.wv-header-title { font-size: 15px; font-weight: 600; margin: 0; }
.wv-header-count { font-size: 13px; color: var(--text-dim); white-space: nowrap; }

/* Segmented progress. Width is set inline by the UI from answered/total, where the
 * denominator counts every DECLARED test. A suite that failed to register therefore
 * holds the bar below 100%, which is the point. */
.wv-progress {
  height: 6px;
  background: var(--bg-sunken);
  border-radius: 3px;
  overflow: hidden;
  margin: 8px 0 6px;
}
.wv-progress-bar { height: 100%; background: var(--brand-accent); width: 0; transition: width .2s ease; }
.wv-progress-bar.has-fail { background: var(--fail); }

.wv-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.wv-chip {
  font-size: 12px;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-sunken);
  color: var(--text-dim);
  white-space: nowrap;
}
.wv-chip-fail { color: var(--fail); border-color: var(--fail); background: var(--fail-bg); }
.wv-chip-blocked { color: var(--blocked); border-color: var(--blocked); background: var(--blocked-bg); }
.wv-chip-skipped { color: var(--skipped); border-color: var(--skipped); }
.wv-chip-error { color: var(--error); border-color: var(--error); background: var(--error-bg); }
.wv-chip-pass { color: var(--pass); border-color: var(--pass); background: var(--pass-bg); }
.wv-chip-warn { color: var(--warn); border-color: var(--warn); background: var(--warn-bg); }

.wv-main {
  padding: 14px calc(14px + var(--safe-r)) 14px calc(14px + var(--safe-l));
  max-width: 820px;
  margin: 0 auto;
}

/* ====================================================================== *
 * Banners
 * ====================================================================== */

.wv-banner {
  border: 1px solid var(--border-strong);
  background: var(--bg-raised);
  border-left-width: 4px;
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin: 0 0 12px;
  font-size: 14px;
}
.wv-banner h3 { font-size: 14px; }
.wv-banner-warn { border-left-color: var(--warn); }
.wv-banner-fail { border-left-color: var(--fail); }
.wv-banner-info { border-left-color: var(--pending); }
.wv-banner p:last-child { margin-bottom: 0; }

/* ====================================================================== *
 * Phase gate
 * ====================================================================== */

.wv-phase {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px;
  margin: 0 0 14px;
}
.wv-phase-kicker { font-size: 12px; letter-spacing: .06em; text-transform: uppercase; color: var(--text-faint); }
.wv-phase-title { font-size: 19px; margin: 4px 0 8px; }
.wv-phase-posture {
  font-size: 16px;
  font-weight: 600;
  background: var(--pending-bg);
  border: 1px solid var(--pending);
  border-radius: var(--radius-sm);
  padding: 12px;
  margin: 10px 0;
  color: var(--text);
}
.wv-phase-hint { color: var(--text-dim); font-size: 14px; }
.wv-phase-count { color: var(--text-dim); font-size: 14px; }

/* ====================================================================== *
 * Suites and test cards
 * ====================================================================== */

.wv-suite { margin: 0 0 10px; }
.wv-suite-head {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  text-align: left;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 12px 14px;
  min-height: 48px;
  color: var(--text);
  font: inherit;
  -webkit-appearance: none;
  appearance: none;
  touch-action: manipulation;
}
.wv-suite-head:active { background: var(--card-hover); }
.wv-suite-name { flex: 1 1 auto; font-weight: 600; font-size: 15px; }
.wv-suite-tally { font-size: 13px; color: var(--text-dim); white-space: nowrap; }
.wv-suite-body { padding: 8px 0 0; }

.wv-test {
  background: var(--card);
  border: 1px solid var(--border);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  padding: 12px 14px;
  margin: 0 0 8px;
}
.wv-test[data-status="pass"] { border-left-color: var(--pass); }
.wv-test[data-status="fail"] { border-left-color: var(--fail); background: var(--fail-bg); }
.wv-test[data-status="warn"] { border-left-color: var(--warn); }
.wv-test[data-status="blocked"] { border-left-color: var(--blocked); background: var(--blocked-bg); }
.wv-test[data-status="unsupported"] { border-left-color: var(--unsupported); }
.wv-test[data-status="skipped"] { border-left-color: var(--skipped); }
.wv-test[data-status="error"] { border-left-color: var(--error); background: var(--error-bg); }
.wv-test[data-status="pending"], .wv-test[data-status="navigating"] { border-left-color: var(--pending); }
.wv-test[data-current="1"] { border-color: var(--pending); box-shadow: 0 0 0 2px rgba(56, 189, 248, .25); }

.wv-test-head { display: flex; align-items: flex-start; gap: 10px; }
.wv-test-title { flex: 1 1 auto; font-weight: 600; font-size: 15px; }
.wv-test-sev {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--text-faint);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 1px 7px;
  white-space: nowrap;
}
.wv-test-sev[data-sev="blocker"] { color: var(--fail); border-color: var(--fail); }

.wv-pill {
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .04em;
  border-radius: 999px;
  padding: 2px 9px;
  white-space: nowrap;
  border: 1px solid transparent;
}
.wv-pill[data-status="pass"] { color: var(--pass); background: var(--pass-bg); border-color: var(--pass); }
.wv-pill[data-status="fail"] { color: var(--fail); background: var(--fail-bg); border-color: var(--fail); }
.wv-pill[data-status="warn"] { color: var(--warn); background: var(--warn-bg); border-color: var(--warn); }
.wv-pill[data-status="blocked"] { color: var(--blocked); background: var(--blocked-bg); border-color: var(--blocked); }
.wv-pill[data-status="unsupported"] { color: var(--unsupported); background: var(--unsupported-bg); border-color: var(--unsupported); }
.wv-pill[data-status="skipped"] { color: var(--skipped); background: var(--skipped-bg); border-color: var(--skipped); }
.wv-pill[data-status="error"] { color: var(--error); background: var(--error-bg); border-color: var(--error); }
.wv-pill[data-status="pending"], .wv-pill[data-status="navigating"] { color: var(--pending); background: var(--pending-bg); border-color: var(--pending); }
.wv-pill[data-status="notrun"] { color: var(--text-faint); border-color: var(--border); }

.wv-test-why { font-size: 14px; color: var(--text-dim); margin: 8px 0 0; }
.wv-test-instruction {
  font-size: 15px;
  background: var(--pending-bg);
  border: 1px solid var(--pending);
  border-radius: var(--radius-sm);
  padding: 11px 12px;
  margin: 10px 0;
}
.wv-test-message { font-size: 14px; margin: 8px 0 0; }
.wv-test-scratch { margin: 10px 0 0; }
.wv-test-stale {
  font-size: 12px;
  color: var(--warn);
  border: 1px dashed var(--warn);
  border-radius: var(--radius-sm);
  padding: 4px 8px;
  margin: 8px 0 0;
  display: inline-block;
}

.wv-details { margin: 10px 0 0; }
.wv-details summary {
  font-size: 13px;
  color: var(--text-dim);
  cursor: pointer;
  min-height: 32px;
  padding: 6px 0;
  touch-action: manipulation;
}
.wv-fix dt { font-weight: 600; font-size: 13px; margin: 8px 0 2px; color: var(--text); }
.wv-fix dd { margin: 0 0 6px; font-size: 13px; color: var(--text-dim); }

/* ====================================================================== *
 * Buttons
 * ====================================================================== */

.wv-btn,
button.wv-btn {
  display: inline-block;
  font: inherit;
  font-weight: 600;
  color: var(--text);
  background: var(--bg-raised);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  padding: 12px 16px;
  /* 44px floor so a missed tap is never the harness's fault. */
  min-height: 44px;
  min-width: 44px;
  -webkit-appearance: none;
  appearance: none;
  touch-action: manipulation;
  -webkit-tap-highlight-color: rgba(255, 255, 255, .1);
}
.wv-btn:active { background: var(--card-hover); }
.wv-btn[disabled] { opacity: .45; }

/*
 * These modifiers are written as .wv-btn.wv-btn-X rather than .wv-btn-X on purpose.
 *
 * The base rule above is `.wv-btn, button.wv-btn`, and `button.wv-btn` has specificity
 * (0,1,1) which beats a lone `.wv-btn-primary` at (0,1,0). Written the obvious way, the
 * primary button rendered with the default dark background and the Correct / Wrong /
 * Can't-tell buttons were visually identical - every modifier silently lost.
 *
 * Found by this harness's own accessibility suite, which compares the colour a node
 * actually renders against the colour its stylesheet declares. It was looking for
 * Android algorithmic darkening and found a CSS bug of ours instead, which is a fair
 * demonstration that the check works.
 */
.wv-btn.wv-btn-primary { background: var(--brand); border-color: var(--brand); color: #fff; }
.wv-btn.wv-btn-good { border-color: var(--pass); color: var(--pass); }
.wv-btn.wv-btn-bad { border-color: var(--fail); color: var(--fail); }
.wv-btn.wv-btn-neutral { border-color: var(--border-strong); color: var(--text-dim); }
.wv-btn.wv-btn-block { display: block; width: 100%; }

.wv-ask { margin: 10px 0 0; }
.wv-ask-q { font-size: 15px; font-weight: 600; margin: 0 0 8px; }
/* Verdict buttons are equally weighted and "correct" is never first, so a tester
 * tapping through at speed cannot mash a green default. */
.wv-ask-row { display: flex; flex-wrap: wrap; gap: 8px; }
.wv-ask-row .wv-btn { flex: 1 1 30%; }
.wv-ask-answered { opacity: .6; }

/*
 * Two rows, not one.
 *
 * Four controls across a 375px screen left the secondary labels breaking mid-word
 * ("Suit / es"). The primary action gets its own full-width row because it is pressed
 * roughly two hundred times in a run; the rest share the row below it.
 */
.wv-actionbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 50;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  background: var(--bg-raised);
  border-top: 1px solid var(--border);
  padding: 10px calc(12px + var(--safe-r)) calc(10px + var(--safe-b)) calc(12px + var(--safe-l));
}
.wv-actionbar .wv-btn-primary {
  flex: 1 0 100%;
  order: -1;
}
.wv-actionbar .wv-btn-neutral {
  flex: 1 1 0;
  min-width: 0;
  padding-left: 6px;
  padding-right: 6px;
  font-size: 14px;
  font-weight: 500;
  /* Never split a label across lines; shrink the box instead. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Wide enough for one row: put everything back on it. */
@media (min-width: 560px) {
  .wv-actionbar .wv-btn-primary { flex: 1 1 auto; order: 0; }
}

.wv-filters { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 12px; }
.wv-filter {
  font: inherit;
  font-size: 13px;
  padding: 7px 11px;
  min-height: 36px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg-sunken);
  color: var(--text-dim);
  -webkit-appearance: none;
  appearance: none;
  touch-action: manipulation;
}
.wv-filter[aria-pressed="true"] { color: var(--text); border-color: var(--pending); background: var(--pending-bg); }

/* ====================================================================== *
 * Test fixtures used by the suites
 * ====================================================================== */

.wv-frame { border: 1px solid var(--border); border-radius: var(--radius-sm); width: 100%; background: #fff; }
.wv-frame-hidden { position: absolute; left: -10000px; width: 320px; height: 240px; }

.wv-probe { /* styled per-test; no defaults, so a probe measures what the test set */ }

.wv-target-grid { display: flex; flex-wrap: wrap; gap: 10px; margin: 10px 0; }
.wv-target {
  background: var(--brand-dim);
  color: #fff;
  border: 0;
  border-radius: 4px;
  font-size: 11px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  touch-action: manipulation;
}
.wv-target[data-hit="1"] { background: var(--pass); color: #04210f; }

.wv-field {
  width: 100%;
  font: inherit;
  /* 16px again: a smaller input font makes iOS zoom on focus, which would corrupt
   * every keyboard and viewport measurement taken while it is focused. */
  font-size: 16px;
  padding: 12px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-strong);
  background: var(--bg-sunken);
  color: var(--text);
  -webkit-appearance: none;
  appearance: none;
}
.wv-label { display: block; font-size: 13px; color: var(--text-dim); margin: 10px 0 4px; }

.wv-log {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11.5px;
  line-height: 1.45;
  max-height: 180px;
  overflow-y: auto;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 8px;
  white-space: pre-wrap;
  color: var(--text-dim);
}

.wv-kv { width: 100%; border-collapse: collapse; font-size: 13px; margin: 8px 0; }
.wv-kv th, .wv-kv td {
  text-align: left;
  padding: 5px 8px;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}
.wv-kv th { color: var(--text-dim); font-weight: 600; width: 40%; }
.wv-kv td { word-break: break-word; }

/* ====================================================================== *
 * Final verdict
 * ====================================================================== */

.wv-verdict {
  border-radius: var(--radius);
  border: 2px solid var(--border-strong);
  padding: 18px;
  margin: 0 0 14px;
  text-align: center;
}
.wv-verdict-headline { font-size: 22px; font-weight: 700; margin: 0 0 6px; }
.wv-verdict-sub { color: var(--text-dim); font-size: 14px; margin: 0; }
.wv-verdict[data-state="ok"] { border-color: var(--pass); background: var(--pass-bg); }
.wv-verdict[data-state="ok"] .wv-verdict-headline { color: var(--pass); }
.wv-verdict[data-state="fail"] { border-color: var(--fail); background: var(--fail-bg); }
.wv-verdict[data-state="fail"] .wv-verdict-headline { color: var(--fail); }
/* Inconclusive is styled with the SAME visual weight as a failure. A run with
 * unanswered checks must never read as a soft pass. */
.wv-verdict[data-state="inconclusive"] { border-color: var(--warn); background: var(--warn-bg); }
.wv-verdict[data-state="inconclusive"] .wv-verdict-headline { color: var(--warn); }

.wv-impact { margin: 0 0 14px; }
.wv-impact h3 { font-size: 15px; margin: 14px 0 6px; }
.wv-impact ul { margin: 0; padding-left: 20px; }
.wv-impact li { margin: 5px 0; font-size: 14px; }

.wv-summary-text {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  white-space: pre-wrap;
  background: var(--bg-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  padding: 12px;
  color: var(--text-dim);
}

.wv-provenance { font-size: 12px; color: var(--text-faint); margin: 16px 0 0; }
.wv-provenance td { padding: 2px 8px 2px 0; vertical-align: top; }

@media (prefers-reduced-motion: reduce) {
  .wv-progress-bar { transition: none; }
}

[hidden] { display: none !important; }

.wv-visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
