/* DRIS Test Monitor - the client-facing portal and the sign-in page.
 *
 * Calm and legible first: a person who did not build the tool should be able
 * to read, at a glance, what ran, what changed, and whether it passed. Status
 * is always a word and a shape as well as a colour. The accent is the DRIS
 * display's own navy/blue family; the neutrals lean the same way.
 */

:root {
  /* Pale blue-grey ground, white cards that carry no outline of their own, a
     deep blue sidebar, and one blue accent. Text colours were chosen against
     these grounds, not against white: every one clears 4.5:1 on the page
     colour, the darkest surface any of them sits on. The sidebar's text is
     chosen against the navy, and the lighter row against the brighter blue. */
  --bg: #f3f6fb;
  --surface: #ffffff;
  --surface-2: #f6f8fc;
  --text: #0f1f3f;
  --muted: #52607f;
  --faint: #5d6a89;
  --line: #e4e9f3;
  --line-strong: #d0d8ea;
  /* Light-blue theme. #7DB7E8 and #5FA3D9 are too pale to carry white text or
     to read as text on white, so `--accent-ink` (text on an accent fill) is the
     page's dark ink and `--accent-text` (accent used as text) is a deeper
     step of the same blue. The case list is the pale blue ground with dark
     text, the active row the primary blue. */
  --accent: #7db7e8;
  --accent-bright: #5fa3d9;
  --accent-ink: #0f1f3f;
  --accent-text: #2a6aa3;
  --accent-weak: #dceeff;
  --focus: #4a93cc;
  --navy: #eff7ff;
  --navy-active: #7db7e8;
  --navy-text: #0f1f3f;
  --navy-muted: #52607f;

  --pass: #167249;  --pass-bg: #e3f6ec;
  --fail: #c1352b;  --fail-bg: #fdecea;
  --partial: #a75c08; --partial-bg: #fdf1df;
  --manual: #5a40cc; --manual-bg: #efebfc;
  --incomplete: #4b5675; --incomplete-bg: #eaeef6;
  --running: #2a6aa3; --running-bg: #dceeff;

  --shadow: 0 2px 12px rgba(20, 45, 110, .07), 0 0 1px rgba(20, 45, 110, .08);
  --radius: 10px;
  --radius-sm: 6px;
  --font: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --mono: ui-monospace, "Cascadia Mono", Consolas, "SFMono-Regular", monospace;
}

/* Light only. The palette above is the whole theme: there is deliberately no
   dark override, so the portal looks the same whatever the viewer's operating
   system prefers. `color-scheme: light` also keeps the browser's own controls
   (select arrows, form fields, the picture viewer's backdrop) light. */
:root { color-scheme: light; }

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.5 var(--font);
  font-feature-settings: "cv11", "ss01";
}
h1, h2, h3 { margin: 0; line-height: 1.25; text-wrap: balance; }
p { margin: 0; }
a { color: var(--accent-text); }
img { max-width: 100%; display: block; }
code { font-family: var(--mono); font-size: 12px; }
.visually-hidden {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}
:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: 4px; }
[hidden] { display: none !important; }

/* -- controls ---------------------------------------------------------- */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 36px; padding: 0 14px; border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong); background: var(--surface); color: var(--text);
  font: 500 14px/1 var(--font); cursor: pointer; white-space: nowrap;
}
.btn:hover { background: var(--surface-2); }
.btn svg { flex: none; }
.btn:disabled { opacity: .55; cursor: not-allowed; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.btn-primary:hover { background: var(--accent-bright); border-color: var(--accent-bright); }
.btn-quiet { border-color: transparent; background: transparent; color: var(--muted); }
.btn-quiet:hover { background: var(--surface-2); color: var(--text); }
.btn-approve { border-color: var(--pass); color: var(--pass); }
.btn-approve:hover, .btn-approve.is-on { background: var(--pass-bg); }
.btn-reject { border-color: var(--fail); color: var(--fail); }
.btn-reject:hover, .btn-reject.is-on { background: var(--fail-bg); }

.input, .select, .textarea {
  width: 100%; border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text); font: 14px/1.4 var(--font);
}
.input { height: 40px; padding: 0 12px; }
.select { height: 36px; padding: 0 30px 0 10px; }
.textarea { min-height: 58px; padding: 8px 10px; resize: vertical; }
.field { display: grid; gap: 6px; }
.field-label { font-size: 12px; font-weight: 600; color: var(--muted); letter-spacing: .02em; }

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

