/* testai. The look is FitKeep's Tests screen, lifted with its tokens:
   the same greens and reds, one border weight, and nothing that shouts on
   a green run.

   HIDDEN MEANS HIDDEN. `hidden` is a UA `display: none` that any
   `display` below would override -- which is exactly how FitKeep's Tests
   screen once drew a group's tests underneath a list that would not hide.
   One rule, said once, instead of a guard per component. */
[hidden] { display: none !important; }

/* Light by default, dark when the system says so, and either when the
   reader picks one (data-theme, set before paint in index.html). */
:root {
  --bg: #F7F4EE;
  --surface: #FFFFFF;
  --line: #DED7C9;
  --ink: #1A1C20;
  --muted: #5C6068;
  /* "Not run" and "skipped": an absence of news, not a control that
     will not respond. */
  --affordance: #6B6F76;
  --danger: #C62B1E;
  --ok: #2F7D4F;
  --accent: #B4791A;
  --r-field: 14px;
  --r-card: 12px;
  --tap: 44px;
  --gutter: 16px;
  color-scheme: light;
  font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0D0E10;
    --surface: #17191D;
    --line: #2E3238;
    --ink: #F4F2ED;
    --muted: #A9ADB4;
    --affordance: #7D828A;
    --danger: #FF5B4F;
    --ok: #7ED69A;
    --accent: #F2B544;
    color-scheme: dark;
  }
}

:root[data-theme="dark"] {
  --bg: #0D0E10;
  --surface: #17191D;
  --line: #2E3238;
  --ink: #F4F2ED;
  --muted: #A9ADB4;
  --affordance: #7D828A;
  --danger: #FF5B4F;
  --ok: #7ED69A;
  --accent: #F2B544;
  color-scheme: dark;
}

* { box-sizing: border-box; }

html, body { margin: 0; }
body {
  background: var(--bg);
  color: var(--ink);
  font-size: 1rem;
  line-height: 1.4;
  /* The page never scrolls sideways. Only the runs table does, inside
     its own scroller. */
  overflow-x: hidden;
}

