/* critic-loop — one stylesheet. Light and dark are both first class.
   Contrast ratios are stated where they were computed. */

:root {
  color-scheme: light dark;

  /* light (measured against the surfaces they sit on) */
  --bg:        #fbfbf9;
  --surface:   #ffffff;
  --quote:     #e1dacb;   /*  1.39 against surface — the critique panels step away from the drafts */
  --text:      #14161a;   /* 18.11 on surface, 13.52 on quote */
  --muted:     #53585f;   /*  7.17 on surface,  5.15 on quote */
  --border:    #6f747c;   /*  4.70 on surface,  3.38 on quote */
  --accent:    #8f3405;   /*  7.89 on surface */
  --on-accent: #ffffff;   /*  7.89 on accent */
  --del:       #8a2a2a;   /*  8.59 on surface */
  --ins:       #0f5c36;   /*  8.06 on surface */
  --disabled-bg:   #efeee9;
  --disabled-text: #5f646b; /* 5.13 on disabled-bg */

  /* The run-control strip is fixed to the foot of the viewport while a run is in
     flight. This much room is left below the footer at all times, so the strip
     appearing and disappearing never moves a pixel of the page. */
  --runbar-space: 4.75rem;

  --radius: 3px;
  --pad: 1rem;
  --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg:        #0f1114;
    --surface:   #171a1f;
    --quote:     #2e343c;  /*  1.39 against surface */
    --text:      #e9eaec;  /* 14.49 on surface, 10.43 on quote */
    --muted:     #a7adb5;  /*  7.71 on surface,  5.55 on quote */
    --border:    #7b838d;  /*  4.55 on surface,  3.27 on quote */
    --accent:    #ff9a52;  /*  8.30 on surface */
    --on-accent: #0f1114;  /*  9.00 on accent */
    --del:       #ff9d9d;  /*  8.77 on surface */
    --ins:       #6fd39b;  /*  9.52 on surface */
    --disabled-bg:   #22262c;
    --disabled-text: #9aa1a9; /* 5.82 on disabled-bg */
  }
}

* { box-sizing: border-box; }

html, body { margin: 0; padding: 0; }

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.55;
  -webkit-text-size-adjust: 100%;
  overflow-wrap: break-word;
}

.wrap {
  max-width: 44rem;
  margin: 0 auto;
  padding: 1.5rem 1rem calc(3rem + var(--runbar-space));
}

/* ---------- type ---------- */

h1 {
  font-size: 1.6rem;
  letter-spacing: -0.01em;
  margin: 0 0 0.5rem;
  font-weight: 600;
}

h2 {
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  font-weight: 600;
  color: var(--muted);
  margin: 0 0 0.7rem;
}

p { margin: 0 0 0.75rem; }

.lede { font-size: 1.02rem; margin-bottom: 0.9rem; }

.honesty {
  border-left: 3px solid var(--accent);
  padding: 0.1rem 0 0.1rem 0.75rem;
  margin: 0 0 1.5rem;
  color: var(--text);
}
.honesty strong { font-weight: 600; }

.masthead { margin-bottom: 1.5rem; }

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

.skip-link {
  position: absolute; left: 0.5rem; top: 0.5rem;
  background: var(--surface); color: var(--accent);
  padding: 0.5rem 0.75rem; border: 2px solid var(--accent); z-index: 10;
  transform: translateY(-250%);
}
.skip-link:focus { transform: none; }

/* ---------- cards ---------- */

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad);
  margin: 0 0 1.25rem;
}

/* ---------- form ---------- */

.field-label {
  display: block;
  font-size: 0.8rem;
  color: var(--muted);
  margin-bottom: 0.3rem;
}

textarea, input[type="password"], input[type="text"] {
  width: 100%;
  max-width: 100%;
  font-family: inherit;
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.6rem;
}
textarea { resize: vertical; min-height: 7.5rem; }
/* Read-only while a run is in flight: the draft stays readable, selectable and copyable,
   and the character counter beside it cannot drift away from the text the run captured. */
textarea.is-locked { background: var(--disabled-bg); cursor: default; }
::placeholder { color: var(--muted); opacity: 1; }