.login-body { min-height: 100vh; display: grid; place-items: center; padding: 24px 16px; }
.login { width: 100%; max-width: 380px; display: grid; gap: 20px; }
.login-brand { display: flex; align-items: center; gap: 12px; justify-content: center; }
.login-card { padding: 28px; display: grid; gap: 16px; }
.login-title { font-size: 22px; font-weight: 700; }
.login-lede { color: var(--muted); margin-top: -8px; }
.login-error {
  padding: 10px 12px; border-radius: var(--radius-sm);
  background: var(--fail-bg); color: var(--fail); font-weight: 500;
}
.login-notice {
  padding: 10px 12px; border-radius: var(--radius-sm);
  background: var(--accent-weak); color: var(--text); font-size: 13px;
}
.login-submit { height: 42px; margin-top: 4px; }
.login-foot { text-align: center; color: var(--faint); font-size: 12px; }

/* -- frame ------------------------------------------------------------- */
/*
 * The window is the page. `body` is a column - top bar, alerts, the three
 * working columns, then the dock - and only the regions inside the middle row
 * scroll. Nothing is hidden to achieve that: a five-case run with evidence is
 * roughly 6000px of material, so the choice is between regions that scroll and
 * data that disappears, and the data stays.
 */

/* Only the portal's own body becomes the fixed shell. The sign-in page keeps a
   normal-flow body - it is a centred card that must be free to grow past the
   fold on a short window. `html` can take the height unconditionally: a body
   taller than it simply overflows, which is what the viewport scrolls. Stated
   this way rather than with :has(), so an engine without it degrades to a
   scrolling page instead of a shell that does not fill the window. */
html { height: 100%; }
.portal-body {
  height: 100%; overflow: hidden;
  display: flex; flex-direction: column; min-height: 0;
}

/* No scrollbars anywhere in the portal.
 *
 * The regions still scroll - wheel, trackpad, touch, and the keyboard once
 * focus is inside them - only the bar itself is not drawn. Written as a
 * blanket rule rather than listed per region so a scroller added later is
 * covered without anyone remembering to. Scoped to the portal body, so the
 * sign-in page is untouched.
 *
 * Worth knowing: on Windows the bar is also what tells a reader there is more
 * below. Without it that cue is gone, which is the trade this makes.
 */
.portal-body,
.portal-body * {
  scrollbar-width: none;        /* Firefox */
  -ms-overflow-style: none;     /* legacy Edge */
}
.portal-body::-webkit-scrollbar,
.portal-body *::-webkit-scrollbar {
  width: 0;
  height: 0;
  display: none;                /* Chrome, Edge, Safari */
}

/* -- top bar ----------------------------------------------------------- */
/* No bottom rule: the bar's own white against the pale page separates it. */