.bar {
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 3rem;
  padding: env(safe-area-inset-top) var(--gutter) 0;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.bar h1 {
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
  font-size: 1.05rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.column {
  max-width: 34rem;
  margin: 0 auto;
  padding: 0.75rem var(--gutter) calc(2rem + env(safe-area-inset-bottom));
}

button, input { font: inherit; color: inherit; }
button { cursor: pointer; }
button:disabled { cursor: default; opacity: 0.7; }

.chip {
  min-height: 2rem;
  padding: 0 0.7rem;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--surface);
  font-size: 0.8rem;
}
.linkish {
  padding: 0;
  border: 0;
  background: none;
  color: var(--accent);
  font-size: 0.9rem;
  text-decoration: none;
}

.status { min-height: 1.2em; margin: 0 0 0.5rem; color: var(--muted); font-size: 0.85rem; }
.note { color: var(--muted); font-size: 0.9rem; }
.error { min-height: 1.2em; margin: 0; color: var(--danger); font-size: 0.85rem; }

/* ---- sign in ------------------------------------------------------------ */

.login { display: flex; flex-direction: column; gap: 0.5rem; max-width: 22rem; margin-top: 2rem; }
.login input {
  min-height: var(--tap);
  padding: 0 0.8rem;
  border: 1px solid var(--line);
  border-radius: var(--r-field);
  background: var(--surface);
}
.login button[type="submit"] {
  min-height: var(--tap);
  border: 0;
  border-radius: var(--r-field);
  background: var(--accent);
  color: #17120A;
  font-weight: 600;
}

/* ---- projects ----------------------------------------------------------- */

.project-list { display: flex; flex-direction: column; gap: 0.4rem; }
.project-card {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  min-height: var(--tap);
  padding: 0.7rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  background: var(--surface);
  color: var(--ink);
  text-decoration: none;
}
.project-card[data-ok="false"] { border-color: var(--danger); }
.project-name { min-width: 0; font-weight: 600; overflow-wrap: anywhere; }
.project-result {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  flex: 0 0 auto;
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
}
.project-card[data-ok="true"] .run-clock { color: var(--ok); font-weight: 600; }
.project-card[data-ok="false"] .run-count { color: var(--danger); font-weight: 600; }
.project-result .run-release { color: var(--muted); font-size: 0.72rem; }

/* ---- one project: the runs strip ---------------------------------------- */

/* A TABLE THAT SLIDES. The result leads; the release is reached by
   sliding right. The last visible edge fades, which says there is more
   without a scrollbar to point at it. */
.runs-scroll {
  overflow-x: auto;
  scrollbar-width: none;
  margin-bottom: 0.9rem;
  -webkit-mask-image: linear-gradient(to left, transparent 0, #000 0.4cm);
  mask-image: linear-gradient(to left, transparent 0, #000 0.4cm);
}
.runs-scroll::-webkit-scrollbar { display: none; }

.runs-table {
  border-collapse: collapse;
  font-size: 0.82rem;
  /* Wide enough that the second column peeks in on a narrow phone: a
     table that stops exactly at the edge looks like the whole table. */
  min-width: 22rem;
}
.runs-table th {
  text-align: start;
  padding: 0.2rem 0.6rem 0.2rem 0;
  border-bottom: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
}
.runs-table td {
  padding: 0.4rem 0.6rem 0.4rem 0;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
  white-space: nowrap;
}
.runs-table tr:last-child td { border-bottom: 0; }

/* Sticky, so sliding never takes away the thing that identifies the row.
   The page's background, or the columns scroll visibly under it. */
.runs-table th:first-child,
.runs-table td:first-child {
  position: sticky;
  inset-inline-start: 0;
  background: var(--bg);
  padding-inline-end: 0.75rem;
}

.run-result { display: flex; flex-direction: column; gap: 0.1rem; font-weight: 600; }
.run-clock { font-variant-numeric: tabular-nums; }
.runs-table tr[data-ok="true"] .run-clock { color: var(--ok); }
.runs-table tr[data-ok="false"] .run-count { color: var(--danger); }
/* Muted under the count: on a red row the number is the headline and
   the time is the footnote. */
.runs-table tr[data-ok="false"] .run-clock { color: var(--muted); font-weight: 400; font-size: 0.75rem; }
.run-partial { color: var(--affordance); font-size: 0.7rem; font-style: italic; font-weight: 400; }
.run-version { font-variant-numeric: tabular-nums; }
.run-release { display: block; }
.run-commit { display: block; color: var(--muted); font-size: 0.7rem; font-family: ui-monospace, monospace; }

/* ---- what failed, named ------------------------------------------------- */

.failures {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin-bottom: 0.9rem;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--danger);
  border-radius: var(--r-card);
}
.failures-title {
  margin: 0;
  color: var(--danger);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.failure { display: flex; flex-direction: column; gap: 0.1rem; }
.failure-name { font-size: 0.88rem; overflow-wrap: anywhere; }
/* Output, not prose: monospace, and wrapping anywhere because an
   assertion message has no spaces to break at. */
.failure-message {
  color: var(--danger);
  font-size: 0.74rem;
  font-family: ui-monospace, monospace;
  overflow-wrap: anywhere;
}
.failure-where { color: var(--muted); font-size: 0.7rem; overflow-wrap: anywhere; }

/* ---- groups ------------------------------------------------------------- */

.groups { display: flex; flex-direction: column; gap: 0.4rem; }
.group {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  width: 100%;
  min-height: var(--tap);
  padding: 0.7rem 0.75rem;
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  background: var(--surface);
  text-align: start;
}
.group-name { min-width: 0; overflow-wrap: anywhere; }
.group-counts { display: flex; align-items: baseline; gap: 0.5rem; flex: 0 0 auto; }
.group-size { color: var(--muted); font-size: 0.8rem; white-space: nowrap; }
.group-good { color: var(--ok); font-size: 0.8rem; }
.group-bad { color: var(--danger); font-size: 0.8rem; font-weight: 600; }
/* "Not run" is deliberately not green: it is not a pass. */
.group-idle { color: var(--affordance); font-size: 0.8rem; font-style: italic; }

/* ---- one group's tests -------------------------------------------------- */

.detail { display: flex; flex-direction: column; gap: 0.5rem; }
.detail-head { display: flex; align-items: baseline; gap: 0.75rem; }
.detail-head h3 { margin: 0; font-size: 1rem; overflow-wrap: anywhere; }

.test-file {
  margin: 0.75rem 0 0;
  color: var(--muted);
  font-size: 0.72rem;
  font-family: ui-monospace, monospace;
  overflow-wrap: anywhere;
}
.test-item { padding: 0.45rem 0; border-bottom: 1px solid var(--line); }
.test-item:last-child { border-bottom: 0; }
.test-title { font-size: 0.92rem; overflow-wrap: anywhere; }
.test-context { margin-top: 0.15rem; color: var(--muted); font-size: 0.72rem; overflow-wrap: anywhere; }

/* A line under the title rather than a badge beside it: the title often
   wraps, and a badge against wrapping text lands somewhere different on
   every row. */
.verdict { display: flex; align-items: baseline; gap: 0.5rem; font-size: 0.75rem; font-variant-numeric: tabular-nums; }
.verdict-mark { font-weight: 600; }
.verdict[data-state="good"] .verdict-mark { color: var(--ok); }
.verdict[data-state="bad"] .verdict-mark { color: var(--danger); }
.verdict[data-state="idle"] .verdict-mark { color: var(--affordance); font-style: italic; font-weight: 400; }
.verdict-when { color: var(--affordance); }

/* A failing test marked where it sits, so the reader need not hold the
   failure list in their head while scrolling. One border weight. */
.test-item[data-failed="true"] {
  margin-inline: -0.4rem;
  padding-inline: 0.4rem;
  border: 1px solid var(--danger);
  border-radius: var(--r-field);
}
.test-item[data-failed="true"] .test-title { color: var(--danger); }