.counter { font-size: 0.8rem; color: var(--muted); margin: 0.35rem 0 0.9rem; }

.samples { margin-bottom: 0.9rem; }
.samples-label {
  display: block; font-size: 0.8rem; color: var(--muted); margin-bottom: 0.35rem;
}

.btn-row, .run-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}
.run-row { margin-top: 0.25rem; }

.btn {
  font: inherit;
  font-size: 0.9rem;
  line-height: 1.2;
  min-height: 44px;          /* tap target */
  padding: 0.5rem 0.8rem;
  color: var(--text);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  cursor: pointer;
  max-width: 100%;
}
.btn:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.btn-primary {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
  font-weight: 600;
}
.btn-primary:hover:not(:disabled) { color: var(--on-accent); }
/* Run stays the filled accent while it runs: the page's one accent element should not
   vanish exactly when the loop is alive. */
.btn.btn-primary:disabled {
  background: var(--accent);
  color: var(--on-accent);
  border-color: var(--accent);
  border-style: solid;
  cursor: progress;
}
.btn-quiet { background: transparent; }
/* A toggle button keeps its name in both positions and says which position it is in with
   aria-pressed, so the pressed position has to be drawn as well as exposed: the quote fill
   the page already uses for a stepped-away surface, an accent boundary, and a heavier
   name. The boundary is what carries it — its ratios are in the audit. */
.btn[aria-pressed="true"] {
  background: var(--quote);
  border-color: var(--accent);
  font-weight: 600;
}
.btn:disabled {
  background: var(--disabled-bg);
  color: var(--disabled-text);
  border-color: var(--border);
  border-style: dashed;
  cursor: not-allowed;
}

:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}
/* Run is the one control that is filled with the accent, so an accent ring on it is the
   accent on itself. Two rings instead: the inner one is the button's own text colour,
   which is measured against the fill it sits on, and the outer one is the page's text
   colour, measured against the page behind it. Both are stated in the audit. */
.btn-primary:focus-visible {
  outline: 2px solid var(--text);
  outline-offset: 3px;
  box-shadow: 0 0 0 3px var(--on-accent);
}

.status { font-size: 0.85rem; color: var(--muted); }
.status.is-error { color: var(--del); }
.status.is-busy::before {
  content: "\25CF";
  margin-right: 0.35rem;
  color: var(--accent);
}

/* ---------- the run-control strip ----------
   Stop and Skip are the two controls a reader needs at every moment of a run, and the
   transcript scrolls away from them as it follows the newest panel. So they ride a strip
   fixed to the foot of the viewport for as long as the run lasts: on screen at 320 and at
   1200, at 80 ms and at the end, without the reader scrolling away from the transcript.
   It is opaque, so its own contrast is measured against its own surface, and the page
   already reserves its height (--runbar-space), so nothing under it is unreachable. */

.runbar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 20;
  display: flex;
  justify-content: center;
  background: var(--surface);
  border-top: 2px solid var(--border);
  padding: 0.5rem 1rem calc(0.5rem + env(safe-area-inset-bottom, 0px));
}
.runbar[hidden] { display: none; }

.runbar-inner {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  width: 100%;
  max-width: 42rem;
}

/* ---------- engine ---------- */

.engine { border: 0; margin: 0; padding: 0; }
.radio {
  display: flex;
  gap: 0.55rem;
  align-items: flex-start;
  padding: 0.35rem 0;
  cursor: pointer;
}
.radio input { margin: 0.35rem 0 0; flex: none; accent-color: var(--accent); }
.radio-title { font-weight: 600; }
.radio-note { color: var(--muted); font-size: 0.85rem; display: block; }

.live-panel {
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--border);
}
.live-fields { display: flex; flex-wrap: wrap; gap: 0.75rem; }
.live-field { flex: 1 1 12rem; margin: 0; min-width: 0; }
.live-note { color: var(--muted); font-size: 0.85rem; margin: 0.75rem 0 0; }

/* ---------- transcript ---------- */

.empty-note { color: var(--muted); font-size: 0.9rem; }

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-left-width: 3px;
  border-radius: var(--radius);
  padding: var(--pad);
  margin: 0 0 0.75rem;
}
.panel-draft    { border-left-color: var(--border); }
.panel-critique { border-left-color: var(--accent); background: var(--quote); }
.panel-final    { border-left-color: var(--accent); }
.panel-note     { border-left-color: var(--accent); }