.topbar { flex: none; position: relative; z-index: 20; background: var(--surface); }
.topbar-inner {
  margin: 0 auto; padding: 10px 28px; width: 100%;
  display: flex; align-items: center; gap: 20px;
}
.brand { display: flex; align-items: center; gap: 14px; color: var(--text); text-decoration: none; }
.brand-mark {
  width: 42px; height: 42px; border-radius: 10px; display: grid; place-items: center;
  background: linear-gradient(135deg, #7db7e8, #5fa3d9); color: var(--accent-ink); flex: none;
}
.brand-mark svg { width: 22px; height: 22px; }
.brand-name { display: block; font-weight: 700; font-size: 18px; letter-spacing: -.01em; line-height: 1.25; }
.brand-sub { display: block; font-size: 14px; color: var(--muted); }

.topbar-run { position: relative; margin-left: auto; min-width: 0; }
.topbar-run .select { width: 400px; max-width: 100%; height: 44px; padding: 0 36px 0 44px; font-size: 14px;
                      border-radius: 8px; }
.run-file { position: absolute; left: 15px; top: 50%; transform: translateY(-50%); color: var(--text);
            pointer-events: none; }

.topbar-user { display: flex; align-items: center; gap: 20px; }
.who { font-size: 14px; color: var(--muted); white-space: nowrap; }
.who strong { color: var(--text); font-weight: 600; }
.admin-link {
  display: inline-flex; align-items: center; gap: 8px; color: var(--text);
  font-size: 14px; font-weight: 500; text-decoration: none; white-space: nowrap;
}
.admin-link:hover { color: var(--accent-text); }
.avatar {
  width: 36px; height: 36px; border-radius: 50%; display: grid; place-items: center;
  background: var(--accent); color: var(--accent-ink); font-weight: 700; font-size: 15px;
}
.account { position: relative; }
.avatar-btn {
  display: inline-flex; align-items: center; gap: 6px; padding: 0 4px 0 0; border: 0;
  background: transparent; color: var(--muted); cursor: pointer; border-radius: 999px;
}
.avatar-btn:hover { color: var(--text); }
.menu {
  position: absolute; right: 0; top: calc(100% + 8px); min-width: 170px; padding: 6px;
  background: var(--surface); border-radius: var(--radius); box-shadow: 0 10px 30px rgba(20, 45, 110, .18),
  0 0 1px rgba(20, 45, 110, .2); z-index: 30;
}
.menu-item {
  display: block; width: 100%; padding: 10px 12px; border: 0; border-radius: 6px; background: transparent;
  color: var(--text); font: 500 14px/1.2 var(--font); text-align: left; cursor: pointer;
}
.menu-item:hover { background: var(--surface-2); }

/* -- page -------------------------------------------------------------- */

.page { flex: 1; min-height: 0; margin: 0; width: 100%; padding: 0; display: flex; flex-direction: column; }
/* Slots with nothing to say take no room - an empty one would still add a gap. */
.page > div:empty, .detail > div:empty, .aside > div:empty,
.alerts:empty, .alerts > div:empty { display: none; }
.alerts { flex: none; }
.alerts > div:not(:empty) { margin: 12px 16px 0; }
.empty { padding: 48px 0; text-align: center; color: var(--muted); }

/* Cards carry no outline; the soft shadow separates them from the page. The
   transparent border keeps every card exactly the size it would otherwise be. */
.card {
  background: var(--surface); border: 1px solid transparent;
  border-radius: var(--radius); box-shadow: var(--shadow);
}
.section { padding: 18px 20px; display: grid; gap: 14px; }

/* A section opens with an icon, a title, and a line of explanation. */
.section-head { display: flex; align-items: flex-start; gap: 14px; }
.sec-icon { flex: none; color: var(--accent); margin-top: 1px; }
.sec-text { min-width: 0; flex: 1; }
.sec-line { display: flex; align-items: baseline; gap: 6px 14px; flex-wrap: wrap; }
.section-title { font-size: 17px; font-weight: 700; letter-spacing: -.005em; }
.section-note { font-size: 13.5px; color: var(--muted); }
p.section-note { margin-top: 2px; }
.sec-end { margin-left: auto; align-self: center; text-align: right; }
.eyebrow {
  font-size: 12px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase;
  color: var(--faint);
}

/* -- status ------------------------------------------------------------ */

.badge {
  display: inline-flex; align-items: center; gap: 7px; height: 26px; padding: 0 12px;
  border-radius: 999px; font-size: 13px; font-weight: 600; white-space: nowrap;
  background: var(--incomplete-bg); color: var(--incomplete);
}
.badge-lg { height: 36px; padding: 0 18px; font-size: 14px; }
.badge svg { flex: none; }
.dot { width: 8px; height: 8px; border-radius: 50%; background: currentColor; flex: none; }
.s-PASS { background: var(--pass-bg); color: var(--pass); }
.s-FAIL { background: var(--fail-bg); color: var(--fail); }
.s-PARTIAL { background: var(--partial-bg); color: var(--partial); }
.s-MANUAL { background: var(--manual-bg); color: var(--manual); }
.s-INCOMPLETE { background: var(--incomplete-bg); color: var(--incomplete); }
.s-RUNNING { background: var(--running-bg); color: var(--running); }
.s-WAITING { background: transparent; color: var(--faint); box-shadow: inset 0 0 0 1px var(--line-strong); }

.pulse {
  width: 8px; height: 8px; border-radius: 50%; background: currentColor;
  animation: pulse 1.6s ease-in-out infinite;
}
@keyframes pulse { 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) { .pulse { animation: none; } }

/* -- the progress dock ------------------------------------------------- */
/*
 * The run's status, progress, tally and downloads, along the foot of the
 * window: a flat white bar, edge to edge, lit from above by a soft shadow. It
 * is a row of the page rather than something floating over it - with a page
 * that does not scroll there is nothing for it to float above, and occupying
 * its own row means it can never cover a control.
 */

.dock { flex: none; position: relative; z-index: 10; background: var(--surface);
        box-shadow: 0 -6px 20px rgba(20, 45, 110, .08); }
.dock .run-strip {
  padding: 12px 28px; border-radius: 0; box-shadow: none; background: transparent;
  display: grid; gap: 10px 24px; align-items: center;
  grid-template-columns: auto minmax(0, 1fr) minmax(280px, 1.1fr) auto auto;
}
.run-icon { color: var(--accent); display: grid; place-items: center; }
.run-heading { min-width: 0; }
.run-title { font-size: 17px; font-weight: 700; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* Wraps rather than ellipsising. Some of these test files are named
   Dashboard_Automation_30_Test_Cases_No_Colours.csv, and a dock that hides
   which file ran would be withholding the one fact the line exists to give.
   The dock takes the height it needs; the columns above give it up. */
.run-meta { color: var(--muted); font-size: 13.5px; overflow-wrap: anywhere; }
.run-progress { display: grid; gap: 6px; min-width: 0; padding-left: 24px; border-left: 1px solid var(--line); }
.progress-label { display: flex; justify-content: space-between; gap: 10px; font-size: 13.5px; color: var(--muted); }
.progress-label strong { color: var(--text); font-variant-numeric: tabular-nums; font-size: 15px; }
.progress { height: 8px; border-radius: 999px; background: var(--line); overflow: hidden; }
.progress > span { display: block; height: 100%; border-radius: inherit; background: var(--accent);
                   transition: width .4s ease; }
.counts { display: flex; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.count { font-variant-numeric: tabular-nums; height: 32px; }
.links { display: flex; gap: 12px; flex-wrap: wrap; justify-content: flex-end; }
.dock .btn { height: 40px; }
.dock .form-error { margin-top: 2px; }

/* -- layout ------------------------------------------------------------ */
/*
 * Three columns, each its own scroll region: the cases to choose from, the
 * chosen case's evidence, and the context and actions that belong to it. The
 * case list is a flush navy sidebar; the other two sit on the page.
 */

.layout {
  flex: 1; min-height: 0;
  display: grid; grid-template-columns: 290px minmax(0, 1fr) 410px; gap: 0;
  align-items: stretch;
}

.case-list {
  min-height: 0; overflow-y: auto; display: block; padding: 18px 10px; border-radius: 0;
  background: var(--navy); box-shadow: inset -1px 0 0 var(--line); border: 0;
}
.case-list-head { padding: 6px 14px 14px; position: sticky; top: 0; background: var(--navy); z-index: 1; }
.case-list .eyebrow { color: var(--navy-muted); font-size: 13.5px; letter-spacing: .14em; }
.case-item {
  display: grid; grid-template-columns: 28px minmax(0, 1fr); gap: 10px; align-items: start;
  width: 100%; text-align: left; padding: 13px 10px; margin-bottom: 6px; border-radius: 10px; border: 0;
  background: transparent; color: var(--navy-text); font: inherit; cursor: pointer;
}
.case-item:hover { background: rgba(125, 183, 232, .18); }
/* The active case is lit in the primary blue with a hairline edge. Drawn with
   an inset shadow so the row does not change size. */
.case-item[aria-current="true"] {
  background: var(--navy-active); box-shadow: inset 0 0 0 1px rgba(95, 163, 217, .7);
}
/* Status as a solid disc with a white glyph. The fills are deep enough that the
   glyph keeps 3:1 against them; the word under the title says it too, so colour
   is never the only carrier. */
.case-dot {
  width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center;
  font-size: 11px; font-weight: 700; color: #fff; background: rgba(255, 255, 255, .22);
}
.case-dot svg { width: 14px; height: 14px; }
.case-dot.s-PASS { background: #1f9d63; }
.case-dot.s-FAIL { background: #dc4538; }
.case-dot.s-PARTIAL { background: #cf7a0c; }
.case-dot.s-MANUAL { background: #7b61ff; }
.case-dot.s-INCOMPLETE { background: var(--incomplete); }
.case-dot.s-RUNNING { background: #3f86bf; }
.case-dot.s-WAITING { background: transparent; color: var(--faint); box-shadow: inset 0 0 0 1px rgba(82, 96, 127, .5); }
.case-name { display: block; font-weight: 600; font-size: 14px; line-height: 1.35; color: var(--navy-text);
             white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.case-sub { display: block; font-size: 13.5px; color: var(--navy-muted); margin-top: 3px; }
.case-item[aria-current="true"] .case-sub { color: #1c3556; }
.case-picker { display: none; }

/* The head and the review prompt stay put; the evidence below them scrolls, so
   which case you are reading is never scrolled off the screen. */
.detail {
  display: grid; gap: 14px; min-width: 0; min-height: 0; padding: 13px 16px 0 15px;
  grid-template-rows: auto auto auto minmax(0, 1fr);
}
/* The bottom padding is for the cards' shadows, which a scroll box would clip. */
.detail-scroll {
  min-height: 0; overflow-y: auto; display: grid; gap: 14px; align-content: start; padding: 2px 2px 18px;
  margin: -2px -2px 0;
}
.aside {
  min-height: 0; overflow-y: auto; display: grid; gap: 14px; align-content: start; padding: 13px 15px 18px 0;
}
.case-head { padding: 18px 22px; display: grid; gap: 10px; }
.case-title-row { display: flex; gap: 12px; align-items: center; justify-content: space-between; flex-wrap: wrap; }
.case-title { font-size: 22px; font-weight: 700; letter-spacing: -.01em; overflow-wrap: anywhere; }
.outcome { font-size: 15px; max-width: 80ch; }
.outcome.o-FAIL { color: var(--fail); }
.now {
  display: flex; gap: 10px; align-items: center; padding: 10px 14px; border-radius: 8px;
  background: var(--running-bg); color: var(--running);
}
.now-text { color: var(--text); }
.now-text strong { color: var(--running); font-weight: 600; }
.callout {
  display: flex; gap: 12px; align-items: center; justify-content: space-between; flex-wrap: wrap;
  padding: 12px 16px; border-radius: 10px; background: var(--partial-bg); color: var(--text);
  border: 1px solid color-mix(in srgb, var(--partial) 30%, transparent);
}

/* -- screens ----------------------------------------------------------- */
/* Two panels with an arrow between them: before, then after. */

.screens { display: flex; align-items: stretch; gap: 14px; }
.screen {
  flex: 1; min-width: 0; margin: 0; display: grid; gap: 10px; align-content: start;
  padding: 14px; border-radius: 10px; background: var(--surface-2);
}
.screen-head { display: flex; align-items: center; gap: 10px; }
.screen-icon { color: var(--accent); flex: none; }
.screen-title { font-weight: 700; font-size: 14.5px; }
.screen-when { margin-left: auto; font-size: 13px; color: var(--muted); font-variant-numeric: tabular-nums; }
.screen-arrow { flex: none; width: 36px; display: flex; flex-direction: column; align-items: center; gap: 8px; }
.screen-arrow::before, .screen-arrow::after { content: ""; flex: 1; width: 1px; background: var(--line-strong); }
.screen-arrow svg {
  width: 36px; height: 36px; padding: 9px; border-radius: 50%; background: var(--surface);
  border: 1px solid var(--line-strong); color: var(--accent); flex: none;
}
.shot {
  border-radius: 8px; overflow: hidden; border: 1px solid var(--line); background: var(--surface);
  cursor: zoom-in; padding: 6px; display: block; width: 100%;
}
/* Capped rather than cropped: the panel is wide and short, and at full column
   width two of them ate half the window. The whole picture is still here, and
   clicking it opens it full size in the viewer. */
.shot img { width: auto; max-width: 100%; max-height: 210px; margin-inline: auto; image-rendering: pixelated; }
.shot-empty {
  min-height: 96px; display: grid; place-items: center; text-align: center; padding: 12px;
  border-radius: 8px; border: 1px dashed var(--line-strong); color: var(--faint); font-size: 13.5px;
  background: var(--surface);
}
.screen-rows { font-size: 13.5px; color: var(--muted); }

/* -- changes ----------------------------------------------------------- */
/* Two labelled rows. When a row has a table in it the label sits above, so the
   table keeps the full width it needs. */

.change-rows { border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
.change-row { display: grid; grid-template-columns: 220px minmax(0, 1fr); }
.change-row + .change-row { border-top: 1px solid var(--line); }
.change-label {
  display: flex; align-items: center; gap: 12px; padding: 16px 18px; font-weight: 600;
  background: #fafbfe; border-right: 1px solid var(--line);
}
.change-label svg { color: var(--text); flex: none; }
.change-body { padding: 16px 18px; display: grid; gap: 10px; min-width: 0; align-content: center; }
.change-row.has-table { grid-template-columns: minmax(0, 1fr); }
.change-row.has-table .change-label { border-right: 0; border-bottom: 1px solid var(--line); padding: 11px 18px; }
.change-row.has-table .change-body { padding: 6px 12px 12px; }

.table-wrap { overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; font-size: 13.5px; font-variant-numeric: tabular-nums; }
.table th { text-align: left; font-weight: 600; font-size: 12px; color: var(--muted);
            text-transform: uppercase; letter-spacing: .04em;
            padding: 8px 8px; border-bottom: 1px solid var(--line-strong); white-space: nowrap; }
.table td { padding: 8px 8px; border-bottom: 1px solid var(--line); vertical-align: top; }
.table tr:last-child td { border-bottom: 0; }
.arrow { color: var(--faint); padding: 0 4px; }
.yes { color: var(--pass); font-weight: 600; }
.no { color: var(--fail); font-weight: 600; }

/* A setting's stage, live. Moving stages pulse; the row they are on is tinted
   so a change in progress is found at a glance. */
.chg { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; white-space: nowrap; }
.chg-changing, .chg-restarting, .chg-restoring { color: var(--running); }
.chg-active { color: var(--text); }
.chg-restored { color: var(--pass); }
.chg-kept, .chg-failed { color: var(--fail); }
.row-live td { background: var(--running-bg); }
.muted { color: var(--muted); }

/* -- steps ------------------------------------------------------------- */
/* Each step is a card that opens and shuts. */

.steps { display: grid; gap: 12px; }
.step { border: 1px solid var(--line); border-radius: 10px; overflow: hidden; background: var(--surface); }
.step-head {
  display: flex; align-items: center; gap: 12px; padding: 14px 18px; cursor: pointer;
  background: var(--surface-2); list-style: none;
}
.step-head::-webkit-details-marker { display: none; }
.step[open] > .step-head { border-bottom: 1px solid var(--line); }
.step-num { font-weight: 700; font-size: 16px; }
.step-chev { margin-left: auto; color: var(--text); transition: transform .15s; }
.step[open] .step-chev { transform: rotate(180deg); }
.step-body { padding: 0 0 14px; display: grid; gap: 0; }
.pairs { display: grid; grid-template-columns: 150px minmax(0, 1fr); gap: 0; margin: 0; }
.pairs dt, .pairs dd { padding: 14px 18px; margin: 0; }
.pairs dt { color: var(--text); font-size: 14px; font-weight: 600; }
.pairs dd { color: var(--muted); font-size: 14px; }
/* dt and dd are flat siblings, two to a row: everything past the first pair
   gets a rule above it. */
.pairs > :nth-child(n + 3) { border-top: 1px solid var(--line); }
.step-body > .check, .step-body > .manual-note, .step-body > p { margin: 10px 18px 0; }
.check { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 14px; align-items: start;
         padding: 12px 14px; border-radius: 10px; background: var(--surface-2); }
.check-lines { display: grid; gap: 6px; }
.check-line { display: grid; grid-template-columns: 130px minmax(0, 1fr); gap: 12px; font-size: 14px; }
.check-line span:first-child { color: var(--muted); font-size: 13px; font-weight: 600; }
.check-thumb { width: 140px; display: grid; gap: 6px; align-content: start; }
.check-thumb .part { margin: 0; display: grid; gap: 2px; }
.check-thumb .part figcaption { font-size: 12px; }
.manual-note { font-size: 14px; padding: 12px 14px; border-radius: 10px;
               background: var(--manual-bg); color: var(--text); }

/* -- review ------------------------------------------------------------ */

.review-actions { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.review-actions .btn { height: 44px; padding: 0 10px; font-weight: 600; border-width: 1.5px; }
.btn-save { border-color: var(--accent); color: var(--accent-text); }
.btn-save:hover { background: var(--accent-weak); }
.history { display: grid; gap: 8px; margin: 0; padding: 0; list-style: none; }
.history li { display: grid; gap: 2px; padding: 10px 12px; border-radius: 10px; background: var(--surface-2); }
.history-head { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; font-size: 14px; }
.history-when { color: var(--muted); font-size: 13px; }
.form-error { color: var(--fail); font-size: 14px; }
.form-ok { color: var(--pass); font-size: 14px; }

/* -- activity ---------------------------------------------------------- */
/* A timeline: a dot per line, joined by a thin rule. The dot takes the line's
   tone, so a failure is a red dot as well as red text. */

.log { margin: 0; padding: 0; list-style: none; display: grid; gap: 0;
       max-height: 260px; overflow-y: auto; font-size: 14px; }
.log li {
  position: relative; display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: 16px;
  padding: 12px 0 12px 28px; border-bottom: 1px solid var(--line);
}
.log li::before {
  content: ""; position: absolute; left: 1px; top: 17px; width: 12px; height: 12px; border-radius: 50%;
  background: var(--accent-bright); z-index: 1;
}
.log li::after {
  content: ""; position: absolute; left: 6px; top: 29px; bottom: -17px; width: 2px; background: var(--line-strong);
}
.log li:last-child::after { display: none; }
.log li.tone-good::before { background: #1f9d63; }
.log li.tone-bad::before { background: #dc4538; }
.log li:last-child { border-bottom: 0; }
/* Case names are underscore_joined and have no space to break at, so without
   this the whole log scrolls sideways inside the side column. */
.log li > span { overflow-wrap: anywhere; }
.log time { color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.log .t-good { color: var(--pass); }
.log .t-bad { color: var(--fail); }

/* -- technical --------------------------------------------------------- */

.tech summary { cursor: pointer; font-weight: 700; font-size: 16px; list-style: none;
                display: flex; align-items: center; gap: 14px; }
.tech summary::-webkit-details-marker { display: none; }
.tech-chev { margin-left: auto; color: var(--text); transition: transform .15s; }
.tech[open] .tech-chev { transform: rotate(180deg); }
.tech-body { display: grid; gap: 12px; margin-top: 12px; }
.tech pre {
  margin: 0; padding: 10px 12px; border-radius: 8px; background: var(--surface-2);
  border: 1px solid var(--line); font: 12px/1.5 var(--mono); white-space: pre-wrap;
  overflow-wrap: anywhere; max-height: 200px; overflow-y: auto;
}

/* -- picture viewer ---------------------------------------------------- */

.viewer {
  border: 0; padding: 16px; border-radius: 12px; background: var(--surface); color: var(--text);
  max-width: min(960px, calc(100vw - 32px)); box-shadow: 0 20px 60px rgba(0, 0, 0, .35);
}
.viewer::backdrop { background: rgba(13, 19, 30, .6); }
.viewer img { width: 100%; image-rendering: pixelated; border-radius: 6px; }
.viewer-close-row { display: flex; justify-content: flex-end; margin: -4px -4px 8px 0; }
.viewer-caption { margin-top: 10px; color: var(--muted); font-size: 13px; }

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

/* Three columns need about 1280px. Below that the case list folds into the
   select that already exists for it, and the two remaining columns keep the
   window-height layout. */
@media (max-width: 1279px) {
  .layout { grid-template-columns: minmax(0, 1fr) 350px; }
  .case-list { display: none; }
  .case-picker { display: block; }
  .topbar-run .select { width: 320px; }
  .dock .run-strip { grid-template-columns: auto minmax(0, 1fr) minmax(220px, 1fr) auto; }
  .dock .counts { display: none; }
}

/* Narrower than this there is no honest way to hold five cases of evidence in
   one screen, so the shell is given up rather than the data: the page returns
   to one scrolling column, with the dock as a sticky footer. */
@media (max-width: 1023px) {
  html:has(.portal-body) { height: auto; }
  /* Here the body hands scrolling back to the viewport, whose bar is drawn
     from `html` and so is out of reach of the .portal-body rule above. Scoped
     with :has() to leave the sign-in page its own scrollbar; if an engine does
     not support it the bar simply stays, which costs nothing but tidiness. */
  html:has(.portal-body) { scrollbar-width: none; -ms-overflow-style: none; }
  html:has(.portal-body)::-webkit-scrollbar { width: 0; height: 0; display: none; }
  .portal-body { height: auto; overflow: visible; display: block; }
  .page { display: block; }
  .layout { display: block; min-height: 0; }
  .detail { display: block; padding: 12px 14px 0; }
  .detail-scroll, .aside { display: block; overflow: visible; min-height: 0; padding: 0; margin: 0; }
  .aside { padding: 0 14px 14px; }
  .detail > div, .detail-scroll > div, .aside > div { margin-bottom: 12px; }
  .dock { position: sticky; bottom: 0; }
  .dock .run-strip { grid-template-columns: minmax(0, 1fr); padding: 12px 16px; }
  .run-icon { display: none; }
  .run-progress { padding-left: 0; border-left: 0; }
  .counts, .links { justify-content: flex-start; }
  .dock .counts { display: flex; }
  .topbar-inner { padding: 10px 16px; flex-wrap: wrap; }
  .screens { flex-wrap: wrap; }
  .screen { flex-basis: 220px; }
  .screen-arrow { display: none; }
}

@media (max-width: 640px) {
  .topbar-run { order: 3; width: 100%; margin-left: 0; }
  .topbar-run .select { width: 100%; }
  .topbar-user { margin-left: auto; }
  .brand-sub { display: none; }
  .who { display: none; }
  .pairs, .check-line { grid-template-columns: minmax(0, 1fr); gap: 2px; }
  .change-row { grid-template-columns: minmax(0, 1fr); }
  .change-label { border-right: 0; border-bottom: 1px solid var(--line); }
  .check { grid-template-columns: minmax(0, 1fr); }
  .check-thumb { width: 100%; max-width: 260px; }
  .shot img { max-height: none; }
  .review-actions { grid-template-columns: minmax(0, 1fr); }
}

/* -- scale ------------------------------------------------------------- */
/* The whole portal, sign-in included, is drawn at 90%. */
html { zoom: .9; }

/* -- glass ------------------------------------------------------------- */
/*
 * Frosted surfaces over a soft light-blue ground: translucent white, a blur of
 * whatever is behind, a bright hairline edge and a gentle shadow. Only colours,
 * borders and shadows change - no sizes, so nothing moves. Where the browser
 * cannot blur, or the viewer has asked for less transparency, none of this
 * applies and the solid surfaces above stand as they were.
 */
@media (prefers-reduced-transparency: no-preference) {
  @supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    :root {
      --glass: rgba(255, 255, 255, .42);
      --glass-soft: rgba(255, 255, 255, .32);
      --glass-edge: rgba(255, 255, 255, .9);
      --glass-shadow: 0 6px 22px rgba(31, 70, 120, .12);
      --glass-blur: blur(22px) saturate(190%);
    }

    body {
      background:
        radial-gradient(900px 600px at 4% -6%, rgba(95, 163, 217, .72), transparent 66%),
        radial-gradient(720px 520px at 98% 24%, rgba(95, 163, 217, .46), transparent 64%),
        radial-gradient(640px 460px at 38% 52%, rgba(125, 183, 232, .30), transparent 66%),
        radial-gradient(780px 560px at 56% 112%, rgba(125, 183, 232, .58), transparent 66%),
        var(--bg);
      background-attachment: fixed;
    }

    .card {
      background: var(--glass); border-color: var(--glass-edge);
      -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
      background-image: linear-gradient(160deg, rgba(255, 255, 255, .5), rgba(255, 255, 255, 0) 55%);
      box-shadow: var(--glass-shadow), inset 0 1px 0 rgba(255, 255, 255, .95),
                  inset 0 0 0 1px rgba(255, 255, 255, .25);
    }
    .topbar {
      background: var(--glass);
      -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
      box-shadow: 0 1px 0 var(--glass-edge), 0 4px 18px rgba(31, 70, 120, .06);
    }
    .dock {
      background: var(--glass);
      -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
      box-shadow: 0 -1px 0 var(--glass-edge), 0 -6px 20px rgba(31, 70, 120, .07);
    }
    .case-list {
      background: rgba(239, 247, 255, .4);
      -webkit-backdrop-filter: var(--glass-blur); backdrop-filter: var(--glass-blur);
      box-shadow: inset -1px 0 0 var(--glass-edge);
    }
    .case-list-head { background: rgba(239, 247, 255, .7); }
    .menu {
      background: rgba(255, 255, 255, .78); border: 1px solid var(--glass-edge);
      -webkit-backdrop-filter: blur(18px) saturate(170%); backdrop-filter: blur(18px) saturate(170%);
    }
    .viewer {
      background: rgba(255, 255, 255, .82);
      -webkit-backdrop-filter: blur(18px) saturate(160%); backdrop-filter: blur(18px) saturate(160%);
    }
    .viewer::backdrop {
      background: rgba(13, 31, 63, .45);
      -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
    }

    /* Surfaces that sit inside a card go lighter-on-glass rather than solid. */
    .screen, .check, .history li, .step-head, .tech pre { background: var(--glass-soft); }
    .step, .shot, .shot-empty { background: rgba(255, 255, 255, .55); border-color: var(--glass-edge); }
    .change-label { background: var(--glass-soft); }
    .input, .select, .textarea { background: rgba(255, 255, 255, .72); }
  }
}

/* -- motion ------------------------------------------------------------ */
/*
 * Short, quiet, and only where it cannot misfire. The portal rebuilds a slot
 * whenever its data changes, so anything that animates on *arrival* would replay
 * on every live update: entrance motion is limited to elements that are built
 * once (the page, the sign-in card) or that appear on a user's action (the
 * account menu, the picture viewer). Everything else is a transition between
 * states, which a rebuilt element simply does not run.
 */

:root {
  --ease: cubic-bezier(.2, .7, .2, 1);
  interpolate-size: allow-keywords;
}

@keyframes page-in { from { opacity: 0; } }
@keyframes rise-in { from { opacity: 0; transform: translateY(8px); } }
@keyframes pop-in  { from { opacity: 0; transform: translateY(-6px) scale(.97); } }
@keyframes zoom-in { from { opacity: 0; transform: scale(.96); } }
@keyframes fade-in { from { opacity: 0; } }

.portal-body, .login-body { animation: page-in .4s var(--ease) both; }
.login-card { animation: rise-in .5s var(--ease) .05s both; }
.login-brand { animation: rise-in .5s var(--ease) both; }
.menu { transform-origin: top right; animation: pop-in .16s var(--ease) both; }
.viewer[open] { animation: zoom-in .22s var(--ease) both; }
.viewer[open]::backdrop { animation: fade-in .22s ease both; }

.btn {
  transition: background-color .18s var(--ease), border-color .18s var(--ease),
              color .18s var(--ease), box-shadow .18s var(--ease), transform .12s var(--ease);
}
.btn:active:not(:disabled) { transform: translateY(1px) scale(.985); }
.btn-primary:hover:not(:disabled) { box-shadow: 0 4px 14px rgba(95, 163, 217, .4); }
.input, .select, .textarea { transition: border-color .18s var(--ease), box-shadow .18s var(--ease); }
.input:hover, .select:hover, .textarea:hover { border-color: var(--accent-bright); }
.input:focus, .select:focus, .textarea:focus { border-color: var(--accent-bright); }
a, .admin-link, .avatar-btn { transition: color .18s var(--ease); }
.avatar { transition: box-shadow .18s var(--ease), transform .18s var(--ease); }
.avatar-btn:hover .avatar { box-shadow: 0 0 0 4px var(--accent-weak); }
.menu-item { transition: background-color .15s var(--ease); }
.case-item { transition: background-color .2s var(--ease), box-shadow .2s var(--ease); }
.step-head { transition: background-color .18s var(--ease); }
.step-head:hover { background: var(--accent-weak); }
.shot { transition: transform .2s var(--ease), box-shadow .2s var(--ease), border-color .2s var(--ease); }
.shot:hover { transform: translateY(-2px); border-color: var(--accent); box-shadow: 0 6px 18px rgba(20, 45, 110, .12); }
.shot:active { transform: translateY(0); }
.progress > span { transition: width .6s var(--ease); }

/* Step and technical-detail panels glide open and shut where the browser can
   animate a <details> (Chromium 131+); elsewhere they open instantly as before. */
@supports selector(::details-content) {
  .step::details-content, .tech::details-content {
    block-size: 0; overflow: clip;
    transition: block-size .28s var(--ease), content-visibility .28s allow-discrete;
  }
  .step[open]::details-content, .tech[open]::details-content { block-size: auto; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important; animation-delay: 0s !important;
    transition-duration: .001ms !important; transition-delay: 0s !important;
  }
}