.panel-label {
  font-family: var(--mono);
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text);
  margin: 0 0 0.55rem;
  display: block;
}
.panel-label .lens { color: var(--accent); }

.panel-blurb { color: var(--muted); font-size: 0.88rem; margin: 0 0 0.75rem; }

.draft-text {
  margin: 0;
  font-size: 0.97rem;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  word-break: break-word;
}

.truncated-note, .capped-note {
  font-size: 0.82rem;
  color: var(--muted);
  margin: 0.5rem 0 0;
}
/* The note about a capped quote rides with its quote inside the summary. Closed, the row
   is one line and the stylesheet clips the quote to it, so the note is not part of that
   line; open, it sits directly under the quote box it describes. */
.finding:not([open]) > summary .quote-note { display: none; }
.finding[open] > summary .quote-note {
  display: block;
  margin: 0.25rem 0 0 1em;
}

/* metrics */
.metrics {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 1rem;
  margin: 0.8rem 0 0;
  padding: 0.55rem 0 0;
  border-top: 1px solid var(--border);
  font-size: 0.82rem;
  color: var(--muted);
  font-family: var(--mono);
}
.metric { white-space: normal; overflow-wrap: anywhere; }
.metric-name { text-transform: uppercase; letter-spacing: 0.05em; }
.metric-val { color: var(--text); }
.metric-delta { color: var(--accent); }

/* findings */
.findings { list-style: none; margin: 0; padding: 0; }
.finding {
  border-top: 1px solid var(--border);
  padding: 0.1rem 0;
}
.finding:first-child { border-top: 0; }

/* Closed, a finding is one row: the disclosure triangle, the rule name and the quoted
   span side by side, both clipped with an ellipsis rather than allowed to wrap, and the
   row is at least a tap target tall. Open, the quote drops to its own full-width line and
   the why and the replacement follow it. */
.finding > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  gap: 0.1rem 0.45rem;
  min-height: 44px;
  padding: 0.2rem 0;
}
.finding:not([open]) > summary { flex-wrap: nowrap; }
/* Open, the row is a block again: the rule name sits on the first line and the quote drops
   below it as a box the width of the quote, not a field stretched to the panel edge. */
.finding[open] > summary { display: block; padding-top: 0.5rem; }
.finding > summary::-webkit-details-marker { display: none; }
.finding > summary::before {
  content: "\25B8";
  color: var(--accent);
  flex: none;
  display: inline-block;
  width: 1em;
  font-size: 1rem;
  font-family: var(--mono);
}
.finding[open] > summary::before { content: "\25BE"; }

.rule-name {
  /* The rule name keeps its own column: without a floor the quote's flex basis crushes a
     long quote's rule name down to one letter and an ellipsis. */
  flex: 0 1 auto;
  min-width: 5.5rem;
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  font-family: var(--mono);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.quote {
  display: block;
  max-width: 100%;
  margin: 0.4rem 0 0 1em;
  padding: 0.25rem 0.5rem;
  background: var(--surface);
  border-left: 3px solid var(--accent);
  font-family: var(--mono);
  font-size: 0.86rem;
  overflow-wrap: anywhere;
  word-break: break-word;
  white-space: pre-wrap;
}
/* Closed, the quote box is as wide as the quote and no wider: a row of boxes stretched to
   the panel edge reads as a column of empty form fields. */
.finding > summary .quote {
  flex: 0 1 auto;
  min-width: 0;
  margin: 0;
}
.finding:not([open]) > summary .quote {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.finding[open] > summary .quote {
  display: block;
  width: fit-content;
  max-width: 100%;
  margin: 0.35rem 0 0 1em;
}

/* Expand all / collapse all, one per critique panel. */
.findings-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  margin: 0 0 0.35rem;
}
.findings-controls .btn { flex: 0 0 auto; }
.expand-all { font-size: 0.85rem; }

.finding-body { margin: 0.3rem 0 0.5rem 1em; }
.why { margin: 0 0 0.35rem; font-size: 0.9rem; }
.repl {
  margin: 0;
  font-size: 0.88rem;
  overflow-wrap: anywhere;
}
.repl-arrow { color: var(--accent); font-family: var(--mono); }
.repl-text { font-family: var(--mono); color: var(--ins); }
.repl-none { color: var(--muted); font-style: italic; }
.repl-empty { color: var(--muted); }

.metrics-head {
  font-size: 0.82rem;
  color: var(--muted);
  font-family: var(--mono);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin: 0.9rem 0 0;
}

.pass-summary { font-size: 0.85rem; color: var(--muted); margin: 0.75rem 0 0; }

/* diff */
.diff-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  margin: 0 0 0.6rem;
}
.diff-legend { font-size: 0.78rem; color: var(--muted); font-family: var(--mono); }

.diff { font-size: 0.97rem; white-space: pre-wrap; overflow-wrap: anywhere; word-break: break-word; }
/* The marked run holds every character of its op, whitespace included, so the diff still
   reconstructs both drafts byte for byte. The − and + are generated content and the gap
   that keeps them off the preceding word is margin and padding, so neither the marker nor
   the spacing puts a character into the text.

   The marker hangs off .d-head, which holds the run's own first word and nothing else —
   no character moves between nodes to build it. .d-head is an atomic inline, so the line
   can never break between the marker and that word, and the marker is never left alone at
   a right margin reading as an em dash. A first word too long for the line still wraps,
   inside .d-head, because the head lets its own content break. */
.d-del, .d-ins {
  margin-left: 0.22em;
  padding: 0 0.1em;
  border-radius: 2px;
}
/* The line belongs to the marked words, not to the run's outer spaces: <del> and <ins>
   carry no decoration of their own (the browser's default is turned off), and the head and
   the tail — the run's own first word and everything after it up to its last non-space
   character — draw it themselves. A space left hanging at a right margin then draws
   nothing, instead of a short line that reads as one more dash. */
.d-head, .d-tail { text-decoration-thickness: 2px; }
.d-head {
  display: inline-block;
  max-width: 100%;
  overflow-wrap: anywhere;
  word-break: break-word;
}
.d-head::before {
  display: inline-block;   /* atomic inline: the line through the run does not cross it */
  font-family: var(--mono);
  font-weight: 700;
  padding-right: 0.06em;
}
.d-del {
  color: var(--del);
  text-decoration: none;
}
.d-del .d-head, .d-del .d-tail { text-decoration: line-through; }
.d-del .d-head::before { content: "\2212"; }
.d-ins {
  color: var(--ins);
  text-decoration: none;
}
.d-ins .d-head, .d-ins .d-tail {
  text-decoration: underline;
  text-underline-offset: 2px;
}
.d-ins .d-head::before { content: "+"; }

/* final */
.final-head { font-weight: 600; margin: 0 0 0.5rem; }
.final-actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 0.9rem; }

.foot {
  margin-top: 2rem;
  padding-top: 1rem;
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: 0.82rem;
}

/* ---------- stepping ---------- */

.panel-enter { animation: fade-in 220ms ease-out both; }
@keyframes fade-in {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .panel-enter { animation: none; }
  * { transition: none !important; }
}

/* ---------- narrow ---------- */

@media (max-width: 26rem) {
  .wrap { padding: 0.75rem 0.75rem calc(2rem + var(--runbar-space)); }
  h1 { font-size: 1.25rem; margin-bottom: 0.35rem; }
  .lede { font-size: 0.92rem; margin-bottom: 0.6rem; }
  .honesty { font-size: 0.86rem; margin-bottom: 0.85rem; padding-left: 0.6rem; }
  .masthead { margin-bottom: 0.85rem; }
  h2 { margin-bottom: 0.5rem; }
  .card { padding: 0.75rem; margin-bottom: 0.85rem; }
  textarea { min-height: 5.5rem; }
  .counter { margin: 0.3rem 0 0.6rem; }
  .samples { margin: 0.9rem 0 0; }
  .btn { flex: 1 1 auto; }
  .run-row .status { flex: 1 1 100%; }
  .quote, .finding-body { margin-left: 0.6rem; }
}
